From f8796cf0a4259b4b24b3be8ece0e0c26e2c017d5 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 19 Nov 2021 05:13:01 -0500 Subject: Issue %3003 - Move cairo up one level - This also rewrites the moz.build file to be not insane from years of script manipulation - This also eliminates the remaining mac/uikit quartz shit as well as the BeOS junk. --- libs/cairo/AUTHORS | 99 + libs/cairo/COPYING | 11 + libs/cairo/README | 203 + libs/cairo/cairo/AUTHORS | 99 - libs/cairo/cairo/COPYING | 11 - libs/cairo/cairo/README | 203 - .../cairo/src/cairo-analysis-surface-private.h | 45 - libs/cairo/cairo/src/cairo-analysis-surface.c | 893 --- libs/cairo/cairo/src/cairo-arc-private.h | 26 - libs/cairo/cairo/src/cairo-arc.c | 260 - libs/cairo/cairo/src/cairo-array.c | 529 -- libs/cairo/cairo/src/cairo-atomic-private.h | 385 -- libs/cairo/cairo/src/cairo-atomic.c | 91 - libs/cairo/cairo/src/cairo-base64-stream.c | 110 - libs/cairo/cairo/src/cairo-base85-stream.c | 99 - .../cairo/src/cairo-bentley-ottmann-rectangular.c | 805 --- .../cairo/src/cairo-bentley-ottmann-rectilinear.c | 634 -- libs/cairo/cairo/src/cairo-bentley-ottmann.c | 2100 ------- libs/cairo/cairo/src/cairo-beos-surface.cpp | 949 --- libs/cairo/cairo/src/cairo-beos.h | 29 - libs/cairo/cairo/src/cairo-botor-scan-converter.c | 2162 ------- libs/cairo/cairo/src/cairo-boxes-private.h | 55 - libs/cairo/cairo/src/cairo-boxes.c | 271 - libs/cairo/cairo/src/cairo-cache-private.h | 111 - libs/cairo/cairo/src/cairo-cache.c | 304 - libs/cairo/cairo/src/cairo-cff-subset.c | 2246 ------- libs/cairo/cairo/src/cairo-clip-private.h | 120 - libs/cairo/cairo/src/cairo-clip.c | 1553 ----- libs/cairo/cairo/src/cairo-color.c | 178 - libs/cairo/cairo/src/cairo-combsort-private.h | 41 - libs/cairo/cairo/src/cairo-compiler-private.h | 244 - .../cairo/src/cairo-composite-rectangles-private.h | 73 - libs/cairo/cairo/src/cairo-composite-rectangles.c | 164 - libs/cairo/cairo/src/cairo-d2d-private-fx.h | 1164 ---- libs/cairo/cairo/src/cairo-d2d-private.fx | 96 - libs/cairo/cairo/src/cairo-d2d-private.h | 160 - libs/cairo/cairo/src/cairo-d2d-surface.cpp | 4835 --------------- libs/cairo/cairo/src/cairo-debug.c | 221 - libs/cairo/cairo/src/cairo-deflate-stream.c | 119 - libs/cairo/cairo/src/cairo-deprecated.h | 92 - libs/cairo/cairo/src/cairo-device-private.h | 55 - libs/cairo/cairo/src/cairo-device.c | 502 -- libs/cairo/cairo/src/cairo-directfb-surface.c | 1931 ------ libs/cairo/cairo/src/cairo-directfb.h | 35 - libs/cairo/cairo/src/cairo-drm.h | 92 - libs/cairo/cairo/src/cairo-dwrite-font.cpp | 1590 ----- libs/cairo/cairo/src/cairo-dwrite-private.h | 193 - libs/cairo/cairo/src/cairo-eagle-context.c | 147 - libs/cairo/cairo/src/cairo-error-private.h | 27 - libs/cairo/cairo/src/cairo-features-win32.h | 16 - libs/cairo/cairo/src/cairo-features.h.in | 63 - libs/cairo/cairo/src/cairo-fixed-private.h | 326 - libs/cairo/cairo/src/cairo-fixed-type-private.h | 43 - libs/cairo/cairo/src/cairo-fixed.c | 7 - libs/cairo/cairo/src/cairo-font-face-twin-data.c | 1072 ---- libs/cairo/cairo/src/cairo-font-face-twin.c | 729 --- libs/cairo/cairo/src/cairo-font-face.c | 272 - libs/cairo/cairo/src/cairo-font-options.c | 481 -- libs/cairo/cairo/src/cairo-fontconfig-private.h | 41 - libs/cairo/cairo/src/cairo-freed-pool-private.h | 97 - libs/cairo/cairo/src/cairo-freed-pool.c | 60 - libs/cairo/cairo/src/cairo-freelist-private.h | 139 - libs/cairo/cairo/src/cairo-freelist-type-private.h | 54 - libs/cairo/cairo/src/cairo-freelist.c | 191 - libs/cairo/cairo/src/cairo-ft-font.c | 3319 ----------- libs/cairo/cairo/src/cairo-ft-private.h | 41 - libs/cairo/cairo/src/cairo-ft.h | 50 - libs/cairo/cairo/src/cairo-gl-glyphs.c | 571 -- libs/cairo/cairo/src/cairo-gl-private.h | 448 -- libs/cairo/cairo/src/cairo-gl-shaders.c | 960 --- libs/cairo/cairo/src/cairo-gl-surface.c | 1601 ----- libs/cairo/cairo/src/cairo-gl.h | 90 - libs/cairo/cairo/src/cairo-glitz-private.h | 13 - libs/cairo/cairo/src/cairo-glitz-surface.c | 2424 -------- libs/cairo/cairo/src/cairo-glitz.h | 25 - libs/cairo/cairo/src/cairo-glx-context.c | 226 - libs/cairo/cairo/src/cairo-gstate-private.h | 354 -- libs/cairo/cairo/src/cairo-gstate.c | 2298 -------- libs/cairo/cairo/src/cairo-hash-private.h | 53 - libs/cairo/cairo/src/cairo-hash.c | 508 -- libs/cairo/cairo/src/cairo-hull.c | 203 - libs/cairo/cairo/src/cairo-image-info-private.h | 32 - libs/cairo/cairo/src/cairo-image-info.c | 259 - libs/cairo/cairo/src/cairo-image-surface.c | 4743 --------------- libs/cairo/cairo/src/cairo-list-private.h | 183 - libs/cairo/cairo/src/cairo-lzw.c | 372 -- libs/cairo/cairo/src/cairo-malloc-private.h | 116 - libs/cairo/cairo/src/cairo-matrix.c | 974 --- libs/cairo/cairo/src/cairo-meta-surface-private.h | 155 - libs/cairo/cairo/src/cairo-misc.c | 895 --- libs/cairo/cairo/src/cairo-mutex-impl-private.h | 244 - libs/cairo/cairo/src/cairo-mutex-list-private.h | 49 - libs/cairo/cairo/src/cairo-mutex-private.h | 31 - libs/cairo/cairo/src/cairo-mutex-type-private.h | 158 - libs/cairo/cairo/src/cairo-mutex.c | 53 - libs/cairo/cairo/src/cairo-no-features.h | 12 - libs/cairo/cairo/src/cairo-observer.c | 18 - libs/cairo/cairo/src/cairo-os2-private.h | 34 - libs/cairo/cairo/src/cairo-os2-surface.c | 1440 ----- libs/cairo/cairo/src/cairo-os2.h | 76 - libs/cairo/cairo/src/cairo-output-stream-private.h | 165 - libs/cairo/cairo/src/cairo-output-stream.c | 738 --- libs/cairo/cairo/src/cairo-paginated-private.h | 134 - .../cairo/src/cairo-paginated-surface-private.h | 31 - libs/cairo/cairo/src/cairo-paginated-surface.c | 617 -- libs/cairo/cairo/src/cairo-path-bounds.c | 318 - libs/cairo/cairo/src/cairo-path-fill.c | 433 -- libs/cairo/cairo/src/cairo-path-fixed-private.h | 134 - libs/cairo/cairo/src/cairo-path-fixed.c | 1390 ----- libs/cairo/cairo/src/cairo-path-in-fill.c | 260 - libs/cairo/cairo/src/cairo-path-private.h | 25 - libs/cairo/cairo/src/cairo-path-stroke.c | 2109 ------- libs/cairo/cairo/src/cairo-path.c | 504 -- libs/cairo/cairo/src/cairo-pattern.c | 3202 ---------- libs/cairo/cairo/src/cairo-pdf-operators-private.h | 134 - libs/cairo/cairo/src/cairo-pdf-operators.c | 1433 ----- libs/cairo/cairo/src/cairo-pdf-surface-private.h | 159 - libs/cairo/cairo/src/cairo-pdf-surface.c | 6213 -------------------- libs/cairo/cairo/src/cairo-pdf.h | 62 - libs/cairo/cairo/src/cairo-pen.c | 364 -- libs/cairo/cairo/src/cairo-platform.h | 37 - libs/cairo/cairo/src/cairo-png.c | 764 --- libs/cairo/cairo/src/cairo-polygon.c | 460 -- libs/cairo/cairo/src/cairo-private.h | 26 - libs/cairo/cairo/src/cairo-ps-surface-private.h | 73 - libs/cairo/cairo/src/cairo-ps-surface.c | 3890 ------------ libs/cairo/cairo/src/cairo-ps.h | 82 - libs/cairo/cairo/src/cairo-qt-surface.cpp | 1716 ------ libs/cairo/cairo/src/cairo-qt.h | 53 - libs/cairo/cairo/src/cairo-quartz-font.c | 811 --- libs/cairo/cairo/src/cairo-quartz-image-surface.c | 258 - libs/cairo/cairo/src/cairo-quartz-image.h | 33 - libs/cairo/cairo/src/cairo-quartz-private.h | 86 - libs/cairo/cairo/src/cairo-quartz-surface.c | 3768 ------------ libs/cairo/cairo/src/cairo-quartz.h | 81 - .../cairo/src/cairo-recording-surface-private.h | 139 - libs/cairo/cairo/src/cairo-recording-surface.c | 1099 ---- libs/cairo/cairo/src/cairo-rectangle.c | 231 - .../cairo/src/cairo-rectangular-scan-converter.c | 694 --- .../cairo/src/cairo-reference-count-private.h | 29 - libs/cairo/cairo/src/cairo-region-private.h | 37 - libs/cairo/cairo/src/cairo-region.c | 871 --- libs/cairo/cairo/src/cairo-rename.h | 411 -- libs/cairo/cairo/src/cairo-rtree-private.h | 102 - libs/cairo/cairo/src/cairo-rtree.c | 353 -- libs/cairo/cairo/src/cairo-scaled-font-private.h | 98 - .../cairo/src/cairo-scaled-font-subsets-private.h | 636 -- libs/cairo/cairo/src/cairo-scaled-font-subsets.c | 1053 ---- libs/cairo/cairo/src/cairo-scaled-font.c | 2954 ---------- libs/cairo/cairo/src/cairo-script-surface.c | 3590 ----------- libs/cairo/cairo/src/cairo-script.h | 58 - libs/cairo/cairo/src/cairo-skia.h | 52 - libs/cairo/cairo/src/cairo-slope-private.h | 39 - libs/cairo/cairo/src/cairo-slope.c | 67 - libs/cairo/cairo/src/cairo-spans-private.h | 188 - libs/cairo/cairo/src/cairo-spans.c | 322 - libs/cairo/cairo/src/cairo-spline.c | 339 -- libs/cairo/cairo/src/cairo-stroke-style.c | 279 - libs/cairo/cairo/src/cairo-supported-features.h | 25 - .../cairo/src/cairo-surface-clipper-private.h | 40 - libs/cairo/cairo/src/cairo-surface-clipper.c | 104 - .../cairo/src/cairo-surface-fallback-private.h | 105 - libs/cairo/cairo/src/cairo-surface-fallback.c | 1602 ----- .../cairo/cairo/src/cairo-surface-offset-private.h | 61 - libs/cairo/cairo/src/cairo-surface-offset.c | 309 - libs/cairo/cairo/src/cairo-surface-private.h | 70 - .../cairo/src/cairo-surface-snapshot-private.h | 17 - libs/cairo/cairo/src/cairo-surface-snapshot.c | 220 - .../cairo/src/cairo-surface-subsurface-private.h | 18 - libs/cairo/cairo/src/cairo-surface-subsurface.c | 521 -- .../cairo/src/cairo-surface-wrapper-private.h | 137 - libs/cairo/cairo/src/cairo-surface-wrapper.c | 680 --- libs/cairo/cairo/src/cairo-surface.c | 3292 ----------- libs/cairo/cairo/src/cairo-svg-surface-private.h | 38 - libs/cairo/cairo/src/cairo-svg-surface.c | 2811 --------- libs/cairo/cairo/src/cairo-svg.h | 55 - libs/cairo/cairo/src/cairo-system.c | 63 - libs/cairo/cairo/src/cairo-tee-surface-private.h | 15 - libs/cairo/cairo/src/cairo-tee-surface.c | 685 --- libs/cairo/cairo/src/cairo-tee.h | 35 - libs/cairo/cairo/src/cairo-tor-scan-converter.c | 2221 ------- libs/cairo/cairo/src/cairo-toy-font-face.c | 489 -- libs/cairo/cairo/src/cairo-traps.c | 570 -- .../cairo/src/cairo-truetype-subset-private.h | 171 - libs/cairo/cairo/src/cairo-truetype-subset.c | 1401 ----- libs/cairo/cairo/src/cairo-type1-fallback.c | 856 --- libs/cairo/cairo/src/cairo-type1-private.h | 20 - libs/cairo/cairo/src/cairo-type1-subset.c | 1403 ----- .../cairo/src/cairo-type3-glyph-surface-private.h | 55 - libs/cairo/cairo/src/cairo-type3-glyph-surface.c | 531 -- libs/cairo/cairo/src/cairo-types-private.h | 450 -- libs/cairo/cairo/src/cairo-unicode.c | 384 -- libs/cairo/cairo/src/cairo-user-font-private.h | 14 - libs/cairo/cairo/src/cairo-user-font.c | 795 --- libs/cairo/cairo/src/cairo-version.c | 210 - libs/cairo/cairo/src/cairo-version.h | 16 - libs/cairo/cairo/src/cairo-vg-surface.c | 1894 ------ libs/cairo/cairo/src/cairo-vg.h | 69 - libs/cairo/cairo/src/cairo-wideint-private.h | 288 - libs/cairo/cairo/src/cairo-wideint-type-private.h | 130 - libs/cairo/cairo/src/cairo-wideint.c | 788 --- libs/cairo/cairo/src/cairo-win32-font.c | 2319 -------- .../cairo/cairo/src/cairo-win32-printing-surface.c | 1956 ------ libs/cairo/cairo/src/cairo-win32-private.h | 218 - libs/cairo/cairo/src/cairo-win32-refptr.h | 147 - libs/cairo/cairo/src/cairo-win32-surface.c | 4056 ------------- libs/cairo/cairo/src/cairo-win32.h | 307 - libs/cairo/cairo/src/cairo-xcb-surface.c | 1332 ----- libs/cairo/cairo/src/cairo-xcb-xrender.h | 31 - libs/cairo/cairo/src/cairo-xcb.h | 62 - libs/cairo/cairo/src/cairo-xlib-display.c | 638 -- libs/cairo/cairo/src/cairo-xlib-private.h | 168 - libs/cairo/cairo/src/cairo-xlib-screen.c | 439 -- libs/cairo/cairo/src/cairo-xlib-surface-private.h | 84 - libs/cairo/cairo/src/cairo-xlib-surface.c | 4896 --------------- libs/cairo/cairo/src/cairo-xlib-visual.c | 156 - libs/cairo/cairo/src/cairo-xlib-xrender-private.h | 1138 ---- libs/cairo/cairo/src/cairo-xlib-xrender.h | 34 - libs/cairo/cairo/src/cairo-xlib.h | 68 - libs/cairo/cairo/src/cairo-xml-surface.c | 1120 ---- libs/cairo/cairo/src/cairo-xml.h | 36 - libs/cairo/cairo/src/cairo.c | 4167 ------------- libs/cairo/cairo/src/cairo.h | 2696 --------- libs/cairo/cairo/src/cairoint.h | 2554 -------- libs/cairo/cairo/src/check-has-hidden-symbols.c | 3 - libs/cairo/cairo/src/check-link.c | 24 - libs/cairo/cairo/src/filterpublic.awk | 22 - libs/cairo/cairo/src/moz.build | 266 - libs/cairo/cairo/src/pixman-rename.h | 145 - libs/cairo/cairo/src/test-fallback-surface.c | 206 - libs/cairo/cairo/src/test-fallback-surface.h | 19 - libs/cairo/cairo/src/test-meta-surface.c | 311 - libs/cairo/cairo/src/test-meta-surface.h | 19 - libs/cairo/cairo/src/test-paginated-surface.c | 260 - libs/cairo/cairo/src/test-paginated-surface.h | 17 - libs/cairo/moz.build | 199 +- libs/cairo/src/cairo-analysis-surface-private.h | 45 + libs/cairo/src/cairo-analysis-surface.c | 893 +++ libs/cairo/src/cairo-arc-private.h | 26 + libs/cairo/src/cairo-arc.c | 260 + libs/cairo/src/cairo-array.c | 529 ++ libs/cairo/src/cairo-atomic-private.h | 385 ++ libs/cairo/src/cairo-atomic.c | 91 + libs/cairo/src/cairo-base64-stream.c | 110 + libs/cairo/src/cairo-base85-stream.c | 99 + libs/cairo/src/cairo-bentley-ottmann-rectangular.c | 805 +++ libs/cairo/src/cairo-bentley-ottmann-rectilinear.c | 634 ++ libs/cairo/src/cairo-bentley-ottmann.c | 2100 +++++++ libs/cairo/src/cairo-botor-scan-converter.c | 2162 +++++++ libs/cairo/src/cairo-boxes-private.h | 55 + libs/cairo/src/cairo-boxes.c | 271 + libs/cairo/src/cairo-cache-private.h | 111 + libs/cairo/src/cairo-cache.c | 304 + libs/cairo/src/cairo-cff-subset.c | 2246 +++++++ libs/cairo/src/cairo-clip-private.h | 120 + libs/cairo/src/cairo-clip.c | 1553 +++++ libs/cairo/src/cairo-color.c | 178 + libs/cairo/src/cairo-combsort-private.h | 41 + libs/cairo/src/cairo-compiler-private.h | 244 + .../cairo/src/cairo-composite-rectangles-private.h | 73 + libs/cairo/src/cairo-composite-rectangles.c | 164 + libs/cairo/src/cairo-d2d-private-fx.h | 1164 ++++ libs/cairo/src/cairo-d2d-private.fx | 96 + libs/cairo/src/cairo-d2d-private.h | 160 + libs/cairo/src/cairo-d2d-surface.cpp | 4835 +++++++++++++++ libs/cairo/src/cairo-debug.c | 221 + libs/cairo/src/cairo-deflate-stream.c | 119 + libs/cairo/src/cairo-deprecated.h | 92 + libs/cairo/src/cairo-device-private.h | 55 + libs/cairo/src/cairo-device.c | 502 ++ libs/cairo/src/cairo-directfb-surface.c | 1931 ++++++ libs/cairo/src/cairo-directfb.h | 35 + libs/cairo/src/cairo-drm.h | 92 + libs/cairo/src/cairo-dwrite-font.cpp | 1590 +++++ libs/cairo/src/cairo-dwrite-private.h | 193 + libs/cairo/src/cairo-eagle-context.c | 147 + libs/cairo/src/cairo-error-private.h | 27 + libs/cairo/src/cairo-features-win32.h | 16 + libs/cairo/src/cairo-features.h.in | 63 + libs/cairo/src/cairo-fixed-private.h | 326 + libs/cairo/src/cairo-fixed-type-private.h | 43 + libs/cairo/src/cairo-fixed.c | 7 + libs/cairo/src/cairo-font-face-twin-data.c | 1072 ++++ libs/cairo/src/cairo-font-face-twin.c | 729 +++ libs/cairo/src/cairo-font-face.c | 272 + libs/cairo/src/cairo-font-options.c | 481 ++ libs/cairo/src/cairo-fontconfig-private.h | 41 + libs/cairo/src/cairo-freed-pool-private.h | 97 + libs/cairo/src/cairo-freed-pool.c | 60 + libs/cairo/src/cairo-freelist-private.h | 139 + libs/cairo/src/cairo-freelist-type-private.h | 54 + libs/cairo/src/cairo-freelist.c | 191 + libs/cairo/src/cairo-ft-font.c | 3319 +++++++++++ libs/cairo/src/cairo-ft-private.h | 41 + libs/cairo/src/cairo-ft.h | 50 + libs/cairo/src/cairo-gl-glyphs.c | 571 ++ libs/cairo/src/cairo-gl-private.h | 448 ++ libs/cairo/src/cairo-gl-shaders.c | 960 +++ libs/cairo/src/cairo-gl-surface.c | 1601 +++++ libs/cairo/src/cairo-gl.h | 90 + libs/cairo/src/cairo-glitz-private.h | 13 + libs/cairo/src/cairo-glitz-surface.c | 2424 ++++++++ libs/cairo/src/cairo-glitz.h | 25 + libs/cairo/src/cairo-glx-context.c | 226 + libs/cairo/src/cairo-gstate-private.h | 354 ++ libs/cairo/src/cairo-gstate.c | 2298 ++++++++ libs/cairo/src/cairo-hash-private.h | 53 + libs/cairo/src/cairo-hash.c | 508 ++ libs/cairo/src/cairo-hull.c | 203 + libs/cairo/src/cairo-image-info-private.h | 32 + libs/cairo/src/cairo-image-info.c | 259 + libs/cairo/src/cairo-image-surface.c | 4743 +++++++++++++++ libs/cairo/src/cairo-list-private.h | 183 + libs/cairo/src/cairo-lzw.c | 372 ++ libs/cairo/src/cairo-malloc-private.h | 116 + libs/cairo/src/cairo-matrix.c | 974 +++ libs/cairo/src/cairo-meta-surface-private.h | 155 + libs/cairo/src/cairo-misc.c | 895 +++ libs/cairo/src/cairo-mutex-impl-private.h | 244 + libs/cairo/src/cairo-mutex-list-private.h | 49 + libs/cairo/src/cairo-mutex-private.h | 31 + libs/cairo/src/cairo-mutex-type-private.h | 158 + libs/cairo/src/cairo-mutex.c | 53 + libs/cairo/src/cairo-no-features.h | 12 + libs/cairo/src/cairo-observer.c | 18 + libs/cairo/src/cairo-os2-private.h | 34 + libs/cairo/src/cairo-os2-surface.c | 1440 +++++ libs/cairo/src/cairo-os2.h | 76 + libs/cairo/src/cairo-output-stream-private.h | 165 + libs/cairo/src/cairo-output-stream.c | 738 +++ libs/cairo/src/cairo-paginated-private.h | 134 + libs/cairo/src/cairo-paginated-surface-private.h | 31 + libs/cairo/src/cairo-paginated-surface.c | 617 ++ libs/cairo/src/cairo-path-bounds.c | 318 + libs/cairo/src/cairo-path-fill.c | 433 ++ libs/cairo/src/cairo-path-fixed-private.h | 134 + libs/cairo/src/cairo-path-fixed.c | 1390 +++++ libs/cairo/src/cairo-path-in-fill.c | 260 + libs/cairo/src/cairo-path-private.h | 25 + libs/cairo/src/cairo-path-stroke.c | 2109 +++++++ libs/cairo/src/cairo-path.c | 504 ++ libs/cairo/src/cairo-pattern.c | 3202 ++++++++++ libs/cairo/src/cairo-pdf-operators-private.h | 134 + libs/cairo/src/cairo-pdf-operators.c | 1433 +++++ libs/cairo/src/cairo-pdf-surface-private.h | 159 + libs/cairo/src/cairo-pdf-surface.c | 6213 ++++++++++++++++++++ libs/cairo/src/cairo-pdf.h | 62 + libs/cairo/src/cairo-pen.c | 364 ++ libs/cairo/src/cairo-platform.h | 37 + libs/cairo/src/cairo-png.c | 764 +++ libs/cairo/src/cairo-polygon.c | 460 ++ libs/cairo/src/cairo-private.h | 26 + libs/cairo/src/cairo-ps-surface-private.h | 73 + libs/cairo/src/cairo-ps-surface.c | 3890 ++++++++++++ libs/cairo/src/cairo-ps.h | 82 + libs/cairo/src/cairo-recording-surface-private.h | 139 + libs/cairo/src/cairo-recording-surface.c | 1099 ++++ libs/cairo/src/cairo-rectangle.c | 231 + libs/cairo/src/cairo-rectangular-scan-converter.c | 694 +++ libs/cairo/src/cairo-reference-count-private.h | 29 + libs/cairo/src/cairo-region-private.h | 37 + libs/cairo/src/cairo-region.c | 871 +++ libs/cairo/src/cairo-rename.h | 411 ++ libs/cairo/src/cairo-rtree-private.h | 102 + libs/cairo/src/cairo-rtree.c | 353 ++ libs/cairo/src/cairo-scaled-font-private.h | 98 + libs/cairo/src/cairo-scaled-font-subsets-private.h | 636 ++ libs/cairo/src/cairo-scaled-font-subsets.c | 1053 ++++ libs/cairo/src/cairo-scaled-font.c | 2954 ++++++++++ libs/cairo/src/cairo-script-surface.c | 3590 +++++++++++ libs/cairo/src/cairo-script.h | 58 + libs/cairo/src/cairo-skia.h | 52 + libs/cairo/src/cairo-slope-private.h | 39 + libs/cairo/src/cairo-slope.c | 67 + libs/cairo/src/cairo-spans-private.h | 188 + libs/cairo/src/cairo-spans.c | 322 + libs/cairo/src/cairo-spline.c | 339 ++ libs/cairo/src/cairo-stroke-style.c | 279 + libs/cairo/src/cairo-supported-features.h | 25 + libs/cairo/src/cairo-surface-clipper-private.h | 40 + libs/cairo/src/cairo-surface-clipper.c | 104 + libs/cairo/src/cairo-surface-fallback-private.h | 105 + libs/cairo/src/cairo-surface-fallback.c | 1602 +++++ libs/cairo/src/cairo-surface-offset-private.h | 61 + libs/cairo/src/cairo-surface-offset.c | 309 + libs/cairo/src/cairo-surface-private.h | 70 + libs/cairo/src/cairo-surface-snapshot-private.h | 17 + libs/cairo/src/cairo-surface-snapshot.c | 220 + libs/cairo/src/cairo-surface-subsurface-private.h | 18 + libs/cairo/src/cairo-surface-subsurface.c | 521 ++ libs/cairo/src/cairo-surface-wrapper-private.h | 137 + libs/cairo/src/cairo-surface-wrapper.c | 680 +++ libs/cairo/src/cairo-surface.c | 3292 +++++++++++ libs/cairo/src/cairo-svg-surface-private.h | 38 + libs/cairo/src/cairo-svg-surface.c | 2811 +++++++++ libs/cairo/src/cairo-svg.h | 55 + libs/cairo/src/cairo-system.c | 63 + libs/cairo/src/cairo-tee-surface-private.h | 15 + libs/cairo/src/cairo-tee-surface.c | 685 +++ libs/cairo/src/cairo-tee.h | 35 + libs/cairo/src/cairo-tor-scan-converter.c | 2221 +++++++ libs/cairo/src/cairo-toy-font-face.c | 489 ++ libs/cairo/src/cairo-traps.c | 570 ++ libs/cairo/src/cairo-truetype-subset-private.h | 171 + libs/cairo/src/cairo-truetype-subset.c | 1401 +++++ libs/cairo/src/cairo-type1-fallback.c | 856 +++ libs/cairo/src/cairo-type1-private.h | 20 + libs/cairo/src/cairo-type1-subset.c | 1403 +++++ libs/cairo/src/cairo-type3-glyph-surface-private.h | 55 + libs/cairo/src/cairo-type3-glyph-surface.c | 531 ++ libs/cairo/src/cairo-types-private.h | 450 ++ libs/cairo/src/cairo-unicode.c | 384 ++ libs/cairo/src/cairo-user-font-private.h | 14 + libs/cairo/src/cairo-user-font.c | 795 +++ libs/cairo/src/cairo-version.c | 210 + libs/cairo/src/cairo-version.h | 16 + libs/cairo/src/cairo-vg-surface.c | 1894 ++++++ libs/cairo/src/cairo-vg.h | 69 + libs/cairo/src/cairo-wideint-private.h | 288 + libs/cairo/src/cairo-wideint-type-private.h | 130 + libs/cairo/src/cairo-wideint.c | 788 +++ libs/cairo/src/cairo-win32-font.c | 2319 ++++++++ libs/cairo/src/cairo-win32-printing-surface.c | 1956 ++++++ libs/cairo/src/cairo-win32-private.h | 218 + libs/cairo/src/cairo-win32-refptr.h | 147 + libs/cairo/src/cairo-win32-surface.c | 4056 +++++++++++++ libs/cairo/src/cairo-win32.h | 307 + libs/cairo/src/cairo-xcb-surface.c | 1332 +++++ libs/cairo/src/cairo-xcb-xrender.h | 31 + libs/cairo/src/cairo-xcb.h | 62 + libs/cairo/src/cairo-xlib-display.c | 638 ++ libs/cairo/src/cairo-xlib-private.h | 168 + libs/cairo/src/cairo-xlib-screen.c | 439 ++ libs/cairo/src/cairo-xlib-surface-private.h | 84 + libs/cairo/src/cairo-xlib-surface.c | 4896 +++++++++++++++ libs/cairo/src/cairo-xlib-visual.c | 156 + libs/cairo/src/cairo-xlib-xrender-private.h | 1138 ++++ libs/cairo/src/cairo-xlib-xrender.h | 34 + libs/cairo/src/cairo-xlib.h | 68 + libs/cairo/src/cairo-xml-surface.c | 1120 ++++ libs/cairo/src/cairo-xml.h | 36 + libs/cairo/src/cairo.c | 4167 +++++++++++++ libs/cairo/src/cairo.h | 2696 +++++++++ libs/cairo/src/cairoint.h | 2554 ++++++++ libs/cairo/src/check-has-hidden-symbols.c | 3 + libs/cairo/src/check-link.c | 24 + libs/cairo/src/filterpublic.awk | 22 + libs/cairo/src/pixman-rename.h | 145 + libs/cairo/src/test-fallback-surface.c | 206 + libs/cairo/src/test-fallback-surface.h | 19 + libs/cairo/src/test-meta-surface.c | 311 + libs/cairo/src/test-meta-surface.h | 19 + libs/cairo/src/test-paginated-surface.c | 260 + libs/cairo/src/test-paginated-surface.h | 17 + libs/libpixman/moz.build | 4 +- 455 files changed, 142593 insertions(+), 150448 deletions(-) create mode 100644 libs/cairo/AUTHORS create mode 100644 libs/cairo/COPYING create mode 100644 libs/cairo/README delete mode 100644 libs/cairo/cairo/AUTHORS delete mode 100644 libs/cairo/cairo/COPYING delete mode 100644 libs/cairo/cairo/README delete mode 100644 libs/cairo/cairo/src/cairo-analysis-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-analysis-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-arc-private.h delete mode 100644 libs/cairo/cairo/src/cairo-arc.c delete mode 100644 libs/cairo/cairo/src/cairo-array.c delete mode 100644 libs/cairo/cairo/src/cairo-atomic-private.h delete mode 100644 libs/cairo/cairo/src/cairo-atomic.c delete mode 100644 libs/cairo/cairo/src/cairo-base64-stream.c delete mode 100644 libs/cairo/cairo/src/cairo-base85-stream.c delete mode 100644 libs/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c delete mode 100644 libs/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c delete mode 100644 libs/cairo/cairo/src/cairo-bentley-ottmann.c delete mode 100644 libs/cairo/cairo/src/cairo-beos-surface.cpp delete mode 100644 libs/cairo/cairo/src/cairo-beos.h delete mode 100644 libs/cairo/cairo/src/cairo-botor-scan-converter.c delete mode 100644 libs/cairo/cairo/src/cairo-boxes-private.h delete mode 100644 libs/cairo/cairo/src/cairo-boxes.c delete mode 100644 libs/cairo/cairo/src/cairo-cache-private.h delete mode 100644 libs/cairo/cairo/src/cairo-cache.c delete mode 100644 libs/cairo/cairo/src/cairo-cff-subset.c delete mode 100644 libs/cairo/cairo/src/cairo-clip-private.h delete mode 100644 libs/cairo/cairo/src/cairo-clip.c delete mode 100644 libs/cairo/cairo/src/cairo-color.c delete mode 100644 libs/cairo/cairo/src/cairo-combsort-private.h delete mode 100644 libs/cairo/cairo/src/cairo-compiler-private.h delete mode 100644 libs/cairo/cairo/src/cairo-composite-rectangles-private.h delete mode 100644 libs/cairo/cairo/src/cairo-composite-rectangles.c delete mode 100644 libs/cairo/cairo/src/cairo-d2d-private-fx.h delete mode 100644 libs/cairo/cairo/src/cairo-d2d-private.fx delete mode 100644 libs/cairo/cairo/src/cairo-d2d-private.h delete mode 100644 libs/cairo/cairo/src/cairo-d2d-surface.cpp delete mode 100644 libs/cairo/cairo/src/cairo-debug.c delete mode 100644 libs/cairo/cairo/src/cairo-deflate-stream.c delete mode 100644 libs/cairo/cairo/src/cairo-deprecated.h delete mode 100644 libs/cairo/cairo/src/cairo-device-private.h delete mode 100644 libs/cairo/cairo/src/cairo-device.c delete mode 100644 libs/cairo/cairo/src/cairo-directfb-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-directfb.h delete mode 100644 libs/cairo/cairo/src/cairo-drm.h delete mode 100644 libs/cairo/cairo/src/cairo-dwrite-font.cpp delete mode 100644 libs/cairo/cairo/src/cairo-dwrite-private.h delete mode 100644 libs/cairo/cairo/src/cairo-eagle-context.c delete mode 100644 libs/cairo/cairo/src/cairo-error-private.h delete mode 100644 libs/cairo/cairo/src/cairo-features-win32.h delete mode 100644 libs/cairo/cairo/src/cairo-features.h.in delete mode 100644 libs/cairo/cairo/src/cairo-fixed-private.h delete mode 100644 libs/cairo/cairo/src/cairo-fixed-type-private.h delete mode 100644 libs/cairo/cairo/src/cairo-fixed.c delete mode 100644 libs/cairo/cairo/src/cairo-font-face-twin-data.c delete mode 100644 libs/cairo/cairo/src/cairo-font-face-twin.c delete mode 100644 libs/cairo/cairo/src/cairo-font-face.c delete mode 100644 libs/cairo/cairo/src/cairo-font-options.c delete mode 100644 libs/cairo/cairo/src/cairo-fontconfig-private.h delete mode 100644 libs/cairo/cairo/src/cairo-freed-pool-private.h delete mode 100644 libs/cairo/cairo/src/cairo-freed-pool.c delete mode 100644 libs/cairo/cairo/src/cairo-freelist-private.h delete mode 100644 libs/cairo/cairo/src/cairo-freelist-type-private.h delete mode 100644 libs/cairo/cairo/src/cairo-freelist.c delete mode 100644 libs/cairo/cairo/src/cairo-ft-font.c delete mode 100644 libs/cairo/cairo/src/cairo-ft-private.h delete mode 100644 libs/cairo/cairo/src/cairo-ft.h delete mode 100644 libs/cairo/cairo/src/cairo-gl-glyphs.c delete mode 100644 libs/cairo/cairo/src/cairo-gl-private.h delete mode 100644 libs/cairo/cairo/src/cairo-gl-shaders.c delete mode 100644 libs/cairo/cairo/src/cairo-gl-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-gl.h delete mode 100644 libs/cairo/cairo/src/cairo-glitz-private.h delete mode 100644 libs/cairo/cairo/src/cairo-glitz-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-glitz.h delete mode 100644 libs/cairo/cairo/src/cairo-glx-context.c delete mode 100644 libs/cairo/cairo/src/cairo-gstate-private.h delete mode 100644 libs/cairo/cairo/src/cairo-gstate.c delete mode 100644 libs/cairo/cairo/src/cairo-hash-private.h delete mode 100644 libs/cairo/cairo/src/cairo-hash.c delete mode 100644 libs/cairo/cairo/src/cairo-hull.c delete mode 100644 libs/cairo/cairo/src/cairo-image-info-private.h delete mode 100644 libs/cairo/cairo/src/cairo-image-info.c delete mode 100644 libs/cairo/cairo/src/cairo-image-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-list-private.h delete mode 100644 libs/cairo/cairo/src/cairo-lzw.c delete mode 100644 libs/cairo/cairo/src/cairo-malloc-private.h delete mode 100644 libs/cairo/cairo/src/cairo-matrix.c delete mode 100644 libs/cairo/cairo/src/cairo-meta-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-misc.c delete mode 100644 libs/cairo/cairo/src/cairo-mutex-impl-private.h delete mode 100644 libs/cairo/cairo/src/cairo-mutex-list-private.h delete mode 100644 libs/cairo/cairo/src/cairo-mutex-private.h delete mode 100644 libs/cairo/cairo/src/cairo-mutex-type-private.h delete mode 100644 libs/cairo/cairo/src/cairo-mutex.c delete mode 100644 libs/cairo/cairo/src/cairo-no-features.h delete mode 100644 libs/cairo/cairo/src/cairo-observer.c delete mode 100644 libs/cairo/cairo/src/cairo-os2-private.h delete mode 100644 libs/cairo/cairo/src/cairo-os2-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-os2.h delete mode 100644 libs/cairo/cairo/src/cairo-output-stream-private.h delete mode 100644 libs/cairo/cairo/src/cairo-output-stream.c delete mode 100644 libs/cairo/cairo/src/cairo-paginated-private.h delete mode 100644 libs/cairo/cairo/src/cairo-paginated-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-paginated-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-path-bounds.c delete mode 100644 libs/cairo/cairo/src/cairo-path-fill.c delete mode 100644 libs/cairo/cairo/src/cairo-path-fixed-private.h delete mode 100644 libs/cairo/cairo/src/cairo-path-fixed.c delete mode 100644 libs/cairo/cairo/src/cairo-path-in-fill.c delete mode 100644 libs/cairo/cairo/src/cairo-path-private.h delete mode 100644 libs/cairo/cairo/src/cairo-path-stroke.c delete mode 100644 libs/cairo/cairo/src/cairo-path.c delete mode 100644 libs/cairo/cairo/src/cairo-pattern.c delete mode 100644 libs/cairo/cairo/src/cairo-pdf-operators-private.h delete mode 100644 libs/cairo/cairo/src/cairo-pdf-operators.c delete mode 100644 libs/cairo/cairo/src/cairo-pdf-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-pdf-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-pdf.h delete mode 100644 libs/cairo/cairo/src/cairo-pen.c delete mode 100644 libs/cairo/cairo/src/cairo-platform.h delete mode 100644 libs/cairo/cairo/src/cairo-png.c delete mode 100644 libs/cairo/cairo/src/cairo-polygon.c delete mode 100644 libs/cairo/cairo/src/cairo-private.h delete mode 100644 libs/cairo/cairo/src/cairo-ps-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-ps-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-ps.h delete mode 100644 libs/cairo/cairo/src/cairo-qt-surface.cpp delete mode 100644 libs/cairo/cairo/src/cairo-qt.h delete mode 100644 libs/cairo/cairo/src/cairo-quartz-font.c delete mode 100644 libs/cairo/cairo/src/cairo-quartz-image-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-quartz-image.h delete mode 100644 libs/cairo/cairo/src/cairo-quartz-private.h delete mode 100644 libs/cairo/cairo/src/cairo-quartz-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-quartz.h delete mode 100644 libs/cairo/cairo/src/cairo-recording-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-recording-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-rectangle.c delete mode 100644 libs/cairo/cairo/src/cairo-rectangular-scan-converter.c delete mode 100644 libs/cairo/cairo/src/cairo-reference-count-private.h delete mode 100644 libs/cairo/cairo/src/cairo-region-private.h delete mode 100644 libs/cairo/cairo/src/cairo-region.c delete mode 100644 libs/cairo/cairo/src/cairo-rename.h delete mode 100644 libs/cairo/cairo/src/cairo-rtree-private.h delete mode 100644 libs/cairo/cairo/src/cairo-rtree.c delete mode 100644 libs/cairo/cairo/src/cairo-scaled-font-private.h delete mode 100644 libs/cairo/cairo/src/cairo-scaled-font-subsets-private.h delete mode 100644 libs/cairo/cairo/src/cairo-scaled-font-subsets.c delete mode 100644 libs/cairo/cairo/src/cairo-scaled-font.c delete mode 100644 libs/cairo/cairo/src/cairo-script-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-script.h delete mode 100644 libs/cairo/cairo/src/cairo-skia.h delete mode 100644 libs/cairo/cairo/src/cairo-slope-private.h delete mode 100644 libs/cairo/cairo/src/cairo-slope.c delete mode 100644 libs/cairo/cairo/src/cairo-spans-private.h delete mode 100644 libs/cairo/cairo/src/cairo-spans.c delete mode 100644 libs/cairo/cairo/src/cairo-spline.c delete mode 100644 libs/cairo/cairo/src/cairo-stroke-style.c delete mode 100644 libs/cairo/cairo/src/cairo-supported-features.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-clipper-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-clipper.c delete mode 100644 libs/cairo/cairo/src/cairo-surface-fallback-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-fallback.c delete mode 100644 libs/cairo/cairo/src/cairo-surface-offset-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-offset.c delete mode 100644 libs/cairo/cairo/src/cairo-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-snapshot-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-snapshot.c delete mode 100644 libs/cairo/cairo/src/cairo-surface-subsurface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-subsurface.c delete mode 100644 libs/cairo/cairo/src/cairo-surface-wrapper-private.h delete mode 100644 libs/cairo/cairo/src/cairo-surface-wrapper.c delete mode 100644 libs/cairo/cairo/src/cairo-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-svg-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-svg-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-svg.h delete mode 100644 libs/cairo/cairo/src/cairo-system.c delete mode 100644 libs/cairo/cairo/src/cairo-tee-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-tee-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-tee.h delete mode 100644 libs/cairo/cairo/src/cairo-tor-scan-converter.c delete mode 100644 libs/cairo/cairo/src/cairo-toy-font-face.c delete mode 100644 libs/cairo/cairo/src/cairo-traps.c delete mode 100644 libs/cairo/cairo/src/cairo-truetype-subset-private.h delete mode 100644 libs/cairo/cairo/src/cairo-truetype-subset.c delete mode 100644 libs/cairo/cairo/src/cairo-type1-fallback.c delete mode 100644 libs/cairo/cairo/src/cairo-type1-private.h delete mode 100644 libs/cairo/cairo/src/cairo-type1-subset.c delete mode 100644 libs/cairo/cairo/src/cairo-type3-glyph-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-type3-glyph-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-types-private.h delete mode 100644 libs/cairo/cairo/src/cairo-unicode.c delete mode 100644 libs/cairo/cairo/src/cairo-user-font-private.h delete mode 100644 libs/cairo/cairo/src/cairo-user-font.c delete mode 100644 libs/cairo/cairo/src/cairo-version.c delete mode 100644 libs/cairo/cairo/src/cairo-version.h delete mode 100644 libs/cairo/cairo/src/cairo-vg-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-vg.h delete mode 100644 libs/cairo/cairo/src/cairo-wideint-private.h delete mode 100644 libs/cairo/cairo/src/cairo-wideint-type-private.h delete mode 100644 libs/cairo/cairo/src/cairo-wideint.c delete mode 100644 libs/cairo/cairo/src/cairo-win32-font.c delete mode 100644 libs/cairo/cairo/src/cairo-win32-printing-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-win32-private.h delete mode 100644 libs/cairo/cairo/src/cairo-win32-refptr.h delete mode 100644 libs/cairo/cairo/src/cairo-win32-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-win32.h delete mode 100644 libs/cairo/cairo/src/cairo-xcb-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-xcb-xrender.h delete mode 100644 libs/cairo/cairo/src/cairo-xcb.h delete mode 100644 libs/cairo/cairo/src/cairo-xlib-display.c delete mode 100644 libs/cairo/cairo/src/cairo-xlib-private.h delete mode 100644 libs/cairo/cairo/src/cairo-xlib-screen.c delete mode 100644 libs/cairo/cairo/src/cairo-xlib-surface-private.h delete mode 100644 libs/cairo/cairo/src/cairo-xlib-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-xlib-visual.c delete mode 100644 libs/cairo/cairo/src/cairo-xlib-xrender-private.h delete mode 100644 libs/cairo/cairo/src/cairo-xlib-xrender.h delete mode 100644 libs/cairo/cairo/src/cairo-xlib.h delete mode 100644 libs/cairo/cairo/src/cairo-xml-surface.c delete mode 100644 libs/cairo/cairo/src/cairo-xml.h delete mode 100644 libs/cairo/cairo/src/cairo.c delete mode 100644 libs/cairo/cairo/src/cairo.h delete mode 100644 libs/cairo/cairo/src/cairoint.h delete mode 100644 libs/cairo/cairo/src/check-has-hidden-symbols.c delete mode 100644 libs/cairo/cairo/src/check-link.c delete mode 100644 libs/cairo/cairo/src/filterpublic.awk delete mode 100644 libs/cairo/cairo/src/moz.build delete mode 100644 libs/cairo/cairo/src/pixman-rename.h delete mode 100644 libs/cairo/cairo/src/test-fallback-surface.c delete mode 100644 libs/cairo/cairo/src/test-fallback-surface.h delete mode 100644 libs/cairo/cairo/src/test-meta-surface.c delete mode 100644 libs/cairo/cairo/src/test-meta-surface.h delete mode 100644 libs/cairo/cairo/src/test-paginated-surface.c delete mode 100644 libs/cairo/cairo/src/test-paginated-surface.h create mode 100644 libs/cairo/src/cairo-analysis-surface-private.h create mode 100644 libs/cairo/src/cairo-analysis-surface.c create mode 100644 libs/cairo/src/cairo-arc-private.h create mode 100644 libs/cairo/src/cairo-arc.c create mode 100644 libs/cairo/src/cairo-array.c create mode 100644 libs/cairo/src/cairo-atomic-private.h create mode 100644 libs/cairo/src/cairo-atomic.c create mode 100644 libs/cairo/src/cairo-base64-stream.c create mode 100644 libs/cairo/src/cairo-base85-stream.c create mode 100644 libs/cairo/src/cairo-bentley-ottmann-rectangular.c create mode 100644 libs/cairo/src/cairo-bentley-ottmann-rectilinear.c create mode 100644 libs/cairo/src/cairo-bentley-ottmann.c create mode 100644 libs/cairo/src/cairo-botor-scan-converter.c create mode 100644 libs/cairo/src/cairo-boxes-private.h create mode 100644 libs/cairo/src/cairo-boxes.c create mode 100644 libs/cairo/src/cairo-cache-private.h create mode 100644 libs/cairo/src/cairo-cache.c create mode 100644 libs/cairo/src/cairo-cff-subset.c create mode 100644 libs/cairo/src/cairo-clip-private.h create mode 100644 libs/cairo/src/cairo-clip.c create mode 100644 libs/cairo/src/cairo-color.c create mode 100644 libs/cairo/src/cairo-combsort-private.h create mode 100644 libs/cairo/src/cairo-compiler-private.h create mode 100644 libs/cairo/src/cairo-composite-rectangles-private.h create mode 100644 libs/cairo/src/cairo-composite-rectangles.c create mode 100644 libs/cairo/src/cairo-d2d-private-fx.h create mode 100644 libs/cairo/src/cairo-d2d-private.fx create mode 100644 libs/cairo/src/cairo-d2d-private.h create mode 100644 libs/cairo/src/cairo-d2d-surface.cpp create mode 100644 libs/cairo/src/cairo-debug.c create mode 100644 libs/cairo/src/cairo-deflate-stream.c create mode 100644 libs/cairo/src/cairo-deprecated.h create mode 100644 libs/cairo/src/cairo-device-private.h create mode 100644 libs/cairo/src/cairo-device.c create mode 100644 libs/cairo/src/cairo-directfb-surface.c create mode 100644 libs/cairo/src/cairo-directfb.h create mode 100644 libs/cairo/src/cairo-drm.h create mode 100644 libs/cairo/src/cairo-dwrite-font.cpp create mode 100644 libs/cairo/src/cairo-dwrite-private.h create mode 100644 libs/cairo/src/cairo-eagle-context.c create mode 100644 libs/cairo/src/cairo-error-private.h create mode 100644 libs/cairo/src/cairo-features-win32.h create mode 100644 libs/cairo/src/cairo-features.h.in create mode 100644 libs/cairo/src/cairo-fixed-private.h create mode 100644 libs/cairo/src/cairo-fixed-type-private.h create mode 100644 libs/cairo/src/cairo-fixed.c create mode 100644 libs/cairo/src/cairo-font-face-twin-data.c create mode 100644 libs/cairo/src/cairo-font-face-twin.c create mode 100644 libs/cairo/src/cairo-font-face.c create mode 100644 libs/cairo/src/cairo-font-options.c create mode 100644 libs/cairo/src/cairo-fontconfig-private.h create mode 100644 libs/cairo/src/cairo-freed-pool-private.h create mode 100644 libs/cairo/src/cairo-freed-pool.c create mode 100644 libs/cairo/src/cairo-freelist-private.h create mode 100644 libs/cairo/src/cairo-freelist-type-private.h create mode 100644 libs/cairo/src/cairo-freelist.c create mode 100644 libs/cairo/src/cairo-ft-font.c create mode 100644 libs/cairo/src/cairo-ft-private.h create mode 100644 libs/cairo/src/cairo-ft.h create mode 100644 libs/cairo/src/cairo-gl-glyphs.c create mode 100644 libs/cairo/src/cairo-gl-private.h create mode 100644 libs/cairo/src/cairo-gl-shaders.c create mode 100644 libs/cairo/src/cairo-gl-surface.c create mode 100644 libs/cairo/src/cairo-gl.h create mode 100644 libs/cairo/src/cairo-glitz-private.h create mode 100644 libs/cairo/src/cairo-glitz-surface.c create mode 100644 libs/cairo/src/cairo-glitz.h create mode 100644 libs/cairo/src/cairo-glx-context.c create mode 100644 libs/cairo/src/cairo-gstate-private.h create mode 100644 libs/cairo/src/cairo-gstate.c create mode 100644 libs/cairo/src/cairo-hash-private.h create mode 100644 libs/cairo/src/cairo-hash.c create mode 100644 libs/cairo/src/cairo-hull.c create mode 100644 libs/cairo/src/cairo-image-info-private.h create mode 100644 libs/cairo/src/cairo-image-info.c create mode 100644 libs/cairo/src/cairo-image-surface.c create mode 100644 libs/cairo/src/cairo-list-private.h create mode 100644 libs/cairo/src/cairo-lzw.c create mode 100644 libs/cairo/src/cairo-malloc-private.h create mode 100644 libs/cairo/src/cairo-matrix.c create mode 100644 libs/cairo/src/cairo-meta-surface-private.h create mode 100644 libs/cairo/src/cairo-misc.c create mode 100644 libs/cairo/src/cairo-mutex-impl-private.h create mode 100644 libs/cairo/src/cairo-mutex-list-private.h create mode 100644 libs/cairo/src/cairo-mutex-private.h create mode 100644 libs/cairo/src/cairo-mutex-type-private.h create mode 100644 libs/cairo/src/cairo-mutex.c create mode 100644 libs/cairo/src/cairo-no-features.h create mode 100644 libs/cairo/src/cairo-observer.c create mode 100644 libs/cairo/src/cairo-os2-private.h create mode 100644 libs/cairo/src/cairo-os2-surface.c create mode 100644 libs/cairo/src/cairo-os2.h create mode 100644 libs/cairo/src/cairo-output-stream-private.h create mode 100644 libs/cairo/src/cairo-output-stream.c create mode 100644 libs/cairo/src/cairo-paginated-private.h create mode 100644 libs/cairo/src/cairo-paginated-surface-private.h create mode 100644 libs/cairo/src/cairo-paginated-surface.c create mode 100644 libs/cairo/src/cairo-path-bounds.c create mode 100644 libs/cairo/src/cairo-path-fill.c create mode 100644 libs/cairo/src/cairo-path-fixed-private.h create mode 100644 libs/cairo/src/cairo-path-fixed.c create mode 100644 libs/cairo/src/cairo-path-in-fill.c create mode 100644 libs/cairo/src/cairo-path-private.h create mode 100644 libs/cairo/src/cairo-path-stroke.c create mode 100644 libs/cairo/src/cairo-path.c create mode 100644 libs/cairo/src/cairo-pattern.c create mode 100644 libs/cairo/src/cairo-pdf-operators-private.h create mode 100644 libs/cairo/src/cairo-pdf-operators.c create mode 100644 libs/cairo/src/cairo-pdf-surface-private.h create mode 100644 libs/cairo/src/cairo-pdf-surface.c create mode 100644 libs/cairo/src/cairo-pdf.h create mode 100644 libs/cairo/src/cairo-pen.c create mode 100644 libs/cairo/src/cairo-platform.h create mode 100644 libs/cairo/src/cairo-png.c create mode 100644 libs/cairo/src/cairo-polygon.c create mode 100644 libs/cairo/src/cairo-private.h create mode 100644 libs/cairo/src/cairo-ps-surface-private.h create mode 100644 libs/cairo/src/cairo-ps-surface.c create mode 100644 libs/cairo/src/cairo-ps.h create mode 100644 libs/cairo/src/cairo-recording-surface-private.h create mode 100644 libs/cairo/src/cairo-recording-surface.c create mode 100644 libs/cairo/src/cairo-rectangle.c create mode 100644 libs/cairo/src/cairo-rectangular-scan-converter.c create mode 100644 libs/cairo/src/cairo-reference-count-private.h create mode 100644 libs/cairo/src/cairo-region-private.h create mode 100644 libs/cairo/src/cairo-region.c create mode 100644 libs/cairo/src/cairo-rename.h create mode 100644 libs/cairo/src/cairo-rtree-private.h create mode 100644 libs/cairo/src/cairo-rtree.c create mode 100644 libs/cairo/src/cairo-scaled-font-private.h create mode 100644 libs/cairo/src/cairo-scaled-font-subsets-private.h create mode 100644 libs/cairo/src/cairo-scaled-font-subsets.c create mode 100644 libs/cairo/src/cairo-scaled-font.c create mode 100644 libs/cairo/src/cairo-script-surface.c create mode 100644 libs/cairo/src/cairo-script.h create mode 100644 libs/cairo/src/cairo-skia.h create mode 100644 libs/cairo/src/cairo-slope-private.h create mode 100644 libs/cairo/src/cairo-slope.c create mode 100644 libs/cairo/src/cairo-spans-private.h create mode 100644 libs/cairo/src/cairo-spans.c create mode 100644 libs/cairo/src/cairo-spline.c create mode 100644 libs/cairo/src/cairo-stroke-style.c create mode 100644 libs/cairo/src/cairo-supported-features.h create mode 100644 libs/cairo/src/cairo-surface-clipper-private.h create mode 100644 libs/cairo/src/cairo-surface-clipper.c create mode 100644 libs/cairo/src/cairo-surface-fallback-private.h create mode 100644 libs/cairo/src/cairo-surface-fallback.c create mode 100644 libs/cairo/src/cairo-surface-offset-private.h create mode 100644 libs/cairo/src/cairo-surface-offset.c create mode 100644 libs/cairo/src/cairo-surface-private.h create mode 100644 libs/cairo/src/cairo-surface-snapshot-private.h create mode 100644 libs/cairo/src/cairo-surface-snapshot.c create mode 100644 libs/cairo/src/cairo-surface-subsurface-private.h create mode 100644 libs/cairo/src/cairo-surface-subsurface.c create mode 100644 libs/cairo/src/cairo-surface-wrapper-private.h create mode 100644 libs/cairo/src/cairo-surface-wrapper.c create mode 100644 libs/cairo/src/cairo-surface.c create mode 100644 libs/cairo/src/cairo-svg-surface-private.h create mode 100644 libs/cairo/src/cairo-svg-surface.c create mode 100644 libs/cairo/src/cairo-svg.h create mode 100644 libs/cairo/src/cairo-system.c create mode 100644 libs/cairo/src/cairo-tee-surface-private.h create mode 100644 libs/cairo/src/cairo-tee-surface.c create mode 100644 libs/cairo/src/cairo-tee.h create mode 100644 libs/cairo/src/cairo-tor-scan-converter.c create mode 100644 libs/cairo/src/cairo-toy-font-face.c create mode 100644 libs/cairo/src/cairo-traps.c create mode 100644 libs/cairo/src/cairo-truetype-subset-private.h create mode 100644 libs/cairo/src/cairo-truetype-subset.c create mode 100644 libs/cairo/src/cairo-type1-fallback.c create mode 100644 libs/cairo/src/cairo-type1-private.h create mode 100644 libs/cairo/src/cairo-type1-subset.c create mode 100644 libs/cairo/src/cairo-type3-glyph-surface-private.h create mode 100644 libs/cairo/src/cairo-type3-glyph-surface.c create mode 100644 libs/cairo/src/cairo-types-private.h create mode 100644 libs/cairo/src/cairo-unicode.c create mode 100644 libs/cairo/src/cairo-user-font-private.h create mode 100644 libs/cairo/src/cairo-user-font.c create mode 100644 libs/cairo/src/cairo-version.c create mode 100644 libs/cairo/src/cairo-version.h create mode 100644 libs/cairo/src/cairo-vg-surface.c create mode 100644 libs/cairo/src/cairo-vg.h create mode 100644 libs/cairo/src/cairo-wideint-private.h create mode 100644 libs/cairo/src/cairo-wideint-type-private.h create mode 100644 libs/cairo/src/cairo-wideint.c create mode 100644 libs/cairo/src/cairo-win32-font.c create mode 100644 libs/cairo/src/cairo-win32-printing-surface.c create mode 100644 libs/cairo/src/cairo-win32-private.h create mode 100644 libs/cairo/src/cairo-win32-refptr.h create mode 100644 libs/cairo/src/cairo-win32-surface.c create mode 100644 libs/cairo/src/cairo-win32.h create mode 100644 libs/cairo/src/cairo-xcb-surface.c create mode 100644 libs/cairo/src/cairo-xcb-xrender.h create mode 100644 libs/cairo/src/cairo-xcb.h create mode 100644 libs/cairo/src/cairo-xlib-display.c create mode 100644 libs/cairo/src/cairo-xlib-private.h create mode 100644 libs/cairo/src/cairo-xlib-screen.c create mode 100644 libs/cairo/src/cairo-xlib-surface-private.h create mode 100644 libs/cairo/src/cairo-xlib-surface.c create mode 100644 libs/cairo/src/cairo-xlib-visual.c create mode 100644 libs/cairo/src/cairo-xlib-xrender-private.h create mode 100644 libs/cairo/src/cairo-xlib-xrender.h create mode 100644 libs/cairo/src/cairo-xlib.h create mode 100644 libs/cairo/src/cairo-xml-surface.c create mode 100644 libs/cairo/src/cairo-xml.h create mode 100644 libs/cairo/src/cairo.c create mode 100644 libs/cairo/src/cairo.h create mode 100644 libs/cairo/src/cairoint.h create mode 100644 libs/cairo/src/check-has-hidden-symbols.c create mode 100644 libs/cairo/src/check-link.c create mode 100644 libs/cairo/src/filterpublic.awk create mode 100644 libs/cairo/src/pixman-rename.h create mode 100644 libs/cairo/src/test-fallback-surface.c create mode 100644 libs/cairo/src/test-fallback-surface.h create mode 100644 libs/cairo/src/test-meta-surface.c create mode 100644 libs/cairo/src/test-meta-surface.h create mode 100644 libs/cairo/src/test-paginated-surface.c create mode 100644 libs/cairo/src/test-paginated-surface.h (limited to 'libs') diff --git a/libs/cairo/AUTHORS b/libs/cairo/AUTHORS new file mode 100644 index 000000000..bdde62a31 --- /dev/null +++ b/libs/cairo/AUTHORS @@ -0,0 +1,99 @@ +Josh Aas Memory leak fix for quartz backend +Daniel Amelang Many (magic) floating-point optimizations +Shawn T. Amundson Build fix +Olivier Andrieu PNG backend +Peter Dennis Bartok Bug fix for clipping +Dave Beckett Build fixes, Debian packaging +Christian Biesinger BeOS backend +Billy Biggs Pixman code merge. Optimization. Fixes for subtle rendering bugs. +Hans Breuer win32 bug fixes, build fixes, and improvements +Brian Cameron Flag bug in Sun's X server +Damien Carbery Build fixes +Andrew Chant Adding const where needed +Steve Chaplin Bug fixes for PNG reading +Tomasz Cholewo Bug fixes +Manu Cornet SVG build fix +Frederic Crozat Fix test suite for OPD platforms (IA64 or PPC64) +Radek Doulík Bug report and test case +John Ehresman Build fixes for win32 +John Ellson First font/glyph extents functions +Michael Emmel DirectFB backend +Miklós Erdélyi Fix typo leading to a crash +Behdad Esfahbod Huge piles of bug fixes, improvements, and general maintenance +Brian Ewins ATSUI maintenance (first success at making it really work) +Bertram Felgenhauer Fixes for subtle arithmetic errors +Bdale Garbee Provided essential support for cairo achitecture sessions +Jens Granseuer Fixes to generate proper compiler flags +Laxmi Harikumar Build fix +J. Ali Harlow win32 backend updates +Mathias Hasselmann Significant reduction of calls to malloc +Richard Henderson "slim" macros for better shared libraries +James Henstridge Build fixes related to freetype +Graydon Hoare Support for non-render X server, first real text support +Thomas Hunger Initial version of cairo_in_stroke/fill +Kristian Høgsberg PDF backend, PS backend with meta-surfaces +Amaury Jacquot Documentation review, appplication testing +Adrian Johnson PDF backend improvement +Michael Johnson Bug fix for pre-C99 compilers +Jonathon Jongsma Fix documentation typos +Øyvind Kolås Bug fixes. Better default values. +Martin Kretzschmar Arithmetic fix for 64-bit architectures +Mathieu Lacage several bug/typo fixes +Dominic Lachowicz PDF conformance fix, fix image surface to zero out contents +Alexander Larsson Profiling and performance fixes. +Tor Lillqvist win32 build fixes, build scripts +Jinghua Luo Add bitmap glyph transformation, many freetype and glitz fixes +Luke-Jr Build fix for cross-compiling +Kjartan Maraas Several fixes for sparse, lots of debug help for multi-thread bugs +Jordi Mas Bug fix for cairo_show_text +Nicholas Miell Fixes for linking bugs on AMD64 +Eugeniy Meshcheryakov PS/PDF font subsetting improvements +Zakharov Mikhail Build fix for HP-UX +Christopher (Monty) Montgomery Performnace fix (subimage_copy), multi-thread testing +Tim Mooney Fix test suite to compile with Solaris compiler +Jeff Muizelaar Patient, painful, pixman code merge. Many fixes for intricacies of dashing. +Yevgen Muntyan win32 build fix +Declan Naughton Fix documentation typos +Peter Nilsson Glitz backend +Henning Noren Fix memory leak +Geoff Norton Build fixes +Robert O'Callahan Const-correctness fixes, several new API functions for completeness (and to help mozilla) +Ian Osgood XCB backend maintenance +Benjamin Otte Refinements to cairo/perf timing +Mike Owens Bug fixes +Emmanuel Pacaud SVG backend +Keith Packard Original concept, polygon tessellation, dashing, font metrics rewrite +Stuart Parmenter Original GDI+ backend, win32 fixes +Alfred Peng Fixes for Sun compilers and for a memory leak +Christof Petig Build fixes related to freetype +Joonas Pihlaja Huge improvements to the tessellator performance +Mart Raudsepp Build fixes +David Reveman New pattern API, glitz backend +Calum Robinson Quartz backend +Pavel Roskin Several cleanups to eliminate warnings +Tim Rowley Quartz/ATSUI fixes, X server workarounds, win32 glyph path support, test case to expose gradient regression +Soeren Sandmann Lots of MMX love for pixman compositing +Torsten Schönfeld Build fixes +Jamey Sharp Surface/font backend virtualization, XCB backend +Jason Dorje Short Build fixes and bug fixes +Jeff Smith Fixes for intricacies of stroking code +Travis Spencer XCB backend fix +Bill Spitzak Build fix to find Xrender.h without xrender.pc +Zhe Su Add support for fontconfig's embeddedbitmap option +Owen Taylor Font rewrite, documentation, win32 backend +Alp Toker Fix several code/comment typos +Malcolm Tredinnick Documentation fixes +David Turner Optimize gradient calculations +Kalle Vahlman Allow perf reports to be compared across different platforms +Sasha Vasko Build fix to compile without xlib backend +Vladimir Vukicevic Quartz backend rewrite, win32/quartz maintenance +Jonathan Watt win32 fixes +Peter Weilbacher OS/2 backend +Dan Williams Implemnt MMX function to help OLPC +Chris Wilson Large-scale robustness improvements, (warn_unsed_result and malloc failure injection) +Carl Worth Original library, support for paths, images +Richard D. Worth Build fixes for cygwin +Kent Worsnop Fix PDF dashing bug +Dave Yeo Build fix for win32 + +(please let us know if we have missed anyone) diff --git a/libs/cairo/COPYING b/libs/cairo/COPYING new file mode 100644 index 000000000..184e603c3 --- /dev/null +++ b/libs/cairo/COPYING @@ -0,0 +1,11 @@ +Cairo is free software. + +This (modified) version of the cairo implementation is available to be +redistributed and/or modified under the terms the Mozilla Public License +(MPL) version 2.0. Some files in the original cairo source code are +available under more liberal terms, but we believe that in all cases, +each file may be used under the MPL 2.0. + +Where the original code was not explicitly MPL licensed, the original +more liberal license information and copyright has been retained for +clarity of licensing and authorship. diff --git a/libs/cairo/README b/libs/cairo/README new file mode 100644 index 000000000..fc801fee3 --- /dev/null +++ b/libs/cairo/README @@ -0,0 +1,203 @@ +Important note: the source code included in this directory is a subset +of the library, insofar as is needed to supply functionality to the +UXP source. For the complete and unmodified source of Cairo, please +go to the website indicated below. + +Cairo - Multi-platform 2D graphics library +http://cairographics.org + +What is cairo +============= +Cairo is a 2D graphics library with support for multiple output +devices. Currently supported output targets include the X Window +System, win32, and image buffers, as well as PDF, PostScript, and SVG +file output. Experimental backends include OpenGL (through glitz), +Quartz, XCB, BeOS, OS/2, and DirectFB. + +Cairo is designed to produce consistent output on all output media +while taking advantage of display hardware acceleration when available +(for example, through the X Render Extension). + +The cairo API provides operations similar to the drawing operators of +PostScript and PDF. Operations in cairo include stroking and filling +cubic Bézier splines, transforming and compositing translucent images, +and antialiased text rendering. All drawing operations can be +transformed by any affine transformation (scale, rotation, shear, +etc.). + +Cairo has been designed to let you draw anything you want in a modern +2D graphical user interface. At the same time, the cairo API has been +designed to be as fun and easy to learn as possible. If you're not +having fun while programming with cairo, then we have failed +somewhere---let us know and we'll try to fix it next time around. + +Cairo is free software and is available to be redistributed and/or +modified under the terms of either the GNU Lesser General Public +License (LGPL) version 2.1 or the Mozilla Public License (MPL) version +1.1. + +Where to get more information about cairo +========================================= +The primary source of information about cairo is: + + http://cairographics.org/ + +The latest versions of cairo can always be found at: + + http://cairographics.org/download + +Documentation on using cairo and frequently-asked questions: + + http://cairographics.org/documentation + http://cairographics.org/FAQ + +Mailing lists for contacting cairo users and developers: + + http://cairographics.org/lists + +Roadmap and unscheduled things to do, (please feel free to help out): + + http://cairographics.org/roadmap + http://cairographics.org/todo + +Dependencies +============ +The set of libraries needed to compile cairo depends on which backends +are enabled when cairo is configured. So look at the list below to +determine which dependencies are needed for the backends of interest. + +For the surface backends, we have both "supported" and "experimental" +backends. Further, the supported backends can be divided into the +"standard" backends which can be easily built on any platform, and the +"platform" backends which depend on some underlying platform-specific +system, (such as the X Window System or some other window system). + +As an example, for a standard Linux build, (with image, png, pdf, +PostScript, svg, and xlib surface backends, and the freetype font +backend), the following sample commands will install necessary +dependencies: + + Debian (and similar): + + apt-get install libpng12-dev libz-dev libxrender-dev libfontconfig1-dev + + Fedora (and similar): + + yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel + +(Those commands intentionally don't install pixman from a distribution +package since if you're manually compiling cairo, then you likely want +to grab pixman from the same place at the same time and compile it as +well.) + +Supported, "standard" surface backends +------------------------------------ + image backend (required) + ------------------------ + pixman >= 0.10.0 http://cairographics.org/releases + + png support (can be left out if desired, but many + ----------- applications expect it to be present) + libpng http://www.libpng.org/pub/png/libpng.html + + pdf backend + ----------- + zlib http://www.gzip.org/zlib + + postscript backend + ------------------ + zlib http://www.gzip.org/zlib + + svg backend + ----------- + [none] + +Supported, "platform" surface backends +----------------------------------- + xlib backend + ------------ + X11 http://freedesktop.org/Software/xlibs + + xlib-xrender backend + -------------------- + Xrender >= 0.6 http://freedesktop.org/Software/xlibs + + quartz backend + -------------- + MacOS X >= 10.4 with Xcode >= 2.4 + + win32 backend + ------------- + Microsoft Windows 2000 or newer[*]. + +Font backends (required to have at least one) +--------------------------------------------- + freetype font backend + --------------------- + freetype >= 2.1.9 http://freetype.org + fontconfig http://fontconfig.org + + quartz-font backend + ------------------- + MacOS X >= 10.4 with Xcode >= 2.4 + + win32 font backend + ------------------ + Microsoft Windows 2000 or newer[*]. + + [*] The Win32 backend should work on Windows 2000 and newer + (excluding Windows Me.) Most testing has been done on + Windows XP. While some portions of the code have been + adapted to work on older versions of Windows, considerable + work still needs to be done to get cairo running in those + environments. + + Cairo can be compiled on Windows with either the gcc + toolchain (see http://www.mingw.org) or with Microsoft + Visual C++. If the gcc toolchain is used, the standard + build instructions using configure apply, (see INSTALL). + If Visual C++ is desired, GNU make is required and + Makefile.win32 can be used via 'make -f Makefile.win32'. + The compiler, include paths, and library paths must be set + up correctly in the environment. + + MSVC versions earlier than 7.1 are known to miscompile + parts of cairo and pixman, and so should be avoided. MSVC + 7.1 or later, including the free Microsoft Visual Studio + Express editions, produce correct code. + +Experimental surface backends +----------------------------- + glitz + ------------- + glitz >= 0.4.4 http://freedesktop.org/Software/glitz + + xcb backend + ----------- + XCB http://xcb.freedesktop.org + + beos backend + ------------ + No dependencies in itself other than an installed BeOS system, but cairo + requires a font backend. See the freetype dependency list. + + os2 backend + ----------- + Cairo should run on any recent version of OS/2 or eComStation, but it + requires a font backend. See the freetype dependency list. Ready to use + packages and developer dependencies are available at Netlabs: + ftp://ftp.netlabs.org/pub/cairo + +Compiling +========= +See the INSTALL document for build instructions. + +History +======= +Cairo was originally developed by Carl Worth and +Keith Packard . Many thanks are due to Lyle Ramshaw +without whose patient help our ignorance would be much more apparent. + +Since the original development, many more people have contributed to +cairo. See the AUTHORS files for as complete a list as we've been able +to compile so far. diff --git a/libs/cairo/cairo/AUTHORS b/libs/cairo/cairo/AUTHORS deleted file mode 100644 index bdde62a31..000000000 --- a/libs/cairo/cairo/AUTHORS +++ /dev/null @@ -1,99 +0,0 @@ -Josh Aas Memory leak fix for quartz backend -Daniel Amelang Many (magic) floating-point optimizations -Shawn T. Amundson Build fix -Olivier Andrieu PNG backend -Peter Dennis Bartok Bug fix for clipping -Dave Beckett Build fixes, Debian packaging -Christian Biesinger BeOS backend -Billy Biggs Pixman code merge. Optimization. Fixes for subtle rendering bugs. -Hans Breuer win32 bug fixes, build fixes, and improvements -Brian Cameron Flag bug in Sun's X server -Damien Carbery Build fixes -Andrew Chant Adding const where needed -Steve Chaplin Bug fixes for PNG reading -Tomasz Cholewo Bug fixes -Manu Cornet SVG build fix -Frederic Crozat Fix test suite for OPD platforms (IA64 or PPC64) -Radek Doulík Bug report and test case -John Ehresman Build fixes for win32 -John Ellson First font/glyph extents functions -Michael Emmel DirectFB backend -Miklós Erdélyi Fix typo leading to a crash -Behdad Esfahbod Huge piles of bug fixes, improvements, and general maintenance -Brian Ewins ATSUI maintenance (first success at making it really work) -Bertram Felgenhauer Fixes for subtle arithmetic errors -Bdale Garbee Provided essential support for cairo achitecture sessions -Jens Granseuer Fixes to generate proper compiler flags -Laxmi Harikumar Build fix -J. Ali Harlow win32 backend updates -Mathias Hasselmann Significant reduction of calls to malloc -Richard Henderson "slim" macros for better shared libraries -James Henstridge Build fixes related to freetype -Graydon Hoare Support for non-render X server, first real text support -Thomas Hunger Initial version of cairo_in_stroke/fill -Kristian Høgsberg PDF backend, PS backend with meta-surfaces -Amaury Jacquot Documentation review, appplication testing -Adrian Johnson PDF backend improvement -Michael Johnson Bug fix for pre-C99 compilers -Jonathon Jongsma Fix documentation typos -Øyvind Kolås Bug fixes. Better default values. -Martin Kretzschmar Arithmetic fix for 64-bit architectures -Mathieu Lacage several bug/typo fixes -Dominic Lachowicz PDF conformance fix, fix image surface to zero out contents -Alexander Larsson Profiling and performance fixes. -Tor Lillqvist win32 build fixes, build scripts -Jinghua Luo Add bitmap glyph transformation, many freetype and glitz fixes -Luke-Jr Build fix for cross-compiling -Kjartan Maraas Several fixes for sparse, lots of debug help for multi-thread bugs -Jordi Mas Bug fix for cairo_show_text -Nicholas Miell Fixes for linking bugs on AMD64 -Eugeniy Meshcheryakov PS/PDF font subsetting improvements -Zakharov Mikhail Build fix for HP-UX -Christopher (Monty) Montgomery Performnace fix (subimage_copy), multi-thread testing -Tim Mooney Fix test suite to compile with Solaris compiler -Jeff Muizelaar Patient, painful, pixman code merge. Many fixes for intricacies of dashing. -Yevgen Muntyan win32 build fix -Declan Naughton Fix documentation typos -Peter Nilsson Glitz backend -Henning Noren Fix memory leak -Geoff Norton Build fixes -Robert O'Callahan Const-correctness fixes, several new API functions for completeness (and to help mozilla) -Ian Osgood XCB backend maintenance -Benjamin Otte Refinements to cairo/perf timing -Mike Owens Bug fixes -Emmanuel Pacaud SVG backend -Keith Packard Original concept, polygon tessellation, dashing, font metrics rewrite -Stuart Parmenter Original GDI+ backend, win32 fixes -Alfred Peng Fixes for Sun compilers and for a memory leak -Christof Petig Build fixes related to freetype -Joonas Pihlaja Huge improvements to the tessellator performance -Mart Raudsepp Build fixes -David Reveman New pattern API, glitz backend -Calum Robinson Quartz backend -Pavel Roskin Several cleanups to eliminate warnings -Tim Rowley Quartz/ATSUI fixes, X server workarounds, win32 glyph path support, test case to expose gradient regression -Soeren Sandmann Lots of MMX love for pixman compositing -Torsten Schönfeld Build fixes -Jamey Sharp Surface/font backend virtualization, XCB backend -Jason Dorje Short Build fixes and bug fixes -Jeff Smith Fixes for intricacies of stroking code -Travis Spencer XCB backend fix -Bill Spitzak Build fix to find Xrender.h without xrender.pc -Zhe Su Add support for fontconfig's embeddedbitmap option -Owen Taylor Font rewrite, documentation, win32 backend -Alp Toker Fix several code/comment typos -Malcolm Tredinnick Documentation fixes -David Turner Optimize gradient calculations -Kalle Vahlman Allow perf reports to be compared across different platforms -Sasha Vasko Build fix to compile without xlib backend -Vladimir Vukicevic Quartz backend rewrite, win32/quartz maintenance -Jonathan Watt win32 fixes -Peter Weilbacher OS/2 backend -Dan Williams Implemnt MMX function to help OLPC -Chris Wilson Large-scale robustness improvements, (warn_unsed_result and malloc failure injection) -Carl Worth Original library, support for paths, images -Richard D. Worth Build fixes for cygwin -Kent Worsnop Fix PDF dashing bug -Dave Yeo Build fix for win32 - -(please let us know if we have missed anyone) diff --git a/libs/cairo/cairo/COPYING b/libs/cairo/cairo/COPYING deleted file mode 100644 index 184e603c3..000000000 --- a/libs/cairo/cairo/COPYING +++ /dev/null @@ -1,11 +0,0 @@ -Cairo is free software. - -This (modified) version of the cairo implementation is available to be -redistributed and/or modified under the terms the Mozilla Public License -(MPL) version 2.0. Some files in the original cairo source code are -available under more liberal terms, but we believe that in all cases, -each file may be used under the MPL 2.0. - -Where the original code was not explicitly MPL licensed, the original -more liberal license information and copyright has been retained for -clarity of licensing and authorship. diff --git a/libs/cairo/cairo/README b/libs/cairo/cairo/README deleted file mode 100644 index fc801fee3..000000000 --- a/libs/cairo/cairo/README +++ /dev/null @@ -1,203 +0,0 @@ -Important note: the source code included in this directory is a subset -of the library, insofar as is needed to supply functionality to the -UXP source. For the complete and unmodified source of Cairo, please -go to the website indicated below. - -Cairo - Multi-platform 2D graphics library -http://cairographics.org - -What is cairo -============= -Cairo is a 2D graphics library with support for multiple output -devices. Currently supported output targets include the X Window -System, win32, and image buffers, as well as PDF, PostScript, and SVG -file output. Experimental backends include OpenGL (through glitz), -Quartz, XCB, BeOS, OS/2, and DirectFB. - -Cairo is designed to produce consistent output on all output media -while taking advantage of display hardware acceleration when available -(for example, through the X Render Extension). - -The cairo API provides operations similar to the drawing operators of -PostScript and PDF. Operations in cairo include stroking and filling -cubic Bézier splines, transforming and compositing translucent images, -and antialiased text rendering. All drawing operations can be -transformed by any affine transformation (scale, rotation, shear, -etc.). - -Cairo has been designed to let you draw anything you want in a modern -2D graphical user interface. At the same time, the cairo API has been -designed to be as fun and easy to learn as possible. If you're not -having fun while programming with cairo, then we have failed -somewhere---let us know and we'll try to fix it next time around. - -Cairo is free software and is available to be redistributed and/or -modified under the terms of either the GNU Lesser General Public -License (LGPL) version 2.1 or the Mozilla Public License (MPL) version -1.1. - -Where to get more information about cairo -========================================= -The primary source of information about cairo is: - - http://cairographics.org/ - -The latest versions of cairo can always be found at: - - http://cairographics.org/download - -Documentation on using cairo and frequently-asked questions: - - http://cairographics.org/documentation - http://cairographics.org/FAQ - -Mailing lists for contacting cairo users and developers: - - http://cairographics.org/lists - -Roadmap and unscheduled things to do, (please feel free to help out): - - http://cairographics.org/roadmap - http://cairographics.org/todo - -Dependencies -============ -The set of libraries needed to compile cairo depends on which backends -are enabled when cairo is configured. So look at the list below to -determine which dependencies are needed for the backends of interest. - -For the surface backends, we have both "supported" and "experimental" -backends. Further, the supported backends can be divided into the -"standard" backends which can be easily built on any platform, and the -"platform" backends which depend on some underlying platform-specific -system, (such as the X Window System or some other window system). - -As an example, for a standard Linux build, (with image, png, pdf, -PostScript, svg, and xlib surface backends, and the freetype font -backend), the following sample commands will install necessary -dependencies: - - Debian (and similar): - - apt-get install libpng12-dev libz-dev libxrender-dev libfontconfig1-dev - - Fedora (and similar): - - yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel - -(Those commands intentionally don't install pixman from a distribution -package since if you're manually compiling cairo, then you likely want -to grab pixman from the same place at the same time and compile it as -well.) - -Supported, "standard" surface backends ------------------------------------- - image backend (required) - ------------------------ - pixman >= 0.10.0 http://cairographics.org/releases - - png support (can be left out if desired, but many - ----------- applications expect it to be present) - libpng http://www.libpng.org/pub/png/libpng.html - - pdf backend - ----------- - zlib http://www.gzip.org/zlib - - postscript backend - ------------------ - zlib http://www.gzip.org/zlib - - svg backend - ----------- - [none] - -Supported, "platform" surface backends ------------------------------------ - xlib backend - ------------ - X11 http://freedesktop.org/Software/xlibs - - xlib-xrender backend - -------------------- - Xrender >= 0.6 http://freedesktop.org/Software/xlibs - - quartz backend - -------------- - MacOS X >= 10.4 with Xcode >= 2.4 - - win32 backend - ------------- - Microsoft Windows 2000 or newer[*]. - -Font backends (required to have at least one) ---------------------------------------------- - freetype font backend - --------------------- - freetype >= 2.1.9 http://freetype.org - fontconfig http://fontconfig.org - - quartz-font backend - ------------------- - MacOS X >= 10.4 with Xcode >= 2.4 - - win32 font backend - ------------------ - Microsoft Windows 2000 or newer[*]. - - [*] The Win32 backend should work on Windows 2000 and newer - (excluding Windows Me.) Most testing has been done on - Windows XP. While some portions of the code have been - adapted to work on older versions of Windows, considerable - work still needs to be done to get cairo running in those - environments. - - Cairo can be compiled on Windows with either the gcc - toolchain (see http://www.mingw.org) or with Microsoft - Visual C++. If the gcc toolchain is used, the standard - build instructions using configure apply, (see INSTALL). - If Visual C++ is desired, GNU make is required and - Makefile.win32 can be used via 'make -f Makefile.win32'. - The compiler, include paths, and library paths must be set - up correctly in the environment. - - MSVC versions earlier than 7.1 are known to miscompile - parts of cairo and pixman, and so should be avoided. MSVC - 7.1 or later, including the free Microsoft Visual Studio - Express editions, produce correct code. - -Experimental surface backends ------------------------------ - glitz - ------------- - glitz >= 0.4.4 http://freedesktop.org/Software/glitz - - xcb backend - ----------- - XCB http://xcb.freedesktop.org - - beos backend - ------------ - No dependencies in itself other than an installed BeOS system, but cairo - requires a font backend. See the freetype dependency list. - - os2 backend - ----------- - Cairo should run on any recent version of OS/2 or eComStation, but it - requires a font backend. See the freetype dependency list. Ready to use - packages and developer dependencies are available at Netlabs: - ftp://ftp.netlabs.org/pub/cairo - -Compiling -========= -See the INSTALL document for build instructions. - -History -======= -Cairo was originally developed by Carl Worth and -Keith Packard . Many thanks are due to Lyle Ramshaw -without whose patient help our ignorance would be much more apparent. - -Since the original development, many more people have contributed to -cairo. See the AUTHORS files for as complete a list as we've been able -to compile so far. diff --git a/libs/cairo/cairo/src/cairo-analysis-surface-private.h b/libs/cairo/cairo/src/cairo-analysis-surface-private.h deleted file mode 100644 index 81832126c..000000000 --- a/libs/cairo/cairo/src/cairo-analysis-surface-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_ANALYSIS_SURFACE_H -#define CAIRO_ANALYSIS_SURFACE_H - -#include "cairoint.h" - -CAIRO_BEGIN_DECLS - -cairo_private cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target); - -cairo_private void -_cairo_analysis_surface_set_ctm (cairo_surface_t *surface, - const cairo_matrix_t *ctm); - -cairo_private void -_cairo_analysis_surface_get_ctm (cairo_surface_t *surface, - cairo_matrix_t *ctm); - -cairo_private cairo_region_t * -_cairo_analysis_surface_get_supported (cairo_surface_t *surface); - -cairo_private cairo_region_t * -_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface); - -cairo_private cairo_bool_t -_cairo_analysis_surface_has_supported (cairo_surface_t *surface); - -cairo_private cairo_bool_t -_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface); - -cairo_private void -_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface, - cairo_box_t *bbox); - -cairo_private cairo_int_status_t -_cairo_analysis_surface_merge_status (cairo_int_status_t status_a, - cairo_int_status_t status_b); - -CAIRO_END_DECLS - -#endif /* CAIRO_ANALYSIS_SURFACE_H */ diff --git a/libs/cairo/cairo/src/cairo-analysis-surface.c b/libs/cairo/cairo/src/cairo-analysis-surface.c deleted file mode 100644 index 2be419994..000000000 --- a/libs/cairo/cairo/src/cairo-analysis-surface.c +++ /dev/null @@ -1,893 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" -#include "cairo-paginated-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-subsurface-private.h" -#include "cairo-region-private.h" - -typedef struct { - cairo_surface_t base; - - cairo_surface_t *target; - - cairo_bool_t first_op; - cairo_bool_t has_supported; - cairo_bool_t has_unsupported; - - cairo_region_t supported_region; - cairo_region_t fallback_region; - cairo_box_t page_bbox; - - cairo_bool_t has_ctm; - cairo_matrix_t ctm; - -} cairo_analysis_surface_t; - -cairo_int_status_t -_cairo_analysis_surface_merge_status (cairo_int_status_t status_a, - cairo_int_status_t status_b) -{ - /* fatal errors should be checked and propagated at source */ - assert (! _cairo_status_is_error (status_a)); - assert (! _cairo_status_is_error (status_b)); - - /* return the most important status */ - if (status_a == CAIRO_INT_STATUS_UNSUPPORTED || - status_b == CAIRO_INT_STATUS_UNSUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK || - status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK) - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - - if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN || - status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - - if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || - status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - - /* at this point we have checked all the valid internal codes, so... */ - assert (status_a == CAIRO_STATUS_SUCCESS && - status_b == CAIRO_STATUS_SUCCESS); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, - const cairo_pattern_t *pattern) -{ - const cairo_surface_pattern_t *surface_pattern; - cairo_bool_t old_has_ctm; - cairo_matrix_t old_ctm, p2d; - cairo_status_t status; - cairo_surface_t *source; - - assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); - surface_pattern = (const cairo_surface_pattern_t *) pattern; - assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); - - old_ctm = surface->ctm; - old_has_ctm = surface->has_ctm; - - p2d = pattern->matrix; - status = cairo_matrix_invert (&p2d); - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm); - surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); - - source = surface_pattern->surface; - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - source = sub->target; - } - - status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base); - - surface->ctm = old_ctm; - surface->has_ctm = old_has_ctm; - - return status; -} - -static cairo_int_status_t -_add_operation (cairo_analysis_surface_t *surface, - cairo_rectangle_int_t *rect, - cairo_int_status_t backend_status) -{ - cairo_int_status_t status; - cairo_box_t bbox; - - if (rect->width == 0 || rect->height == 0) { - /* Even though the operation is not visible we must be careful - * to not allow unsupported operations to be replayed to the - * backend during CAIRO_PAGINATED_MODE_RENDER */ - if (backend_status == CAIRO_STATUS_SUCCESS || - backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) - { - return CAIRO_STATUS_SUCCESS; - } - else - { - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - } - } - - _cairo_box_from_rectangle (&bbox, rect); - - if (surface->has_ctm) { - int tx, ty; - - if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) { - rect->x += tx; - rect->y += ty; - } else { - _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, - &bbox, NULL); - - if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { - /* Even though the operation is not visible we must be - * careful to not allow unsupported operations to be - * replayed to the backend during - * CAIRO_PAGINATED_MODE_RENDER */ - if (backend_status == CAIRO_STATUS_SUCCESS || - backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) - { - return CAIRO_STATUS_SUCCESS; - } - else - { - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - } - } - - _cairo_box_round_to_rectangle (&bbox, rect); - } - } - - if (surface->first_op) { - surface->first_op = FALSE; - surface->page_bbox = bbox; - } else { - if (bbox.p1.x < surface->page_bbox.p1.x) - surface->page_bbox.p1.x = bbox.p1.x; - if (bbox.p1.y < surface->page_bbox.p1.y) - surface->page_bbox.p1.y = bbox.p1.y; - if (bbox.p2.x > surface->page_bbox.p2.x) - surface->page_bbox.p2.x = bbox.p2.x; - if (bbox.p2.y > surface->page_bbox.p2.y) - surface->page_bbox.p2.y = bbox.p2.y; - } - - /* If the operation is completely enclosed within the fallback - * region there is no benefit in emitting a native operation as - * the fallback image will be painted on top. - */ - if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - - if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { - /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates - * that the backend only supports this operation if the - * transparency removed. If the extents of this operation does - * not intersect any other native operation, the operation is - * natively supported and the backend will blend the - * transparency into the white background. - */ - if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) - backend_status = CAIRO_STATUS_SUCCESS; - } - - if (backend_status == CAIRO_STATUS_SUCCESS) { - /* Add the operation to the supported region. Operations in - * this region will be emitted as native operations. - */ - surface->has_supported = TRUE; - return cairo_region_union_rectangle (&surface->supported_region, rect); - } - - /* Add the operation to the unsupported region. This region will - * be painted as an image after all native operations have been - * emitted. - */ - surface->has_unsupported = TRUE; - status = cairo_region_union_rectangle (&surface->fallback_region, rect); - - /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate - * unsupported operations to the recording surface as using - * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to - * invoke the cairo-surface-fallback path then return - * CAIRO_STATUS_SUCCESS. - */ - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - else - return status; -} - -static cairo_status_t -_cairo_analysis_surface_finish (void *abstract_surface) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - _cairo_region_fini (&surface->supported_region); - _cairo_region_fini (&surface->fallback_region); - - cairo_surface_destroy (surface->target); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_analysis_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_analysis_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->target, rectangle); -} - -static void -_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) -{ - const cairo_rectangle_int_t *clip_extents; - cairo_bool_t is_empty; - - clip_extents = NULL; - if (clip != NULL) - clip_extents = _cairo_clip_get_extents (clip); - - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (extents, clip_extents); -} - -static void -_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - is_empty = _cairo_surface_get_extents (&surface->base, extents); - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - _cairo_pattern_get_extents (source, &source_extents); - is_empty = _cairo_rectangle_intersect (extents, &source_extents); - } - - _rectangle_intersect_clip (extents, clip); -} - -static cairo_int_status_t -_cairo_analysis_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; - cairo_rectangle_int_t extents; - - if (surface->target->backend->paint == NULL) { - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - backend_status = - surface->target->backend->paint (surface->target, - op, source, clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - return _add_operation (surface, &extents, backend_status); -} - -static cairo_int_status_t -_cairo_analysis_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_int_status_t backend_status; - cairo_rectangle_int_t extents; - cairo_bool_t is_empty; - - if (surface->target->backend->mask == NULL) { - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - backend_status = - surface->target->backend->mask (surface->target, - op, source, mask, clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { - cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; - cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; - if (_cairo_surface_is_recording (surface_pattern->surface)) { - backend_source_status = - _analyze_recording_surface_pattern (surface, source); - if (_cairo_status_is_error (backend_source_status)) - return backend_source_status; - } - } - - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; - if (_cairo_surface_is_recording (surface_pattern->surface)) { - backend_mask_status = - _analyze_recording_surface_pattern (surface, mask); - if (_cairo_status_is_error (backend_mask_status)) - return backend_mask_status; - } - } - - backend_status = - _cairo_analysis_surface_merge_status (backend_source_status, - backend_mask_status); - } - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - _cairo_pattern_get_extents (mask, &mask_extents); - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); - - } - - return _add_operation (surface, &extents, backend_status); -} - -static cairo_int_status_t -_cairo_analysis_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; - cairo_rectangle_int_t extents; - cairo_bool_t is_empty; - - if (surface->target->backend->stroke == NULL) { - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - backend_status = - surface->target->backend->stroke (surface->target, op, - source, path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - /* If the backend can handle the stroke, then mark the approximate - * extents of the operation. However, if we need to fallback in order - * to draw the stroke, then ensure that the fallback is as tight as - * possible -- both to minimise output file size and to ensure good - * quality printed output for neighbouring regions. - */ - if (backend_status == CAIRO_STATUS_SUCCESS) { - _cairo_path_fixed_approximate_stroke_extents (path, - style, ctm, - &mask_extents); - } else { - cairo_status_t status; - - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &mask_extents); - if (unlikely (status)) - return status; - } - - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); - } - - return _add_operation (surface, &extents, backend_status); -} - -static cairo_int_status_t -_cairo_analysis_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; - cairo_rectangle_int_t extents; - cairo_bool_t is_empty; - - if (surface->target->backend->fill == NULL) { - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - backend_status = - surface->target->backend->fill (surface->target, op, - source, path, fill_rule, - tolerance, antialias, - clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - /* We want speed for the likely case where the operation can be - * performed natively, but accuracy if we have to resort to - * using images. - */ - if (backend_status == CAIRO_STATUS_SUCCESS) { - _cairo_path_fixed_approximate_fill_extents (path, &mask_extents); - } else { - _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &mask_extents); - } - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); - } - - return _add_operation (surface, &extents, backend_status); -} - -static cairo_int_status_t -_cairo_analysis_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; - cairo_rectangle_int_t extents, glyph_extents; - cairo_bool_t is_empty; - - /* Adapted from _cairo_surface_show_glyphs */ - if (surface->target->backend->show_glyphs != NULL) { - backend_status = - surface->target->backend->show_glyphs (surface->target, op, - source, - glyphs, num_glyphs, - scaled_font, - clip, - remaining_glyphs); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - else if (surface->target->backend->show_text_glyphs != NULL) - { - backend_status = - surface->target->backend->show_text_glyphs (surface->target, op, - source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE, - scaled_font, - clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - else - { - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - if (_cairo_operator_bounded_by_mask (op)) { - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); - } - - return _add_operation (surface, &extents, backend_status); -} - -static cairo_bool_t -_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface) -{ - cairo_analysis_surface_t *surface = abstract_surface; - - return cairo_surface_has_show_text_glyphs (surface->target); -} - -static cairo_int_status_t -_cairo_analysis_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; - cairo_rectangle_int_t extents, glyph_extents; - cairo_bool_t is_empty; - - /* Adapted from _cairo_surface_show_glyphs */ - backend_status = CAIRO_INT_STATUS_UNSUPPORTED; - if (surface->target->backend->show_text_glyphs != NULL) { - backend_status = - surface->target->backend->show_text_glyphs (surface->target, op, - source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); - if (_cairo_status_is_error (backend_status)) - return backend_status; - } - if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && - surface->target->backend->show_glyphs != NULL) - { - int remaining_glyphs = num_glyphs; - backend_status = - surface->target->backend->show_glyphs (surface->target, op, - source, - glyphs, num_glyphs, - scaled_font, - clip, - &remaining_glyphs); - if (_cairo_status_is_error (backend_status)) - return backend_status; - - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (remaining_glyphs == 0) - backend_status = CAIRO_STATUS_SUCCESS; - } - - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - - if (_cairo_operator_bounded_by_mask (op)) { - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); - } - - return _add_operation (surface, &extents, backend_status); -} - -static const cairo_surface_backend_t cairo_analysis_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, - NULL, /* create_similar */ - _cairo_analysis_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_analysis_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _cairo_analysis_surface_paint, - _cairo_analysis_surface_mask, - _cairo_analysis_surface_stroke, - _cairo_analysis_surface_fill, - _cairo_analysis_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _cairo_analysis_surface_has_show_text_glyphs, - _cairo_analysis_surface_show_text_glyphs -}; - -cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target) -{ - cairo_analysis_surface_t *surface; - cairo_status_t status; - - status = target->status; - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - surface = malloc (sizeof (cairo_analysis_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - /* I believe the content type here is truly arbitrary. I'm quite - * sure nothing will ever use this value. */ - _cairo_surface_init (&surface->base, - &cairo_analysis_surface_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); - - cairo_matrix_init_identity (&surface->ctm); - surface->has_ctm = FALSE; - - surface->target = cairo_surface_reference (target); - surface->first_op = TRUE; - surface->has_supported = FALSE; - surface->has_unsupported = FALSE; - - _cairo_region_init (&surface->supported_region); - _cairo_region_init (&surface->fallback_region); - - surface->page_bbox.p1.x = 0; - surface->page_bbox.p1.y = 0; - surface->page_bbox.p2.x = 0; - surface->page_bbox.p2.y = 0; - - return &surface->base; -} - -void -_cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, - const cairo_matrix_t *ctm) -{ - cairo_analysis_surface_t *surface; - - if (abstract_surface->status) - return; - - surface = (cairo_analysis_surface_t *) abstract_surface; - - surface->ctm = *ctm; - surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); -} - -void -_cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface, - cairo_matrix_t *ctm) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - *ctm = surface->ctm; -} - - -cairo_region_t * -_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - return &surface->supported_region; -} - -cairo_region_t * -_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - return &surface->fallback_region; -} - -cairo_bool_t -_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - return surface->has_supported; -} - -cairo_bool_t -_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - return surface->has_unsupported; -} - -void -_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, - cairo_box_t *bbox) -{ - cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; - - *bbox = surface->page_bbox; -} - -/* null surface type: a surface that does nothing (has no side effects, yay!) */ - -static cairo_int_status_t -_return_success (void) -{ - return CAIRO_STATUS_SUCCESS; -} - -/* These typedefs are just to silence the compiler... */ -typedef cairo_int_status_t -(*_paint_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_mask_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_stroke_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_fill_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_show_glyphs_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -static const cairo_surface_backend_t cairo_null_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_NULL, - - NULL, /* create_similar */ - NULL, /* finish */ - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - NULL, /* get_extents */ - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - (_paint_func) _return_success, /* paint */ - (_mask_func) _return_success, /* mask */ - (_stroke_func) _return_success, /* stroke */ - (_fill_func) _return_success, /* fill */ - (_show_glyphs_func) _return_success, /* show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - NULL, /* has_show_text_glyphs */ - NULL /* show_text_glyphs */ -}; - -cairo_surface_t * -cairo_null_surface_create (cairo_content_t content) -{ - cairo_surface_t *surface; - - surface = malloc (sizeof (cairo_surface_t)); - if (unlikely (surface == NULL)) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (surface, - &cairo_null_surface_backend, - NULL, /* device */ - content); - - return surface; -} diff --git a/libs/cairo/cairo/src/cairo-arc-private.h b/libs/cairo/cairo/src/cairo-arc-private.h deleted file mode 100644 index e8dcc1394..000000000 --- a/libs/cairo/cairo/src/cairo-arc-private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_ARC_PRIVATE_H -#define CAIRO_ARC_PRIVATE_H - -#include "cairoint.h" - -cairo_private void -_cairo_arc_path (cairo_t *cr, - double xc, - double yc, - double radius, - double angle1, - double angle2); - -cairo_private void -_cairo_arc_path_negative (cairo_t *cr, - double xc, - double yc, - double radius, - double angle1, - double angle2); - -#endif /* CAIRO_ARC_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-arc.c b/libs/cairo/cairo/src/cairo-arc.c deleted file mode 100644 index 54a0723d0..000000000 --- a/libs/cairo/cairo/src/cairo-arc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-arc-private.h" - -/* Spline deviation from the circle in radius would be given by: - - error = sqrt (x**2 + y**2) - 1 - - A simpler error function to work with is: - - e = x**2 + y**2 - 1 - - From "Good approximation of circles by curvature-continuous Bezier - curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric - Design 8 (1990) 22-41, we learn: - - abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) - - and - abs (error) =~ 1/2 * e - - Of course, this error value applies only for the particular spline - approximation that is used in _cairo_gstate_arc_segment. -*/ -static double -_arc_error_normalized (double angle) -{ - return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); -} - -static double -_arc_max_angle_for_tolerance_normalized (double tolerance) -{ - double angle, error; - int i; - - /* Use table lookup to reduce search time in most cases. */ - struct { - double angle; - double error; - } table[] = { - { M_PI / 1.0, 0.0185185185185185036127 }, - { M_PI / 2.0, 0.000272567143730179811158 }, - { M_PI / 3.0, 2.38647043651461047433e-05 }, - { M_PI / 4.0, 4.2455377443222443279e-06 }, - { M_PI / 5.0, 1.11281001494389081528e-06 }, - { M_PI / 6.0, 3.72662000942734705475e-07 }, - { M_PI / 7.0, 1.47783685574284411325e-07 }, - { M_PI / 8.0, 6.63240432022601149057e-08 }, - { M_PI / 9.0, 3.2715520137536980553e-08 }, - { M_PI / 10.0, 1.73863223499021216974e-08 }, - { M_PI / 11.0, 9.81410988043554039085e-09 }, - }; - int table_size = ARRAY_LENGTH (table); - - for (i = 0; i < table_size; i++) - if (table[i].error < tolerance) - return table[i].angle; - - ++i; - do { - angle = M_PI / i++; - error = _arc_error_normalized (angle); - } while (error > tolerance); - - return angle; -} - -static int -_arc_segments_needed (double angle, - double radius, - cairo_matrix_t *ctm, - double tolerance) -{ - double major_axis, max_angle; - - /* the error is amplified by at most the length of the - * major axis of the circle; see cairo-pen.c for a more detailed analysis - * of this. */ - major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius); - max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis); - - return ceil (fabs (angle) / max_angle); -} - -/* We want to draw a single spline approximating a circular arc radius - R from angle A to angle B. Since we want a symmetric spline that - matches the endpoints of the arc in position and slope, we know - that the spline control points must be: - - (R * cos(A), R * sin(A)) - (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) - (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) - (R * cos(B), R * sin(B)) - - for some value of h. - - "Approximation of circular arcs by cubic poynomials", Michael - Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides - various values of h along with error analysis for each. - - From that paper, a very practical value of h is: - - h = 4/3 * tan(angle/4) - - This value does not give the spline with minimal error, but it does - provide a very good approximation, (6th-order convergence), and the - error expression is quite simple, (see the comment for - _arc_error_normalized). -*/ -static void -_cairo_arc_segment (cairo_t *cr, - double xc, - double yc, - double radius, - double angle_A, - double angle_B) -{ - double r_sin_A, r_cos_A; - double r_sin_B, r_cos_B; - double h; - - r_sin_A = radius * sin (angle_A); - r_cos_A = radius * cos (angle_A); - r_sin_B = radius * sin (angle_B); - r_cos_B = radius * cos (angle_B); - - h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); - - cairo_curve_to (cr, - xc + r_cos_A - h * r_sin_A, - yc + r_sin_A + h * r_cos_A, - xc + r_cos_B + h * r_sin_B, - yc + r_sin_B - h * r_cos_B, - xc + r_cos_B, - yc + r_sin_B); -} - -static void -_cairo_arc_in_direction (cairo_t *cr, - double xc, - double yc, - double radius, - double angle_min, - double angle_max, - cairo_direction_t dir) -{ - if (cairo_status (cr)) - return; - - while (angle_max - angle_min > 4 * M_PI) - angle_max -= 2 * M_PI; - - /* Recurse if drawing arc larger than pi */ - if (angle_max - angle_min > M_PI) { - double angle_mid = angle_min + (angle_max - angle_min) / 2.0; - if (dir == CAIRO_DIRECTION_FORWARD) { - _cairo_arc_in_direction (cr, xc, yc, radius, - angle_min, angle_mid, - dir); - - _cairo_arc_in_direction (cr, xc, yc, radius, - angle_mid, angle_max, - dir); - } else { - _cairo_arc_in_direction (cr, xc, yc, radius, - angle_mid, angle_max, - dir); - - _cairo_arc_in_direction (cr, xc, yc, radius, - angle_min, angle_mid, - dir); - } - } else if (angle_max != angle_min) { - cairo_matrix_t ctm; - int i, segments; - double angle, angle_step; - - cairo_get_matrix (cr, &ctm); - segments = _arc_segments_needed (angle_max - angle_min, - radius, &ctm, - cairo_get_tolerance (cr)); - angle_step = (angle_max - angle_min) / (double) segments; - - if (dir == CAIRO_DIRECTION_FORWARD) { - angle = angle_min; - } else { - angle = angle_max; - angle_step = - angle_step; - } - - for (i = 0; i < segments; i++, angle += angle_step) { - _cairo_arc_segment (cr, xc, yc, - radius, - angle, - angle + angle_step); - } - } -} - -/** - * _cairo_arc_path - * @cr: a cairo context - * @xc: X position of the center of the arc - * @yc: Y position of the center of the arc - * @radius: the radius of the arc - * @angle1: the start angle, in radians - * @angle2: the end angle, in radians - * - * Compute a path for the given arc and append it onto the current - * path within @cr. The arc will be accurate within the current - * tolerance and given the current transformation. - **/ -void -_cairo_arc_path (cairo_t *cr, - double xc, - double yc, - double radius, - double angle1, - double angle2) -{ - _cairo_arc_in_direction (cr, xc, yc, - radius, - angle1, angle2, - CAIRO_DIRECTION_FORWARD); -} - -/** - * _cairo_arc_path_negative: - * @xc: X position of the center of the arc - * @yc: Y position of the center of the arc - * @radius: the radius of the arc - * @angle1: the start angle, in radians - * @angle2: the end angle, in radians - * @ctm: the current transformation matrix - * @tolerance: the current tolerance value - * @path: the path onto which the arc will be appended - * - * Compute a path for the given arc (defined in the negative - * direction) and append it onto the current path within @cr. The arc - * will be accurate within the current tolerance and given the current - * transformation. - **/ -void -_cairo_arc_path_negative (cairo_t *cr, - double xc, - double yc, - double radius, - double angle1, - double angle2) -{ - _cairo_arc_in_direction (cr, xc, yc, - radius, - angle2, angle1, - CAIRO_DIRECTION_REVERSE); -} diff --git a/libs/cairo/cairo/src/cairo-array.c b/libs/cairo/cairo/src/cairo-array.c deleted file mode 100644 index 442540c58..000000000 --- a/libs/cairo/cairo/src/cairo-array.c +++ /dev/null @@ -1,529 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -/** - * _cairo_array_init: - * - * Initialize a new #cairo_array_t object to store objects each of size - * @element_size. - * - * The #cairo_array_t object provides grow-by-doubling storage. It - * never interprets the data passed to it, nor does it provide any - * sort of callback mechanism for freeing resources held onto by - * stored objects. - * - * When finished using the array, _cairo_array_fini() should be - * called to free resources allocated during use of the array. - **/ -void -_cairo_array_init (cairo_array_t *array, int element_size) -{ - array->size = 0; - array->num_elements = 0; - array->element_size = element_size; - array->elements = NULL; - - array->is_snapshot = FALSE; - -} - -/** - * _cairo_array_init_snapshot: - * @array: A #cairo_array_t to be initialized as a snapshot - * @other: The #cairo_array_t from which to create the snapshot - * - * Initialize @array as an immutable copy of @other. It is an error to - * call an array-modifying function (other than _cairo_array_fini) on - * @array after calling this function. - **/ -void -_cairo_array_init_snapshot (cairo_array_t *array, - const cairo_array_t *other) -{ - array->size = other->size; - array->num_elements = other->num_elements; - array->element_size = other->element_size; - array->elements = other->elements; - - array->is_snapshot = TRUE; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); -} - -/** - * _cairo_array_fini: - * @array: A #cairo_array_t - * - * Free all resources associated with @array. After this call, @array - * should not be used again without a subsequent call to - * _cairo_array_init() again first. - **/ -void -_cairo_array_fini (cairo_array_t *array) -{ - if (array->is_snapshot) - return; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - if (array->elements) { - free (* array->elements); - free (array->elements); - } -} - -/** - * _cairo_array_grow_by: - * @array: a #cairo_array_t - * - * Increase the size of @array (if needed) so that there are at least - * @additional free spaces in the array. The actual size of the array - * is always increased by doubling as many times as necessary. - **/ -cairo_status_t -_cairo_array_grow_by (cairo_array_t *array, unsigned int additional) -{ - char *new_elements; - unsigned int old_size = array->size; - unsigned int required_size = array->num_elements + additional; - unsigned int new_size; - - assert (! array->is_snapshot); - - /* check for integer overflow */ - if (required_size > INT_MAX || required_size < array->num_elements) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (required_size <= old_size) - return CAIRO_STATUS_SUCCESS; - - if (old_size == 0) - new_size = 1; - else - new_size = old_size * 2; - - while (new_size < required_size) - new_size = new_size * 2; - - if (array->elements == NULL) { - array->elements = malloc (sizeof (char *)); - if (unlikely (array->elements == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *array->elements = NULL; - } - - array->size = new_size; - new_elements = _cairo_realloc_ab (*array->elements, - array->size, array->element_size); - - if (unlikely (new_elements == NULL)) { - array->size = old_size; - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *array->elements = new_elements; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_array_truncate: - * @array: a #cairo_array_t - * - * Truncate size of the array to @num_elements if less than the - * current size. No memory is actually freed. The stored objects - * beyond @num_elements are simply "forgotten". - **/ -void -_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) -{ - assert (! array->is_snapshot); - - if (num_elements < array->num_elements) - array->num_elements = num_elements; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); -} - -/** - * _cairo_array_index: - * @array: a #cairo_array_t - * Returns: A pointer to the object stored at @index. - * - * If the resulting value is assigned to a pointer to an object of the same - * element_size as initially passed to _cairo_array_init() then that - * pointer may be used for further direct indexing with []. For - * example: - * - * - * cairo_array_t array; - * double *values; - * - * _cairo_array_init (&array, sizeof(double)); - * ... calls to _cairo_array_append() here ... - * - * values = _cairo_array_index (&array, 0); - * for (i = 0; i < _cairo_array_num_elements (&array); i++) - * ... use values[i] here ... - * - **/ -void * -_cairo_array_index (cairo_array_t *array, unsigned int index) -{ - /* We allow an index of 0 for the no-elements case. - * This makes for cleaner calling code which will often look like: - * - * elements = _cairo_array_index (array, num_elements); - * for (i=0; i < num_elements; i++) { - * ... use elements[i] here ... - * } - * - * which in the num_elements==0 case gets the NULL pointer here, - * but never dereferences it. - */ - if (index == 0 && array->num_elements == 0) - return NULL; - - assert (index < array->num_elements); - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - return (void *) &(*array->elements)[index * array->element_size]; -} - -/** - * _cairo_array_copy_element: - * @array: a #cairo_array_t - * - * Copy a single element out of the array from index @index into the - * location pointed to by @dst. - **/ -void -_cairo_array_copy_element (cairo_array_t *array, int index, void *dst) -{ - memcpy (dst, _cairo_array_index (array, index), array->element_size); -} - -/** - * _cairo_array_append: - * @array: a #cairo_array_t - * - * Append a single item onto the array by growing the array by at - * least one element, then copying element_size bytes from @element - * into the array. The address of the resulting object within the - * array can be determined with: - * - * _cairo_array_index (array, _cairo_array_num_elements (array) - 1); - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the - * operation. - **/ -cairo_status_t -_cairo_array_append (cairo_array_t *array, - const void *element) -{ - assert (! array->is_snapshot); - - return _cairo_array_append_multiple (array, element, 1); -} - -/** - * _cairo_array_append_multiple: - * @array: a #cairo_array_t - * - * Append one or more items onto the array by growing the array by - * @num_elements, then copying @num_elements * element_size bytes from - * @elements into the array. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the - * operation. - **/ -cairo_status_t -_cairo_array_append_multiple (cairo_array_t *array, - const void *elements, - int num_elements) -{ - cairo_status_t status; - void *dest; - - assert (! array->is_snapshot); - - status = _cairo_array_allocate (array, num_elements, &dest); - if (unlikely (status)) - return status; - - memcpy (dest, elements, num_elements * array->element_size); - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_array_allocate: - * @array: a #cairo_array_t - * - * Allocate space at the end of the array for @num_elements additional - * elements, providing the address of the new memory chunk in - * @elements. This memory will be unitialized, but will be accounted - * for in the return value of _cairo_array_num_elements(). - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the - * operation. - **/ -cairo_status_t -_cairo_array_allocate (cairo_array_t *array, - unsigned int num_elements, - void **elements) -{ - cairo_status_t status; - - assert (! array->is_snapshot); - - status = _cairo_array_grow_by (array, num_elements); - if (unlikely (status)) - return status; - - assert (array->num_elements + num_elements <= array->size); - - *elements = &(*array->elements)[array->num_elements * array->element_size]; - - array->num_elements += num_elements; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_array_num_elements: - * @array: a #cairo_array_t - * Returns: The number of elements stored in @array. - * - * This space was left intentionally blank, but gtk-doc filled it. - **/ -int -_cairo_array_num_elements (cairo_array_t *array) -{ - return array->num_elements; -} - -/** - * _cairo_array_size: - * @array: a #cairo_array_t - * Returns: The number of elements for which there is currently space - * allocated in @array. - * - * This space was left intentionally blank, but gtk-doc filled it. - **/ -int -_cairo_array_size (cairo_array_t *array) -{ - return array->size; -} - -/** - * _cairo_user_data_array_init: - * @array: a #cairo_user_data_array_t - * - * Initializes a #cairo_user_data_array_t structure for future - * use. After initialization, the array has no keys. Call - * _cairo_user_data_array_fini() to free any allocated memory - * when done using the array. - **/ -void -_cairo_user_data_array_init (cairo_user_data_array_t *array) -{ - _cairo_array_init (array, sizeof (cairo_user_data_slot_t)); -} - -/** - * _cairo_user_data_array_fini: - * @array: a #cairo_user_data_array_t - * - * Destroys all current keys in the user data array and deallocates - * any memory allocated for the array itself. - **/ -void -_cairo_user_data_array_fini (cairo_user_data_array_t *array) -{ - unsigned int num_slots; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - num_slots = array->num_elements; - if (num_slots) { - cairo_user_data_slot_t *slots; - - slots = _cairo_array_index (array, 0); - do { - if (slots->user_data != NULL && slots->destroy != NULL) - slots->destroy (slots->user_data); - slots++; - } while (--num_slots); - } - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - _cairo_array_fini (array); -} - -/** - * _cairo_user_data_array_get_data: - * @array: a #cairo_user_data_array_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Returns user data previously attached using the specified - * key. If no user data has been attached with the given key this - * function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - **/ -void * -_cairo_user_data_array_get_data (cairo_user_data_array_t *array, - const cairo_user_data_key_t *key) -{ - int i, num_slots; - cairo_user_data_slot_t *slots; - - /* We allow this to support degenerate objects such as cairo_surface_nil. */ - if (array == NULL) - return NULL; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - num_slots = array->num_elements; - slots = _cairo_array_index (array, 0); - for (i = 0; i < num_slots; i++) { - if (slots[i].key == key) - return slots[i].user_data; - } - - return NULL; -} - -/** - * _cairo_user_data_array_set_data: - * @array: a #cairo_user_data_array_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach - * @destroy: a #cairo_destroy_func_t which will be called when the - * user data array is destroyed or when new user data is attached using the - * same key. - * - * Attaches user data to a user data array. To remove user data, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - **/ -cairo_status_t -_cairo_user_data_array_set_data (cairo_user_data_array_t *array, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - cairo_status_t status; - int i, num_slots; - cairo_user_data_slot_t *slots, *slot, new_slot; - - if (user_data) { - new_slot.key = key; - new_slot.user_data = user_data; - new_slot.destroy = destroy; - } else { - new_slot.key = NULL; - new_slot.user_data = NULL; - new_slot.destroy = NULL; - } - - slot = NULL; - num_slots = array->num_elements; - slots = _cairo_array_index (array, 0); - for (i = 0; i < num_slots; i++) { - if (slots[i].key == key) { - slot = &slots[i]; - if (slot->destroy && slot->user_data) - slot->destroy (slot->user_data); - break; - } - if (user_data && slots[i].user_data == NULL) { - slot = &slots[i]; /* Have to keep searching for an exact match */ - } - } - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - if (slot) { - *slot = new_slot; - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_array_append (array, &new_slot); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_user_data_array_copy (cairo_user_data_array_t *dst, - cairo_user_data_array_t *src) -{ - /* discard any existing user-data */ - if (dst->num_elements != 0) { - _cairo_user_data_array_fini (dst); - _cairo_user_data_array_init (dst); - } - - if (src->num_elements == 0) - return CAIRO_STATUS_SUCCESS; - - return _cairo_array_append_multiple (dst, - _cairo_array_index (src, 0), - src->num_elements); -} - -void -_cairo_user_data_array_foreach (cairo_user_data_array_t *array, - void (*func) (const void *key, - void *elt, - void *closure), - void *closure) -{ - cairo_user_data_slot_t *slots; - int i, num_slots; - - num_slots = array->num_elements; - slots = _cairo_array_index (array, 0); - for (i = 0; i < num_slots; i++) { - if (slots[i].user_data != NULL) - func (slots[i].key, slots[i].user_data, closure); - } -} diff --git a/libs/cairo/cairo/src/cairo-atomic-private.h b/libs/cairo/cairo/src/cairo-atomic-private.h deleted file mode 100644 index e14c5fdaf..000000000 --- a/libs/cairo/cairo/src/cairo-atomic-private.h +++ /dev/null @@ -1,385 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_ATOMIC_PRIVATE_H -#define CAIRO_ATOMIC_PRIVATE_H - -# include "cairo-compiler-private.h" - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_WIN32_ATOMIC_PRIMITIVES -#include -#endif - -/* The autoconf on OpenBSD 4.5 produces the malformed constant name - * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ -#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__) -# define SIZEOF_VOID_P SIZEOF_VOID__ -#endif - -CAIRO_BEGIN_DECLS - -/* C++11 atomic primitives were designed to be more flexible than the - * __sync_* family of primitives. Despite the name, they are available - * in C as well as C++. The motivating reason for using them is that - * for _cairo_atomic_{int,ptr}_get, the compiler is able to see that - * the load is intended to be atomic, as opposed to the __sync_* - * version, below, where the load looks like a plain load. Having - * the load appear atomic to the compiler is particular important for - * tools like ThreadSanitizer so they don't report false positives on - * memory operations that we intend to be atomic. - */ -#if HAVE_CXX11_ATOMIC_PRIMITIVES - -#define HAS_ATOMIC_OPS 1 - -typedef int cairo_atomic_int_t; - -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_get (cairo_atomic_int_t *x) -{ - return __atomic_load_n(x, __ATOMIC_SEQ_CST); -} - -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) -{ - return __atomic_load_n(x, __ATOMIC_RELAXED); -} - -static cairo_always_inline void -_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) -{ - __atomic_store_n(x, val, __ATOMIC_RELAXED); -} - -static cairo_always_inline void * -_cairo_atomic_ptr_get (void **x) -{ - return __atomic_load_n(x, __ATOMIC_SEQ_CST); -} - -# define _cairo_atomic_int_inc(x) ((void) __atomic_fetch_add(x, 1, __ATOMIC_SEQ_CST)) -# define _cairo_atomic_int_dec(x) ((void) __atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST)) -# define _cairo_atomic_int_dec_and_test(x) (__atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST) == 1) - -#if SIZEOF_VOID_P==SIZEOF_INT -typedef int cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG -typedef long cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG -typedef long long cairo_atomic_intptr_t; -#else -#error No matching integer pointer type -#endif - -static cairo_always_inline cairo_bool_t -_cairo_atomic_int_cmpxchg_impl(cairo_atomic_int_t *x, - cairo_atomic_int_t oldv, - cairo_atomic_int_t newv) -{ - cairo_atomic_int_t expected = oldv; - return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -#define _cairo_atomic_int_cmpxchg(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg_impl(x, oldv, newv) - -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_cmpxchg_return_old_impl(cairo_atomic_int_t *x, - cairo_atomic_int_t oldv, - cairo_atomic_int_t newv) -{ - cairo_atomic_int_t expected = oldv; - (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; -} - -#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg_return_old_impl(x, oldv, newv) - -static cairo_always_inline cairo_bool_t -_cairo_atomic_ptr_cmpxchg_impl(void **x, void *oldv, void *newv) -{ - void *expected = oldv; - return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - _cairo_atomic_ptr_cmpxchg_impl(x, oldv, newv) - -static cairo_always_inline void * -_cairo_atomic_ptr_cmpxchg_return_old_impl(void **x, void *oldv, void *newv) -{ - void *expected = oldv; - (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; -} - -#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ - _cairo_atomic_ptr_cmpxchg_return_old_impl(x, oldv, newv) - -#endif - -#if HAVE_WIN32_ATOMIC_PRIMITIVES - -#define HAS_ATOMIC_OPS 1 - -typedef volatile long cairo_atomic_int_t; - -# define _cairo_atomic_int_get(x) ((int)*x) -# define _cairo_atomic_int_get_relaxed(x) ((int)*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) -# define _cairo_atomic_ptr_get(x) ((void*)*x) - -# define _cairo_atomic_int_inc(x) ((void) InterlockedIncrement(x)) -# define _cairo_atomic_int_dec(x) ((void) InterlockedDecrement(x)) -# define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement(x) == 0) -# define _cairo_atomic_int_cmpxchg(x, oldv, newv) (InterlockedCompareExchange(x, newv, oldv) == oldv) -# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) InterlockedCompareExchange(x, newv, oldv) - -typedef volatile void* cairo_atomic_intptr_t; - -#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv) == oldv) -#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv)) - -#endif - -#if HAVE_INTEL_ATOMIC_PRIMITIVES - -#define HAS_ATOMIC_OPS 1 - -typedef int cairo_atomic_int_t; - -#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_get (cairo_atomic_int_t *x) -{ - __sync_synchronize (); - return *x; -} - -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) -{ - return *x; -} - -static cairo_always_inline void -_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) -{ - *x = val; -} - -static cairo_always_inline void * -_cairo_atomic_ptr_get (void **x) -{ - __sync_synchronize (); - return *x; -} -#else -# define _cairo_atomic_int_get(x) (*x) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) -# define _cairo_atomic_ptr_get(x) (*x) -#endif - -# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) -# define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) -# define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv) -# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) - -#if SIZEOF_VOID_P==SIZEOF_INT -typedef int cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG -typedef long cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG -typedef long long cairo_atomic_intptr_t; -#else -#error No matching integer pointer type -#endif - -# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - __sync_bool_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) - -# define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ - _cairo_atomic_intptr_to_voidptr (__sync_val_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)) - -#endif - -#if HAVE_LIB_ATOMIC_OPS -#include - -#define HAS_ATOMIC_OPS 1 - -typedef AO_t cairo_atomic_int_t; - -# define _cairo_atomic_int_get(x) (AO_load_full (x)) -# define _cairo_atomic_int_get_relaxed(x) (AO_load_full (x)) -# define _cairo_atomic_int_set_relaxed(x, val) (AO_store_full ((x), (val))) - -# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) -# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1) -# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv) - -#if SIZEOF_VOID_P==SIZEOF_INT -typedef unsigned int cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG -typedef unsigned long cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG -typedef unsigned long long cairo_atomic_intptr_t; -#else -#error No matching integer pointer type -#endif - -# define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x)) -# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) - -#endif - -#if HAVE_OS_ATOMIC_OPS -#include - -#define HAS_ATOMIC_OPS 1 - -typedef int32_t cairo_atomic_int_t; - -# define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x)) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) - -# define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x)) -# define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0) -# define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x) - -#if SIZEOF_VOID_P==4 -typedef int32_t cairo_atomic_intptr_t; -# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) - -#elif SIZEOF_VOID_P==8 -typedef int64_t cairo_atomic_intptr_t; -# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) - -#else -#error No matching integer pointer type -#endif - -# define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x)) - -#endif - -#ifndef HAS_ATOMIC_OPS - -#if SIZEOF_VOID_P==SIZEOF_INT -typedef unsigned int cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG -typedef unsigned long cairo_atomic_intptr_t; -#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG -typedef unsigned long long cairo_atomic_intptr_t; -#else -#error No matching integer pointer type -#endif - -typedef cairo_atomic_intptr_t cairo_atomic_int_t; - -cairo_private void -_cairo_atomic_int_inc (cairo_atomic_int_t *x); - -cairo_private cairo_bool_t -_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x); - -cairo_private cairo_atomic_int_t -_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv); - -cairo_private void * -_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv); - -#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv) -#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv) - -#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER -cairo_private cairo_atomic_int_t -_cairo_atomic_int_get (cairo_atomic_int_t *x); -cairo_private cairo_atomic_int_t -_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x); -void -_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val); -# define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) -#else -# define _cairo_atomic_int_get(x) (*x) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) -# define _cairo_atomic_ptr_get(x) (*x) -#endif - -#else - -/* Workaround GCC complaining about casts */ -static cairo_always_inline void * -_cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x) -{ - return (void *) x; -} - -static cairo_always_inline cairo_atomic_int_t -_cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) -{ - cairo_atomic_int_t curr; - - do { - curr = _cairo_atomic_int_get (x); - } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv)); - - return curr; -} - -static cairo_always_inline void * -_cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) -{ - void *curr; - - do { - curr = _cairo_atomic_ptr_get (x); - } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv)); - - return curr; -} -#endif - -#ifndef _cairo_atomic_int_cmpxchg_return_old -#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv) -#endif - -#ifndef _cairo_atomic_ptr_cmpxchg_return_old -#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv) -#endif - -#ifndef _cairo_atomic_int_cmpxchg -#define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv) -#endif - -#ifndef _cairo_atomic_ptr_cmpxchg -#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv) -#endif - -#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) -#define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv) - -#define _cairo_status_set_error(status, err) do { \ - /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ - * an unsigned integer instead, and about ignoring the return value. */ \ - int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ - (void) ret__; \ -} while (0) - -CAIRO_END_DECLS - -#endif diff --git a/libs/cairo/cairo/src/cairo-atomic.c b/libs/cairo/cairo/src/cairo-atomic.c deleted file mode 100644 index fd5ee930b..000000000 --- a/libs/cairo/cairo/src/cairo-atomic.c +++ /dev/null @@ -1,91 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-atomic-private.h" -#include "cairo-mutex-private.h" - -#ifdef HAS_ATOMIC_OPS -COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) || - sizeof(void*) == sizeof(long) || - sizeof(void*) == sizeof(long long)); -#else -void -_cairo_atomic_int_inc (cairo_atomic_intptr_t *x) -{ - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - *x += 1; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); -} - -cairo_bool_t -_cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) -{ - cairo_bool_t ret; - - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - ret = --*x == 0; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); - - return ret; -} - -cairo_atomic_intptr_t -_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv) -{ - cairo_atomic_intptr_t ret; - - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - ret = *x; - if (ret == oldv) - *x = newv; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); - - return ret; -} - -void * -_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv) -{ - void *ret; - - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - ret = *x; - if (ret == oldv) - *x = newv; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); - - return ret; -} - -#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER -cairo_atomic_intptr_t -_cairo_atomic_int_get (cairo_atomic_intptr_t *x) -{ - cairo_atomic_intptr_t ret; - - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - ret = *x; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); - - return ret; -} - -cairo_atomic_intptr_t -_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x) -{ - return _cairo_atomic_int_get (x); -} - -void -_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val) -{ - CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); - *x = val; - CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); -} -#endif - -#endif diff --git a/libs/cairo/cairo/src/cairo-base64-stream.c b/libs/cairo/cairo/src/cairo-base64-stream.c deleted file mode 100644 index 02ca8bd45..000000000 --- a/libs/cairo/cairo/src/cairo-base64-stream.c +++ /dev/null @@ -1,110 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-base85-stream.c b/libs/cairo/cairo/src/cairo-base85-stream.c deleted file mode 100644 index ba19ff4a2..000000000 --- a/libs/cairo/cairo/src/cairo-base85-stream.c +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c b/libs/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c deleted file mode 100644 index 4736f4f41..000000000 --- a/libs/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c +++ /dev/null @@ -1,805 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Provide definitions for standalone compilation */ -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-error-private.h" -#include "cairo-combsort-private.h" -#include "cairo-list-private.h" - -#include - -typedef struct _rectangle rectangle_t; -typedef struct _edge edge_t; - -struct _edge { - edge_t *next, *prev; - edge_t *right; - cairo_fixed_t x, top; - int dir; -}; - -struct _rectangle { - edge_t left, right; - int32_t top, bottom; -}; - -#define UNROLL3(x) x x x - -/* the parent is always given by index/2 */ -#define PQ_PARENT_INDEX(i) ((i) >> 1) -#define PQ_FIRST_ENTRY 1 - -/* left and right children are index * 2 and (index * 2) +1 respectively */ -#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) - -typedef struct _pqueue { - int size, max_size; - - rectangle_t **elements; - rectangle_t *elements_embedded[1024]; -} pqueue_t; - -typedef struct _sweep_line { - rectangle_t **rectangles; - pqueue_t pq; - edge_t head, tail; - edge_t *insert_left, *insert_right; - int32_t current_y; - int32_t last_y; - - cairo_fill_rule_t fill_rule; - - jmp_buf unwind; -} sweep_line_t; - -#define DEBUG_TRAPS 0 - -#if DEBUG_TRAPS -static void -dump_traps (cairo_traps_t *traps, const char *filename) -{ - FILE *file; - int n; - - if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) - return; - - file = fopen (filename, "a"); - if (file != NULL) { - for (n = 0; n < traps->num_traps; n++) { - fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", - traps->traps[n].top, - traps->traps[n].bottom, - traps->traps[n].left.p1.x, - traps->traps[n].left.p1.y, - traps->traps[n].left.p2.x, - traps->traps[n].left.p2.y, - traps->traps[n].right.p1.x, - traps->traps[n].right.p1.y, - traps->traps[n].right.p2.x, - traps->traps[n].right.p2.y); - } - fprintf (file, "\n"); - fclose (file); - } -} -#else -#define dump_traps(traps, filename) -#endif - -static inline int -rectangle_compare_start (const rectangle_t *a, - const rectangle_t *b) -{ - return a->top - b->top; -} - -static inline int -rectangle_compare_stop (const rectangle_t *a, - const rectangle_t *b) -{ - return a->bottom - b->bottom; -} - -static inline void -pqueue_init (pqueue_t *pq) -{ - pq->max_size = ARRAY_LENGTH (pq->elements_embedded); - pq->size = 0; - - pq->elements = pq->elements_embedded; - pq->elements[PQ_FIRST_ENTRY] = NULL; -} - -static inline void -pqueue_fini (pqueue_t *pq) -{ - if (pq->elements != pq->elements_embedded) - free (pq->elements); -} - -static cairo_bool_t -pqueue_grow (pqueue_t *pq) -{ - rectangle_t **new_elements; - pq->max_size *= 2; - - if (pq->elements == pq->elements_embedded) { - new_elements = _cairo_malloc_ab (pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - - memcpy (new_elements, pq->elements_embedded, - sizeof (pq->elements_embedded)); - } else { - new_elements = _cairo_realloc_ab (pq->elements, - pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - } - - pq->elements = new_elements; - return TRUE; -} - -static inline void -pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) -{ - rectangle_t **elements; - int i, parent; - - if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) { - if (unlikely (! pqueue_grow (&sweep->pq))) { - longjmp (sweep->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - elements = sweep->pq.elements; - for (i = ++sweep->pq.size; - i != PQ_FIRST_ENTRY && - rectangle_compare_stop (rectangle, - elements[parent = PQ_PARENT_INDEX (i)]) < 0; - i = parent) - { - elements[i] = elements[parent]; - } - - elements[i] = rectangle; -} - -static inline void -pqueue_pop (pqueue_t *pq) -{ - rectangle_t **elements = pq->elements; - rectangle_t *tail; - int child, i; - - tail = elements[pq->size--]; - if (pq->size == 0) { - elements[PQ_FIRST_ENTRY] = NULL; - return; - } - - for (i = PQ_FIRST_ENTRY; - (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; - i = child) - { - if (child != pq->size && - rectangle_compare_stop (elements[child+1], - elements[child]) < 0) - { - child++; - } - - if (rectangle_compare_stop (elements[child], tail) >= 0) - break; - - elements[i] = elements[child]; - } - elements[i] = tail; -} - -static inline rectangle_t * -rectangle_pop_start (sweep_line_t *sweep_line) -{ - return *sweep_line->rectangles++; -} - -static inline rectangle_t * -rectangle_peek_stop (sweep_line_t *sweep_line) -{ - return sweep_line->pq.elements[PQ_FIRST_ENTRY]; -} - -CAIRO_COMBSORT_DECLARE (_rectangle_sort, - rectangle_t *, - rectangle_compare_start) - -static void -sweep_line_init (sweep_line_t *sweep_line, - rectangle_t **rectangles, - int num_rectangles, - cairo_fill_rule_t fill_rule) -{ - _rectangle_sort (rectangles, num_rectangles); - rectangles[num_rectangles] = NULL; - sweep_line->rectangles = rectangles; - - sweep_line->head.x = INT32_MIN; - sweep_line->head.right = NULL; - sweep_line->head.dir = 0; - sweep_line->head.next = &sweep_line->tail; - /* we need to initialize prev so that we can check - * if this edge is the left most and make sure - * we always insert to the right of it, even if - * our x coordinate matches */ - sweep_line->head.prev = NULL; - - sweep_line->tail.x = INT32_MAX; - sweep_line->tail.right = NULL; - sweep_line->tail.dir = 0; - sweep_line->tail.prev = &sweep_line->head; - sweep_line->tail.next = NULL; - - sweep_line->insert_left = &sweep_line->tail; - sweep_line->insert_right = &sweep_line->tail; - - sweep_line->current_y = INT32_MIN; - sweep_line->last_y = INT32_MIN; - - sweep_line->fill_rule = fill_rule; - - pqueue_init (&sweep_line->pq); -} - -static void -sweep_line_fini (sweep_line_t *sweep_line) -{ - pqueue_fini (&sweep_line->pq); -} - -static void -edge_end_box (sweep_line_t *sweep_line, - edge_t *left, - int32_t bot, - cairo_bool_t do_traps, - void *container) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - /* Only emit (trivial) non-degenerate trapezoids with positive height. */ - if (likely (left->top < bot)) { - if (do_traps) { - cairo_line_t _left = { - { left->x, left->top }, - { left->x, bot }, - }, _right = { - { left->right->x, left->top }, - { left->right->x, bot }, - }; - _cairo_traps_add_trap (container, left->top, bot, &_left, &_right); - status = _cairo_traps_status ((cairo_traps_t *) container); - } else { - cairo_box_t box; - - box.p1.x = left->x; - box.p1.y = left->top; - box.p2.x = left->right->x; - box.p2.y = bot; - - status = _cairo_boxes_add (container, &box); - } - } - if (unlikely (status)) - longjmp (sweep_line->unwind, status); - - left->right = NULL; -} - -/* Start a new trapezoid at the given top y coordinate, whose edges - * are `edge' and `edge->next'. If `edge' already has a trapezoid, - * then either add it to the traps in `traps', if the trapezoid's - * right edge differs from `edge->next', or do nothing if the new - * trapezoid would be a continuation of the existing one. */ -static inline void -edge_start_or_continue_box (sweep_line_t *sweep_line, - edge_t *left, - edge_t *right, - int top, - cairo_bool_t do_traps, - void *container) -{ - if (left->right == right) - return; - - if (left->right != NULL) { - if (right != NULL && left->right->x == right->x) { - /* continuation on right, so just swap edges */ - left->right = right; - return; - } - - edge_end_box (sweep_line, - left, top, do_traps, container); - } - - if (right != NULL && left->x != right->x) { - left->top = top; - left->right = right; - } -} - -static inline void -active_edges_to_traps (sweep_line_t *sweep, - cairo_bool_t do_traps, - void *container) -{ - int top = sweep->current_y; - edge_t *pos; - - if (sweep->last_y == sweep->current_y) - return; - - pos = sweep->head.next; - if (pos == &sweep->tail) - return; - - if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) { - do { - edge_t *left, *right; - int winding; - - left = pos; - winding = left->dir; - - right = left->next; - - /* Check if there is a co-linear edge with an existing trap */ - if (left->right == NULL) { - while (unlikely (right->x == left->x)) { - winding += right->dir; - if (right->right != NULL) { - /* continuation on left */ - left->top = right->top; - left->right = right->right; - right->right = NULL; - winding -= right->dir; - break; - } - - right = right->next; - } - - if (winding == 0) { - pos = right; - continue; - } - } - - /* Greedily search for the closing edge, so that we generate the - * maximal span width with the minimal number of trapezoids. - */ - - do { - /* End all subsumed traps */ - if (unlikely (right->right != NULL)) { - edge_end_box (sweep, - right, top, do_traps, container); - } - - winding += right->dir; - if (winding == 0) { - /* skip co-linear edges */ - if (likely (right->x != right->next->x)) - break; - } - - right = right->next; - } while (TRUE); - - edge_start_or_continue_box (sweep, - left, right, top, - do_traps, container); - - pos = right->next; - } while (pos != &sweep->tail); - } else { - do { - edge_t *right = pos->next; - int count = 0; - - do { - /* End all subsumed traps */ - if (unlikely (right->right != NULL)) { - edge_end_box (sweep, - right, top, do_traps, container); - } - - if (++count & 1) { - /* skip co-linear edges */ - if (likely (right->x != right->next->x)) - break; - } - - right = right->next; - } while (TRUE); - - edge_start_or_continue_box (sweep, - pos, right, top, - do_traps, container); - - pos = right->next; - } while (pos != &sweep->tail); - } - - sweep->last_y = sweep->current_y; -} - -static inline void -sweep_line_delete_edge (sweep_line_t *sweep_line, - edge_t *edge, - cairo_bool_t do_traps, - void *container) -{ - if (edge->right != NULL) { - edge_t *next = edge->next; - if (next->x == edge->x) { - next->top = edge->top; - next->right = edge->right; - } else { - edge_end_box (sweep_line, - edge, - sweep_line->current_y, - do_traps, container); - } - } - - if (sweep_line->insert_left == edge) - sweep_line->insert_left = edge->next; - if (sweep_line->insert_right == edge) - sweep_line->insert_right = edge->next; - - edge->prev->next = edge->next; - edge->next->prev = edge->prev; -} - -static inline cairo_bool_t -sweep_line_delete (sweep_line_t *sweep, - rectangle_t *rectangle, - cairo_bool_t do_traps, - void *container) -{ - cairo_bool_t update; - - update = TRUE; - if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && - rectangle->left.prev->dir == rectangle->left.dir) - { - update = rectangle->left.next != &rectangle->right; - } - - sweep_line_delete_edge (sweep, - &rectangle->left, - do_traps, container); - - sweep_line_delete_edge (sweep, - &rectangle->right, - do_traps, container); - - pqueue_pop (&sweep->pq); - return update; -} - -static inline void -insert_edge (edge_t *edge, edge_t *pos) -{ - if (pos->x != edge->x) { - if (pos->x > edge->x) { - do { - UNROLL3({ - if (pos->prev->x <= edge->x) - break; - pos = pos->prev; - }) - } while (TRUE); - } else { - do { - UNROLL3({ - pos = pos->next; - if (pos->x >= edge->x) - break; - }) - } while (TRUE); - } - } - if (pos->prev) { - pos->prev->next = edge; - edge->prev = pos->prev; - edge->next = pos; - pos->prev = edge; - } else { - /* we have edge that shares an x coordinate with the left most sentinal. - * instead of inserting before pos and ruining our sentinal we insert after pos. */ - pos->next->prev = edge; - edge->next = pos->next; - edge->prev = pos; - pos->next = edge; - } -} - -static inline cairo_bool_t -sweep_line_insert (sweep_line_t *sweep, - rectangle_t *rectangle) -{ - edge_t *pos; - - /* right edge */ - pos = sweep->insert_right; - insert_edge (&rectangle->right, pos); - sweep->insert_right = &rectangle->right; - - /* left edge */ - pos = sweep->insert_left; - if (pos->x > sweep->insert_right->x) - pos = sweep->insert_right->prev; - insert_edge (&rectangle->left, pos); - sweep->insert_left = &rectangle->left; - - pqueue_push (sweep, rectangle); - - if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && - rectangle->left.prev->dir == rectangle->left.dir) - { - return rectangle->left.next != &rectangle->right; - } - - return TRUE; -} - -static cairo_status_t -_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, - int num_rectangles, - cairo_fill_rule_t fill_rule, - cairo_bool_t do_traps, - void *container) -{ - sweep_line_t sweep_line; - rectangle_t *rectangle; - cairo_status_t status; - cairo_bool_t update = FALSE; - - sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule); - if ((status = setjmp (sweep_line.unwind))) - goto unwind; - - rectangle = rectangle_pop_start (&sweep_line); - do { - if (rectangle->top != sweep_line.current_y) { - rectangle_t *stop; - - stop = rectangle_peek_stop (&sweep_line); - while (stop != NULL && stop->bottom < rectangle->top) { - if (stop->bottom != sweep_line.current_y) { - if (update) { - active_edges_to_traps (&sweep_line, - do_traps, container); - update = FALSE; - } - - sweep_line.current_y = stop->bottom; - } - - update |= sweep_line_delete (&sweep_line, stop, do_traps, container); - - stop = rectangle_peek_stop (&sweep_line); - } - - if (update) { - active_edges_to_traps (&sweep_line, do_traps, container); - update = FALSE; - } - - sweep_line.current_y = rectangle->top; - } - - update |= sweep_line_insert (&sweep_line, rectangle); - } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL); - - while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { - if (rectangle->bottom != sweep_line.current_y) { - if (update) { - active_edges_to_traps (&sweep_line, do_traps, container); - update = FALSE; - } - - sweep_line.current_y = rectangle->bottom; - } - - update |= sweep_line_delete (&sweep_line, rectangle, do_traps, container); - } - -unwind: - sweep_line_fini (&sweep_line); - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule) -{ - rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; - rectangle_t *rectangles; - rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; - rectangle_t **rectangles_ptrs; - cairo_status_t status; - int i; - - assert (traps->is_rectangular); - - if (unlikely (traps->num_traps <= 1)) { - if (traps->num_traps == 1) { - cairo_trapezoid_t *trap = traps->traps; - if (trap->left.p1.x > trap->right.p1.x) { - cairo_line_t tmp = trap->left; - trap->left = trap->right; - trap->right = tmp; - } - } - return CAIRO_STATUS_SUCCESS; - } - - dump_traps (traps, "bo-rects-traps-in.txt"); - - rectangles = stack_rectangles; - rectangles_ptrs = stack_rectangles_ptrs; - if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { - rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, - sizeof (rectangle_t) + - sizeof (rectangle_t *), - sizeof (rectangle_t *)); - if (unlikely (rectangles == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); - } - - for (i = 0; i < traps->num_traps; i++) { - if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { - rectangles[i].left.x = traps->traps[i].left.p1.x; - rectangles[i].left.dir = 1; - - rectangles[i].right.x = traps->traps[i].right.p1.x; - rectangles[i].right.dir = -1; - } else { - rectangles[i].right.x = traps->traps[i].left.p1.x; - rectangles[i].right.dir = 1; - - rectangles[i].left.x = traps->traps[i].right.p1.x; - rectangles[i].left.dir = -1; - } - - rectangles[i].left.right = NULL; - rectangles[i].right.right = NULL; - - rectangles[i].top = traps->traps[i].top; - rectangles[i].bottom = traps->traps[i].bottom; - - rectangles_ptrs[i] = &rectangles[i]; - } - - _cairo_traps_clear (traps); - status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i, - fill_rule, - TRUE, traps); - traps->is_rectilinear = TRUE; - traps->is_rectangular = TRUE; - - if (rectangles != stack_rectangles) - free (rectangles); - - dump_traps (traps, "bo-rects-traps-out.txt"); - - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *out) -{ - rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; - rectangle_t *rectangles; - rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; - rectangle_t **rectangles_ptrs; - const struct _cairo_boxes_chunk *chunk; - cairo_status_t status; - int i, j; - - if (unlikely (in->num_boxes == 0)) { - _cairo_boxes_clear (out); - return CAIRO_STATUS_SUCCESS; - } - - if (in->num_boxes == 1) { - if (in == out) { - cairo_box_t *box = &in->chunks.base[0]; - - if (box->p1.x > box->p2.x) { - cairo_fixed_t tmp = box->p1.x; - box->p1.x = box->p2.x; - box->p2.x = tmp; - } - } else { - cairo_box_t box = in->chunks.base[0]; - - if (box.p1.x > box.p2.x) { - cairo_fixed_t tmp = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = tmp; - } - - _cairo_boxes_clear (out); - status = _cairo_boxes_add (out, &box); - assert (status == CAIRO_STATUS_SUCCESS); - } - return CAIRO_STATUS_SUCCESS; - } - - rectangles = stack_rectangles; - rectangles_ptrs = stack_rectangles_ptrs; - if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { - rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, - sizeof (rectangle_t) + - sizeof (rectangle_t *), - sizeof (rectangle_t *)); - if (unlikely (rectangles == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); - } - - j = 0; - for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - for (i = 0; i < chunk->count; i++) { - if (box[i].p1.x < box[i].p2.x) { - rectangles[j].left.x = box[i].p1.x; - rectangles[j].left.dir = 1; - - rectangles[j].right.x = box[i].p2.x; - rectangles[j].right.dir = -1; - } else { - rectangles[j].right.x = box[i].p1.x; - rectangles[j].right.dir = 1; - - rectangles[j].left.x = box[i].p2.x; - rectangles[j].left.dir = -1; - } - - rectangles[j].left.right = NULL; - rectangles[j].right.right = NULL; - - rectangles[j].top = box[i].p1.y; - rectangles[j].bottom = box[i].p2.y; - - rectangles_ptrs[j] = &rectangles[j]; - j++; - } - } - - _cairo_boxes_clear (out); - status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, j, - fill_rule, - FALSE, out); - if (rectangles != stack_rectangles) - free (rectangles); - - return status; -} diff --git a/libs/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c b/libs/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c deleted file mode 100644 index b00d2823b..000000000 --- a/libs/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c +++ /dev/null @@ -1,634 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Provide definitions for standalone compilation */ -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-combsort-private.h" -#include "cairo-error-private.h" - -typedef struct _cairo_bo_edge cairo_bo_edge_t; -typedef struct _cairo_bo_trap cairo_bo_trap_t; - -/* A deferred trapezoid of an edge */ -struct _cairo_bo_trap { - cairo_bo_edge_t *right; - int32_t top; -}; - -struct _cairo_bo_edge { - cairo_edge_t edge; - cairo_bo_edge_t *prev; - cairo_bo_edge_t *next; - cairo_bo_trap_t deferred_trap; -}; - -typedef enum { - CAIRO_BO_EVENT_TYPE_START, - CAIRO_BO_EVENT_TYPE_STOP -} cairo_bo_event_type_t; - -typedef struct _cairo_bo_event { - cairo_bo_event_type_t type; - cairo_point_t point; - cairo_bo_edge_t *edge; -} cairo_bo_event_t; - -typedef struct _cairo_bo_sweep_line { - cairo_bo_event_t **events; - cairo_bo_edge_t *head; - cairo_bo_edge_t *stopped; - int32_t current_y; - cairo_bo_edge_t *current_edge; -} cairo_bo_sweep_line_t; - -static inline int -_cairo_point_compare (const cairo_point_t *a, - const cairo_point_t *b) -{ - int cmp; - - cmp = a->y - b->y; - if (likely (cmp)) - return cmp; - - return a->x - b->x; -} - -static inline int -_cairo_bo_edge_compare (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b) -{ - int cmp; - - cmp = a->edge.line.p1.x - b->edge.line.p1.x; - if (likely (cmp)) - return cmp; - - return b->edge.bottom - a->edge.bottom; -} - -static inline int -cairo_bo_event_compare (const cairo_bo_event_t *a, - const cairo_bo_event_t *b) -{ - int cmp; - - cmp = _cairo_point_compare (&a->point, &b->point); - if (likely (cmp)) - return cmp; - - cmp = a->type - b->type; - if (cmp) - return cmp; - - return a - b; -} - -static inline cairo_bo_event_t * -_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line) -{ - return *sweep_line->events++; -} - -CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, - cairo_bo_event_t *, - cairo_bo_event_compare) - -static void -_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_event_t **events, - int num_events) -{ - _cairo_bo_event_queue_sort (events, num_events); - events[num_events] = NULL; - sweep_line->events = events; - - sweep_line->head = NULL; - sweep_line->current_y = INT32_MIN; - sweep_line->current_edge = NULL; -} - -static void -_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *edge) -{ - if (sweep_line->current_edge != NULL) { - cairo_bo_edge_t *prev, *next; - int cmp; - - cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge); - if (cmp < 0) { - prev = sweep_line->current_edge; - next = prev->next; - while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0) - prev = next, next = prev->next; - - prev->next = edge; - edge->prev = prev; - edge->next = next; - if (next != NULL) - next->prev = edge; - } else if (cmp > 0) { - next = sweep_line->current_edge; - prev = next->prev; - while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0) - next = prev, prev = next->prev; - - next->prev = edge; - edge->next = next; - edge->prev = prev; - if (prev != NULL) - prev->next = edge; - else - sweep_line->head = edge; - } else { - prev = sweep_line->current_edge; - edge->prev = prev; - edge->next = prev->next; - if (prev->next != NULL) - prev->next->prev = edge; - prev->next = edge; - } - } else { - sweep_line->head = edge; - } - - sweep_line->current_edge = edge; -} - -static void -_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *edge) -{ - if (edge->prev != NULL) - edge->prev->next = edge->next; - else - sweep_line->head = edge->next; - - if (edge->next != NULL) - edge->next->prev = edge->prev; - - if (sweep_line->current_edge == edge) - sweep_line->current_edge = edge->prev ? edge->prev : edge->next; -} - -static inline cairo_bool_t -edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) -{ - return a->edge.line.p1.x == b->edge.line.p1.x; -} - -static cairo_status_t -_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, - int32_t bot, - cairo_bool_t do_traps, - void *container) -{ - cairo_bo_trap_t *trap = &left->deferred_trap; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - /* Only emit (trivial) non-degenerate trapezoids with positive height. */ - if (likely (trap->top < bot)) { - if (do_traps) { - _cairo_traps_add_trap (container, - trap->top, bot, - &left->edge.line, &trap->right->edge.line); - status = _cairo_traps_status ((cairo_traps_t *) container); - } else { - cairo_box_t box; - - box.p1.x = left->edge.line.p1.x; - box.p1.y = trap->top; - box.p2.x = trap->right->edge.line.p1.x; - box.p2.y = bot; - status = _cairo_boxes_add (container, &box); - } - } - - trap->right = NULL; - - return status; -} - -/* Start a new trapezoid at the given top y coordinate, whose edges - * are `edge' and `edge->next'. If `edge' already has a trapezoid, - * then either add it to the traps in `traps', if the trapezoid's - * right edge differs from `edge->next', or do nothing if the new - * trapezoid would be a continuation of the existing one. */ -static inline cairo_status_t -_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, - cairo_bo_edge_t *right, - int top, - cairo_bool_t do_traps, - void *container) -{ - cairo_status_t status; - - if (left->deferred_trap.right == right) - return CAIRO_STATUS_SUCCESS; - - if (left->deferred_trap.right != NULL) { - if (right != NULL && edges_collinear (left->deferred_trap.right, right)) - { - /* continuation on right, so just swap edges */ - left->deferred_trap.right = right; - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_bo_edge_end_trap (left, top, do_traps, container); - if (unlikely (status)) - return status; - } - - if (right != NULL && ! edges_collinear (left, right)) { - left->deferred_trap.top = top; - left->deferred_trap.right = right; - } - - return CAIRO_STATUS_SUCCESS; -} - -static inline cairo_status_t -_active_edges_to_traps (cairo_bo_edge_t *left, - int32_t top, - cairo_fill_rule_t fill_rule, - cairo_bool_t do_traps, - void *container) -{ - cairo_bo_edge_t *right; - cairo_status_t status; - - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - while (left != NULL) { - int in_out; - - /* Greedily search for the closing edge, so that we generate the - * maximal span width with the minimal number of trapezoids. - */ - in_out = left->edge.dir; - - /* Check if there is a co-linear edge with an existing trap */ - right = left->next; - if (left->deferred_trap.right == NULL) { - while (right != NULL && right->deferred_trap.right == NULL) - right = right->next; - - if (right != NULL && edges_collinear (left, right)) { - /* continuation on left */ - left->deferred_trap = right->deferred_trap; - right->deferred_trap.right = NULL; - } - } - - /* End all subsumed traps */ - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, do_traps, container); - if (unlikely (status)) - return status; - } - - in_out += right->edge.dir; - if (in_out == 0) { - /* skip co-linear edges */ - if (right->next == NULL || - ! edges_collinear (right, right->next)) - { - break; - } - } - - right = right->next; - } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, top, - do_traps, container); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; - } - } else { - while (left != NULL) { - int in_out = 0; - - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, do_traps, container); - if (unlikely (status)) - return status; - } - - if ((in_out++ & 1) == 0) { - cairo_bo_edge_t *next; - cairo_bool_t skip = FALSE; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_collinear (right, next); - - if (! skip) - break; - } - - right = right->next; - } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, top, - do_traps, container); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events, - int num_events, - cairo_fill_rule_t fill_rule, - cairo_bool_t do_traps, - void *container) -{ - cairo_bo_sweep_line_t sweep_line; - cairo_bo_event_t *event; - cairo_status_t status; - - _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events); - - while ((event = _cairo_bo_event_dequeue (&sweep_line))) { - if (event->point.y != sweep_line.current_y) { - status = _active_edges_to_traps (sweep_line.head, - sweep_line.current_y, - fill_rule, do_traps, container); - if (unlikely (status)) - return status; - - sweep_line.current_y = event->point.y; - } - - switch (event->type) { - case CAIRO_BO_EVENT_TYPE_START: - _cairo_bo_sweep_line_insert (&sweep_line, event->edge); - break; - - case CAIRO_BO_EVENT_TYPE_STOP: - _cairo_bo_sweep_line_delete (&sweep_line, event->edge); - - if (event->edge->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (event->edge, - sweep_line.current_y, - do_traps, container); - if (unlikely (status)) - return status; - } - - break; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, - const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule) -{ - cairo_status_t status; - cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; - cairo_bo_event_t *events; - cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - cairo_bo_event_t **event_ptrs; - cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; - cairo_bo_edge_t *edges; - int num_events; - int i, j; - - if (unlikely (polygon->num_edges == 0)) - return CAIRO_STATUS_SUCCESS; - - num_events = 2 * polygon->num_edges; - - events = stack_events; - event_ptrs = stack_event_ptrs; - edges = stack_edges; - if (num_events > ARRAY_LENGTH (stack_events)) { - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (cairo_bo_event_t) + - sizeof (cairo_bo_edge_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (cairo_bo_event_t **) (events + num_events); - edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); - } - - for (i = j = 0; i < polygon->num_edges; i++) { - edges[i].edge = polygon->edges[i]; - edges[i].deferred_trap.right = NULL; - edges[i].prev = NULL; - edges[i].next = NULL; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_START; - events[j].point.y = polygon->edges[i].top; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_STOP; - events[j].point.y = polygon->edges[i].bottom; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - } - - status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, - fill_rule, - TRUE, traps); - if (events != stack_events) - free (events); - - traps->is_rectilinear = TRUE; - - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *boxes) -{ - cairo_status_t status; - cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; - cairo_bo_event_t *events; - cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - cairo_bo_event_t **event_ptrs; - cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; - cairo_bo_edge_t *edges; - int num_events; - int i, j; - - if (unlikely (polygon->num_edges == 0)) - return CAIRO_STATUS_SUCCESS; - - num_events = 2 * polygon->num_edges; - - events = stack_events; - event_ptrs = stack_event_ptrs; - edges = stack_edges; - if (num_events > ARRAY_LENGTH (stack_events)) { - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (cairo_bo_event_t) + - sizeof (cairo_bo_edge_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (cairo_bo_event_t **) (events + num_events); - edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); - } - - for (i = j = 0; i < polygon->num_edges; i++) { - edges[i].edge = polygon->edges[i]; - edges[i].deferred_trap.right = NULL; - edges[i].prev = NULL; - edges[i].next = NULL; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_START; - events[j].point.y = polygon->edges[i].top; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_STOP; - events[j].point.y = polygon->edges[i].bottom; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - } - - status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, - fill_rule, - FALSE, boxes); - if (events != stack_events) - free (events); - - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule) -{ - cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; - cairo_bo_event_t *events; - cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - cairo_bo_event_t **event_ptrs; - cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; - cairo_bo_edge_t *edges; - cairo_status_t status; - int i, j, k; - - if (unlikely (traps->num_traps == 0)) - return CAIRO_STATUS_SUCCESS; - - assert (traps->is_rectilinear); - - i = 4 * traps->num_traps; - - events = stack_events; - event_ptrs = stack_event_ptrs; - edges = stack_edges; - if (i > ARRAY_LENGTH (stack_events)) { - events = _cairo_malloc_ab_plus_c (i, - sizeof (cairo_bo_event_t) + - sizeof (cairo_bo_edge_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (cairo_bo_event_t **) (events + i); - edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); - } - - for (i = j = k = 0; i < traps->num_traps; i++) { - edges[k].edge.top = traps->traps[i].top; - edges[k].edge.bottom = traps->traps[i].bottom; - edges[k].edge.line = traps->traps[i].left; - edges[k].edge.dir = 1; - edges[k].deferred_trap.right = NULL; - edges[k].prev = NULL; - edges[k].next = NULL; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_START; - events[j].point.y = traps->traps[i].top; - events[j].point.x = traps->traps[i].left.p1.x; - events[j].edge = &edges[k]; - j++; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_STOP; - events[j].point.y = traps->traps[i].bottom; - events[j].point.x = traps->traps[i].left.p1.x; - events[j].edge = &edges[k]; - j++; - k++; - - edges[k].edge.top = traps->traps[i].top; - edges[k].edge.bottom = traps->traps[i].bottom; - edges[k].edge.line = traps->traps[i].right; - edges[k].edge.dir = -1; - edges[k].deferred_trap.right = NULL; - edges[k].prev = NULL; - edges[k].next = NULL; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_START; - events[j].point.y = traps->traps[i].top; - events[j].point.x = traps->traps[i].right.p1.x; - events[j].edge = &edges[k]; - j++; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_STOP; - events[j].point.y = traps->traps[i].bottom; - events[j].point.x = traps->traps[i].right.p1.x; - events[j].edge = &edges[k]; - j++; - k++; - } - - _cairo_traps_clear (traps); - status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, - fill_rule, - TRUE, traps); - traps->is_rectilinear = TRUE; - - if (events != stack_events) - free (events); - - return status; -} diff --git a/libs/cairo/cairo/src/cairo-bentley-ottmann.c b/libs/cairo/cairo/src/cairo-bentley-ottmann.c deleted file mode 100644 index c333becaa..000000000 --- a/libs/cairo/cairo/src/cairo-bentley-ottmann.c +++ /dev/null @@ -1,2100 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Provide definitions for standalone compilation */ -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-freelist-private.h" -#include "cairo-combsort-private.h" - -#define DEBUG_PRINT_STATE 0 -#define DEBUG_EVENTS 0 -#define DEBUG_TRAPS 0 - -typedef cairo_point_t cairo_bo_point32_t; - -typedef struct _cairo_bo_intersect_ordinate { - int32_t ordinate; - enum { EXACT, INEXACT } exactness; -} cairo_bo_intersect_ordinate_t; - -typedef struct _cairo_bo_intersect_point { - cairo_bo_intersect_ordinate_t x; - cairo_bo_intersect_ordinate_t y; -} cairo_bo_intersect_point_t; - -typedef struct _cairo_bo_edge cairo_bo_edge_t; -typedef struct _cairo_bo_trap cairo_bo_trap_t; - -/* A deferred trapezoid of an edge */ -struct _cairo_bo_trap { - cairo_bo_edge_t *right; - int32_t top; -}; - -struct _cairo_bo_edge { - cairo_edge_t edge; - cairo_bo_edge_t *prev; - cairo_bo_edge_t *next; - cairo_bo_trap_t deferred_trap; -}; - -/* the parent is always given by index/2 */ -#define PQ_PARENT_INDEX(i) ((i) >> 1) -#define PQ_FIRST_ENTRY 1 - -/* left and right children are index * 2 and (index * 2) +1 respectively */ -#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) - -typedef enum { - CAIRO_BO_EVENT_TYPE_STOP, - CAIRO_BO_EVENT_TYPE_INTERSECTION, - CAIRO_BO_EVENT_TYPE_START -} cairo_bo_event_type_t; - -typedef struct _cairo_bo_event { - cairo_bo_event_type_t type; - cairo_point_t point; -} cairo_bo_event_t; - -typedef struct _cairo_bo_start_event { - cairo_bo_event_type_t type; - cairo_point_t point; - cairo_bo_edge_t edge; -} cairo_bo_start_event_t; - -typedef struct _cairo_bo_queue_event { - cairo_bo_event_type_t type; - cairo_point_t point; - cairo_bo_edge_t *e1; - cairo_bo_edge_t *e2; -} cairo_bo_queue_event_t; - -typedef struct _pqueue { - int size, max_size; - - cairo_bo_event_t **elements; - cairo_bo_event_t *elements_embedded[1024]; -} pqueue_t; - -typedef struct _cairo_bo_event_queue { - cairo_freepool_t pool; - pqueue_t pqueue; - cairo_bo_event_t **start_events; -} cairo_bo_event_queue_t; - -typedef struct _cairo_bo_sweep_line { - cairo_bo_edge_t *head; - cairo_bo_edge_t *stopped; - int32_t current_y; - cairo_bo_edge_t *current_edge; -} cairo_bo_sweep_line_t; - -#if DEBUG_TRAPS -static void -dump_traps (cairo_traps_t *traps, const char *filename) -{ - FILE *file; - cairo_box_t extents; - int n; - - if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) - return; - -#if 0 - if (traps->has_limits) { - printf ("%s: limits=(%d, %d, %d, %d)\n", - filename, - traps->limits.p1.x, traps->limits.p1.y, - traps->limits.p2.x, traps->limits.p2.y); - } -#endif - _cairo_traps_extents (traps, &extents); - printf ("%s: extents=(%d, %d, %d, %d)\n", - filename, - extents.p1.x, extents.p1.y, - extents.p2.x, extents.p2.y); - - file = fopen (filename, "a"); - if (file != NULL) { - for (n = 0; n < traps->num_traps; n++) { - fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", - traps->traps[n].top, - traps->traps[n].bottom, - traps->traps[n].left.p1.x, - traps->traps[n].left.p1.y, - traps->traps[n].left.p2.x, - traps->traps[n].left.p2.y, - traps->traps[n].right.p1.x, - traps->traps[n].right.p1.y, - traps->traps[n].right.p2.x, - traps->traps[n].right.p2.y); - } - fprintf (file, "\n"); - fclose (file); - } -} - -static void -dump_edges (cairo_bo_start_event_t *events, - int num_edges, - const char *filename) -{ - FILE *file; - int n; - - if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) - return; - - file = fopen (filename, "a"); - if (file != NULL) { - for (n = 0; n < num_edges; n++) { - fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n", - events[n].edge.edge.line.p1.x, - events[n].edge.edge.line.p1.y, - events[n].edge.edge.line.p2.x, - events[n].edge.edge.line.p2.y, - events[n].edge.edge.top, - events[n].edge.edge.bottom, - events[n].edge.edge.dir); - } - fprintf (file, "\n"); - fclose (file); - } -} -#endif - -static cairo_fixed_t -_line_compute_intersection_x_for_y (const cairo_line_t *line, - cairo_fixed_t y) -{ - cairo_fixed_t x, dy; - - if (y == line->p1.y) - return line->p1.x; - if (y == line->p2.y) - return line->p2.x; - - x = line->p1.x; - dy = line->p2.y - line->p1.y; - if (dy != 0) { - x += _cairo_fixed_mul_div_floor (y - line->p1.y, - line->p2.x - line->p1.x, - dy); - } - - return x; -} - -static inline int -_cairo_bo_point32_compare (cairo_bo_point32_t const *a, - cairo_bo_point32_t const *b) -{ - int cmp; - - cmp = a->y - b->y; - if (cmp) - return cmp; - - return a->x - b->x; -} - -/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the - * slope a is respectively greater than, equal to, or less than the - * slope of b. - * - * For each edge, consider the direction vector formed from: - * - * top -> bottom - * - * which is: - * - * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) - * - * We then define the slope of each edge as dx/dy, (which is the - * inverse of the slope typically used in math instruction). We never - * compute a slope directly as the value approaches infinity, but we - * can derive a slope comparison without division as follows, (where - * the ? represents our compare operator). - * - * 1. slope(a) ? slope(b) - * 2. adx/ady ? bdx/bdy - * 3. (adx * bdy) ? (bdx * ady) - * - * Note that from step 2 to step 3 there is no change needed in the - * sign of the result since both ady and bdy are guaranteed to be - * greater than or equal to 0. - * - * When using this slope comparison to sort edges, some care is needed - * when interpreting the results. Since the slope compare operates on - * distance vectors from top to bottom it gives a correct left to - * right sort for edges that have a common top point, (such as two - * edges with start events at the same location). On the other hand, - * the sense of the result will be exactly reversed for two edges that - * have a common stop point. - */ -static inline int -_slope_compare (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b) -{ - /* XXX: We're assuming here that dx and dy will still fit in 32 - * bits. That's not true in general as there could be overflow. We - * should prevent that before the tessellation algorithm - * begins. - */ - int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; - int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; - - /* Since the dy's are all positive by construction we can fast - * path several common cases. - */ - - /* First check for vertical lines. */ - if (adx == 0) - return -bdx; - if (bdx == 0) - return adx; - - /* Then where the two edges point in different directions wrt x. */ - if ((adx ^ bdx) < 0) - return adx; - - /* Finally we actually need to do the general comparison. */ - { - int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; - int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; - cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); - cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); - - return _cairo_int64_cmp (adx_bdy, bdx_ady); - } -} - -/* - * We need to compare the x-coordinates of a pair of lines for a particular y, - * without loss of precision. - * - * The x-coordinate along an edge for a given y is: - * X = A_x + (Y - A_y) * A_dx / A_dy - * - * So the inequality we wish to test is: - * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, - * where ∘ is our inequality operator. - * - * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are - * all positive, so we can rearrange it thus without causing a sign change: - * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy - * - (Y - A_y) * A_dx * B_dy - * - * Given the assumption that all the deltas fit within 32 bits, we can compute - * this comparison directly using 128 bit arithmetic. For certain, but common, - * input we can reduce this down to a single 32 bit compare by inspecting the - * deltas. - * - * (And put the burden of the work on developing fast 128 bit ops, which are - * required throughout the tessellator.) - * - * See the similar discussion for _slope_compare(). - */ -static int -edges_compare_x_for_y_general (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b, - int32_t y) -{ - /* XXX: We're assuming here that dx and dy will still fit in 32 - * bits. That's not true in general as there could be overflow. We - * should prevent that before the tessellation algorithm - * begins. - */ - int32_t dx; - int32_t adx, ady; - int32_t bdx, bdy; - enum { - HAVE_NONE = 0x0, - HAVE_DX = 0x1, - HAVE_ADX = 0x2, - HAVE_DX_ADX = HAVE_DX | HAVE_ADX, - HAVE_BDX = 0x4, - HAVE_DX_BDX = HAVE_DX | HAVE_BDX, - HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, - HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX - } have_dx_adx_bdx = HAVE_ALL; - - /* don't bother solving for abscissa if the edges' bounding boxes - * can be used to order them. */ - { - int32_t amin, amax; - int32_t bmin, bmax; - if (a->edge.line.p1.x < a->edge.line.p2.x) { - amin = a->edge.line.p1.x; - amax = a->edge.line.p2.x; - } else { - amin = a->edge.line.p2.x; - amax = a->edge.line.p1.x; - } - if (b->edge.line.p1.x < b->edge.line.p2.x) { - bmin = b->edge.line.p1.x; - bmax = b->edge.line.p2.x; - } else { - bmin = b->edge.line.p2.x; - bmax = b->edge.line.p1.x; - } - if (amax < bmin) return -1; - if (amin > bmax) return +1; - } - - ady = a->edge.line.p2.y - a->edge.line.p1.y; - adx = a->edge.line.p2.x - a->edge.line.p1.x; - if (adx == 0) - have_dx_adx_bdx &= ~HAVE_ADX; - - bdy = b->edge.line.p2.y - b->edge.line.p1.y; - bdx = b->edge.line.p2.x - b->edge.line.p1.x; - if (bdx == 0) - have_dx_adx_bdx &= ~HAVE_BDX; - - dx = a->edge.line.p1.x - b->edge.line.p1.x; - if (dx == 0) - have_dx_adx_bdx &= ~HAVE_DX; - -#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) -#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) -#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) - switch (have_dx_adx_bdx) { - default: - case HAVE_NONE: - return 0; - case HAVE_DX: - /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ - return dx; /* ady * bdy is positive definite */ - case HAVE_ADX: - /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ - return adx; /* bdy * (y - a->top.y) is positive definite */ - case HAVE_BDX: - /* 0 ∘ (Y - B_y) * B_dx * A_dy */ - return -bdx; /* ady * (y - b->top.y) is positive definite */ - case HAVE_ADX_BDX: - /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ - if ((adx ^ bdx) < 0) { - return adx; - } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ - cairo_int64_t adx_bdy, bdx_ady; - - /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ - - adx_bdy = _cairo_int32x32_64_mul (adx, bdy); - bdx_ady = _cairo_int32x32_64_mul (bdx, ady); - - return _cairo_int64_cmp (adx_bdy, bdx_ady); - } else - return _cairo_int128_cmp (A, B); - case HAVE_DX_ADX: - /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ - if ((-adx ^ dx) < 0) { - return dx; - } else { - cairo_int64_t ady_dx, dy_adx; - - ady_dx = _cairo_int32x32_64_mul (ady, dx); - dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); - - return _cairo_int64_cmp (ady_dx, dy_adx); - } - case HAVE_DX_BDX: - /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ - if ((bdx ^ dx) < 0) { - return dx; - } else { - cairo_int64_t bdy_dx, dy_bdx; - - bdy_dx = _cairo_int32x32_64_mul (bdy, dx); - dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); - - return _cairo_int64_cmp (bdy_dx, dy_bdx); - } - case HAVE_ALL: - /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ - return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); - } -#undef B -#undef A -#undef L -} - -/* - * We need to compare the x-coordinate of a line for a particular y wrt to a - * given x, without loss of precision. - * - * The x-coordinate along an edge for a given y is: - * X = A_x + (Y - A_y) * A_dx / A_dy - * - * So the inequality we wish to test is: - * A_x + (Y - A_y) * A_dx / A_dy ∘ X - * where ∘ is our inequality operator. - * - * By construction, we know that A_dy (and (Y - A_y)) are - * all positive, so we can rearrange it thus without causing a sign change: - * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy - * - * Given the assumption that all the deltas fit within 32 bits, we can compute - * this comparison directly using 64 bit arithmetic. - * - * See the similar discussion for _slope_compare() and - * edges_compare_x_for_y_general(). - */ -static int -edge_compare_for_y_against_x (const cairo_bo_edge_t *a, - int32_t y, - int32_t x) -{ - int32_t adx, ady; - int32_t dx, dy; - cairo_int64_t L, R; - - if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) - return 1; - if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) - return -1; - - adx = a->edge.line.p2.x - a->edge.line.p1.x; - dx = x - a->edge.line.p1.x; - - if (adx == 0) - return -dx; - if (dx == 0 || (adx ^ dx) < 0) - return adx; - - dy = y - a->edge.line.p1.y; - ady = a->edge.line.p2.y - a->edge.line.p1.y; - - L = _cairo_int32x32_64_mul (dy, adx); - R = _cairo_int32x32_64_mul (dx, ady); - - return _cairo_int64_cmp (L, R); -} - -static int -edges_compare_x_for_y (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b, - int32_t y) -{ - /* If the sweep-line is currently on an end-point of a line, - * then we know its precise x value (and considering that we often need to - * compare events at end-points, this happens frequently enough to warrant - * special casing). - */ - enum { - HAVE_NEITHER = 0x0, - HAVE_AX = 0x1, - HAVE_BX = 0x2, - HAVE_BOTH = HAVE_AX | HAVE_BX - } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; - - if (y == a->edge.line.p1.y) - ax = a->edge.line.p1.x; - else if (y == a->edge.line.p2.y) - ax = a->edge.line.p2.x; - else - have_ax_bx &= ~HAVE_AX; - - if (y == b->edge.line.p1.y) - bx = b->edge.line.p1.x; - else if (y == b->edge.line.p2.y) - bx = b->edge.line.p2.x; - else - have_ax_bx &= ~HAVE_BX; - - switch (have_ax_bx) { - default: - case HAVE_NEITHER: - return edges_compare_x_for_y_general (a, b, y); - case HAVE_AX: - return -edge_compare_for_y_against_x (b, y, ax); - case HAVE_BX: - return edge_compare_for_y_against_x (a, y, bx); - case HAVE_BOTH: - return ax - bx; - } -} - -static inline int -_line_equal (const cairo_line_t *a, const cairo_line_t *b) -{ - return a->p1.x == b->p1.x && a->p1.y == b->p1.y && - a->p2.x == b->p2.x && a->p2.y == b->p2.y; -} - -static int -_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, - const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b) -{ - int cmp; - - /* compare the edges if not identical */ - if (! _line_equal (&a->edge.line, &b->edge.line)) { - cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); - if (cmp) - return cmp; - - /* The two edges intersect exactly at y, so fall back on slope - * comparison. We know that this compare_edges function will be - * called only when starting a new edge, (not when stopping an - * edge), so we don't have to worry about conditionally inverting - * the sense of _slope_compare. */ - cmp = _slope_compare (a, b); - if (cmp) - return cmp; - } - - /* We've got two collinear edges now. */ - return b->edge.bottom - a->edge.bottom; -} - -static inline cairo_int64_t -det32_64 (int32_t a, int32_t b, - int32_t c, int32_t d) -{ - /* det = a * d - b * c */ - return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), - _cairo_int32x32_64_mul (b, c)); -} - -static inline cairo_int128_t -det64x32_128 (cairo_int64_t a, int32_t b, - cairo_int64_t c, int32_t d) -{ - /* det = a * d - b * c */ - return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), - _cairo_int64x32_128_mul (c, b)); -} - -/* Compute the intersection of two lines as defined by two edges. The - * result is provided as a coordinate pair of 128-bit integers. - * - * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or - * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. - */ -static cairo_bool_t -intersect_lines (cairo_bo_edge_t *a, - cairo_bo_edge_t *b, - cairo_bo_intersect_point_t *intersection) -{ - cairo_int64_t a_det, b_det; - - /* XXX: We're assuming here that dx and dy will still fit in 32 - * bits. That's not true in general as there could be overflow. We - * should prevent that before the tessellation algorithm begins. - * What we're doing to mitigate this is to perform clamping in - * cairo_bo_tessellate_polygon(). - */ - int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; - int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; - - int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; - int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; - - cairo_int64_t den_det; - cairo_int64_t R; - cairo_quorem64_t qr; - - den_det = det32_64 (dx1, dy1, dx2, dy2); - - /* Q: Can we determine that the lines do not intersect (within range) - * much more cheaply than computing the intersection point i.e. by - * avoiding the division? - * - * X = ax + t * adx = bx + s * bdx; - * Y = ay + t * ady = by + s * bdy; - * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) - * => t * L = R - * - * Therefore we can reject any intersection (under the criteria for - * valid intersection events) if: - * L^R < 0 => t < 0, or - * L t > 1 - * - * (where top/bottom must at least extend to the line endpoints). - * - * A similar substitution can be performed for s, yielding: - * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) - */ - R = det32_64 (dx2, dy2, - b->edge.line.p1.x - a->edge.line.p1.x, - b->edge.line.p1.y - a->edge.line.p1.y); - if (_cairo_int64_negative (den_det)) { - if (_cairo_int64_ge (den_det, R)) - return FALSE; - } else { - if (_cairo_int64_le (den_det, R)) - return FALSE; - } - - R = det32_64 (dy1, dx1, - a->edge.line.p1.y - b->edge.line.p1.y, - a->edge.line.p1.x - b->edge.line.p1.x); - if (_cairo_int64_negative (den_det)) { - if (_cairo_int64_ge (den_det, R)) - return FALSE; - } else { - if (_cairo_int64_le (den_det, R)) - return FALSE; - } - - /* We now know that the two lines should intersect within range. */ - - a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, - a->edge.line.p2.x, a->edge.line.p2.y); - b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, - b->edge.line.p2.x, b->edge.line.p2.y); - - /* x = det (a_det, dx1, b_det, dx2) / den_det */ - qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, - b_det, dx2), - den_det); - if (_cairo_int64_eq (qr.rem, den_det)) - return FALSE; -#if 0 - intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; -#else - intersection->x.exactness = EXACT; - if (! _cairo_int64_is_zero (qr.rem)) { - if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) - qr.rem = _cairo_int64_negate (qr.rem); - qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); - if (_cairo_int64_ge (qr.rem, den_det)) { - qr.quo = _cairo_int64_add (qr.quo, - _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); - } else - intersection->x.exactness = INEXACT; - } -#endif - intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); - - /* y = det (a_det, dy1, b_det, dy2) / den_det */ - qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, - b_det, dy2), - den_det); - if (_cairo_int64_eq (qr.rem, den_det)) - return FALSE; -#if 0 - intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; -#else - intersection->y.exactness = EXACT; - if (! _cairo_int64_is_zero (qr.rem)) { - if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) - qr.rem = _cairo_int64_negate (qr.rem); - qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); - if (_cairo_int64_ge (qr.rem, den_det)) { - qr.quo = _cairo_int64_add (qr.quo, - _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); - } else - intersection->y.exactness = INEXACT; - } -#endif - intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); - - return TRUE; -} - -static int -_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, - int32_t b) -{ - /* First compare the quotient */ - if (a.ordinate > b) - return +1; - if (a.ordinate < b) - return -1; - /* With quotient identical, if remainder is 0 then compare equal */ - /* Otherwise, the non-zero remainder makes a > b */ - return INEXACT == a.exactness; -} - -/* Does the given edge contain the given point. The point must already - * be known to be contained within the line determined by the edge, - * (most likely the point results from an intersection of this edge - * with another). - * - * If we had exact arithmetic, then this function would simply be a - * matter of examining whether the y value of the point lies within - * the range of y values of the edge. But since intersection points - * are not exact due to being rounded to the nearest integer within - * the available precision, we must also examine the x value of the - * point. - * - * The definition of "contains" here is that the given intersection - * point will be seen by the sweep line after the start event for the - * given edge and before the stop event for the edge. See the comments - * in the implementation for more details. - */ -static cairo_bool_t -_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, - cairo_bo_intersect_point_t *point) -{ - int cmp_top, cmp_bottom; - - /* XXX: When running the actual algorithm, we don't actually need to - * compare against edge->top at all here, since any intersection above - * top is eliminated early via a slope comparison. We're leaving these - * here for now only for the sake of the quadratic-time intersection - * finder which needs them. - */ - - cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, - edge->edge.top); - cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, - edge->edge.bottom); - - if (cmp_top < 0 || cmp_bottom > 0) - { - return FALSE; - } - - if (cmp_top > 0 && cmp_bottom < 0) - { - return TRUE; - } - - /* At this stage, the point lies on the same y value as either - * edge->top or edge->bottom, so we have to examine the x value in - * order to properly determine containment. */ - - /* If the y value of the point is the same as the y value of the - * top of the edge, then the x value of the point must be greater - * to be considered as inside the edge. Similarly, if the y value - * of the point is the same as the y value of the bottom of the - * edge, then the x value of the point must be less to be - * considered as inside. */ - - if (cmp_top == 0) { - cairo_fixed_t top_x; - - top_x = _line_compute_intersection_x_for_y (&edge->edge.line, - edge->edge.top); - return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; - } else { /* cmp_bottom == 0 */ - cairo_fixed_t bot_x; - - bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, - edge->edge.bottom); - return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; - } -} - -/* Compute the intersection of two edges. The result is provided as a - * coordinate pair of 128-bit integers. - * - * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection - * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the - * intersection of the lines defined by the edges occurs outside of - * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges - * are exactly parallel. - * - * Note that when determining if a candidate intersection is "inside" - * an edge, we consider both the infinitesimal shortening and the - * infinitesimal tilt rules described by John Hobby. Specifically, if - * the intersection is exactly the same as an edge point, it is - * effectively outside (no intersection is returned). Also, if the - * intersection point has the same - */ -static cairo_bool_t -_cairo_bo_edge_intersect (cairo_bo_edge_t *a, - cairo_bo_edge_t *b, - cairo_bo_point32_t *intersection) -{ - cairo_bo_intersect_point_t quorem; - - if (! intersect_lines (a, b, &quorem)) - return FALSE; - - if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) - return FALSE; - - if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) - return FALSE; - - /* Now that we've correctly compared the intersection point and - * determined that it lies within the edge, then we know that we - * no longer need any more bits of storage for the intersection - * than we do for our edge coordinates. We also no longer need the - * remainder from the division. */ - intersection->x = quorem.x.ordinate; - intersection->y = quorem.y.ordinate; - - return TRUE; -} - -static inline int -cairo_bo_event_compare (const cairo_bo_event_t *a, - const cairo_bo_event_t *b) -{ - int cmp; - - cmp = _cairo_bo_point32_compare (&a->point, &b->point); - if (cmp) - return cmp; - - cmp = a->type - b->type; - if (cmp) - return cmp; - - return a - b; -} - -static inline void -_pqueue_init (pqueue_t *pq) -{ - pq->max_size = ARRAY_LENGTH (pq->elements_embedded); - pq->size = 0; - - pq->elements = pq->elements_embedded; -} - -static inline void -_pqueue_fini (pqueue_t *pq) -{ - if (pq->elements != pq->elements_embedded) - free (pq->elements); -} - -static cairo_status_t -_pqueue_grow (pqueue_t *pq) -{ - cairo_bo_event_t **new_elements; - pq->max_size *= 2; - - if (pq->elements == pq->elements_embedded) { - new_elements = _cairo_malloc_ab (pq->max_size, - sizeof (cairo_bo_event_t *)); - if (unlikely (new_elements == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (new_elements, pq->elements_embedded, - sizeof (pq->elements_embedded)); - } else { - new_elements = _cairo_realloc_ab (pq->elements, - pq->max_size, - sizeof (cairo_bo_event_t *)); - if (unlikely (new_elements == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pq->elements = new_elements; - return CAIRO_STATUS_SUCCESS; -} - -static inline cairo_status_t -_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) -{ - cairo_bo_event_t **elements; - int i, parent; - - if (unlikely (pq->size + 1 == pq->max_size)) { - cairo_status_t status; - - status = _pqueue_grow (pq); - if (unlikely (status)) - return status; - } - - elements = pq->elements; - - for (i = ++pq->size; - i != PQ_FIRST_ENTRY && - cairo_bo_event_compare (event, - elements[parent = PQ_PARENT_INDEX (i)]) < 0; - i = parent) - { - elements[i] = elements[parent]; - } - - elements[i] = event; - - return CAIRO_STATUS_SUCCESS; -} - -static inline void -_pqueue_pop (pqueue_t *pq) -{ - cairo_bo_event_t **elements = pq->elements; - cairo_bo_event_t *tail; - int child, i; - - tail = elements[pq->size--]; - if (pq->size == 0) { - elements[PQ_FIRST_ENTRY] = NULL; - return; - } - - for (i = PQ_FIRST_ENTRY; - (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; - i = child) - { - if (child != pq->size && - cairo_bo_event_compare (elements[child+1], - elements[child]) < 0) - { - child++; - } - - if (cairo_bo_event_compare (elements[child], tail) >= 0) - break; - - elements[i] = elements[child]; - } - elements[i] = tail; -} - -static inline cairo_status_t -_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, - cairo_bo_event_type_t type, - cairo_bo_edge_t *e1, - cairo_bo_edge_t *e2, - const cairo_point_t *point) -{ - cairo_bo_queue_event_t *event; - - event = _cairo_freepool_alloc (&queue->pool); - if (unlikely (event == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event->type = type; - event->e1 = e1; - event->e2 = e2; - event->point = *point; - - return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); -} - -static void -_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, - cairo_bo_event_t *event) -{ - _cairo_freepool_free (&queue->pool, event); -} - -static cairo_bo_event_t * -_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) -{ - cairo_bo_event_t *event, *cmp; - - event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; - cmp = *event_queue->start_events; - if (event == NULL || - (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) - { - event = cmp; - event_queue->start_events++; - } - else - { - _pqueue_pop (&event_queue->pqueue); - } - - return event; -} - -CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, - cairo_bo_event_t *, - cairo_bo_event_compare) - -static void -_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, - cairo_bo_event_t **start_events, - int num_events) -{ - _cairo_bo_event_queue_sort (start_events, num_events); - start_events[num_events] = NULL; - - event_queue->start_events = start_events; - - _cairo_freepool_init (&event_queue->pool, - sizeof (cairo_bo_queue_event_t)); - _pqueue_init (&event_queue->pqueue); - event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; -} - -static cairo_status_t -_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, - cairo_bo_edge_t *edge) -{ - cairo_bo_point32_t point; - - point.y = edge->edge.bottom; - point.x = _line_compute_intersection_x_for_y (&edge->edge.line, - point.y); - return _cairo_bo_event_queue_insert (event_queue, - CAIRO_BO_EVENT_TYPE_STOP, - edge, NULL, - &point); -} - -static void -_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) -{ - _pqueue_fini (&event_queue->pqueue); - _cairo_freepool_fini (&event_queue->pool); -} - -static inline cairo_status_t -_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, - cairo_bo_edge_t *left, - cairo_bo_edge_t *right) -{ - cairo_bo_point32_t intersection; - - if (_line_equal (&left->edge.line, &right->edge.line)) - return CAIRO_STATUS_SUCCESS; - - /* The names "left" and "right" here are correct descriptions of - * the order of the two edges within the active edge list. So if a - * slope comparison also puts left less than right, then we know - * that the intersection of these two segments has already - * occurred before the current sweep line position. */ - if (_slope_compare (left, right) <= 0) - return CAIRO_STATUS_SUCCESS; - - if (! _cairo_bo_edge_intersect (left, right, &intersection)) - return CAIRO_STATUS_SUCCESS; - - return _cairo_bo_event_queue_insert (event_queue, - CAIRO_BO_EVENT_TYPE_INTERSECTION, - left, right, - &intersection); -} - -static void -_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) -{ - sweep_line->head = NULL; - sweep_line->stopped = NULL; - sweep_line->current_y = INT32_MIN; - sweep_line->current_edge = NULL; -} - -static cairo_status_t -_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *edge) -{ - if (sweep_line->current_edge != NULL) { - cairo_bo_edge_t *prev, *next; - int cmp; - - cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, - sweep_line->current_edge, - edge); - if (cmp < 0) { - prev = sweep_line->current_edge; - next = prev->next; - while (next != NULL && - _cairo_bo_sweep_line_compare_edges (sweep_line, - next, edge) < 0) - { - prev = next, next = prev->next; - } - - prev->next = edge; - edge->prev = prev; - edge->next = next; - if (next != NULL) - next->prev = edge; - } else if (cmp > 0) { - next = sweep_line->current_edge; - prev = next->prev; - while (prev != NULL && - _cairo_bo_sweep_line_compare_edges (sweep_line, - prev, edge) > 0) - { - next = prev, prev = next->prev; - } - - next->prev = edge; - edge->next = next; - edge->prev = prev; - if (prev != NULL) - prev->next = edge; - else - sweep_line->head = edge; - } else { - prev = sweep_line->current_edge; - edge->prev = prev; - edge->next = prev->next; - if (prev->next != NULL) - prev->next->prev = edge; - prev->next = edge; - } - } else { - sweep_line->head = edge; - } - - sweep_line->current_edge = edge; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *edge) -{ - if (edge->prev != NULL) - edge->prev->next = edge->next; - else - sweep_line->head = edge->next; - - if (edge->next != NULL) - edge->next->prev = edge->prev; - - if (sweep_line->current_edge == edge) - sweep_line->current_edge = edge->prev ? edge->prev : edge->next; -} - -static void -_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, - cairo_bo_edge_t *left, - cairo_bo_edge_t *right) -{ - if (left->prev != NULL) - left->prev->next = right; - else - sweep_line->head = right; - - if (right->next != NULL) - right->next->prev = left; - - left->next = right->next; - right->next = left; - - right->prev = left->prev; - left->prev = right; -} - -#if DEBUG_PRINT_STATE -static void -_cairo_bo_edge_print (cairo_bo_edge_t *edge) -{ - printf ("(0x%x, 0x%x)-(0x%x, 0x%x)", - edge->edge.line.p1.x, edge->edge.line.p1.y, - edge->edge.line.p2.x, edge->edge.line.p2.y); -} - -static void -_cairo_bo_event_print (cairo_bo_event_t *event) -{ - switch (event->type) { - case CAIRO_BO_EVENT_TYPE_START: - printf ("Start: "); - break; - case CAIRO_BO_EVENT_TYPE_STOP: - printf ("Stop: "); - break; - case CAIRO_BO_EVENT_TYPE_INTERSECTION: - printf ("Intersection: "); - break; - } - printf ("(%d, %d)\t", event->point.x, event->point.y); - _cairo_bo_edge_print (event->e1); - if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) { - printf (" X "); - _cairo_bo_edge_print (event->e2); - } - printf ("\n"); -} - -static void -_cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue) -{ - /* XXX: fixme to print the start/stop array too. */ - printf ("Event queue:\n"); -} - -static void -_cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) -{ - cairo_bool_t first = TRUE; - cairo_bo_edge_t *edge; - - printf ("Sweep line from edge list: "); - first = TRUE; - for (edge = sweep_line->head; - edge; - edge = edge->next) - { - if (!first) - printf (", "); - _cairo_bo_edge_print (edge); - first = FALSE; - } - printf ("\n"); -} - -static void -print_state (const char *msg, - cairo_bo_event_t *event, - cairo_bo_event_queue_t *event_queue, - cairo_bo_sweep_line_t *sweep_line) -{ - printf ("%s ", msg); - _cairo_bo_event_print (event); - _cairo_bo_event_queue_print (event_queue); - _cairo_bo_sweep_line_print (sweep_line); - printf ("\n"); -} -#endif - -#if DEBUG_EVENTS -static void CAIRO_PRINTF_FORMAT (1, 2) -event_log (const char *fmt, ...) -{ - FILE *file; - - if (getenv ("CAIRO_DEBUG_EVENTS") == NULL) - return; - - file = fopen ("bo-events.txt", "a"); - if (file != NULL) { - va_list ap; - - va_start (ap, fmt); - vfprintf (file, fmt, ap); - va_end (ap); - - fclose (file); - } -} -#endif - -static inline cairo_bool_t -edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) -{ - if (_line_equal (&a->edge.line, &b->edge.line)) - return TRUE; - - if (_slope_compare (a, b)) - return FALSE; - - /* The choice of y is not truly arbitrary since we must guarantee that it - * is greater than the start of either line. - */ - if (a->edge.line.p1.y == b->edge.line.p1.y) { - return a->edge.line.p1.x == b->edge.line.p1.x; - } else if (a->edge.line.p1.y < b->edge.line.p1.y) { - return edge_compare_for_y_against_x (b, - a->edge.line.p1.y, - a->edge.line.p1.x) == 0; - } else { - return edge_compare_for_y_against_x (a, - b->edge.line.p1.y, - b->edge.line.p1.x) == 0; - } -} - -/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ -static cairo_status_t -_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, - int32_t bot, - cairo_traps_t *traps) -{ - cairo_bo_trap_t *trap = &left->deferred_trap; - - /* Only emit (trivial) non-degenerate trapezoids with positive height. */ - if (likely (trap->top < bot)) { - _cairo_traps_add_trap (traps, - trap->top, bot, - &left->edge.line, &trap->right->edge.line); - -#if DEBUG_PRINT_STATE - printf ("Deferred trap: left=(%x, %x)-(%x,%x) " - "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n", - left->edge.line.p1.x, left->edge.line.p1.y, - left->edge.line.p2.x, left->edge.line.p2.y, - trap->right->edge.line.p1.x, trap->right->edge.line.p1.y, - trap->right->edge.line.p2.x, trap->right->edge.line.p2.y, - trap->top, bot); -#endif -#if DEBUG_EVENTS - event_log ("end trap: %lu %lu %d %d\n", - (long) left, - (long) trap->right, - trap->top, - bot); -#endif - } - - trap->right = NULL; - - return _cairo_traps_status (traps); -} - - -/* Start a new trapezoid at the given top y coordinate, whose edges - * are `edge' and `edge->next'. If `edge' already has a trapezoid, - * then either add it to the traps in `traps', if the trapezoid's - * right edge differs from `edge->next', or do nothing if the new - * trapezoid would be a continuation of the existing one. */ -static inline cairo_status_t -_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, - cairo_bo_edge_t *right, - int top, - cairo_traps_t *traps) -{ - cairo_status_t status; - - if (left->deferred_trap.right == right) - return CAIRO_STATUS_SUCCESS; - - if (left->deferred_trap.right != NULL) { - if (right != NULL && edges_colinear (left->deferred_trap.right, right)) - { - /* continuation on right, so just swap edges */ - left->deferred_trap.right = right; - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_bo_edge_end_trap (left, top, traps); - if (unlikely (status)) - return status; - } - - if (right != NULL && ! edges_colinear (left, right)) { - left->deferred_trap.top = top; - left->deferred_trap.right = right; - -#if DEBUG_EVENTS - event_log ("begin trap: %lu %lu %d\n", - (long) left, - (long) right, - top); -#endif - } - - return CAIRO_STATUS_SUCCESS; -} - -static inline cairo_status_t -_active_edges_to_traps (cairo_bo_edge_t *left, - int32_t top, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) -{ - cairo_bo_edge_t *right; - cairo_status_t status; - -#if DEBUG_PRINT_STATE - printf ("Processing active edges for %x\n", top); -#endif - - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - while (left != NULL) { - int in_out; - - /* Greedily search for the closing edge, so that we generate the - * maximal span width with the minimal number of trapezoids. - */ - in_out = left->edge.dir; - - /* Check if there is a co-linear edge with an existing trap */ - right = left->next; - if (left->deferred_trap.right == NULL) { - while (right != NULL && right->deferred_trap.right == NULL) - right = right->next; - - if (right != NULL && edges_colinear (left, right)) { - /* continuation on left */ - left->deferred_trap = right->deferred_trap; - right->deferred_trap.right = NULL; - } - } - - /* End all subsumed traps */ - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, traps); - if (unlikely (status)) - return status; - } - - in_out += right->edge.dir; - if (in_out == 0) { - cairo_bo_edge_t *next; - cairo_bool_t skip = FALSE; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_colinear (right, next); - - if (! skip) - break; - } - - right = right->next; - } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, - top, traps); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; - } - } else { - while (left != NULL) { - int in_out = 0; - - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, traps); - if (unlikely (status)) - return status; - } - - if ((in_out++ & 1) == 0) { - cairo_bo_edge_t *next; - cairo_bool_t skip = FALSE; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_colinear (right, next); - - if (! skip) - break; - } - - right = right->next; - } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, - top, traps); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; - } - } - - return CAIRO_STATUS_SUCCESS; -} - - -/* Execute a single pass of the Bentley-Ottmann algorithm on edges, - * generating trapezoids according to the fill_rule and appending them - * to traps. */ -static cairo_status_t -_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, - int num_events, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps, - int *num_intersections) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ - int intersection_count = 0; - cairo_bo_event_queue_t event_queue; - cairo_bo_sweep_line_t sweep_line; - cairo_bo_event_t *event; - cairo_bo_edge_t *left, *right; - cairo_bo_edge_t *e1, *e2; - -#if DEBUG_EVENTS - { - int i; - - for (i = 0; i < num_events; i++) { - cairo_bo_start_event_t *event = - ((cairo_bo_start_event_t **) start_events)[i]; - event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n", - (long) &events[i].edge, - event->edge.edge.line.p1.x, - event->edge.edge.line.p1.y, - event->edge.edge.line.p2.x, - event->edge.edge.line.p2.y, - event->edge.top, - event->edge.bottom, - event->edge.edge.dir); - } - } -#endif - - _cairo_bo_event_queue_init (&event_queue, start_events, num_events); - _cairo_bo_sweep_line_init (&sweep_line); - - while ((event = _cairo_bo_event_dequeue (&event_queue))) { - if (event->point.y != sweep_line.current_y) { - for (e1 = sweep_line.stopped; e1; e1 = e1->next) { - if (e1->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (e1, - e1->edge.bottom, - traps); - if (unlikely (status)) - goto unwind; - } - } - sweep_line.stopped = NULL; - - status = _active_edges_to_traps (sweep_line.head, - sweep_line.current_y, - fill_rule, traps); - if (unlikely (status)) - goto unwind; - - sweep_line.current_y = event->point.y; - } - -#if DEBUG_EVENTS - event_log ("event: %d (%ld, %ld) %lu, %lu\n", - event->type, - (long) event->point.x, - (long) event->point.y, - (long) event->e1, - (long) event->e2); -#endif - - switch (event->type) { - case CAIRO_BO_EVENT_TYPE_START: - e1 = &((cairo_bo_start_event_t *) event)->edge; - - status = _cairo_bo_sweep_line_insert (&sweep_line, e1); - if (unlikely (status)) - goto unwind; - - status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); - if (unlikely (status)) - goto unwind; - - /* check to see if this is a continuation of a stopped edge */ - /* XXX change to an infinitesimal lengthening rule */ - for (left = sweep_line.stopped; left; left = left->next) { - if (e1->edge.top <= left->edge.bottom && - edges_colinear (e1, left)) - { - e1->deferred_trap = left->deferred_trap; - if (left->prev != NULL) - left->prev = left->next; - else - sweep_line.stopped = left->next; - if (left->next != NULL) - left->next->prev = left->prev; - break; - } - } - - left = e1->prev; - right = e1->next; - - if (left != NULL) { - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); - if (unlikely (status)) - goto unwind; - } - - if (right != NULL) { - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); - if (unlikely (status)) - goto unwind; - } - - break; - - case CAIRO_BO_EVENT_TYPE_STOP: - e1 = ((cairo_bo_queue_event_t *) event)->e1; - _cairo_bo_event_queue_delete (&event_queue, event); - - left = e1->prev; - right = e1->next; - - _cairo_bo_sweep_line_delete (&sweep_line, e1); - - /* first, check to see if we have a continuation via a fresh edge */ - if (e1->deferred_trap.right != NULL) { - e1->next = sweep_line.stopped; - if (sweep_line.stopped != NULL) - sweep_line.stopped->prev = e1; - sweep_line.stopped = e1; - e1->prev = NULL; - } - - if (left != NULL && right != NULL) { - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); - if (unlikely (status)) - goto unwind; - } - - break; - - case CAIRO_BO_EVENT_TYPE_INTERSECTION: - e1 = ((cairo_bo_queue_event_t *) event)->e1; - e2 = ((cairo_bo_queue_event_t *) event)->e2; - _cairo_bo_event_queue_delete (&event_queue, event); - - /* skip this intersection if its edges are not adjacent */ - if (e2 != e1->next) - break; - - intersection_count++; - - left = e1->prev; - right = e2->next; - - _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); - - /* after the swap e2 is left of e1 */ - - if (left != NULL) { - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); - if (unlikely (status)) - goto unwind; - } - - if (right != NULL) { - status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); - if (unlikely (status)) - goto unwind; - } - - break; - } - } - - *num_intersections = intersection_count; - for (e1 = sweep_line.stopped; e1; e1 = e1->next) { - if (e1->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); - if (unlikely (status)) - break; - } - } - unwind: - _cairo_bo_event_queue_fini (&event_queue); - -#if DEBUG_EVENTS - event_log ("\n"); -#endif - - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, - const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule) -{ - int intersections; - cairo_status_t status; - cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; - cairo_bo_start_event_t *events; - cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - cairo_bo_event_t **event_ptrs; - int num_events; - int i; - - num_events = polygon->num_edges; - if (unlikely (0 == num_events)) - return CAIRO_STATUS_SUCCESS; - - events = stack_events; - event_ptrs = stack_event_ptrs; - if (num_events > ARRAY_LENGTH (stack_events)) { - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (cairo_bo_start_event_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (cairo_bo_event_t **) (events + num_events); - } - - for (i = 0; i < num_events; i++) { - event_ptrs[i] = (cairo_bo_event_t *) &events[i]; - - events[i].type = CAIRO_BO_EVENT_TYPE_START; - events[i].point.y = polygon->edges[i].top; - events[i].point.x = - _line_compute_intersection_x_for_y (&polygon->edges[i].line, - events[i].point.y); - - events[i].edge.edge = polygon->edges[i]; - events[i].edge.deferred_trap.right = NULL; - events[i].edge.prev = NULL; - events[i].edge.next = NULL; - } - -#if DEBUG_TRAPS - dump_edges (events, num_events, "bo-polygon-edges.txt"); -#endif - - /* XXX: This would be the convenient place to throw in multiple - * passes of the Bentley-Ottmann algorithm. It would merely - * require storing the results of each pass into a temporary - * cairo_traps_t. */ - status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, - num_events, - fill_rule, traps, - &intersections); -#if DEBUG_TRAPS - dump_traps (traps, "bo-polygon-out.txt"); -#endif - - if (events != stack_events) - free (events); - - return status; -} - -cairo_status_t -_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule) -{ - cairo_status_t status; - cairo_polygon_t polygon; - int i; - - if (unlikely (0 == traps->num_traps)) - return CAIRO_STATUS_SUCCESS; - -#if DEBUG_TRAPS - dump_traps (traps, "bo-traps-in.txt"); -#endif - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); - - for (i = 0; i < traps->num_traps; i++) { - status = _cairo_polygon_add_line (&polygon, - &traps->traps[i].left, - traps->traps[i].top, - traps->traps[i].bottom, - 1); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_polygon_add_line (&polygon, - &traps->traps[i].right, - traps->traps[i].top, - traps->traps[i].bottom, - -1); - if (unlikely (status)) - goto CLEANUP; - } - - _cairo_traps_clear (traps); - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &polygon, - fill_rule); - -#if DEBUG_TRAPS - dump_traps (traps, "bo-traps-out.txt"); -#endif - - CLEANUP: - _cairo_polygon_fini (&polygon); - - return status; -} - -#if 0 -static cairo_bool_t -edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, - int num_edges) - -{ - int i, j; - cairo_bo_edge_t *a, *b; - cairo_bo_point32_t intersection; - - /* We must not be given any upside-down edges. */ - for (i = 0; i < num_edges; i++) { - assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0); - edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS; - edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS; - edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS; - edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS; - } - - for (i = 0; i < num_edges; i++) { - for (j = 0; j < num_edges; j++) { - if (i == j) - continue; - - a = &edges[i]; - b = &edges[j]; - - if (! _cairo_bo_edge_intersect (a, b, &intersection)) - continue; - - printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n", - intersection.x, - intersection.y, - a->line.p1.x, a->line.p1.y, - a->line.p2.x, a->line.p2.y, - b->line.p1.x, b->line.p1.y, - b->line.p2.x, b->line.p2.y); - - return TRUE; - } - } - return FALSE; -} - -#define TEST_MAX_EDGES 10 - -typedef struct test { - const char *name; - const char *description; - int num_edges; - cairo_bo_edge_t edges[TEST_MAX_EDGES]; -} test_t; - -static test_t -tests[] = { - { - "3 near misses", - "3 edges all intersecting very close to each other", - 3, - { - { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL }, - { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL }, - { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL } - } - }, - { - "inconsistent data", - "Derived from random testing---was leading to skip list and edge list disagreeing.", - 2, - { - { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL }, - { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL } - } - }, - { - "failed sort", - "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?", - 3, - { - { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL }, - { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL }, - { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL }, - } - }, - { - "minimal-intersection", - "Intersection of a two from among the smallest possible edges.", - 2, - { - { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL }, - { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL } - } - }, - { - "simple", - "A simple intersection of two edges at an integer (2,2).", - 2, - { - { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL }, - { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL } - } - }, - { - "bend-to-horizontal", - "With intersection truncation one edge bends to horizontal", - 2, - { - { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL }, - { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL } - } - } -}; - -/* - { - "endpoint", - "An intersection that occurs at the endpoint of a segment.", - { - { { 4, 6}, { 5, 6}, NULL, { { NULL }} }, - { { 4, 5}, { 5, 7}, NULL, { { NULL }} }, - { { 0, 0}, { 0, 0}, NULL, { { NULL }} }, - } - } - { - name = "overlapping", - desc = "Parallel segments that share an endpoint, with different slopes.", - edges = { - { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}}, - { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}}, - { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}}, - { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}}, - { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}}, - { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}} - } - }, - { - name = "hobby_stage_3", - desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.", - edges = { - { top = { x = -1, y = -2}, bottom = { x = 4, y = 2}}, - { top = { x = 5, y = 3}, bottom = { x = 9, y = 5}}, - { top = { x = 5, y = 3}, bottom = { x = 6, y = 3}}, - } - }, - { - name = "hobby", - desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.", - edges = { - { top = { x = 0, y = 0}, bottom = { x = 9, y = 5}}, - { top = { x = 0, y = 0}, bottom = { x = 13, y = 6}}, - { top = { x = -1, y = -2}, bottom = { x = 9, y = 5}} - } - }, - { - name = "slope", - desc = "Edges with same start/stop points but different slopes", - edges = { - { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}}, - { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}}, - { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}}, - { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}} - } - }, - { - name = "horizontal", - desc = "Test of a horizontal edge", - edges = { - { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}}, - { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}} - } - }, - { - name = "vertical", - desc = "Test of a vertical edge", - edges = { - { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, - { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} - } - }, - { - name = "congruent", - desc = "Two overlapping edges with the same slope", - edges = { - { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, - { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}}, - { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} - } - }, - { - name = "multi", - desc = "Several segments with a common intersection point", - edges = { - { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} }, - { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} }, - { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} }, - { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} }, - { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} }, - { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} } - } - } -}; -*/ - -static int -run_test (const char *test_name, - cairo_bo_edge_t *test_edges, - int num_edges) -{ - int i, intersections, passes; - cairo_bo_edge_t *edges; - cairo_array_t intersected_edges; - - printf ("Testing: %s\n", test_name); - - _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); - - intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges); - if (intersections) - printf ("Pass 1 found %d intersections:\n", intersections); - - - /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a - * pass of Hobby's tolerance-square algorithm instead. */ - passes = 1; - while (intersections) { - int num_edges = _cairo_array_num_elements (&intersected_edges); - passes++; - edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t)); - assert (edges != NULL); - memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t)); - _cairo_array_fini (&intersected_edges); - _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); - intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges); - free (edges); - - if (intersections){ - printf ("Pass %d found %d remaining intersections:\n", passes, intersections); - } else { - if (passes > 3) - for (i = 0; i < passes; i++) - printf ("*"); - printf ("No remainining intersections found after pass %d\n", passes); - } - } - - if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0), - _cairo_array_num_elements (&intersected_edges))) - printf ("*** FAIL ***\n"); - else - printf ("PASS\n"); - - _cairo_array_fini (&intersected_edges); - - return 0; -} - -#define MAX_RANDOM 300 - -int -main (void) -{ - char random_name[] = "random-XX"; - cairo_bo_edge_t random_edges[MAX_RANDOM], *edge; - unsigned int i, num_random; - test_t *test; - - for (i = 0; i < ARRAY_LENGTH (tests); i++) { - test = &tests[i]; - run_test (test->name, test->edges, test->num_edges); - } - - for (num_random = 0; num_random < MAX_RANDOM; num_random++) { - srand (0); - for (i = 0; i < num_random; i++) { - do { - edge = &random_edges[i]; - edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); - if (edge->line.p1.y > edge->line.p2.y) { - int32_t tmp = edge->line.p1.y; - edge->line.p1.y = edge->line.p2.y; - edge->line.p2.y = tmp; - } - } while (edge->line.p1.y == edge->line.p2.y); - } - - sprintf (random_name, "random-%02d", num_random); - - run_test (random_name, random_edges, num_random); - } - - return 0; -} -#endif diff --git a/libs/cairo/cairo/src/cairo-beos-surface.cpp b/libs/cairo/cairo/src/cairo-beos-surface.cpp deleted file mode 100644 index d52ae1513..000000000 --- a/libs/cairo/cairo/src/cairo-beos-surface.cpp +++ /dev/null @@ -1,949 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This is a C++ file in order to use the C++ BeOS API - -#include "cairoint.h" - -#include "cairo-beos.h" - -#include - -#include -#include -#if 0 -#include -#endif -#include -#include -#include - -#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS) - -struct cairo_beos_surface_t { - cairo_surface_t base; - - BView* view; - - /* - * A view is either attached to a bitmap, a window, or unattached. - * If it is attached to a window, we can copy data out of it using BScreen. - * If it is attached to a bitmap, we can read the bitmap data. - * If it is not attached, it doesn't draw anything, we need not bother. - * - * Since there doesn't seem to be a way to get the bitmap from a view if it - * is attached to one, we have to use a special surface creation function. - */ - - BBitmap* bitmap; - - - // If true, surface and view should be deleted when this surface is - // destroyed - bool owns_bitmap_view; -}; - -class AutoLockView { - public: - AutoLockView(BView* view) : mView(view) { - mOK = mView->LockLooper(); - } - - ~AutoLockView() { - if (mOK) - mView->UnlockLooper(); - } - - operator bool() { - return mOK; - } - - private: - BView* mView; - bool mOK; -}; - -static cairo_surface_t * -_cairo_beos_surface_create_internal (BView* view, - BBitmap* bmp, - bool owns_bitmap_view = false); - -static BRect -_cairo_rect_to_brect (const cairo_rectangle_int16_t* rect) -{ - // A BRect is one pixel wider than you'd think - return BRect(rect->x, rect->y, rect->x + rect->width - 1, - rect->y + rect->height - 1); -} - -static cairo_rectangle_int16_t -_brect_to_cairo_rect (const BRect& rect) -{ - cairo_rectangle_int16_t retval; - retval.x = int(rect.left + 0.5); - retval.y = int(rect.top + 0.5); - retval.width = rect.IntegerWidth() + 1; - retval.height = rect.IntegerHeight() + 1; - return retval; -} - -static rgb_color -_cairo_color_to_be_color (const cairo_color_t* color) -{ - // This factor ensures a uniform distribution of numbers - const float factor = 256 - 1e-5; - // Using doubles to have non-premultiplied colors - rgb_color be_color = { uint8(color->red * factor), - uint8(color->green * factor), - uint8(color->blue * factor), - uint8(color->alpha * factor) }; - - return be_color; -} - -enum ViewCopyStatus { - OK, - NOT_VISIBLE, // The view or the interest rect is not visible on screen - ERROR // The view was visible, but the rect could not be copied. Probably OOM -}; - -/** - * _cairo_beos_view_to_bitmap: - * @bitmap: [out] The resulting bitmap. - * @rect: [out] The rectangle that was copied, in the view's coordinate system - * @interestRect: If non-null, only this part of the view will be copied (view's coord system). - * - * Gets the contents of the view as a BBitmap*. Caller must delete the bitmap. - **/ -static ViewCopyStatus -_cairo_beos_view_to_bitmap (BView* view, - BBitmap** bitmap, - BRect* rect = NULL, - const BRect* interestRect = NULL) -{ - *bitmap = NULL; - - BWindow* wnd = view->Window(); - // If we have no window, can't do anything - if (!wnd) - return NOT_VISIBLE; - - view->Sync(); - wnd->Sync(); - -#if 0 - // Is it a direct window? - BDirectWindow* directWnd = dynamic_cast(wnd); - if (directWnd) { - // WRITEME - } -#endif - - // Is it visible? If so, we can copy the content off the screen - if (wnd->IsHidden()) - return NOT_VISIBLE; - - BRect rectToCopy(view->Bounds()); - if (interestRect) - rectToCopy = rectToCopy & *interestRect; - - if (!rectToCopy.IsValid()) - return NOT_VISIBLE; - - BScreen screen(wnd); - BRect screenRect(view->ConvertToScreen(rectToCopy)); - screenRect = screenRect & screen.Frame(); - - if (!screen.IsValid()) - return NOT_VISIBLE; - - if (rect) - *rect = view->ConvertFromScreen(screenRect); - - if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK) - return OK; - - return ERROR; -} - -inline unsigned char -unpremultiply (unsigned char color, - unsigned char alpha) -{ - if (alpha == 0) - return 0; - // plus alpha/2 to round instead of truncate - return (color * 255 + alpha / 2) / alpha; -} - -inline unsigned char -premultiply (unsigned char color, - unsigned char alpha) -{ - // + 127 to round, instead of truncate - return (color * alpha + 127) / 255; -} - -/** - * unpremultiply_rgba: - * - * Takes an input in ABGR premultiplied image data and unmultiplies it. - * The result is stored in retdata. - **/ -static void -unpremultiply_rgba (unsigned char* data, - int width, - int height, - int stride, - unsigned char* retdata) -{ - unsigned char* end = data + stride * height; - for (unsigned char* in = data, *out = retdata; - in < end; - in += stride, out += stride) - { - for (int i = 0; i < width; ++i) { - // XXX for a big-endian platform this'd have to change - int idx = 4 * i; - unsigned char alpha = in[idx + 3]; - out[idx + 0] = unpremultiply(in[idx + 0], alpha); // B - out[idx + 1] = unpremultiply(in[idx + 1], alpha); // G - out[idx + 2] = unpremultiply(in[idx + 2], alpha); // R - out[idx + 3] = in[idx + 3]; // Alpha - } - } -} - -/** - * premultiply_rgba: - * - * Takes an input in ABGR non-premultiplied image data and premultiplies it. - * The returned data must be freed with free(). - **/ -static unsigned char* -premultiply_rgba (unsigned char* data, - int width, - int height, - int stride) -{ - unsigned char* retdata = reinterpret_cast(_cairo_malloc_ab(height, stride)); - if (!retdata) - return NULL; - - unsigned char* end = data + stride * height; - for (unsigned char* in = data, *out = retdata; - in < end; - in += stride, out += stride) - { - for (int i = 0; i < width; ++i) { - // XXX for a big-endian platform this'd have to change - int idx = 4 * i; - unsigned char alpha = in[idx + 3]; - out[idx + 0] = premultiply(in[idx + 0], alpha); // B - out[idx + 1] = premultiply(in[idx + 1], alpha); // G - out[idx + 2] = premultiply(in[idx + 2], alpha); // R - out[idx + 3] = in[idx + 3]; // Alpha - } - } - return retdata; -} - -/** - * _cairo_beos_bitmap_to_surface: - * - * Returns an addrefed image surface for a BBitmap. The bitmap need not outlive - * the surface. - **/ -static cairo_image_surface_t* -_cairo_beos_bitmap_to_surface (BBitmap* bitmap) -{ - color_space format = bitmap->ColorSpace(); - if (format != B_RGB32 && format != B_RGBA32) { - BBitmap bmp(bitmap->Bounds(), B_RGB32, true); - BView view(bitmap->Bounds(), "Cairo bitmap drawing view", - B_FOLLOW_ALL_SIDES, 0); - bmp.AddChild(&view); - - view.LockLooper(); - - view.DrawBitmap(bitmap, BPoint(0.0, 0.0)); - view.Sync(); - - cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp); - - view.UnlockLooper(); - bmp.RemoveChild(&view); - return imgsurf; - } - - cairo_format_t cformat = format == B_RGB32 ? CAIRO_FORMAT_RGB24 - : CAIRO_FORMAT_ARGB32; - - BRect bounds(bitmap->Bounds()); - unsigned char* bits = reinterpret_cast(bitmap->Bits()); - int width = bounds.IntegerWidth() + 1; - int height = bounds.IntegerHeight() + 1; - unsigned char* premultiplied; - if (cformat == CAIRO_FORMAT_ARGB32) { - premultiplied = premultiply_rgba(bits, width, height, - bitmap->BytesPerRow()); - } else { - premultiplied = reinterpret_cast( - _cairo_malloc_ab(bitmap->BytesPerRow(), height)); - if (premultiplied) - memcpy(premultiplied, bits, bitmap->BytesPerRow() * height); - } - if (!premultiplied) - return NULL; - - cairo_image_surface_t* surf = reinterpret_cast - (cairo_image_surface_create_for_data(premultiplied, - cformat, - width, - height, - bitmap->BytesPerRow())); - if (surf->base.status) - free(premultiplied); - else - _cairo_image_surface_assume_ownership_of_data(surf); - return surf; -} - -/** - * _cairo_image_surface_to_bitmap: - * - * Converts an image surface to a BBitmap. The return value must be freed with - * delete. - **/ -static BBitmap* -_cairo_image_surface_to_bitmap (cairo_image_surface_t* surface) -{ - BRect size(0.0, 0.0, surface->width - 1, surface->height - 1); - switch (surface->format) { - case CAIRO_FORMAT_ARGB32: { - BBitmap* data = new BBitmap(size, B_RGBA32); - unpremultiply_rgba(surface->data, - surface->width, - surface->height, - surface->stride, - reinterpret_cast(data->Bits())); - return data; - } - case CAIRO_FORMAT_RGB24: { - BBitmap* data = new BBitmap(size, B_RGB32); - memcpy(data->Bits(), surface->data, surface->height * surface->stride); - return data; - } - default: - assert(0); - return NULL; - } -} - -/** - * _cairo_op_to_be_op: - * - * Converts a cairo drawing operator to a beos drawing_mode. Returns true if - * the operator could be converted, false otherwise. - **/ -static bool -_cairo_op_to_be_op (cairo_operator_t cairo_op, - drawing_mode* beos_op) -{ - switch (cairo_op) { - - case CAIRO_OPERATOR_SOURCE: - *beos_op = B_OP_COPY; - return true; - case CAIRO_OPERATOR_OVER: - *beos_op = B_OP_ALPHA; - return true; - - case CAIRO_OPERATOR_ADD: - // Does not actually work -#if 1 - return false; -#else - *beos_op = B_OP_ADD; - return true; -#endif - - case CAIRO_OPERATOR_CLEAR: - // Does not map to B_OP_ERASE - it replaces the dest with the low - // color, instead of transparency; could be done by setting low - // color appropriately. - - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_SATURATE: - - default: - return false; - }; -} - -static cairo_surface_t * -_cairo_beos_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - fprintf(stderr, "Creating similar\n"); - - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - - if (width <= 0) - width = 1; - if (height <= 0) - height = 1; - - BRect rect(0.0, 0.0, width - 1, height - 1); - BBitmap* bmp; - switch (content) { - case CAIRO_CONTENT_ALPHA: - // Can't support this natively - return _cairo_image_surface_create_with_content(content, width, - height); - case CAIRO_CONTENT_COLOR_ALPHA: - bmp = new BBitmap(rect, B_RGBA32, true); - break; - case CAIRO_CONTENT_COLOR: - // Match the color depth - if (surface->bitmap) { - color_space space = surface->bitmap->ColorSpace(); - // No alpha was requested -> make sure not to return - // a surface with alpha - if (space == B_RGBA32) - space = B_RGB32; - if (space == B_RGBA15) - space = B_RGB15; - bmp = new BBitmap(rect, space, true); - } else { - BScreen scr(surface->view->Window()); - color_space space = B_RGB32; - if (scr.IsValid()) - space = scr.ColorSpace(); - bmp = new BBitmap(rect, space, true); - } - break; - default: - assert(0); - return NULL; - - }; - BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0); - bmp->AddChild(view); - return _cairo_beos_surface_create_internal(view, bmp, true); -} - -static cairo_status_t -_cairo_beos_surface_finish (void *abstract_surface) -{ - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - if (surface->owns_bitmap_view) { - if (surface->bitmap) - surface->bitmap->RemoveChild(surface->view); - - delete surface->view; - delete surface->bitmap; - - surface->view = NULL; - surface->bitmap = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_beos_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - fprintf(stderr, "Getting source image\n"); - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do? - - - surface->view->Sync(); - - if (surface->bitmap) { - *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap); - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; - - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - BBitmap* bmp; - if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK) - return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE - - *image_out = _cairo_beos_bitmap_to_surface(bmp); - if (!*image_out) { - delete bmp; - return CAIRO_STATUS_NO_MEMORY; - } - *image_extra = bmp; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_beos_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); - - BBitmap* bmp = static_cast(image_extra); - delete bmp; -} - - - -static cairo_status_t -_cairo_beos_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int16_t *image_rect, - void **image_extra) -{ - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - - AutoLockView locker(surface->view); - if (!locker) { - *image_out = NULL; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (surface->bitmap) { - surface->view->Sync(); - *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap); - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = (*image_out)->width; - image_rect->height = (*image_out)->height; - - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - BRect b_interest_rect(_cairo_rect_to_brect(interest_rect)); - - BRect rect; - BBitmap* bitmap; - ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap, - &rect, &b_interest_rect); - if (status == NOT_VISIBLE) { - *image_out = NULL; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - if (status == ERROR) - return CAIRO_STATUS_NO_MEMORY; - - *image_rect = _brect_to_cairo_rect(rect); - -#if 0 - fprintf(stderr, "Requested: (cairo rects) (%ix%i) dim (%u, %u) returning (%ix%i) dim (%u, %u)\n", - interest_rect->x, interest_rect->y, interest_rect->width, interest_rect->height, - image_rect->x, image_rect->y, image_rect->width, image_rect->height); -#endif - - *image_out = _cairo_beos_bitmap_to_surface(bitmap); - delete bitmap; - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; - - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - - -static void -_cairo_beos_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *intersect_rect, - cairo_image_surface_t *image, - cairo_rectangle_int16_t *image_rect, - void *image_extra) -{ - fprintf(stderr, "Fallback drawing\n"); - - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - - AutoLockView locker(surface->view); - if (!locker) - return; - - BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image); - surface->view->PushState(); - - surface->view->SetDrawingMode(B_OP_COPY); - BRect rect(_cairo_rect_to_brect(image_rect)); - - surface->view->DrawBitmap(bitmap_to_draw, rect); - - surface->view->PopState(); - - delete bitmap_to_draw; - cairo_surface_destroy(&image->base); -} - -static cairo_int_status_t -_cairo_beos_surface_composite (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, - void *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_beos_surface_t *surface = reinterpret_cast( - dst); - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_INT_STATUS_SUCCESS; - - drawing_mode mode; - if (!_cairo_op_to_be_op(op, &mode)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - // XXX Masks are not yet supported - if (mask) - return CAIRO_INT_STATUS_UNSUPPORTED; - - // XXX should eventually support the others - if (src->type != CAIRO_PATTERN_TYPE_SURFACE || - src->extend != CAIRO_EXTEND_NONE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - // Can we maybe support other matrices as well? (scale? if the filter is right) - int itx, ity; - if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - BRect srcRect(src_x + itx, - src_y + ity, - src_x + itx + width - 1, - src_y + ity + height - 1); - BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1); - - cairo_surface_t* src_surface = reinterpret_cast(src)-> - surface; - - // Get a bitmap - BBitmap* bmp = NULL; - bool free_bmp = false; - if (_cairo_surface_is_image(src_surface)) { - cairo_image_surface_t* img_surface = - reinterpret_cast(src_surface); - - bmp = _cairo_image_surface_to_bitmap(img_surface); - free_bmp = true; - } else if (src_surface->backend == surface->base.backend) { - cairo_beos_surface_t *beos_surface = - reinterpret_cast(src_surface); - if (beos_surface->bitmap) { - AutoLockView locker(beos_surface->view); - if (locker) - beos_surface->view->Sync(); - bmp = beos_surface->bitmap; - } else { - _cairo_beos_view_to_bitmap(surface->view, &bmp); - free_bmp = true; - } - } - - if (!bmp) - return CAIRO_INT_STATUS_UNSUPPORTED; - - // So, BeOS seems to screw up painting an opaque bitmap onto a - // translucent one (it makes them partly transparent). Just return - // unsupported. - if (bmp->ColorSpace() == B_RGB32 && surface->bitmap && - surface->bitmap->ColorSpace() == B_RGBA32 && - (mode == B_OP_COPY || mode == B_OP_ALPHA)) - { - if (free_bmp) - delete bmp; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - fprintf(stderr, "Composite\n"); - - // Draw it on screen. - surface->view->PushState(); - - // If our image rect is only a subrect of the desired size, and we - // aren't using B_OP_ALPHA, then we need to fill the rect first. - if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) { - rgb_color black = { 0, 0, 0, 0 }; - - surface->view->SetDrawingMode(mode); - surface->view->SetHighColor(black); - surface->view->FillRect(dstRect); - } - - if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) { - mode = B_OP_COPY; - } - surface->view->SetDrawingMode(mode); - - if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) - surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); - else - surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); - - surface->view->DrawBitmap(bmp, srcRect, dstRect); - - surface->view->PopState(); - - if (free_bmp) - delete bmp; - - return CAIRO_INT_STATUS_SUCCESS; -} - - -static void -_cairo_beos_surface_fill_rectangle (cairo_beos_surface_t *surface, - cairo_rectangle_int16_t *rect) -{ - BRect brect(_cairo_rect_to_brect(rect)); - surface->view->FillRect(brect); -} - -static cairo_int_status_t -_cairo_beos_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int16_t *rects, - int num_rects) -{ - fprintf(stderr, "Drawing %i rectangles\n", num_rects); - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - - if (num_rects <= 0) - return CAIRO_INT_STATUS_SUCCESS; - - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_INT_STATUS_SUCCESS; - - drawing_mode mode; - if (!_cairo_op_to_be_op(op, &mode)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - rgb_color be_color = _cairo_color_to_be_color(color); - - if (mode == B_OP_ALPHA && be_color.alpha == 0xFF) - mode = B_OP_COPY; - - // For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied - // color info. This is only relevant when drawing into an rgb24 buffer - // (as for others, we can convert when asked for the image) - if (mode == B_OP_COPY && be_color.alpha != 0xFF && - (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32)) - { - be_color.red = premultiply(be_color.red, be_color.alpha); - be_color.green = premultiply(be_color.green, be_color.alpha); - be_color.blue = premultiply(be_color.blue, be_color.alpha); - } - - surface->view->PushState(); - - surface->view->SetDrawingMode(mode); - surface->view->SetHighColor(be_color); - if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) - surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); - else - surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); - - for (int i = 0; i < num_rects; ++i) { - _cairo_beos_surface_fill_rectangle(surface, &rects[i]); - } - - surface->view->PopState(); - - return CAIRO_INT_STATUS_SUCCESS; -} - - - -static cairo_int_status_t -_cairo_beos_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - fprintf(stderr, "Setting clip region\n"); - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_INT_STATUS_SUCCESS; - - if (region == NULL) { - // No clipping - surface->view->ConstrainClippingRegion(NULL); - return CAIRO_INT_STATUS_SUCCESS; - } - - int count = pixman_region_num_rects(region); - pixman_box16_t* rects = pixman_region_rects(region); - BRegion bregion; - for (int i = 0; i < count; ++i) { - // Have to substract one, because for pixman, the second coordinate - // lies outside the rectangle. - bregion.Include(BRect(rects[i].x1, rects[i].y1, rects[i].x2 - 1, rects[i].y2 - 1)); - } - surface->view->ConstrainClippingRegion(&bregion); - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_beos_surface_get_extents (void *abstract_surface, - cairo_rectangle_int16_t *rectangle) -{ - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_INT_STATUS_UNSUPPORTED; - - BRect size = surface->view->Bounds(); - - *rectangle = _brect_to_cairo_rect(size); - - // Make sure to have our upperleft edge as (0,0) - rectangle->x = 0; - rectangle->y = 0; - - return CAIRO_INT_STATUS_SUCCESS; -} - -static const struct _cairo_surface_backend cairo_beos_surface_backend = { - CAIRO_SURFACE_TYPE_BEOS, - _cairo_beos_surface_create_similar, - _cairo_beos_surface_finish, - _cairo_beos_surface_acquire_source_image, - _cairo_beos_surface_release_source_image, - _cairo_beos_surface_acquire_dest_image, - _cairo_beos_surface_release_dest_image, - NULL, /* clone_similar */ - _cairo_beos_surface_composite, /* composite */ - _cairo_beos_surface_fill_rectangles, - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_beos_surface_set_clip_region, - NULL, /* intersect_clip_path */ - _cairo_beos_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL /* show_glyphs */ -}; - -static cairo_surface_t * -_cairo_beos_surface_create_internal (BView* view, - BBitmap* bmp, - bool owns_bitmap_view) -{ - // Must use malloc, because cairo code will use free() on the surface - cairo_beos_surface_t *surface = static_cast( - malloc(sizeof(cairo_beos_surface_t))); - if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return const_cast(&_cairo_surface_nil); - } - - cairo_content_t content = CAIRO_CONTENT_COLOR; - if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15)) - content = CAIRO_CONTENT_COLOR_ALPHA; - _cairo_surface_init(&surface->base, &cairo_beos_surface_backend, content); - - surface->view = view; - surface->bitmap = bmp; - surface->owns_bitmap_view = owns_bitmap_view; - - return (cairo_surface_t *) surface; -} - -/** - * cairo_beos_surface_create: - * @view: The view to draw on - * - * Creates a Cairo surface that draws onto a BeOS BView. - * The caller must ensure that the view does not get deleted before the surface. - * If the view is attached to a bitmap rather than an on-screen window, use - * cairo_beos_surface_create_for_bitmap() instead of this function. - **/ -cairo_surface_t * -cairo_beos_surface_create (BView* view) -{ - return cairo_beos_surface_create_for_bitmap(view, NULL); -} - -/** - * cairo_beos_surface_create_for_bitmap: - * @view: The view to draw on - * @bmp: The bitmap to which the view is attached - * - * Creates a Cairo surface that draws onto a BeOS BView which is attached to a - * BBitmap. - * The caller must ensure that the view and the bitmap do not get deleted - * before the surface. - * - * For views that draw to a bitmap (as opposed to a screen), use this function - * rather than cairo_beos_surface_create(). Not using this function WILL lead to - * incorrect behaviour. - * - * For now, only views that draw to the entire area of bmp are supported. - * The view must already be attached to the bitmap. - **/ -cairo_surface_t * -cairo_beos_surface_create_for_bitmap (BView* view, - BBitmap* bmp) -{ - return _cairo_beos_surface_create_internal(view, bmp); -} diff --git a/libs/cairo/cairo/src/cairo-beos.h b/libs/cairo/cairo/src/cairo-beos.h deleted file mode 100644 index 375c930cc..000000000 --- a/libs/cairo/cairo/src/cairo-beos.h +++ /dev/null @@ -1,29 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_BEOS_H -#define CAIRO_BEOS_H - -#include "cairo.h" - -#if CAIRO_HAS_BEOS_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_beos_surface_create (BView* view); - -cairo_public cairo_surface_t * -cairo_beos_surface_create_for_bitmap (BView* view, - BBitmap* bmp); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_BEOS_SURFACE */ -# error Cairo was not compiled with support for the beos backend -#endif /* CAIRO_HAS_BEOS_SURFACE */ - -#endif /* CAIRO_BEOS_H */ diff --git a/libs/cairo/cairo/src/cairo-botor-scan-converter.c b/libs/cairo/cairo/src/cairo-botor-scan-converter.c deleted file mode 100644 index ae060dc31..000000000 --- a/libs/cairo/cairo/src/cairo-botor-scan-converter.c +++ /dev/null @@ -1,2162 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Provide definitions for standalone compilation */ -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-list-private.h" -#include "cairo-freelist-private.h" -#include "cairo-combsort-private.h" - -#include - -#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 t > 1 - * - * (where top/bottom must at least extend to the line endpoints). - * - * A similar substitution can be performed for s, yielding: - * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) - */ - R = det32_64 (dx2, dy2, - b->edge.line.p1.x - a->edge.line.p1.x, - b->edge.line.p1.y - a->edge.line.p1.y); - if (_cairo_int64_negative (den_det)) { - if (_cairo_int64_ge (den_det, R)) - return FALSE; - } else { - if (_cairo_int64_le (den_det, R)) - return FALSE; - } - - R = det32_64 (dy1, dx1, - a->edge.line.p1.y - b->edge.line.p1.y, - a->edge.line.p1.x - b->edge.line.p1.x); - if (_cairo_int64_negative (den_det)) { - if (_cairo_int64_ge (den_det, R)) - return FALSE; - } else { - if (_cairo_int64_le (den_det, R)) - return FALSE; - } - - /* We now know that the two lines should intersect within range. */ - - a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, - a->edge.line.p2.x, a->edge.line.p2.y); - b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, - b->edge.line.p2.x, b->edge.line.p2.y); - - /* x = det (a_det, dx1, b_det, dx2) / den_det */ - qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, - b_det, dx2), - den_det); - if (_cairo_int64_eq (qr.rem, den_det)) - return FALSE; -#if 0 - intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; -#else - intersection->x.exactness = EXACT; - if (! _cairo_int64_is_zero (qr.rem)) { - if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) - qr.rem = _cairo_int64_negate (qr.rem); - qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); - if (_cairo_int64_ge (qr.rem, den_det)) { - qr.quo = _cairo_int64_add (qr.quo, - _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); - } else - intersection->x.exactness = INEXACT; - } -#endif - intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); - - /* y = det (a_det, dy1, b_det, dy2) / den_det */ - qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, - b_det, dy2), - den_det); - if (_cairo_int64_eq (qr.rem, den_det)) - return FALSE; -#if 0 - intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; -#else - intersection->y.exactness = EXACT; - if (! _cairo_int64_is_zero (qr.rem)) { - /* compute ceiling away from zero */ - qr.quo = _cairo_int64_add (qr.quo, - _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); - intersection->y.exactness = INEXACT; - } -#endif - intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); - - return TRUE; -} - -static int -bo_intersect_ordinate_32_compare (int32_t a, int32_t b, int exactness) -{ - int cmp; - - /* First compare the quotient */ - cmp = a - b; - if (cmp) - return cmp; - - /* With quotient identical, if remainder is 0 then compare equal */ - /* Otherwise, the non-zero remainder makes a > b */ - return -(INEXACT == exactness); -} - -/* Does the given edge contain the given point. The point must already - * be known to be contained within the line determined by the edge, - * (most likely the point results from an intersection of this edge - * with another). - * - * If we had exact arithmetic, then this function would simply be a - * matter of examining whether the y value of the point lies within - * the range of y values of the edge. But since intersection points - * are not exact due to being rounded to the nearest integer within - * the available precision, we must also examine the x value of the - * point. - * - * The definition of "contains" here is that the given intersection - * point will be seen by the sweep line after the start event for the - * given edge and before the stop event for the edge. See the comments - * in the implementation for more details. - */ -static cairo_bool_t -bo_edge_contains_intersect_point (const edge_t *edge, - cairo_bo_intersect_point_t *point) -{ - int cmp_top, cmp_bottom; - - /* XXX: When running the actual algorithm, we don't actually need to - * compare against edge->top at all here, since any intersection above - * top is eliminated early via a slope comparison. We're leaving these - * here for now only for the sake of the quadratic-time intersection - * finder which needs them. - */ - - cmp_top = bo_intersect_ordinate_32_compare (point->y.ordinate, - edge->edge.top, - point->y.exactness); - if (cmp_top < 0) - return FALSE; - - cmp_bottom = bo_intersect_ordinate_32_compare (point->y.ordinate, - edge->edge.bottom, - point->y.exactness); - if (cmp_bottom > 0) - return FALSE; - - if (cmp_top > 0 && cmp_bottom < 0) - return TRUE; - - /* At this stage, the point lies on the same y value as either - * edge->top or edge->bottom, so we have to examine the x value in - * order to properly determine containment. */ - - /* If the y value of the point is the same as the y value of the - * top of the edge, then the x value of the point must be greater - * to be considered as inside the edge. Similarly, if the y value - * of the point is the same as the y value of the bottom of the - * edge, then the x value of the point must be less to be - * considered as inside. */ - - if (cmp_top == 0) { - cairo_fixed_t top_x; - - top_x = line_compute_intersection_x_for_y (&edge->edge.line, - edge->edge.top); - return bo_intersect_ordinate_32_compare (top_x, point->x.ordinate, point->x.exactness) < 0; - } else { /* cmp_bottom == 0 */ - cairo_fixed_t bot_x; - - bot_x = line_compute_intersection_x_for_y (&edge->edge.line, - edge->edge.bottom); - return bo_intersect_ordinate_32_compare (point->x.ordinate, bot_x, point->x.exactness) < 0; - } -} - -static cairo_bool_t -edge_intersect (const edge_t *a, - const edge_t *b, - cairo_point_t *intersection) -{ - cairo_bo_intersect_point_t quorem; - - if (! intersect_lines (a, b, &quorem)) - return FALSE; - - if (a->edge.top != a->edge.line.p1.y || a->edge.bottom != a->edge.line.p2.y) { - if (! bo_edge_contains_intersect_point (a, &quorem)) - return FALSE; - } - - if (b->edge.top != b->edge.line.p1.y || b->edge.bottom != b->edge.line.p2.y) { - if (! bo_edge_contains_intersect_point (b, &quorem)) - return FALSE; - } - - /* Now that we've correctly compared the intersection point and - * determined that it lies within the edge, then we know that we - * no longer need any more bits of storage for the intersection - * than we do for our edge coordinates. We also no longer need the - * remainder from the division. */ - intersection->x = quorem.x.ordinate; - intersection->y = quorem.y.ordinate; - - return TRUE; -} - -static inline int -event_compare (const event_t *a, const event_t *b) -{ - return a->y - b->y; -} - -static void -pqueue_init (pqueue_t *pq) -{ - pq->max_size = ARRAY_LENGTH (pq->elements_embedded); - pq->size = 0; - - pq->elements = pq->elements_embedded; -} - -static void -pqueue_fini (pqueue_t *pq) -{ - if (pq->elements != pq->elements_embedded) - free (pq->elements); -} - -static cairo_bool_t -pqueue_grow (pqueue_t *pq) -{ - event_t **new_elements; - pq->max_size *= 2; - - if (pq->elements == pq->elements_embedded) { - new_elements = _cairo_malloc_ab (pq->max_size, - sizeof (event_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - - memcpy (new_elements, pq->elements_embedded, - sizeof (pq->elements_embedded)); - } else { - new_elements = _cairo_realloc_ab (pq->elements, - pq->max_size, - sizeof (event_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - } - - pq->elements = new_elements; - return TRUE; -} - -static inline void -pqueue_push (sweep_line_t *sweep_line, event_t *event) -{ - event_t **elements; - int i, parent; - - if (unlikely (sweep_line->queue.pq.size + 1 == sweep_line->queue.pq.max_size)) { - if (unlikely (! pqueue_grow (&sweep_line->queue.pq))) { - longjmp (sweep_line->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - elements = sweep_line->queue.pq.elements; - for (i = ++sweep_line->queue.pq.size; - i != PQ_FIRST_ENTRY && - event_compare (event, - elements[parent = PQ_PARENT_INDEX (i)]) < 0; - i = parent) - { - elements[i] = elements[parent]; - } - - elements[i] = event; -} - -static inline void -pqueue_pop (pqueue_t *pq) -{ - event_t **elements = pq->elements; - event_t *tail; - int child, i; - - tail = elements[pq->size--]; - if (pq->size == 0) { - elements[PQ_FIRST_ENTRY] = NULL; - return; - } - - for (i = PQ_FIRST_ENTRY; - (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; - i = child) - { - if (child != pq->size && - event_compare (elements[child+1], - elements[child]) < 0) - { - child++; - } - - if (event_compare (elements[child], tail) >= 0) - break; - - elements[i] = elements[child]; - } - elements[i] = tail; -} - -static inline void -event_insert (sweep_line_t *sweep_line, - event_type_t type, - edge_t *e1, - edge_t *e2, - cairo_fixed_t y) -{ - queue_event_t *event; - - event = _cairo_freepool_alloc (&sweep_line->queue.pool); - if (unlikely (event == NULL)) { - longjmp (sweep_line->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - event->y = y; - event->type = type; - event->e1 = e1; - event->e2 = e2; - - pqueue_push (sweep_line, (event_t *) event); -} - -static void -event_delete (sweep_line_t *sweep_line, - event_t *event) -{ - _cairo_freepool_free (&sweep_line->queue.pool, event); -} - -static inline event_t * -event_next (sweep_line_t *sweep_line) -{ - event_t *event, *cmp; - - event = sweep_line->queue.pq.elements[PQ_FIRST_ENTRY]; - cmp = *sweep_line->queue.start_events; - if (event == NULL || - (cmp != NULL && event_compare (cmp, event) < 0)) - { - event = cmp; - sweep_line->queue.start_events++; - } - else - { - pqueue_pop (&sweep_line->queue.pq); - } - - return event; -} - -CAIRO_COMBSORT_DECLARE (start_event_sort, event_t *, event_compare) - -static inline void -event_insert_stop (sweep_line_t *sweep_line, - edge_t *edge) -{ - event_insert (sweep_line, - EVENT_TYPE_STOP, - edge, NULL, - edge->edge.bottom); -} - -static inline void -event_insert_if_intersect_below_current_y (sweep_line_t *sweep_line, - edge_t *left, - edge_t *right) -{ - cairo_point_t intersection; - - /* start points intersect */ - if (left->edge.line.p1.x == right->edge.line.p1.x && - left->edge.line.p1.y == right->edge.line.p1.y) - { - return; - } - - /* end points intersect, process DELETE events first */ - if (left->edge.line.p2.x == right->edge.line.p2.x && - left->edge.line.p2.y == right->edge.line.p2.y) - { - return; - } - - if (slope_compare (left, right) <= 0) - return; - - if (! edge_intersect (left, right, &intersection)) - return; - - event_insert (sweep_line, - EVENT_TYPE_INTERSECTION, - left, right, - intersection.y); -} - -static inline edge_t * -link_to_edge (cairo_list_t *link) -{ - return (edge_t *) link; -} - -static void -sweep_line_insert (sweep_line_t *sweep_line, - edge_t *edge) -{ - cairo_list_t *pos; - cairo_fixed_t y = sweep_line->current_subrow; - - pos = sweep_line->insert_cursor; - if (pos == &sweep_line->active) - pos = sweep_line->active.next; - if (pos != &sweep_line->active) { - int cmp; - - cmp = sweep_line_compare_edges (link_to_edge (pos), - edge, - y); - if (cmp < 0) { - while (pos->next != &sweep_line->active && - sweep_line_compare_edges (link_to_edge (pos->next), - edge, - y) < 0) - { - pos = pos->next; - } - } else if (cmp > 0) { - do { - pos = pos->prev; - } while (pos != &sweep_line->active && - sweep_line_compare_edges (link_to_edge (pos), - edge, - y) > 0); - } - } - cairo_list_add (&edge->link, pos); - sweep_line->insert_cursor = &edge->link; -} - -inline static void -coverage_rewind (struct coverage *cells) -{ - cells->cursor = &cells->head; -} - -static void -coverage_init (struct coverage *cells) -{ - _cairo_freepool_init (&cells->pool, - sizeof (struct cell)); - cells->head.prev = NULL; - cells->head.next = &cells->tail; - cells->head.x = INT_MIN; - cells->tail.prev = &cells->head; - cells->tail.next = NULL; - cells->tail.x = INT_MAX; - cells->count = 0; - coverage_rewind (cells); -} - -static void -coverage_fini (struct coverage *cells) -{ - _cairo_freepool_fini (&cells->pool); -} - -inline static void -coverage_reset (struct coverage *cells) -{ - cells->head.next = &cells->tail; - cells->tail.prev = &cells->head; - cells->count = 0; - _cairo_freepool_reset (&cells->pool); - coverage_rewind (cells); -} - -inline static struct cell * -coverage_alloc (sweep_line_t *sweep_line, - struct cell *tail, - int x) -{ - struct cell *cell; - - cell = _cairo_freepool_alloc (&sweep_line->coverage.pool); - if (unlikely (NULL == cell)) { - longjmp (sweep_line->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - tail->prev->next = cell; - cell->prev = tail->prev; - cell->next = tail; - tail->prev = cell; - cell->x = x; - cell->uncovered_area = 0; - cell->covered_height = 0; - sweep_line->coverage.count++; - return cell; -} - -inline static struct cell * -coverage_find (sweep_line_t *sweep_line, int x) -{ - struct cell *cell; - - cell = sweep_line->coverage.cursor; - if (unlikely (cell->x > x)) { - do { - if (cell->prev->x < x) - break; - cell = cell->prev; - } while (TRUE); - } else { - if (cell->x == x) - return cell; - - do { - UNROLL3({ - cell = cell->next; - if (cell->x >= x) - break; - }); - } while (TRUE); - } - - if (cell->x != x) - cell = coverage_alloc (sweep_line, cell, x); - - return sweep_line->coverage.cursor = cell; -} - -static void -coverage_render_cells (sweep_line_t *sweep_line, - cairo_fixed_t left, cairo_fixed_t right, - cairo_fixed_t y1, cairo_fixed_t y2, - int sign) -{ - int fx1, fx2; - int ix1, ix2; - int dx, dy; - - /* Orient the edge left-to-right. */ - dx = right - left; - if (dx >= 0) { - ix1 = _cairo_fixed_integer_part (left); - fx1 = _cairo_fixed_fractional_part (left); - - ix2 = _cairo_fixed_integer_part (right); - fx2 = _cairo_fixed_fractional_part (right); - - dy = y2 - y1; - } else { - ix1 = _cairo_fixed_integer_part (right); - fx1 = _cairo_fixed_fractional_part (right); - - ix2 = _cairo_fixed_integer_part (left); - fx2 = _cairo_fixed_fractional_part (left); - - dx = -dx; - sign = -sign; - dy = y1 - y2; - y1 = y2 - dy; - y2 = y1 + dy; - } - - /* Add coverage for all pixels [ix1,ix2] on this row crossed - * by the edge. */ - { - struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx); - struct cell *cell; - - cell = sweep_line->coverage.cursor; - if (cell->x != ix1) { - if (unlikely (cell->x > ix1)) { - do { - if (cell->prev->x < ix1) - break; - cell = cell->prev; - } while (TRUE); - } else do { - UNROLL3({ - if (cell->x >= ix1) - break; - cell = cell->next; - }); - } while (TRUE); - - if (cell->x != ix1) - cell = coverage_alloc (sweep_line, cell, ix1); - } - - cell->uncovered_area += sign * y.quo * (STEP_X + fx1); - cell->covered_height += sign * y.quo; - y.quo += y1; - - cell = cell->next; - if (cell->x != ++ix1) - cell = coverage_alloc (sweep_line, cell, ix1); - if (ix1 < ix2) { - struct quorem dydx_full = floored_divrem (STEP_X*dy, dx); - - do { - cairo_fixed_t y_skip = dydx_full.quo; - y.rem += dydx_full.rem; - if (y.rem >= dx) { - ++y_skip; - y.rem -= dx; - } - - y.quo += y_skip; - - y_skip *= sign; - cell->covered_height += y_skip; - cell->uncovered_area += y_skip*STEP_X; - - cell = cell->next; - if (cell->x != ++ix1) - cell = coverage_alloc (sweep_line, cell, ix1); - } while (ix1 != ix2); - } - cell->uncovered_area += sign*(y2 - y.quo)*fx2; - cell->covered_height += sign*(y2 - y.quo); - sweep_line->coverage.cursor = cell; - } -} - -inline static void -full_inc_edge (edge_t *edge) -{ - edge->x.quo += edge->dxdy_full.quo; - edge->x.rem += edge->dxdy_full.rem; - if (edge->x.rem >= 0) { - ++edge->x.quo; - edge->x.rem -= edge->dy; - } -} - -static void -full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign) -{ - struct cell *cell; - cairo_fixed_t x1, x2; - int ix1, ix2; - int frac; - - edge->current_sign = sign; - - ix1 = _cairo_fixed_integer_part (edge->x.quo); - - if (edge->vertical) { - frac = _cairo_fixed_fractional_part (edge->x.quo); - cell = coverage_find (sweep_line, ix1); - cell->covered_height += sign * STEP_Y; - cell->uncovered_area += sign * 2 * frac * STEP_Y; - return; - } - - x1 = edge->x.quo; - full_inc_edge (edge); - x2 = edge->x.quo; - - ix2 = _cairo_fixed_integer_part (edge->x.quo); - - /* Edge is entirely within a column? */ - if (likely (ix1 == ix2)) { - frac = _cairo_fixed_fractional_part (x1) + - _cairo_fixed_fractional_part (x2); - cell = coverage_find (sweep_line, ix1); - cell->covered_height += sign * STEP_Y; - cell->uncovered_area += sign * frac * STEP_Y; - return; - } - - coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign); -} - -static void -full_nonzero (sweep_line_t *sweep_line) -{ - cairo_list_t *pos; - - sweep_line->is_vertical = TRUE; - pos = sweep_line->active.next; - do { - edge_t *left = link_to_edge (pos), *right; - int winding = left->edge.dir; - - sweep_line->is_vertical &= left->vertical; - - pos = left->link.next; - do { - if (unlikely (pos == &sweep_line->active)) { - full_add_edge (sweep_line, left, +1); - return; - } - - right = link_to_edge (pos); - pos = pos->next; - sweep_line->is_vertical &= right->vertical; - - winding += right->edge.dir; - if (0 == winding) { - if (pos == &sweep_line->active || - link_to_edge (pos)->x.quo != right->x.quo) - { - break; - } - } - - if (! right->vertical) - full_inc_edge (right); - } while (TRUE); - - full_add_edge (sweep_line, left, +1); - full_add_edge (sweep_line, right, -1); - } while (pos != &sweep_line->active); -} - -static void -full_evenodd (sweep_line_t *sweep_line) -{ - cairo_list_t *pos; - - sweep_line->is_vertical = TRUE; - pos = sweep_line->active.next; - do { - edge_t *left = link_to_edge (pos), *right; - int winding = 0; - - sweep_line->is_vertical &= left->vertical; - - pos = left->link.next; - do { - if (pos == &sweep_line->active) { - full_add_edge (sweep_line, left, +1); - return; - } - - right = link_to_edge (pos); - pos = pos->next; - sweep_line->is_vertical &= right->vertical; - - if (++winding & 1) { - if (pos == &sweep_line->active || - link_to_edge (pos)->x.quo != right->x.quo) - { - break; - } - } - - if (! right->vertical) - full_inc_edge (right); - } while (TRUE); - - full_add_edge (sweep_line, left, +1); - full_add_edge (sweep_line, right, -1); - } while (pos != &sweep_line->active); -} - -static void -render_rows (cairo_botor_scan_converter_t *self, - sweep_line_t *sweep_line, - int y, int height, - cairo_span_renderer_t *renderer) -{ - cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; - cairo_half_open_span_t *spans = spans_stack; - struct cell *cell; - int prev_x, cover; - int num_spans; - cairo_status_t status; - - if (unlikely (sweep_line->coverage.count == 0)) { - status = renderer->render_rows (renderer, y, height, NULL, 0); - if (unlikely (status)) - longjmp (sweep_line->unwind, status); - return; - } - - /* Allocate enough spans for the row. */ - - num_spans = 2*sweep_line->coverage.count+2; - if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) { - spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t)); - if (unlikely (spans == NULL)) { - longjmp (sweep_line->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - /* Form the spans from the coverage and areas. */ - num_spans = 0; - prev_x = self->xmin; - cover = 0; - cell = sweep_line->coverage.head.next; - do { - int x = cell->x; - int area; - - if (x > prev_x) { - spans[num_spans].x = prev_x; - spans[num_spans].coverage = AREA_TO_ALPHA (cover); - ++num_spans; - } - - cover += cell->covered_height*STEP_X*2; - area = cover - cell->uncovered_area; - - spans[num_spans].x = x; - spans[num_spans].coverage = AREA_TO_ALPHA (area); - ++num_spans; - - prev_x = x + 1; - } while ((cell = cell->next) != &sweep_line->coverage.tail); - - if (prev_x <= self->xmax) { - spans[num_spans].x = prev_x; - spans[num_spans].coverage = AREA_TO_ALPHA (cover); - ++num_spans; - } - - if (cover && prev_x < self->xmax) { - spans[num_spans].x = self->xmax; - spans[num_spans].coverage = 0; - ++num_spans; - } - - status = renderer->render_rows (renderer, y, height, spans, num_spans); - - if (unlikely (spans != spans_stack)) - free (spans); - - coverage_reset (&sweep_line->coverage); - - if (unlikely (status)) - longjmp (sweep_line->unwind, status); -} - -static void -full_repeat (sweep_line_t *sweep) -{ - edge_t *edge; - - cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { - if (edge->current_sign) - full_add_edge (sweep, edge, edge->current_sign); - else if (! edge->vertical) - full_inc_edge (edge); - } -} - -static void -full_reset (sweep_line_t *sweep) -{ - edge_t *edge; - - cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) - edge->current_sign = 0; -} - -static void -full_step (cairo_botor_scan_converter_t *self, - sweep_line_t *sweep_line, - cairo_fixed_t row, - cairo_span_renderer_t *renderer) -{ - int top, bottom; - - top = _cairo_fixed_integer_part (sweep_line->current_row); - bottom = _cairo_fixed_integer_part (row); - if (cairo_list_is_empty (&sweep_line->active)) { - cairo_status_t status; - - status = renderer->render_rows (renderer, top, bottom - top, NULL, 0); - if (unlikely (status)) - longjmp (sweep_line->unwind, status); - - return; - } - - if (self->fill_rule == CAIRO_FILL_RULE_WINDING) - full_nonzero (sweep_line); - else - full_evenodd (sweep_line); - - if (sweep_line->is_vertical || bottom == top + 1) { - render_rows (self, sweep_line, top, bottom - top, renderer); - full_reset (sweep_line); - return; - } - - render_rows (self, sweep_line, top++, 1, renderer); - do { - full_repeat (sweep_line); - render_rows (self, sweep_line, top, 1, renderer); - } while (++top != bottom); - - full_reset (sweep_line); -} - -cairo_always_inline static void -sub_inc_edge (edge_t *edge, - cairo_fixed_t height) -{ - if (height == 1) { - edge->x.quo += edge->dxdy.quo; - edge->x.rem += edge->dxdy.rem; - if (edge->x.rem >= 0) { - ++edge->x.quo; - edge->x.rem -= edge->dy; - } - } else { - edge->x.quo += height * edge->dxdy.quo; - edge->x.rem += height * edge->dxdy.rem; - if (edge->x.rem >= 0) { - int carry = edge->x.rem / edge->dy + 1; - edge->x.quo += carry; - edge->x.rem -= carry * edge->dy; - } - } -} - -static void -sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign) -{ - struct run *run; - - run = _cairo_freepool_alloc (&sweep_line->runs); - if (unlikely (run == NULL)) - longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); - - run->y = y; - run->sign = sign; - run->next = edge->runs; - edge->runs = run; - - edge->current_sign = sign; -} - -inline static cairo_bool_t -edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y) -{ - /* XXX is compare_x_for_y() worth executing during sub steps? */ - return line_equal (&left->edge.line, &right->edge.line); - //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0; -} - -static void -sub_nonzero (sweep_line_t *sweep_line) -{ - cairo_fixed_t y = sweep_line->current_subrow; - cairo_fixed_t fy = _cairo_fixed_fractional_part (y); - cairo_list_t *pos; - - pos = sweep_line->active.next; - do { - edge_t *left = link_to_edge (pos), *right; - int winding = left->edge.dir; - - pos = left->link.next; - do { - if (unlikely (pos == &sweep_line->active)) { - if (left->current_sign != +1) - sub_add_run (sweep_line, left, fy, +1); - return; - } - - right = link_to_edge (pos); - pos = pos->next; - - winding += right->edge.dir; - if (0 == winding) { - if (pos == &sweep_line->active || - ! edges_coincident (right, link_to_edge (pos), y)) - { - break; - } - } - - if (right->current_sign) - sub_add_run (sweep_line, right, fy, 0); - } while (TRUE); - - if (left->current_sign != +1) - sub_add_run (sweep_line, left, fy, +1); - if (right->current_sign != -1) - sub_add_run (sweep_line, right, fy, -1); - } while (pos != &sweep_line->active); -} - -static void -sub_evenodd (sweep_line_t *sweep_line) -{ - cairo_fixed_t y = sweep_line->current_subrow; - cairo_fixed_t fy = _cairo_fixed_fractional_part (y); - cairo_list_t *pos; - - pos = sweep_line->active.next; - do { - edge_t *left = link_to_edge (pos), *right; - int winding = 0; - - pos = left->link.next; - do { - if (unlikely (pos == &sweep_line->active)) { - if (left->current_sign != +1) - sub_add_run (sweep_line, left, fy, +1); - return; - } - - right = link_to_edge (pos); - pos = pos->next; - - if (++winding & 1) { - if (pos == &sweep_line->active || - ! edges_coincident (right, link_to_edge (pos), y)) - { - break; - } - } - - if (right->current_sign) - sub_add_run (sweep_line, right, fy, 0); - } while (TRUE); - - if (left->current_sign != +1) - sub_add_run (sweep_line, left, fy, +1); - if (right->current_sign != -1) - sub_add_run (sweep_line, right, fy, -1); - } while (pos != &sweep_line->active); -} - -cairo_always_inline static void -sub_step (cairo_botor_scan_converter_t *self, - sweep_line_t *sweep_line) -{ - if (cairo_list_is_empty (&sweep_line->active)) - return; - - if (self->fill_rule == CAIRO_FILL_RULE_WINDING) - sub_nonzero (sweep_line); - else - sub_evenodd (sweep_line); -} - -static void -coverage_render_runs (sweep_line_t *sweep, edge_t *edge, - cairo_fixed_t y1, cairo_fixed_t y2) -{ - struct run tail; - struct run *run = &tail; - - tail.next = NULL; - tail.y = y2; - - /* Order the runs top->bottom */ - while (edge->runs) { - struct run *r; - - r = edge->runs; - edge->runs = r->next; - r->next = run; - run = r; - } - - if (run->y > y1) - sub_inc_edge (edge, run->y - y1); - - do { - cairo_fixed_t x1, x2; - - y1 = run->y; - y2 = run->next->y; - - x1 = edge->x.quo; - if (y2 - y1 == STEP_Y) - full_inc_edge (edge); - else - sub_inc_edge (edge, y2 - y1); - x2 = edge->x.quo; - - if (run->sign) { - int ix1, ix2; - - ix1 = _cairo_fixed_integer_part (x1); - ix2 = _cairo_fixed_integer_part (x2); - - /* Edge is entirely within a column? */ - if (likely (ix1 == ix2)) { - struct cell *cell; - int frac; - - frac = _cairo_fixed_fractional_part (x1) + - _cairo_fixed_fractional_part (x2); - cell = coverage_find (sweep, ix1); - cell->covered_height += run->sign * (y2 - y1); - cell->uncovered_area += run->sign * (y2 - y1) * frac; - } else { - coverage_render_cells (sweep, x1, x2, y1, y2, run->sign); - } - } - - run = run->next; - } while (run->next != NULL); -} - -static void -coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2) -{ - struct cell *cell; - struct run *run; - int height = 0; - - for (run = edge->runs; run != NULL; run = run->next) { - if (run->sign) - height += run->sign * (y2 - run->y); - y2 = run->y; - } - - cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo)); - cell->covered_height += height; - cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height; -} - -cairo_always_inline static void -sub_emit (cairo_botor_scan_converter_t *self, - sweep_line_t *sweep, - cairo_span_renderer_t *renderer) -{ - edge_t *edge; - - sub_step (self, sweep); - - /* convert the runs into coverages */ - - cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { - if (edge->runs == NULL) { - if (! edge->vertical) { - if (edge->flags & START) { - sub_inc_edge (edge, - STEP_Y - _cairo_fixed_fractional_part (edge->edge.top)); - edge->flags &= ~START; - } else - full_inc_edge (edge); - } - } else { - if (edge->vertical) { - coverage_render_vertical_runs (sweep, edge, STEP_Y); - } else { - int y1 = 0; - if (edge->flags & START) { - y1 = _cairo_fixed_fractional_part (edge->edge.top); - edge->flags &= ~START; - } - coverage_render_runs (sweep, edge, y1, STEP_Y); - } - } - edge->current_sign = 0; - edge->runs = NULL; - } - - cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) { - int y2 = _cairo_fixed_fractional_part (edge->edge.bottom); - if (edge->vertical) { - coverage_render_vertical_runs (sweep, edge, y2); - } else { - int y1 = 0; - if (edge->flags & START) - y1 = _cairo_fixed_fractional_part (edge->edge.top); - coverage_render_runs (sweep, edge, y1, y2); - } - } - cairo_list_init (&sweep->stopped); - - _cairo_freepool_reset (&sweep->runs); - - render_rows (self, sweep, - _cairo_fixed_integer_part (sweep->current_row), 1, - renderer); -} - -static void -sweep_line_init (sweep_line_t *sweep_line, - event_t **start_events, - int num_events) -{ - cairo_list_init (&sweep_line->active); - cairo_list_init (&sweep_line->stopped); - sweep_line->insert_cursor = &sweep_line->active; - - sweep_line->current_row = INT32_MIN; - sweep_line->current_subrow = INT32_MIN; - - coverage_init (&sweep_line->coverage); - _cairo_freepool_init (&sweep_line->runs, sizeof (struct run)); - - start_event_sort (start_events, num_events); - start_events[num_events] = NULL; - - sweep_line->queue.start_events = start_events; - - _cairo_freepool_init (&sweep_line->queue.pool, - sizeof (queue_event_t)); - pqueue_init (&sweep_line->queue.pq); - sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL; -} - -static void -sweep_line_delete (sweep_line_t *sweep_line, - edge_t *edge) -{ - if (sweep_line->insert_cursor == &edge->link) - sweep_line->insert_cursor = edge->link.prev; - - cairo_list_del (&edge->link); - if (edge->runs) - cairo_list_add_tail (&edge->link, &sweep_line->stopped); - edge->flags |= STOP; -} - -static void -sweep_line_swap (sweep_line_t *sweep_line, - edge_t *left, - edge_t *right) -{ - right->link.prev = left->link.prev; - left->link.next = right->link.next; - right->link.next = &left->link; - left->link.prev = &right->link; - left->link.next->prev = &left->link; - right->link.prev->next = &right->link; -} - -static void -sweep_line_fini (sweep_line_t *sweep_line) -{ - pqueue_fini (&sweep_line->queue.pq); - _cairo_freepool_fini (&sweep_line->queue.pool); - coverage_fini (&sweep_line->coverage); - _cairo_freepool_fini (&sweep_line->runs); -} - -static cairo_status_t -botor_generate (cairo_botor_scan_converter_t *self, - event_t **start_events, - cairo_span_renderer_t *renderer) -{ - cairo_status_t status; - sweep_line_t sweep_line; - cairo_fixed_t ybot; - event_t *event; - cairo_list_t *left, *right; - edge_t *e1, *e2; - int bottom; - - sweep_line_init (&sweep_line, start_events, self->num_edges); - if ((status = setjmp (sweep_line.unwind))) - goto unwind; - - ybot = self->extents.p2.y; - sweep_line.current_subrow = self->extents.p1.y; - sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y); - event = *sweep_line.queue.start_events++; - do { - /* Can we process a full step in one go? */ - if (event->y >= sweep_line.current_row + STEP_Y) { - bottom = _cairo_fixed_floor (event->y); - full_step (self, &sweep_line, bottom, renderer); - sweep_line.current_row = bottom; - sweep_line.current_subrow = bottom; - } - - do { - if (event->y > sweep_line.current_subrow) { - sub_step (self, &sweep_line); - sweep_line.current_subrow = event->y; - } - - do { - /* Update the active list using Bentley-Ottmann */ - switch (event->type) { - case EVENT_TYPE_START: - e1 = ((start_event_t *) event)->edge; - - sweep_line_insert (&sweep_line, e1); - event_insert_stop (&sweep_line, e1); - - left = e1->link.prev; - right = e1->link.next; - - if (left != &sweep_line.active) { - event_insert_if_intersect_below_current_y (&sweep_line, - link_to_edge (left), e1); - } - - if (right != &sweep_line.active) { - event_insert_if_intersect_below_current_y (&sweep_line, - e1, link_to_edge (right)); - } - - break; - - case EVENT_TYPE_STOP: - e1 = ((queue_event_t *) event)->e1; - event_delete (&sweep_line, event); - - left = e1->link.prev; - right = e1->link.next; - - sweep_line_delete (&sweep_line, e1); - - if (left != &sweep_line.active && - right != &sweep_line.active) - { - event_insert_if_intersect_below_current_y (&sweep_line, - link_to_edge (left), - link_to_edge (right)); - } - - break; - - case EVENT_TYPE_INTERSECTION: - e1 = ((queue_event_t *) event)->e1; - e2 = ((queue_event_t *) event)->e2; - - event_delete (&sweep_line, event); - if (e1->flags & STOP) - break; - if (e2->flags & STOP) - break; - - /* skip this intersection if its edges are not adjacent */ - if (&e2->link != e1->link.next) - break; - - left = e1->link.prev; - right = e2->link.next; - - sweep_line_swap (&sweep_line, e1, e2); - - /* after the swap e2 is left of e1 */ - if (left != &sweep_line.active) { - event_insert_if_intersect_below_current_y (&sweep_line, - link_to_edge (left), e2); - } - - if (right != &sweep_line.active) { - event_insert_if_intersect_below_current_y (&sweep_line, - e1, link_to_edge (right)); - } - - break; - } - - event = event_next (&sweep_line); - if (event == NULL) - goto end; - } while (event->y == sweep_line.current_subrow); - } while (event->y < sweep_line.current_row + STEP_Y); - - bottom = sweep_line.current_row + STEP_Y; - sub_emit (self, &sweep_line, renderer); - sweep_line.current_subrow = bottom; - sweep_line.current_row = sweep_line.current_subrow; - } while (TRUE); - - end: - /* flush any partial spans */ - if (sweep_line.current_subrow != sweep_line.current_row) { - sub_emit (self, &sweep_line, renderer); - sweep_line.current_row += STEP_Y; - sweep_line.current_subrow = sweep_line.current_row; - } - /* clear the rest */ - if (sweep_line.current_subrow < ybot) { - bottom = _cairo_fixed_integer_part (sweep_line.current_row); - status = renderer->render_rows (renderer, - bottom, _cairo_fixed_integer_ceil (ybot) - bottom, - NULL, 0); - } - - unwind: - sweep_line_fini (&sweep_line); - - return status; -} - -static cairo_status_t -_cairo_botor_scan_converter_generate (void *converter, - cairo_span_renderer_t *renderer) -{ - cairo_botor_scan_converter_t *self = converter; - start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)]; - start_event_t *events; - event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - event_t **event_ptrs; - struct _cairo_botor_scan_converter_chunk *chunk; - cairo_status_t status; - int num_events; - int i, j; - - num_events = self->num_edges; - if (unlikely (0 == num_events)) { - return renderer->render_rows (renderer, - _cairo_fixed_integer_floor (self->extents.p1.y), - _cairo_fixed_integer_ceil (self->extents.p2.y) - - _cairo_fixed_integer_floor (self->extents.p1.y), - NULL, 0); - } - - events = stack_events; - event_ptrs = stack_event_ptrs; - if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) { - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (start_event_t) + sizeof (event_t *), - sizeof (event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (event_t **) (events + num_events); - } - - j = 0; - for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { - edge_t *edge; - - edge = chunk->base; - for (i = 0; i < chunk->count; i++) { - event_ptrs[j] = (event_t *) &events[j]; - - events[j].y = edge->edge.top; - events[j].type = EVENT_TYPE_START; - events[j].edge = edge; - - edge++, j++; - } - } - - status = botor_generate (self, event_ptrs, renderer); - - if (events != stack_events) - free (events); - - return status; -} - -static edge_t * -botor_allocate_edge (cairo_botor_scan_converter_t *self) -{ - struct _cairo_botor_scan_converter_chunk *chunk; - - chunk = self->tail; - if (chunk->count == chunk->size) { - int size; - - size = chunk->size * 2; - chunk->next = _cairo_malloc_ab_plus_c (size, - sizeof (edge_t), - sizeof (struct _cairo_botor_scan_converter_chunk)); - if (unlikely (chunk->next == NULL)) - return NULL; - - chunk = chunk->next; - chunk->next = NULL; - chunk->count = 0; - chunk->size = size; - chunk->base = chunk + 1; - self->tail = chunk; - } - - return (edge_t *) chunk->base + chunk->count++; -} - -static cairo_status_t -botor_add_edge (cairo_botor_scan_converter_t *self, - const cairo_edge_t *edge) -{ - edge_t *e; - cairo_fixed_t dx, dy; - - e = botor_allocate_edge (self); - if (unlikely (e == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cairo_list_init (&e->link); - e->edge = *edge; - - dx = edge->line.p2.x - edge->line.p1.x; - dy = edge->line.p2.y - edge->line.p1.y; - e->dy = dy; - - if (dx == 0) { - e->vertical = TRUE; - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - e->dxdy.quo = 0; - e->dxdy.rem = 0; - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - } else { - e->vertical = FALSE; - e->dxdy = floored_divrem (dx, dy); - if (edge->top == edge->line.p1.y) { - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - } else { - e->x = floored_muldivrem (edge->top - edge->line.p1.y, - dx, dy); - e->x.quo += edge->line.p1.x; - } - - if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) { - e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy); - } else { - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - } - } - - e->x.rem = -e->dy; - e->current_sign = 0; - e->runs = NULL; - e->flags = START; - - self->num_edges++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_botor_scan_converter_add_edge (void *converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - cairo_botor_scan_converter_t *self = converter; - cairo_edge_t edge; - - edge.line.p1 = *p1; - edge.line.p2 = *p2; - edge.top = top; - edge.bottom = bottom; - edge.dir = dir; - - return botor_add_edge (self, &edge); -} - -static cairo_status_t -_cairo_botor_scan_converter_add_polygon (void *converter, - const cairo_polygon_t *polygon) -{ - cairo_botor_scan_converter_t *self = converter; - cairo_status_t status; - int i; - - for (i = 0; i < polygon->num_edges; i++) { - status = botor_add_edge (self, &polygon->edges[i]); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_botor_scan_converter_destroy (void *converter) -{ - cairo_botor_scan_converter_t *self = converter; - struct _cairo_botor_scan_converter_chunk *chunk, *next; - - for (chunk = self->chunks.next; chunk != NULL; chunk = next) { - next = chunk->next; - free (chunk); - } -} - -void -_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, - const cairo_box_t *extents, - cairo_fill_rule_t fill_rule) -{ - self->base.destroy = _cairo_botor_scan_converter_destroy; - self->base.add_edge = _cairo_botor_scan_converter_add_edge; - self->base.add_polygon = _cairo_botor_scan_converter_add_polygon; - self->base.generate = _cairo_botor_scan_converter_generate; - - self->extents = *extents; - self->fill_rule = fill_rule; - - self->xmin = _cairo_fixed_integer_floor (extents->p1.x); - self->xmax = _cairo_fixed_integer_ceil (extents->p2.x); - - self->chunks.base = self->buf; - self->chunks.next = NULL; - self->chunks.count = 0; - self->chunks.size = sizeof (self->buf) / sizeof (edge_t); - self->tail = &self->chunks; - - self->num_edges = 0; -} diff --git a/libs/cairo/cairo/src/cairo-boxes-private.h b/libs/cairo/cairo/src/cairo-boxes-private.h deleted file mode 100644 index e61a64d7c..000000000 --- a/libs/cairo/cairo/src/cairo-boxes-private.h +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_BOXES_H -#define CAIRO_BOXES_H - -#include "cairo-types-private.h" -#include "cairo-compiler-private.h" - -struct _cairo_boxes_t { - cairo_status_t status; - cairo_box_t limit; - const cairo_box_t *limits; - int num_limits; - int num_boxes; - unsigned int is_pixel_aligned : 1; - - struct _cairo_boxes_chunk { - struct _cairo_boxes_chunk *next; - cairo_box_t *base; - int count; - int size; - } chunks, *tail; - cairo_box_t boxes_embedded[32]; -}; - -cairo_private void -_cairo_boxes_init (cairo_boxes_t *boxes); - -cairo_private void -_cairo_boxes_init_for_array (cairo_boxes_t *boxes, - cairo_box_t *array, - int num_boxes); - -cairo_private void -_cairo_boxes_limit (cairo_boxes_t *boxes, - const cairo_box_t *limits, - int num_limits); - -cairo_private cairo_status_t -_cairo_boxes_add (cairo_boxes_t *boxes, - const cairo_box_t *box); - -cairo_private void -_cairo_boxes_extents (const cairo_boxes_t *boxes, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_boxes_clear (cairo_boxes_t *boxes); - -cairo_private void -_cairo_boxes_fini (cairo_boxes_t *boxes); - -#endif /* CAIRO_BOXES_H */ diff --git a/libs/cairo/cairo/src/cairo-boxes.c b/libs/cairo/cairo/src/cairo-boxes.c deleted file mode 100644 index e29dd7e13..000000000 --- a/libs/cairo/cairo/src/cairo-boxes.c +++ /dev/null @@ -1,271 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-error-private.h" - -void -_cairo_boxes_init (cairo_boxes_t *boxes) -{ - boxes->status = CAIRO_STATUS_SUCCESS; - boxes->num_limits = 0; - boxes->num_boxes = 0; - - boxes->tail = &boxes->chunks; - boxes->chunks.next = NULL; - boxes->chunks.base = boxes->boxes_embedded; - boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); - boxes->chunks.count = 0; - - boxes->is_pixel_aligned = TRUE; -} - -void -_cairo_boxes_init_for_array (cairo_boxes_t *boxes, - cairo_box_t *array, - int num_boxes) -{ - int n; - - boxes->status = CAIRO_STATUS_SUCCESS; - boxes->num_limits = 0; - boxes->num_boxes = num_boxes; - - boxes->tail = &boxes->chunks; - boxes->chunks.next = NULL; - boxes->chunks.base = array; - boxes->chunks.size = num_boxes; - boxes->chunks.count = num_boxes; - - for (n = 0; n < num_boxes; n++) { - if (! _cairo_fixed_is_integer (array[n].p1.x) || - ! _cairo_fixed_is_integer (array[n].p1.y) || - ! _cairo_fixed_is_integer (array[n].p2.x) || - ! _cairo_fixed_is_integer (array[n].p2.y)) - { - break; - } - } - - boxes->is_pixel_aligned = n == num_boxes; -} - -void -_cairo_boxes_limit (cairo_boxes_t *boxes, - const cairo_box_t *limits, - int num_limits) -{ - int n; - - boxes->limits = limits; - boxes->num_limits = num_limits; - - if (boxes->num_limits) { - boxes->limit = limits[0]; - for (n = 1; n < num_limits; n++) { - if (limits[n].p1.x < boxes->limit.p1.x) - boxes->limit.p1.x = limits[n].p1.x; - - if (limits[n].p1.y < boxes->limit.p1.y) - boxes->limit.p1.y = limits[n].p1.y; - - if (limits[n].p2.x > boxes->limit.p2.x) - boxes->limit.p2.x = limits[n].p2.x; - - if (limits[n].p2.y > boxes->limit.p2.y) - boxes->limit.p2.y = limits[n].p2.y; - } - } -} - -static void -_cairo_boxes_add_internal (cairo_boxes_t *boxes, - const cairo_box_t *box) -{ - struct _cairo_boxes_chunk *chunk; - - if (unlikely (boxes->status)) - return; - - chunk = boxes->tail; - if (unlikely (chunk->count == chunk->size)) { - int size; - - size = chunk->size * 2; - chunk->next = _cairo_malloc_ab_plus_c (size, - sizeof (cairo_box_t), - sizeof (struct _cairo_boxes_chunk)); - - if (unlikely (chunk->next == NULL)) { - boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return; - } - - chunk = chunk->next; - boxes->tail = chunk; - - chunk->next = NULL; - chunk->count = 0; - chunk->size = size; - chunk->base = (cairo_box_t *) (chunk + 1); - } - - chunk->base[chunk->count++] = *box; - boxes->num_boxes++; - - if (boxes->is_pixel_aligned) { - boxes->is_pixel_aligned = - _cairo_fixed_is_integer (box->p1.x) && - _cairo_fixed_is_integer (box->p1.y) && - _cairo_fixed_is_integer (box->p2.x) && - _cairo_fixed_is_integer (box->p2.y); - } -} - -cairo_status_t -_cairo_boxes_add (cairo_boxes_t *boxes, - const cairo_box_t *box) -{ - if (box->p1.y == box->p2.y) - return CAIRO_STATUS_SUCCESS; - - if (box->p1.x == box->p2.x) - return CAIRO_STATUS_SUCCESS; - - if (boxes->num_limits) { - cairo_point_t p1, p2; - cairo_bool_t reversed = FALSE; - int n; - - /* support counter-clockwise winding for rectangular tessellation */ - if (box->p1.x < box->p2.x) { - p1.x = box->p1.x; - p2.x = box->p2.x; - } else { - p2.x = box->p1.x; - p1.x = box->p2.x; - reversed = ! reversed; - } - - if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x) - return CAIRO_STATUS_SUCCESS; - - if (box->p1.y < box->p2.y) { - p1.y = box->p1.y; - p2.y = box->p2.y; - } else { - p2.y = box->p1.y; - p1.y = box->p2.y; - reversed = ! reversed; - } - - if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y) - return CAIRO_STATUS_SUCCESS; - - for (n = 0; n < boxes->num_limits; n++) { - const cairo_box_t *limits = &boxes->limits[n]; - cairo_box_t _box; - cairo_point_t _p1, _p2; - - if (p1.x >= limits->p2.x || p2.x <= limits->p1.x) - continue; - if (p1.y >= limits->p2.y || p2.y <= limits->p1.y) - continue; - - /* Otherwise, clip the box to the limits. */ - _p1 = p1; - if (_p1.x < limits->p1.x) - _p1.x = limits->p1.x; - if (_p1.y < limits->p1.y) - _p1.y = limits->p1.y; - - _p2 = p2; - if (_p2.x > limits->p2.x) - _p2.x = limits->p2.x; - if (_p2.y > limits->p2.y) - _p2.y = limits->p2.y; - - if (_p2.y <= _p1.y || _p2.x <= _p1.x) - continue; - - _box.p1.y = _p1.y; - _box.p2.y = _p2.y; - if (reversed) { - _box.p1.x = _p2.x; - _box.p2.x = _p1.x; - } else { - _box.p1.x = _p1.x; - _box.p2.x = _p2.x; - } - - _cairo_boxes_add_internal (boxes, &_box); - } - } else { - _cairo_boxes_add_internal (boxes, box); - } - - return boxes->status; -} - -void -_cairo_boxes_extents (const cairo_boxes_t *boxes, - cairo_rectangle_int_t *extents) -{ - const struct _cairo_boxes_chunk *chunk; - cairo_box_t box; - int i; - - box.p1.y = box.p1.x = INT_MAX; - box.p2.y = box.p2.x = INT_MIN; - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *b = chunk->base; - for (i = 0; i < chunk->count; i++) { - if (b[i].p1.x < box.p1.x) - box.p1.x = b[i].p1.x; - - if (b[i].p1.y < box.p1.y) - box.p1.y = b[i].p1.y; - - if (b[i].p2.x > box.p2.x) - box.p2.x = b[i].p2.x; - - if (b[i].p2.y > box.p2.y) - box.p2.y = b[i].p2.y; - } - } - - _cairo_box_round_to_rectangle (&box, extents); -} - -void -_cairo_boxes_clear (cairo_boxes_t *boxes) -{ - struct _cairo_boxes_chunk *chunk, *next; - - for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { - next = chunk->next; - free (chunk); - } - - boxes->tail = &boxes->chunks; - boxes->chunks.next = 0; - boxes->chunks.count = 0; - boxes->num_boxes = 0; - - boxes->is_pixel_aligned = TRUE; -} - -void -_cairo_boxes_fini (cairo_boxes_t *boxes) -{ - struct _cairo_boxes_chunk *chunk, *next; - - for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { - next = chunk->next; - free (chunk); - } -} diff --git a/libs/cairo/cairo/src/cairo-cache-private.h b/libs/cairo/cairo/src/cairo-cache-private.h deleted file mode 100644 index 06940a63a..000000000 --- a/libs/cairo/cairo/src/cairo-cache-private.h +++ /dev/null @@ -1,111 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_CACHE_PRIVATE_H -#define CAIRO_CACHE_PRIVATE_H - -#include "cairo-compiler-private.h" -#include "cairo-types-private.h" - -/** - * cairo_cache_entry_t: - * - * A #cairo_cache_entry_t contains both a key and a value for - * #cairo_cache_t. User-derived types for #cairo_cache_entry_t must - * have a #cairo_cache_entry_t as their first field. For example: - * - * typedef _my_entry { - * cairo_cache_entry_t base; - * ... Remainder of key and value fields here .. - * } my_entry_t; - * - * which then allows a pointer to my_entry_t to be passed to any of - * the #cairo_cache_t functions as follows without requiring a cast: - * - * _cairo_cache_insert (cache, &my_entry->base, size); - * - * IMPORTANT: The caller is responsible for initializing - * my_entry->base.hash with a hash code derived from the key. The - * essential property of the hash code is that keys_equal must never - * return %TRUE for two keys that have different hashes. The best hash - * code will reduce the frequency of two keys with the same code for - * which keys_equal returns %FALSE. - * - * The user must also initialize my_entry->base.size to indicate - * the size of the current entry. What units to use for size is - * entirely up to the caller, (though the same units must be used for - * the max_size parameter passed to _cairo_cache_create()). If all - * entries are close to the same size, the simplest thing to do is to - * just use units of "entries", (eg. set size==1 in all entries and - * set max_size to the number of entries which you want to be saved - * in the cache). - * - * Which parts of the entry make up the "key" and which part make up - * the value are entirely up to the caller, (as determined by the - * computation going into base.hash as well as the keys_equal - * function). A few of the #cairo_cache_t functions accept an entry which - * will be used exclusively as a "key", (indicated by a parameter name - * of key). In these cases, the value-related fields of the entry need - * not be initialized if so desired. - **/ -typedef struct _cairo_cache_entry { - uintptr_t hash; - unsigned long size; -} cairo_cache_entry_t; - -typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry); - -struct _cairo_cache { - cairo_hash_table_t *hash_table; - - cairo_cache_predicate_func_t predicate; - cairo_destroy_func_t entry_destroy; - - unsigned long max_size; - unsigned long size; - - int freeze_count; -}; - -typedef cairo_bool_t -(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); - -typedef void -(*cairo_cache_callback_func_t) (void *entry, - void *closure); - -cairo_private cairo_status_t -_cairo_cache_init (cairo_cache_t *cache, - cairo_cache_keys_equal_func_t keys_equal, - cairo_cache_predicate_func_t predicate, - cairo_destroy_func_t entry_destroy, - unsigned long max_size); - -cairo_private void -_cairo_cache_fini (cairo_cache_t *cache); - -cairo_private void -_cairo_cache_freeze (cairo_cache_t *cache); - -cairo_private void -_cairo_cache_thaw (cairo_cache_t *cache); - -cairo_private void * -_cairo_cache_lookup (cairo_cache_t *cache, - cairo_cache_entry_t *key); - -cairo_private cairo_status_t -_cairo_cache_insert (cairo_cache_t *cache, - cairo_cache_entry_t *entry); - -cairo_private void -_cairo_cache_remove (cairo_cache_t *cache, - cairo_cache_entry_t *entry); - -cairo_private void -_cairo_cache_foreach (cairo_cache_t *cache, - cairo_cache_callback_func_t cache_callback, - void *closure); - -#endif diff --git a/libs/cairo/cairo/src/cairo-cache.c b/libs/cairo/cairo/src/cairo-cache.c deleted file mode 100644 index 021b012fd..000000000 --- a/libs/cairo/cairo/src/cairo-cache.c +++ /dev/null @@ -1,304 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-cff-subset.c b/libs/cairo/cairo/src/cairo-cff-subset.c deleted file mode 100644 index 6a5060047..000000000 --- a/libs/cairo/cairo/src/cairo-cff-subset.c +++ /dev/null @@ -1,2246 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Useful links: - * http://www.adobe.com/devnet/font/pdfs/5176.CFF.pdf - */ - -#define _BSD_SOURCE /* for snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-truetype-subset-private.h" -#include - -/* CFF Dict Operators. If the high byte is 0 the command is encoded - * with a single byte. */ -#define BASEFONTNAME_OP 0x0c16 -#define CIDCOUNT_OP 0x0c22 -#define CHARSET_OP 0x000f -#define CHARSTRINGS_OP 0x0011 -#define COPYRIGHT_OP 0x0c00 -#define ENCODING_OP 0x0010 -#define FAMILYNAME_OP 0x0003 -#define FDARRAY_OP 0x0c24 -#define FDSELECT_OP 0x0c25 -#define FONTBBOX_OP 0x0005 -#define FONTNAME_OP 0x0c26 -#define FULLNAME_OP 0x0002 -#define LOCAL_SUB_OP 0x0013 -#define NOTICE_OP 0x0001 -#define POSTSCRIPT_OP 0x0c15 -#define PRIVATE_OP 0x0012 -#define ROS_OP 0x0c1e -#define UNIQUEID_OP 0x000d -#define VERSION_OP 0x0000 -#define WEIGHT_OP 0x0004 -#define XUID_OP 0x000e - -#define NUM_STD_STRINGS 391 - -typedef struct _cff_header { - uint8_t major; - uint8_t minor; - uint8_t header_size; - uint8_t offset_size; -} cff_header_t; - -typedef struct _cff_index_element { - cairo_bool_t is_copy; - unsigned char *data; - int length; -} cff_index_element_t; - -typedef struct _cff_dict_operator { - cairo_hash_entry_t base; - - unsigned short operator; - unsigned char *operand; - int operand_length; - int operand_offset; -} cff_dict_operator_t; - -typedef struct _cairo_cff_font { - - cairo_scaled_font_subset_t *scaled_font_subset; - const cairo_scaled_font_backend_t *backend; - - /* Font Data */ - unsigned char *data; - unsigned long data_length; - unsigned char *current_ptr; - unsigned char *data_end; - cff_header_t *header; - char *font_name; - char *ps_name; - cairo_hash_table_t *top_dict; - cairo_hash_table_t *private_dict; - cairo_array_t strings_index; - cairo_array_t charstrings_index; - cairo_array_t global_sub_index; - cairo_array_t local_sub_index; - int num_glyphs; - cairo_bool_t is_cid; - int units_per_em; - - /* CID Font Data */ - int *fdselect; - unsigned int num_fontdicts; - cairo_hash_table_t **fd_dict; - cairo_hash_table_t **fd_private_dict; - cairo_array_t *fd_local_sub_index; - - /* Subsetted Font Data */ - char *subset_font_name; - cairo_array_t charstrings_subset_index; - cairo_array_t strings_subset_index; - int *fdselect_subset; - unsigned int num_subset_fontdicts; - int *fd_subset_map; - int *private_dict_offset; - cairo_array_t output; - - /* Subset Metrics */ - int *widths; - int x_min, y_min, x_max, y_max; - int ascent, descent; - -} cairo_cff_font_t; - -/* Encoded integer using maximum sized encoding. This is required for - * operands that are later modified after encoding. */ -static unsigned char * -encode_integer_max (unsigned char *p, int i) -{ - *p++ = 29; - *p++ = i >> 24; - *p++ = (i >> 16) & 0xff; - *p++ = (i >> 8) & 0xff; - *p++ = i & 0xff; - return p; -} - -static unsigned char * -encode_integer (unsigned char *p, int i) -{ - if (i >= -107 && i <= 107) { - *p++ = i + 139; - } else if (i >= 108 && i <= 1131) { - i -= 108; - *p++ = (i >> 8)+ 247; - *p++ = i & 0xff; - } else if (i >= -1131 && i <= -108) { - i = -i - 108; - *p++ = (i >> 8)+ 251; - *p++ = i & 0xff; - } else if (i >= -32768 && i <= 32767) { - *p++ = 28; - *p++ = (i >> 8) & 0xff; - *p++ = i & 0xff; - } else { - p = encode_integer_max (p, i); - } - return p; -} - -static unsigned char * -decode_integer (unsigned char *p, int *integer) -{ - if (*p == 28) { - *integer = (int)(p[1]<<8 | p[2]); - p += 3; - } else if (*p == 29) { - *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); - p += 5; - } else if (*p >= 32 && *p <= 246) { - *integer = *p++ - 139; - } else if (*p <= 250) { - *integer = (p[0] - 247) * 256 + p[1] + 108; - p += 2; - } else if (*p <= 254) { - *integer = -(p[0] - 251) * 256 - p[1] - 108; - p += 2; - } else { - *integer = 0; - p += 1; - } - return p; -} - -static unsigned char * -decode_operator (unsigned char *p, unsigned short *operator) -{ - unsigned short op = 0; - - op = *p++; - if (op == 12) { - op <<= 8; - op |= *p++; - } - *operator = op; - return p; -} - -/* return 0 if not an operand */ -static int -operand_length (unsigned char *p) -{ - unsigned char *begin = p; - - if (*p == 28) - return 3; - - if (*p == 29) - return 5; - - if (*p >= 32 && *p <= 246) - return 1; - - if (*p >= 247 && *p <= 254) - return 2; - - if (*p == 30) { - while ((*p & 0x0f) != 0x0f) - p++; - return p - begin + 1; - } - - return 0; -} - -static unsigned char * -encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) -{ - while (--offset_size >= 0) { - p[offset_size] = (unsigned char) (offset & 0xff); - offset >>= 8; - } - return p + offset_size; -} - -static unsigned long -decode_index_offset(unsigned char *p, int off_size) -{ - unsigned long offset = 0; - - while (off_size-- > 0) - offset = offset*256 + *p++; - return offset; -} - -static void -cff_index_init (cairo_array_t *index) -{ - _cairo_array_init (index, sizeof (cff_index_element_t)); -} - -static cairo_int_status_t -cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr) -{ - cff_index_element_t element; - unsigned char *data, *p; - cairo_status_t status; - int offset_size, count, start, i; - int end = 0; - - p = *ptr; - if (p + 2 > end_ptr) - return CAIRO_INT_STATUS_UNSUPPORTED; - count = be16_to_cpu( *((uint16_t *)p) ); - p += 2; - if (count > 0) { - offset_size = *p++; - if (p + (count + 1)*offset_size > end_ptr) - return CAIRO_INT_STATUS_UNSUPPORTED; - data = p + offset_size*(count + 1) - 1; - start = decode_index_offset (p, offset_size); - p += offset_size; - for (i = 0; i < count; i++) { - end = decode_index_offset (p, offset_size); - p += offset_size; - if (p > end_ptr) - return CAIRO_INT_STATUS_UNSUPPORTED; - element.length = end - start; - element.is_copy = FALSE; - element.data = data + start; - status = _cairo_array_append (index, &element); - if (unlikely (status)) - return status; - start = end; - } - p = data + end; - } - *ptr = p; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cff_index_write (cairo_array_t *index, cairo_array_t *output) -{ - int offset_size; - int offset; - int num_elem; - int i; - cff_index_element_t *element; - uint16_t count; - unsigned char buf[5]; - cairo_status_t status; - - num_elem = _cairo_array_num_elements (index); - count = cpu_to_be16 ((uint16_t) num_elem); - status = _cairo_array_append_multiple (output, &count, 2); - if (unlikely (status)) - return status; - - if (num_elem == 0) - return CAIRO_STATUS_SUCCESS; - - /* Find maximum offset to determine offset size */ - offset = 1; - for (i = 0; i < num_elem; i++) { - element = _cairo_array_index (index, i); - offset += element->length; - } - if (offset < 0x100) - offset_size = 1; - else if (offset < 0x10000) - offset_size = 2; - else if (offset < 0x1000000) - offset_size = 3; - else - offset_size = 4; - - buf[0] = (unsigned char) offset_size; - status = _cairo_array_append (output, buf); - if (unlikely (status)) - return status; - - offset = 1; - encode_index_offset (buf, offset_size, offset); - status = _cairo_array_append_multiple (output, buf, offset_size); - if (unlikely (status)) - return status; - - for (i = 0; i < num_elem; i++) { - element = _cairo_array_index (index, i); - offset += element->length; - encode_index_offset (buf, offset_size, offset); - status = _cairo_array_append_multiple (output, buf, offset_size); - if (unlikely (status)) - return status; - } - - for (i = 0; i < num_elem; i++) { - element = _cairo_array_index (index, i); - status = _cairo_array_append_multiple (output, - element->data, - element->length); - if (unlikely (status)) - return status; - } - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cff_index_append (cairo_array_t *index, unsigned char *object , int length) -{ - cff_index_element_t element; - - element.length = length; - element.is_copy = FALSE; - element.data = object; - - return _cairo_array_append (index, &element); -} - -static cairo_status_t -cff_index_append_copy (cairo_array_t *index, - const unsigned char *object, - unsigned int length) -{ - cff_index_element_t element; - cairo_status_t status; - - element.length = length; - element.is_copy = TRUE; - element.data = malloc (element.length); - if (unlikely (element.data == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (element.data, object, element.length); - - status = _cairo_array_append (index, &element); - if (unlikely (status)) { - free (element.data); - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -cff_index_fini (cairo_array_t *index) -{ - cff_index_element_t *element; - int i; - - for (i = 0; i < _cairo_array_num_elements (index); i++) { - element = _cairo_array_index (index, i); - if (element->is_copy) - free (element->data); - } - _cairo_array_fini (index); -} - -static cairo_bool_t -_cairo_cff_dict_equal (const void *key_a, const void *key_b) -{ - const cff_dict_operator_t *op_a = key_a; - const cff_dict_operator_t *op_b = key_b; - - return op_a->operator == op_b->operator; -} - -static cairo_status_t -cff_dict_init (cairo_hash_table_t **dict) -{ - *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); - if (unlikely (*dict == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_dict_init_key (cff_dict_operator_t *key, int operator) -{ - key->base.hash = (unsigned long) operator; - key->operator = operator; -} - -static cairo_status_t -cff_dict_create_operator (int operator, - unsigned char *operand, - int size, - cff_dict_operator_t **out) -{ - cff_dict_operator_t *op; - - op = malloc (sizeof (cff_dict_operator_t)); - if (unlikely (op == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_dict_init_key (op, operator); - op->operand = malloc (size); - if (unlikely (op->operand == NULL)) { - free (op); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (op->operand, operand, size); - op->operand_length = size; - op->operand_offset = -1; - - *out = op; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) -{ - unsigned char *end; - cairo_array_t operands; - cff_dict_operator_t *op; - unsigned short operator; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - int size; - - end = p + dict_size; - _cairo_array_init (&operands, 1); - while (p < end) { - size = operand_length (p); - if (size != 0) { - status = _cairo_array_append_multiple (&operands, p, size); - if (unlikely (status)) - goto fail; - - p += size; - } else { - p = decode_operator (p, &operator); - status = cff_dict_create_operator (operator, - _cairo_array_index (&operands, 0), - _cairo_array_num_elements (&operands), - &op); - if (unlikely (status)) - goto fail; - - status = _cairo_hash_table_insert (dict, &op->base); - if (unlikely (status)) - goto fail; - - _cairo_array_truncate (&operands, 0); - } - } - -fail: - _cairo_array_fini (&operands); - - return status; -} - -static void -cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) -{ - cff_dict_operator_t key, *op; - - _cairo_dict_init_key (&key, operator); - op = _cairo_hash_table_lookup (dict, &key.base); - if (op != NULL) { - free (op->operand); - _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); - free (op); - } -} - -static unsigned char * -cff_dict_get_operands (cairo_hash_table_t *dict, - unsigned short operator, - int *size) -{ - cff_dict_operator_t key, *op; - - _cairo_dict_init_key (&key, operator); - op = _cairo_hash_table_lookup (dict, &key.base); - if (op != NULL) { - *size = op->operand_length; - return op->operand; - } - - return NULL; -} - -static cairo_status_t -cff_dict_set_operands (cairo_hash_table_t *dict, - unsigned short operator, - unsigned char *operand, - int size) -{ - cff_dict_operator_t key, *op; - cairo_status_t status; - - _cairo_dict_init_key (&key, operator); - op = _cairo_hash_table_lookup (dict, &key.base); - if (op != NULL) { - free (op->operand); - op->operand = malloc (size); - if (unlikely (op->operand == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (op->operand, operand, size); - op->operand_length = size; - } - else - { - status = cff_dict_create_operator (operator, operand, size, &op); - if (unlikely (status)) - return status; - - status = _cairo_hash_table_insert (dict, &op->base); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static int -cff_dict_get_location (cairo_hash_table_t *dict, - unsigned short operator, - int *size) -{ - cff_dict_operator_t key, *op; - - _cairo_dict_init_key (&key, operator); - op = _cairo_hash_table_lookup (dict, &key.base); - if (op != NULL) { - *size = op->operand_length; - return op->operand_offset; - } - - return -1; -} - -typedef struct _dict_write_info { - cairo_array_t *output; - cairo_status_t status; -} dict_write_info_t; - -static void -cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info) -{ - unsigned char data; - - op->operand_offset = _cairo_array_num_elements (write_info->output); - write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length); - if (write_info->status) - return; - - if (op->operator & 0xff00) { - data = op->operator >> 8; - write_info->status = _cairo_array_append (write_info->output, &data); - if (write_info->status) - return; - } - data = op->operator & 0xff; - write_info->status = _cairo_array_append (write_info->output, &data); -} - -static void -_cairo_dict_collect (void *entry, void *closure) -{ - dict_write_info_t *write_info = closure; - cff_dict_operator_t *op = entry; - - if (write_info->status) - return; - - /* The ROS operator is handled separately in cff_dict_write() */ - if (op->operator != ROS_OP) - cairo_dict_write_operator (op, write_info); -} - -static cairo_status_t -cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) -{ - dict_write_info_t write_info; - cff_dict_operator_t key, *op; - - write_info.output = output; - write_info.status = CAIRO_STATUS_SUCCESS; - - /* The CFF specification requires that the Top Dict of CID fonts - * begin with the ROS operator. */ - _cairo_dict_init_key (&key, ROS_OP); - op = _cairo_hash_table_lookup (dict, &key.base); - if (op != NULL) - cairo_dict_write_operator (op, &write_info); - - _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); - - return write_info.status; -} - -static void -_cff_dict_entry_pluck (void *_entry, void *dict) -{ - cff_dict_operator_t *entry = _entry; - - _cairo_hash_table_remove (dict, &entry->base); - free (entry->operand); - free (entry); -} - -static void -cff_dict_fini (cairo_hash_table_t *dict) -{ - _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); - _cairo_hash_table_destroy (dict); -} - -static cairo_int_status_t -cairo_cff_font_read_header (cairo_cff_font_t *font) -{ - if (font->data_length < sizeof (cff_header_t)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - font->header = (cff_header_t *) font->data; - font->current_ptr = font->data + font->header->header_size; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_cff_font_read_name (cairo_cff_font_t *font) -{ - cairo_array_t index; - cairo_int_status_t status; - - /* The original font name is not used in the subset. Read the name - * index to skip over it. */ - cff_index_init (&index); - status = cff_index_read (&index, &font->current_ptr, font->data_end); - cff_index_fini (&index); - - return status; -} - -static cairo_int_status_t -cairo_cff_font_read_private_dict (cairo_cff_font_t *font, - cairo_hash_table_t *private_dict, - cairo_array_t *local_sub_index, - unsigned char *ptr, - int size) -{ - cairo_int_status_t status; - unsigned char buf[10]; - unsigned char *end_buf; - int offset; - int i; - unsigned char *operand; - unsigned char *p; - - status = cff_dict_read (private_dict, ptr, size); - if (unlikely (status)) - return status; - - operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); - if (operand) { - decode_integer (operand, &offset); - p = ptr + offset; - status = cff_index_read (local_sub_index, &p, font->data_end); - if (unlikely (status)) - return status; - - /* Use maximum sized encoding to reserve space for later modification. */ - end_buf = encode_integer_max (buf, 0); - status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) -{ - int type, num_ranges, first, last, fd, i, j; - - font->fdselect = calloc (font->num_glyphs, sizeof (int)); - if (unlikely (font->fdselect == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - type = *p++; - if (type == 0) - { - for (i = 0; i < font->num_glyphs; i++) - font->fdselect[i] = *p++; - } else if (type == 3) { - num_ranges = be16_to_cpu( *((uint16_t *)p) ); - p += 2; - for (i = 0; i < num_ranges; i++) - { - first = be16_to_cpu( *((uint16_t *)p) ); - p += 2; - fd = *p++; - last = be16_to_cpu( *((uint16_t *)p) ); - for (j = first; j < last; j++) - font->fdselect[j] = fd; - } - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) -{ - cairo_array_t index; - cff_index_element_t *element; - unsigned int i; - int size; - unsigned char *operand; - int offset; - cairo_int_status_t status; - unsigned char buf[100]; - unsigned char *end_buf; - - cff_index_init (&index); - status = cff_index_read (&index, &ptr, font->data_end); - if (unlikely (status)) - goto fail; - - font->num_fontdicts = _cairo_array_num_elements (&index); - - font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); - if (unlikely (font->fd_dict == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - - font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); - if (unlikely (font->fd_private_dict == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - - font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); - if (unlikely (font->fd_local_sub_index == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - - for (i = 0; i < font->num_fontdicts; i++) { - status = cff_dict_init (&font->fd_dict[i]); - if (unlikely (status)) - goto fail; - - element = _cairo_array_index (&index, i); - status = cff_dict_read (font->fd_dict[i], element->data, element->length); - if (unlikely (status)) - goto fail; - - operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); - if (operand == NULL) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto fail; - } - operand = decode_integer (operand, &size); - decode_integer (operand, &offset); - status = cff_dict_init (&font->fd_private_dict[i]); - if (unlikely (status)) - goto fail; - - cff_index_init (&font->fd_local_sub_index[i]); - status = cairo_cff_font_read_private_dict (font, - font->fd_private_dict[i], - &font->fd_local_sub_index[i], - font->data + offset, - size); - if (unlikely (status)) - goto fail; - - /* Set integer operand to max value to use max size encoding to reserve - * space for any value later */ - end_buf = encode_integer_max (buf, 0); - end_buf = encode_integer_max (end_buf, 0); - status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - } - - return CAIRO_STATUS_SUCCESS; - -fail: - cff_index_fini (&index); - - return status; -} - -static cairo_int_status_t -cairo_cff_font_read_top_dict (cairo_cff_font_t *font) -{ - cairo_array_t index; - cff_index_element_t *element; - unsigned char buf[20]; - unsigned char *end_buf; - unsigned char *operand; - cairo_int_status_t status; - unsigned char *p; - int size; - int offset; - - cff_index_init (&index); - status = cff_index_read (&index, &font->current_ptr, font->data_end); - if (unlikely (status)) - goto fail; - - element = _cairo_array_index (&index, 0); - status = cff_dict_read (font->top_dict, element->data, element->length); - if (unlikely (status)) - goto fail; - - if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) - font->is_cid = TRUE; - else - font->is_cid = FALSE; - - operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size); - decode_integer (operand, &offset); - p = font->data + offset; - status = cff_index_read (&font->charstrings_index, &p, font->data_end); - if (unlikely (status)) - goto fail; - font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); - - if (font->is_cid) { - operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); - decode_integer (operand, &offset); - status = cairo_cff_font_read_fdselect (font, font->data + offset); - if (unlikely (status)) - goto fail; - - operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); - decode_integer (operand, &offset); - status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); - if (unlikely (status)) - goto fail; - } else { - operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); - operand = decode_integer (operand, &size); - decode_integer (operand, &offset); - status = cairo_cff_font_read_private_dict (font, - font->private_dict, - &font->local_sub_index, - font->data + offset, - size); - if (unlikely (status)) - goto fail; - } - - /* Use maximum sized encoding to reserve space for later modification. */ - end_buf = encode_integer_max (buf, 0); - status = cff_dict_set_operands (font->top_dict, - CHARSTRINGS_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - - status = cff_dict_set_operands (font->top_dict, - FDSELECT_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - - status = cff_dict_set_operands (font->top_dict, - FDARRAY_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - - status = cff_dict_set_operands (font->top_dict, - CHARSET_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - - cff_dict_remove (font->top_dict, ENCODING_OP); - cff_dict_remove (font->top_dict, PRIVATE_OP); - - /* Remove the unique identifier operators as the subsetted font is - * not the same is the original font. */ - cff_dict_remove (font->top_dict, UNIQUEID_OP); - cff_dict_remove (font->top_dict, XUID_OP); - -fail: - cff_index_fini (&index); - - return status; -} - -static cairo_int_status_t -cairo_cff_font_read_strings (cairo_cff_font_t *font) -{ - return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end); -} - -static cairo_int_status_t -cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) -{ - return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); -} - -typedef cairo_int_status_t -(*font_read_t) (cairo_cff_font_t *font); - -static const font_read_t font_read_funcs[] = { - cairo_cff_font_read_header, - cairo_cff_font_read_name, - cairo_cff_font_read_top_dict, - cairo_cff_font_read_strings, - cairo_cff_font_read_global_subroutines, -}; - -static cairo_int_status_t -cairo_cff_font_read_font (cairo_cff_font_t *font) -{ - cairo_int_status_t status; - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { - status = font_read_funcs[i] (font); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) -{ - cairo_status_t status; - unsigned char buf[30]; - unsigned char *p; - int sid1, sid2; - const char *registry = "Adobe"; - const char *ordering = "Identity"; - - sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); - status = cff_index_append_copy (&font->strings_subset_index, - (unsigned char *)registry, - strlen(registry)); - if (unlikely (status)) - return status; - - sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); - status = cff_index_append_copy (&font->strings_subset_index, - (unsigned char *)ordering, - strlen(ordering)); - if (unlikely (status)) - return status; - - p = encode_integer (buf, sid1); - p = encode_integer (p, sid2); - p = encode_integer (p, 0); - status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); - if (unlikely (status)) - return status; - - p = encode_integer (buf, font->scaled_font_subset->num_glyphs); - status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, - cairo_hash_table_t *dict, - int operator) -{ - int size; - unsigned char *p; - int sid; - unsigned char buf[100]; - cff_index_element_t *element; - cairo_status_t status; - - p = cff_dict_get_operands (dict, operator, &size); - if (!p) - return CAIRO_STATUS_SUCCESS; - - decode_integer (p, &sid); - if (sid < NUM_STD_STRINGS) - return CAIRO_STATUS_SUCCESS; - - element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); - sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); - status = cff_index_append (&font->strings_subset_index, element->data, element->length); - if (unlikely (status)) - return status; - - p = encode_integer (buf, sid); - status = cff_dict_set_operands (dict, operator, buf, p - buf); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static const int dict_strings[] = { - VERSION_OP, - NOTICE_OP, - COPYRIGHT_OP, - FULLNAME_OP, - FAMILYNAME_OP, - WEIGHT_OP, - POSTSCRIPT_OP, - BASEFONTNAME_OP, - FONTNAME_OP, -}; - -static cairo_status_t -cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, - cairo_hash_table_t *dict) -{ - cairo_status_t status; - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { - status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_subset_charstrings (cairo_cff_font_t *font) -{ - cff_index_element_t *element; - unsigned int i; - cairo_status_t status; - - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - element = _cairo_array_index (&font->charstrings_index, - font->scaled_font_subset->glyphs[i]); - status = cff_index_append (&font->charstrings_subset_index, - element->data, - element->length); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) -{ - unsigned int i; - int fd; - int *reverse_map; - - font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, - sizeof (int)); - if (unlikely (font->fdselect_subset == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); - if (unlikely (font->fd_subset_map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); - if (unlikely (font->private_dict_offset == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - reverse_map = calloc (font->num_fontdicts, sizeof (int)); - if (unlikely (reverse_map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < font->num_fontdicts; i++) - reverse_map[i] = -1; - - font->num_subset_fontdicts = 0; - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - fd = font->fdselect[font->scaled_font_subset->glyphs[i]]; - if (reverse_map[fd] < 0) { - font->fd_subset_map[font->num_subset_fontdicts] = fd; - reverse_map[fd] = font->num_subset_fontdicts++; - } - font->fdselect_subset[i] = reverse_map[fd]; - } - - free (reverse_map); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) -{ - unsigned char buf[100]; - unsigned char *end_buf; - cairo_status_t status; - - font->num_fontdicts = 1; - font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); - if (unlikely (font->fd_dict == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (cff_dict_init (&font->fd_dict[0])) { - free (font->fd_dict); - font->fd_dict = NULL; - font->num_fontdicts = 0; - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - font->fd_subset_map = malloc (sizeof (int)); - if (unlikely (font->fd_subset_map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->private_dict_offset = malloc (sizeof (int)); - if (unlikely (font->private_dict_offset == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->fd_subset_map[0] = 0; - font->num_subset_fontdicts = 1; - - /* Set integer operand to max value to use max size encoding to reserve - * space for any value later */ - end_buf = encode_integer_max (buf, 0); - end_buf = encode_integer_max (end_buf, 0); - status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_subset_strings (cairo_cff_font_t *font) -{ - cairo_status_t status; - unsigned int i; - - status = cairo_cff_font_subset_dict_strings (font, font->top_dict); - if (unlikely (status)) - return status; - - if (font->is_cid) { - for (i = 0; i < font->num_subset_fontdicts; i++) { - status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); - if (unlikely (status)) - return status; - - status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); - if (unlikely (status)) - return status; - } - } else { - status = cairo_cff_font_subset_dict_strings (font, font->private_dict); - } - - return status; -} - -static cairo_status_t -cairo_cff_font_subset_font (cairo_cff_font_t *font) -{ - cairo_status_t status; - - status = cairo_cff_font_set_ros_strings (font); - if (unlikely (status)) - return status; - - status = cairo_cff_font_subset_charstrings (font); - if (unlikely (status)) - return status; - - if (font->is_cid) - status = cairo_cff_font_subset_fontdict (font); - else - status = cairo_cff_font_create_cid_fontdict (font); - if (unlikely (status)) - return status; - - status = cairo_cff_font_subset_strings (font); - if (unlikely (status)) - return status; - - return status; -} - -/* Set the operand of the specified operator in the (already written) - * top dict to point to the current position in the output - * array. Operands updated with this function must have previously - * been encoded with the 5-byte (max) integer encoding. */ -static void -cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font, - int operator) -{ - int cur_pos; - int offset; - int size; - unsigned char buf[10]; - unsigned char *buf_end; - unsigned char *op_ptr; - - cur_pos = _cairo_array_num_elements (&font->output); - buf_end = encode_integer_max (buf, cur_pos); - offset = cff_dict_get_location (font->top_dict, operator, &size); - assert (offset > 0); - op_ptr = _cairo_array_index (&font->output, offset); - memcpy (op_ptr, buf, buf_end - buf); -} - -static cairo_status_t -cairo_cff_font_write_header (cairo_cff_font_t *font) -{ - return _cairo_array_append_multiple (&font->output, - font->header, - font->header->header_size); -} - -static cairo_status_t -cairo_cff_font_write_name (cairo_cff_font_t *font) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_array_t index; - - cff_index_init (&index); - - status = cff_index_append_copy (&index, - (unsigned char *) font->subset_font_name, - strlen(font->subset_font_name)); - if (unlikely (status)) - goto FAIL; - - status = cff_index_write (&index, &font->output); - if (unlikely (status)) - goto FAIL; - -FAIL: - cff_index_fini (&index); - - return status; -} - -static cairo_status_t -cairo_cff_font_write_top_dict (cairo_cff_font_t *font) -{ - uint16_t count; - unsigned char buf[10]; - unsigned char *p; - int offset_index; - int dict_start, dict_size; - int offset_size = 4; - cairo_status_t status; - - /* Write an index containing the top dict */ - - count = cpu_to_be16 (1); - status = _cairo_array_append_multiple (&font->output, &count, 2); - if (unlikely (status)) - return status; - buf[0] = offset_size; - status = _cairo_array_append (&font->output, buf); - if (unlikely (status)) - return status; - encode_index_offset (buf, offset_size, 1); - status = _cairo_array_append_multiple (&font->output, buf, offset_size); - if (unlikely (status)) - return status; - - /* Reserve space for last element of offset array and update after - * dict is written */ - offset_index = _cairo_array_num_elements (&font->output); - status = _cairo_array_append_multiple (&font->output, buf, offset_size); - if (unlikely (status)) - return status; - - dict_start = _cairo_array_num_elements (&font->output); - status = cff_dict_write (font->top_dict, &font->output); - if (unlikely (status)) - return status; - dict_size = _cairo_array_num_elements (&font->output) - dict_start; - - encode_index_offset (buf, offset_size, dict_size + 1); - p = _cairo_array_index (&font->output, offset_index); - memcpy (p, buf, offset_size); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_write_strings (cairo_cff_font_t *font) -{ - return cff_index_write (&font->strings_subset_index, &font->output); -} - -static cairo_status_t -cairo_cff_font_write_global_subrs (cairo_cff_font_t *font) -{ - return cff_index_write (&font->global_sub_index, &font->output); -} - -static cairo_status_t -cairo_cff_font_write_fdselect (cairo_cff_font_t *font) -{ - unsigned char data; - unsigned int i; - cairo_int_status_t status; - - cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP); - - if (font->is_cid) { - data = 0; - status = _cairo_array_append (&font->output, &data); - if (unlikely (status)) - return status; - - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - data = font->fdselect_subset[i]; - status = _cairo_array_append (&font->output, &data); - if (unlikely (status)) - return status; - } - } else { - unsigned char byte; - uint16_t word; - - status = _cairo_array_grow_by (&font->output, 9); - if (unlikely (status)) - return status; - - byte = 3; - status = _cairo_array_append (&font->output, &byte); - assert (status == CAIRO_STATUS_SUCCESS); - - word = cpu_to_be16 (1); - status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); - - word = cpu_to_be16 (0); - status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); - - byte = 0; - status = _cairo_array_append (&font->output, &byte); - assert (status == CAIRO_STATUS_SUCCESS); - - word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); - status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_write_charset (cairo_cff_font_t *font) -{ - unsigned char byte; - uint16_t word; - cairo_status_t status; - - cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); - status = _cairo_array_grow_by (&font->output, 5); - if (unlikely (status)) - return status; - - byte = 2; - status = _cairo_array_append (&font->output, &byte); - assert (status == CAIRO_STATUS_SUCCESS); - - word = cpu_to_be16 (1); - status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); - - word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2); - status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_write_charstrings (cairo_cff_font_t *font) -{ - cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP); - - return cff_index_write (&font->charstrings_subset_index, &font->output); -} - -static cairo_status_t -cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) -{ - unsigned int i; - cairo_int_status_t status; - unsigned int offset_array; - uint32_t *offset_array_ptr; - int offset_base; - uint16_t count; - uint8_t offset_size = 4; - - cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); - count = cpu_to_be16 (font->num_subset_fontdicts); - status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); - if (unlikely (status)) - return status; - status = _cairo_array_append (&font->output, &offset_size); - if (unlikely (status)) - return status; - - offset_array = _cairo_array_num_elements (&font->output); - status = _cairo_array_allocate (&font->output, - (font->num_subset_fontdicts + 1)*offset_size, - (void **) &offset_array_ptr); - if (unlikely (status)) - return status; - offset_base = _cairo_array_num_elements (&font->output) - 1; - *offset_array_ptr = cpu_to_be32(1); - offset_array += sizeof(uint32_t); - for (i = 0; i < font->num_subset_fontdicts; i++) { - status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], - &font->output); - if (unlikely (status)) - return status; - - offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array); - *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); - offset_array += sizeof(uint32_t); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_write_private_dict (cairo_cff_font_t *font, - int dict_num, - cairo_hash_table_t *parent_dict, - cairo_hash_table_t *private_dict) -{ - int offset; - int size; - unsigned char buf[10]; - unsigned char *buf_end; - unsigned char *p; - cairo_status_t status; - - /* Write private dict and update offset and size in top dict */ - font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); - status = cff_dict_write (private_dict, &font->output); - if (unlikely (status)) - return status; - - size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; - /* private entry has two operands - size and offset */ - buf_end = encode_integer_max (buf, size); - buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]); - offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size); - assert (offset > 0); - p = _cairo_array_index (&font->output, offset); - memcpy (p, buf, buf_end - buf); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_cff_font_write_local_sub (cairo_cff_font_t *font, - int dict_num, - cairo_hash_table_t *private_dict, - cairo_array_t *local_sub_index) -{ - int offset; - int size; - unsigned char buf[10]; - unsigned char *buf_end; - unsigned char *p; - cairo_status_t status; - - if (_cairo_array_num_elements (local_sub_index) > 0) { - /* Write local subroutines and update offset in private - * dict. Local subroutines offset is relative to start of - * private dict */ - offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; - buf_end = encode_integer_max (buf, offset); - offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size); - assert (offset > 0); - p = _cairo_array_index (&font->output, offset); - memcpy (p, buf, buf_end - buf); - status = cff_index_write (local_sub_index, &font->output); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) -{ - unsigned int i; - cairo_int_status_t status; - - if (font->is_cid) { - for (i = 0; i < font->num_subset_fontdicts; i++) { - status = cairo_cff_font_write_private_dict ( - font, - i, - font->fd_dict[font->fd_subset_map[i]], - font->fd_private_dict[font->fd_subset_map[i]]); - if (unlikely (status)) - return status; - } - - for (i = 0; i < font->num_subset_fontdicts; i++) { - status = cairo_cff_font_write_local_sub ( - font, - i, - font->fd_private_dict[font->fd_subset_map[i]], - &font->fd_local_sub_index[font->fd_subset_map[i]]); - if (unlikely (status)) - return status; - } - } else { - status = cairo_cff_font_write_private_dict (font, - 0, - font->fd_dict[0], - font->private_dict); - if (unlikely (status)) - return status; - - status = cairo_cff_font_write_local_sub (font, - 0, - font->private_dict, - &font->local_sub_index); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -typedef cairo_status_t -(*font_write_t) (cairo_cff_font_t *font); - -static const font_write_t font_write_funcs[] = { - cairo_cff_font_write_header, - cairo_cff_font_write_name, - cairo_cff_font_write_top_dict, - cairo_cff_font_write_strings, - cairo_cff_font_write_global_subrs, - cairo_cff_font_write_charset, - cairo_cff_font_write_fdselect, - cairo_cff_font_write_charstrings, - cairo_cff_font_write_cid_fontdict, - cairo_cff_font_write_cid_private_dict_and_local_sub, -}; - -static cairo_status_t -cairo_cff_font_write_subset (cairo_cff_font_t *font) -{ - cairo_int_status_t status; - unsigned int i; - - for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) { - status = font_write_funcs[i] (font); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_cff_font_generate (cairo_cff_font_t *font, - const char **data, - unsigned long *length) -{ - cairo_int_status_t status; - - status = cairo_cff_font_read_font (font); - if (unlikely (status)) - return status; - - status = cairo_cff_font_subset_font (font); - if (unlikely (status)) - return status; - - status = cairo_cff_font_write_subset (font); - if (unlikely (status)) - return status; - - *data = _cairo_array_index (&font->output, 0); - *length = _cairo_array_num_elements (&font->output); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_cff_font_create_set_widths (cairo_cff_font_t *font) -{ - unsigned long size; - unsigned long long_entry_size; - unsigned long short_entry_size; - unsigned int i; - tt_hhea_t hhea; - int num_hmetrics; - unsigned char buf[10]; - int glyph_index; - cairo_int_status_t status; - - size = sizeof (tt_hhea_t); - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hhea, 0, - (unsigned char*) &hhea, &size); - if (unlikely (status)) - return status; - num_hmetrics = be16_to_cpu (hhea.num_hmetrics); - - for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { - glyph_index = font->scaled_font_subset->glyphs[i]; - long_entry_size = 2 * sizeof (int16_t); - short_entry_size = sizeof (int16_t); - if (glyph_index < num_hmetrics) { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hmtx, - glyph_index * long_entry_size, - buf, &short_entry_size); - if (unlikely (status)) - return status; - } - else - { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hmtx, - (num_hmetrics - 1) * long_entry_size, - buf, &short_entry_size); - if (unlikely (status)) - return status; - } - font->widths[i] = be16_to_cpu (*((int16_t*)buf)); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_cff_font_t **font_return, - const char *subset_name) -{ - const cairo_scaled_font_backend_t *backend; - cairo_status_t status; - cairo_cff_font_t *font; - tt_head_t head; - tt_hhea_t hhea; - unsigned long size, data_length; - - backend = scaled_font_subset->scaled_font->backend; - if (!backend->load_truetype_table) - return CAIRO_INT_STATUS_UNSUPPORTED; - - data_length = 0; - status = backend->load_truetype_table( scaled_font_subset->scaled_font, - TT_TAG_CFF, 0, NULL, &data_length); - if (unlikely (status)) - return status; - - size = sizeof (tt_head_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_head, 0, - (unsigned char *) &head, &size); - if (unlikely (status)) - return status; - - size = sizeof (tt_hhea_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_hhea, 0, - (unsigned char *) &hhea, &size); - if (unlikely (status)) - return status; - - size = 0; - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_hmtx, 0, NULL, &size); - if (unlikely (status)) - return status; - - font = malloc (sizeof (cairo_cff_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->backend = backend; - font->scaled_font_subset = scaled_font_subset; - - _cairo_array_init (&font->output, sizeof (char)); - status = _cairo_array_grow_by (&font->output, 4096); - if (unlikely (status)) - goto fail2; - - font->subset_font_name = strdup (subset_name); - if (unlikely (font->subset_font_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - font->x_min = (int16_t) be16_to_cpu (head.x_min); - font->y_min = (int16_t) be16_to_cpu (head.y_min); - font->x_max = (int16_t) be16_to_cpu (head.x_max); - font->y_max = (int16_t) be16_to_cpu (head.y_max); - font->ascent = (int16_t) be16_to_cpu (hhea.ascender); - font->descent = (int16_t) be16_to_cpu (hhea.descender); - font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em); - if (font->units_per_em == 0) - font->units_per_em = 1000; - - font->font_name = NULL; - status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, - &font->ps_name, - &font->font_name); - if (_cairo_status_is_error (status)) - goto fail3; - - /* If the PS name is not found, create a CairoFont-x-y name. */ - if (font->ps_name == NULL) { - font->ps_name = malloc (30); - if (unlikely (font->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - - snprintf(font->ps_name, 30, "CairoFont-%u-%u", - scaled_font_subset->font_id, - scaled_font_subset->subset_id); - } - - font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); - if (unlikely (font->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; - } - - status = cairo_cff_font_create_set_widths (font); - if (unlikely (status)) - goto fail5; - - font->data_length = data_length; - font->data = malloc (data_length); - if (unlikely (font->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail5; - } - status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font, - TT_TAG_CFF, 0, font->data, - &font->data_length); - if (unlikely (status)) - goto fail6; - - font->data_end = font->data + font->data_length; - - status = cff_dict_init (&font->top_dict); - if (unlikely (status)) - goto fail6; - - status = cff_dict_init (&font->private_dict); - if (unlikely (status)) - goto fail7; - - cff_index_init (&font->strings_index); - cff_index_init (&font->charstrings_index); - cff_index_init (&font->global_sub_index); - cff_index_init (&font->local_sub_index); - cff_index_init (&font->charstrings_subset_index); - cff_index_init (&font->strings_subset_index); - font->fdselect = NULL; - font->fd_dict = NULL; - font->fd_private_dict = NULL; - font->fd_local_sub_index = NULL; - font->fdselect_subset = NULL; - font->fd_subset_map = NULL; - font->private_dict_offset = NULL; - - *font_return = font; - - return CAIRO_STATUS_SUCCESS; - -fail7: - _cairo_hash_table_destroy (font->top_dict); -fail6: - free (font->data); -fail5: - free (font->widths); -fail4: - if (font->font_name) - free (font->font_name); -fail3: - free (font->subset_font_name); -fail2: - _cairo_array_fini (&font->output); - free (font); - - return status; -} - -static void -cairo_cff_font_destroy (cairo_cff_font_t *font) -{ - unsigned int i; - - free (font->widths); - if (font->font_name) - free (font->font_name); - free (font->ps_name); - free (font->subset_font_name); - _cairo_array_fini (&font->output); - cff_dict_fini (font->top_dict); - cff_dict_fini (font->private_dict); - cff_index_fini (&font->strings_index); - cff_index_fini (&font->charstrings_index); - cff_index_fini (&font->global_sub_index); - cff_index_fini (&font->local_sub_index); - cff_index_fini (&font->charstrings_subset_index); - cff_index_fini (&font->strings_subset_index); - - /* If we bailed out early as a result of an error some of the - * following cairo_cff_font_t members may still be NULL */ - if (font->fd_dict) { - for (i = 0; i < font->num_fontdicts; i++) { - if (font->fd_dict[i]) - cff_dict_fini (font->fd_dict[i]); - } - free (font->fd_dict); - } - if (font->fd_subset_map) - free (font->fd_subset_map); - if (font->private_dict_offset) - free (font->private_dict_offset); - - if (font->is_cid) { - if (font->fdselect) - free (font->fdselect); - if (font->fdselect_subset) - free (font->fdselect_subset); - if (font->fd_private_dict) { - for (i = 0; i < font->num_fontdicts; i++) { - if (font->fd_private_dict[i]) - cff_dict_fini (font->fd_private_dict[i]); - } - free (font->fd_private_dict); - } - if (font->fd_local_sub_index) { - for (i = 0; i < font->num_fontdicts; i++) - cff_index_fini (&font->fd_local_sub_index[i]); - free (font->fd_local_sub_index); - } - } - - if (font->data) - free (font->data); - - free (font); -} - -cairo_status_t -_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, - const char *subset_name, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ - cairo_status_t status; - const char *data = NULL; /* squelch bogus compiler warning */ - unsigned long length = 0; /* squelch bogus compiler warning */ - unsigned int i; - - status = _cairo_cff_font_create (font_subset, &font, subset_name); - if (unlikely (status)) - return status; - - status = cairo_cff_font_generate (font, &data, &length); - if (unlikely (status)) - goto fail1; - - cff_subset->ps_name = strdup (font->ps_name); - if (unlikely (cff_subset->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - if (font->font_name) { - cff_subset->font_name = strdup (font->font_name); - if (cff_subset->font_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - } else { - cff_subset->font_name = NULL; - } - - cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); - if (unlikely (cff_subset->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em; - - cff_subset->x_min = (double)font->x_min/font->units_per_em; - cff_subset->y_min = (double)font->y_min/font->units_per_em; - cff_subset->x_max = (double)font->x_max/font->units_per_em; - cff_subset->y_max = (double)font->y_max/font->units_per_em; - cff_subset->ascent = (double)font->ascent/font->units_per_em; - cff_subset->descent = (double)font->descent/font->units_per_em; - - cff_subset->data = malloc (length); - if (unlikely (cff_subset->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; - } - - memcpy (cff_subset->data, data, length); - cff_subset->data_length = length; - - cairo_cff_font_destroy (font); - - return CAIRO_STATUS_SUCCESS; - - fail4: - free (cff_subset->widths); - fail3: - if (cff_subset->font_name) - free (cff_subset->font_name); - fail2: - free (cff_subset->ps_name); - fail1: - cairo_cff_font_destroy (font); - - return status; -} - -void -_cairo_cff_subset_fini (cairo_cff_subset_t *subset) -{ - free (subset->ps_name); - if (subset->font_name) - free (subset->font_name); - free (subset->widths); - free (subset->data); -} - -static cairo_int_status_t -_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_cff_font_t **font_return, - const char *subset_name) -{ - cairo_status_t status; - cairo_cff_font_t *font; - - font = malloc (sizeof (cairo_cff_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->backend = NULL; - font->scaled_font_subset = scaled_font_subset; - - _cairo_array_init (&font->output, sizeof (char)); - status = _cairo_array_grow_by (&font->output, 4096); - if (unlikely (status)) - goto fail1; - - font->subset_font_name = strdup (subset_name); - if (unlikely (font->subset_font_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - font->ps_name = strdup (subset_name); - if (unlikely (font->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - font->font_name = NULL; - - font->x_min = 0; - font->y_min = 0; - font->x_max = 0; - font->y_max = 0; - font->ascent = 0; - font->descent = 0; - - font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); - if (unlikely (font->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - - font->data_length = 0; - font->data = NULL; - font->data_end = NULL; - - status = cff_dict_init (&font->top_dict); - if (unlikely (status)) - goto fail4; - - status = cff_dict_init (&font->private_dict); - if (unlikely (status)) - goto fail5; - - cff_index_init (&font->strings_index); - cff_index_init (&font->charstrings_index); - cff_index_init (&font->global_sub_index); - cff_index_init (&font->local_sub_index); - cff_index_init (&font->charstrings_subset_index); - cff_index_init (&font->strings_subset_index); - font->fdselect = NULL; - font->fd_dict = NULL; - font->fd_private_dict = NULL; - font->fd_local_sub_index = NULL; - font->fdselect_subset = NULL; - font->fd_subset_map = NULL; - font->private_dict_offset = NULL; - - *font_return = font; - - return CAIRO_STATUS_SUCCESS; - -fail5: - _cairo_hash_table_destroy (font->top_dict); -fail4: - free (font->widths); -fail3: - if (font->font_name) - free (font->font_name); - free (font->ps_name); -fail2: - free (font->subset_font_name); -fail1: - _cairo_array_fini (&font->output); - free (font); - return status; -} - -static cairo_int_status_t -cairo_cff_font_fallback_generate (cairo_cff_font_t *font, - cairo_type2_charstrings_t *type2_subset, - const char **data, - unsigned long *length) -{ - cairo_int_status_t status; - cff_header_t header; - cairo_array_t *charstring; - unsigned char buf[40]; - unsigned char *end_buf; - unsigned int i; - - /* Create header */ - header.major = 1; - header.minor = 0; - header.header_size = 4; - header.offset_size = 4; - font->header = &header; - - /* Create Top Dict */ - font->is_cid = FALSE; - end_buf = encode_integer (buf, type2_subset->x_min); - end_buf = encode_integer (end_buf, type2_subset->y_min); - end_buf = encode_integer (end_buf, type2_subset->x_max); - end_buf = encode_integer (end_buf, type2_subset->y_max); - status = cff_dict_set_operands (font->top_dict, - FONTBBOX_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - end_buf = encode_integer_max (buf, 0); - status = cff_dict_set_operands (font->top_dict, - CHARSTRINGS_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - status = cff_dict_set_operands (font->top_dict, - FDSELECT_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - status = cff_dict_set_operands (font->top_dict, - FDARRAY_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - status = cff_dict_set_operands (font->top_dict, - CHARSET_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - - status = cairo_cff_font_set_ros_strings (font); - if (unlikely (status)) - return status; - - /* Create CID FD dictionary */ - status = cairo_cff_font_create_cid_fontdict (font); - if (unlikely (status)) - return status; - - /* Create charstrings */ - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - charstring = _cairo_array_index(&type2_subset->charstrings, i); - - status = cff_index_append (&font->charstrings_subset_index, - _cairo_array_index (charstring, 0), - _cairo_array_num_elements (charstring)); - - if (unlikely (status)) - return status; - } - - status = cairo_cff_font_write_subset (font); - if (unlikely (status)) - return status; - - *data = _cairo_array_index (&font->output, 0); - *length = _cairo_array_num_elements (&font->output); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, - const char *subset_name, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ - cairo_status_t status; - const char *data = NULL; /* squelch bogus compiler warning */ - unsigned long length = 0; /* squelch bogus compiler warning */ - unsigned int i; - cairo_type2_charstrings_t type2_subset; - - status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); - if (unlikely (status)) - return status; - - status = _cairo_type2_charstrings_init (&type2_subset, font_subset); - if (unlikely (status)) - goto fail1; - - status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); - if (unlikely (status)) - goto fail2; - - cff_subset->font_name = NULL; - cff_subset->ps_name = strdup (font->ps_name); - if (unlikely (cff_subset->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - - cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); - if (unlikely (cff_subset->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - cff_subset->widths[i] = (double)type2_subset.widths[i]/1000; - - cff_subset->x_min = (double)type2_subset.x_min/1000; - cff_subset->y_min = (double)type2_subset.y_min/1000; - cff_subset->x_max = (double)type2_subset.x_max/1000; - cff_subset->y_max = (double)type2_subset.y_max/1000; - cff_subset->ascent = (double)type2_subset.y_max/1000; - cff_subset->descent = (double)type2_subset.y_min/1000; - - cff_subset->data = malloc (length); - if (unlikely (cff_subset->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; - } - - memcpy (cff_subset->data, data, length); - cff_subset->data_length = length; - cff_subset->data_length = length; - - _cairo_type2_charstrings_fini (&type2_subset); - cairo_cff_font_destroy (font); - - return CAIRO_STATUS_SUCCESS; - - fail4: - free (cff_subset->widths); - fail3: - free (cff_subset->ps_name); - fail2: - _cairo_type2_charstrings_fini (&type2_subset); - fail1: - cairo_cff_font_destroy (font); - - return status; -} - -void -_cairo_cff_fallback_fini (cairo_cff_subset_t *subset) -{ - free (subset->ps_name); - free (subset->widths); - free (subset->data); -} - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-clip-private.h b/libs/cairo/cairo/src/cairo-clip-private.h deleted file mode 100644 index 04d80afc7..000000000 --- a/libs/cairo/cairo/src/cairo-clip-private.h +++ /dev/null @@ -1,120 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_CLIP_PRIVATE_H -#define CAIRO_CLIP_PRIVATE_H - -#include "cairo-types-private.h" -#include "cairo-compiler-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-reference-count-private.h" - -extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; - -enum { - CAIRO_CLIP_PATH_HAS_REGION = 0x1, - CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2, - CAIRO_CLIP_PATH_IS_BOX = 0x4 -}; - -struct _cairo_clip_path { - cairo_reference_count_t ref_count; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; - cairo_clip_path_t *prev; - - cairo_rectangle_int_t extents; - - /* partial caches */ - unsigned int flags; - cairo_region_t *region; - cairo_surface_t *surface; -}; - -struct _cairo_clip { - /* can be used as a cairo_hash_entry_t for live clips */ - cairo_clip_path_t *path; - - cairo_bool_t all_clipped; - -}; - -cairo_private void -_cairo_clip_init (cairo_clip_t *clip); - -cairo_private_no_warn cairo_clip_t * -_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other); - -cairo_private cairo_status_t -_cairo_clip_init_copy_transformed (cairo_clip_t *clip, - cairo_clip_t *other, - const cairo_matrix_t *matrix); - -cairo_private void -_cairo_clip_reset (cairo_clip_t *clip); - -cairo_private cairo_bool_t -_cairo_clip_equal (const cairo_clip_t *clip_a, - const cairo_clip_t *clip_b); - -#define _cairo_clip_fini(clip) _cairo_clip_reset (clip) - -cairo_private cairo_status_t -_cairo_clip_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rectangle); - -cairo_private cairo_status_t -_cairo_clip_clip (cairo_clip_t *clip, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias); - -cairo_private cairo_status_t -_cairo_clip_apply_clip (cairo_clip_t *clip, - const cairo_clip_t *other); - -cairo_private const cairo_rectangle_int_t * -_cairo_clip_get_extents (const cairo_clip_t *clip); - -cairo_private cairo_surface_t * -_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty); - -cairo_private cairo_status_t -_cairo_clip_combine_with_surface (cairo_clip_t *clip, - cairo_surface_t *dst, - int dst_x, int dst_y); - -cairo_private cairo_int_status_t -_cairo_clip_get_region (cairo_clip_t *clip, - cairo_region_t **region); - -cairo_private cairo_int_status_t -_cairo_clip_get_boxes (cairo_clip_t *clip, - cairo_box_t **boxes, - int *count); - -cairo_private cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t **clip, - cairo_composite_rectangles_t *extents, - cairo_box_t **boxes, - int *num_boxes); - -cairo_private cairo_bool_t -_cairo_clip_contains_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rect); - -cairo_private cairo_bool_t -_cairo_clip_contains_extents (cairo_clip_t *clip, - const cairo_composite_rectangles_t *extents); - -cairo_private void -_cairo_clip_drop_cache (cairo_clip_t *clip); - -cairo_private cairo_rectangle_list_t* -_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate); - -#endif /* CAIRO_CLIP_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-clip.c b/libs/cairo/cairo/src/cairo-clip.c deleted file mode 100644 index cbbf4d2ce..000000000 --- a/libs/cairo/cairo/src/cairo-clip.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-freed-pool-private.h" -#include "cairo-gstate-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-region-private.h" - -#if HAS_FREED_POOL -static freed_pool_t clip_path_pool; -#endif - -static cairo_clip_path_t * -_cairo_clip_path_create (cairo_clip_t *clip) -{ - cairo_clip_path_t *clip_path; - - clip_path = _freed_pool_get (&clip_path_pool); - if (unlikely (clip_path == NULL)) { - clip_path = malloc (sizeof (cairo_clip_path_t)); - if (unlikely (clip_path == NULL)) - return NULL; - } - - CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); - - clip_path->flags = 0; - clip_path->region = NULL; - clip_path->surface = NULL; - - clip_path->prev = clip->path; - clip->path = clip_path; - - return clip_path; -} - -static cairo_clip_path_t * -_cairo_clip_path_reference (cairo_clip_path_t *clip_path) -{ - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); - - _cairo_reference_count_inc (&clip_path->ref_count); - - return clip_path; -} - -static void -_cairo_clip_path_destroy (cairo_clip_path_t *clip_path) -{ - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) - return; - - _cairo_path_fixed_fini (&clip_path->path); - if (clip_path->region != NULL) - cairo_region_destroy (clip_path->region); - if (clip_path->surface != NULL) - cairo_surface_destroy (clip_path->surface); - - if (clip_path->prev != NULL) - _cairo_clip_path_destroy (clip_path->prev); - - _freed_pool_put (&clip_path_pool, clip_path); -} - -void -_cairo_clip_init (cairo_clip_t *clip) -{ - clip->all_clipped = FALSE; - clip->path = NULL; -} - -static void -_cairo_clip_set_all_clipped (cairo_clip_t *clip) -{ - clip->all_clipped = TRUE; - if (clip->path != NULL) { - _cairo_clip_path_destroy (clip->path); - clip->path = NULL; - } -} - -static cairo_status_t -_cairo_clip_intersect_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rect) -{ - cairo_clip_path_t *clip_path; - cairo_status_t status; - - if (clip->path != NULL) { - if (rect->x <= clip->path->extents.x && - rect->y <= clip->path->extents.y && - rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width && - rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_path_fixed_init (&clip_path->path); - - status = _cairo_path_fixed_move_to (&clip_path->path, - _cairo_fixed_from_int (rect->x), - _cairo_fixed_from_int (rect->y)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (0), - _cairo_fixed_from_int (rect->height)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (-rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_close_path (&clip_path->path); - assert (status == CAIRO_STATUS_SUCCESS); - - clip_path->fill_rule = CAIRO_FILL_RULE_WINDING; - clip_path->tolerance = 1; - clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT; - clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; - - clip_path->extents = *rect; - if (clip_path->prev != NULL) { - if (! _cairo_rectangle_intersect (&clip_path->extents, - &clip_path->prev->extents)) - { - _cairo_clip_set_all_clipped (clip); - } - } - - /* could preallocate the region if it proves worthwhile */ - - return CAIRO_STATUS_SUCCESS; -} - -cairo_clip_t * -_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) -{ - if (other != NULL) { - clip->all_clipped = other->all_clipped; - if (other->path == NULL) { - clip->path = NULL; - if (! clip->all_clipped) - clip = NULL; - } else { - clip->path = _cairo_clip_path_reference (other->path); - } - } else { - _cairo_clip_init (clip); - clip = NULL; - } - - return clip; -} - -void -_cairo_clip_reset (cairo_clip_t *clip) -{ - clip->all_clipped = FALSE; - if (clip->path != NULL) { - _cairo_clip_path_destroy (clip->path); - clip->path = NULL; - } -} - -static cairo_status_t -_cairo_clip_intersect_path (cairo_clip_t *clip, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_clip_path_t *clip_path; - cairo_status_t status; - cairo_rectangle_int_t extents; - cairo_box_t box; - cairo_bool_t is_box = FALSE; - - if (clip->path != NULL) { - if (clip->path->fill_rule == fill_rule && - (path->is_rectilinear || tolerance == clip->path->tolerance) && - antialias == clip->path->antialias && - _cairo_path_fixed_equal (&clip->path->path, path)) - { - return CAIRO_STATUS_SUCCESS; - } - } - - _cairo_path_fixed_approximate_clip_extents (path, &extents); - if (extents.width == 0 || extents.height == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - is_box = _cairo_path_fixed_is_box (path, &box); - if (clip->path != NULL) { - if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - /* does this clip wholly subsume the others? */ - if (is_box && - box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) && - box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) && - box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) && - box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height)) - { - return CAIRO_STATUS_SUCCESS; - } - } - - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - clip_path->extents = extents; - clip_path->fill_rule = fill_rule; - clip_path->tolerance = tolerance; - clip_path->antialias = antialias; - if (is_box) - clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_clip_equal (const cairo_clip_t *clip_a, - const cairo_clip_t *clip_b) -{ - const cairo_clip_path_t *clip_path_a, *clip_path_b; - - clip_path_a = clip_a->path; - clip_path_b = clip_b->path; - - while (clip_path_a && clip_path_b) { - if (clip_path_a == clip_path_b) - return TRUE; - - if (clip_path_a->fill_rule != clip_path_b->fill_rule) - return FALSE; - - if (clip_path_a->tolerance != clip_path_b->tolerance) - return FALSE; - - if (clip_path_a->antialias != clip_path_b->antialias) - return FALSE; - - if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path)) - return FALSE; - - clip_path_a = clip_path_a->prev; - clip_path_b = clip_path_b->prev; - } - - return clip_path_a == clip_path_b; /* ie both NULL */ -} - -cairo_status_t -_cairo_clip_clip (cairo_clip_t *clip, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - /* catch the empty clip path */ - if (_cairo_path_fixed_fill_is_empty (path)) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_clip_intersect_path (clip, - path, fill_rule, tolerance, - antialias); -} - -cairo_status_t -_cairo_clip_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rectangle) -{ - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (rectangle->width == 0 || rectangle->height == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - /* if a smaller clip has already been set, ignore the new path */ - if (clip->path != NULL) { - if (rectangle->x <= clip->path->extents.x && - rectangle->y <= clip->path->extents.y && - rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width && - rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - return _cairo_clip_intersect_rectangle (clip, rectangle); -} - -static cairo_status_t -_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip, - cairo_clip_path_t *other_path, - const cairo_matrix_t *matrix) -{ - cairo_status_t status; - cairo_clip_path_t *clip_path; - cairo_bool_t is_empty; - - if (other_path->prev != NULL) { - status = _cairo_clip_path_reapply_clip_path_transform (clip, - other_path->prev, - matrix); - if (unlikely (status)) - return status; - } - - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_path_fixed_init_copy (&clip_path->path, - &other_path->path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - _cairo_path_fixed_transform (&clip_path->path, matrix); - _cairo_path_fixed_approximate_clip_extents (&clip_path->path, - &clip_path->extents); - if (clip_path->prev != NULL) { - is_empty = _cairo_rectangle_intersect (&clip_path->extents, - &clip_path->prev->extents); - } - - clip_path->fill_rule = other_path->fill_rule; - clip_path->tolerance = other_path->tolerance; - clip_path->antialias = other_path->antialias; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip, - cairo_clip_path_t *other_path, - int tx, int ty) -{ - cairo_status_t status; - cairo_clip_path_t *clip_path; - - if (other_path->prev != NULL) { - status = _cairo_clip_path_reapply_clip_path_translate (clip, - other_path->prev, - tx, ty); - if (unlikely (status)) - return status; - } - - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_path_fixed_init_copy (&clip_path->path, - &other_path->path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (tx), - _cairo_fixed_from_int (ty)); - - clip_path->fill_rule = other_path->fill_rule; - clip_path->tolerance = other_path->tolerance; - clip_path->antialias = other_path->antialias; - - clip_path->flags = other_path->flags; - if (other_path->region != NULL) { - clip_path->region = cairo_region_copy (other_path->region); - status = clip_path->region->status; - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - cairo_region_translate (clip_path->region, tx, ty); - } - clip_path->surface = cairo_surface_reference (other_path->surface); - - clip_path->extents = other_path->extents; - clip_path->extents.x += tx; - clip_path->extents.y += ty; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_clip_init_copy_transformed (cairo_clip_t *clip, - cairo_clip_t *other, - const cairo_matrix_t *matrix) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - int tx, ty; - - if (other == NULL) { - _cairo_clip_init (clip); - return CAIRO_STATUS_SUCCESS; - } - - if (other->all_clipped) { - _cairo_clip_init (clip); - clip->all_clipped = TRUE; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_matrix_is_identity (matrix)) { - _cairo_clip_init_copy (clip, other); - return CAIRO_STATUS_SUCCESS; - } - - if (other->path != NULL) { - _cairo_clip_init (clip); - - /* if we only need to translate, so we can reuse the caches... */ - /* XXX we still loose the benefit of constructs when the copy is - * deleted though. Indirect clip_paths? - */ - if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) { - status = _cairo_clip_path_reapply_clip_path_translate (clip, - other->path, - tx, ty); - } else { - status = _cairo_clip_path_reapply_clip_path_transform (clip, - other->path, - matrix); - if (clip->path->extents.width == 0 && - clip->path->extents.height == 0) - { - _cairo_clip_set_all_clipped (clip); - } - } - } - - return status; -} - -static cairo_status_t -_cairo_clip_apply_clip_path (cairo_clip_t *clip, - const cairo_clip_path_t *path) -{ - cairo_status_t status; - - if (path->prev != NULL) - status = _cairo_clip_apply_clip_path (clip, path->prev); - - return _cairo_clip_intersect_path (clip, - &path->path, - path->fill_rule, - path->tolerance, - path->antialias); -} - -cairo_status_t -_cairo_clip_apply_clip (cairo_clip_t *clip, - const cairo_clip_t *other) -{ - cairo_status_t status; - - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (other->all_clipped) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - status = CAIRO_STATUS_SUCCESS; - if (other->path != NULL) - status = _cairo_clip_apply_clip_path (clip, other->path); - - return status; -} - -static inline cairo_bool_t -_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path) -{ - while (clip_path != NULL) { - if (! clip_path->path.is_rectilinear) - return FALSE; - - clip_path = clip_path->prev; - } - - return TRUE; -} - -static cairo_int_status_t -_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) -{ - cairo_traps_t traps; - cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; - cairo_box_t *boxes = stack_boxes; - cairo_status_t status; - int n; - - /* If we have nothing to intersect with this path, then it cannot - * magically be reduced into a region. - */ - if (clip_path->prev == NULL) - goto UNSUPPORTED; - - /* Start simple... Intersect some boxes with an arbitrary path. */ - if (! clip_path->path.is_rectilinear) - goto UNSUPPORTED; - if (clip_path->prev->prev != NULL) - goto UNSUPPORTED; - - _cairo_traps_init (&traps); - _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); - _cairo_traps_limit (&traps, boxes, 1); - - status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, - clip_path->fill_rule, - &traps); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - goto UNSUPPORTED; - - if (unlikely (traps.num_traps == 0)) { - clip_path->region = cairo_region_create (); - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; - } - - if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { - boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); - if (unlikely (boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (n = 0; n < traps.num_traps; n++) { - boxes[n].p1.x = traps.traps[n].left.p1.x; - boxes[n].p1.y = traps.traps[n].top; - boxes[n].p2.x = traps.traps[n].right.p1.x; - boxes[n].p2.y = traps.traps[n].bottom; - } - - _cairo_traps_clear (&traps); - _cairo_traps_limit (&traps, boxes, n); - status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, - clip_path->prev->fill_rule, - clip_path->prev->tolerance, - &traps); - if (boxes != stack_boxes) - free (boxes); - - if (unlikely (status)) - return status; - - status = _cairo_traps_extract_region (&traps, &clip_path->region); - _cairo_traps_fini (&traps); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - goto UNSUPPORTED; - if (unlikely (status)) - return status; - - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; - -UNSUPPORTED: - clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_clip_path_to_region (cairo_clip_path_t *clip_path) -{ - cairo_int_status_t status; - cairo_region_t *prev = NULL; - - if (clip_path->flags & - (CAIRO_CLIP_PATH_HAS_REGION | - CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED)) - { - return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ? - CAIRO_INT_STATUS_UNSUPPORTED : - CAIRO_STATUS_SUCCESS; - } - - if (! clip_path->path.maybe_fill_region) - return _cairo_clip_path_to_region_geometric (clip_path); - - /* first retrieve the region for our antecedents */ - if (clip_path->prev != NULL) { - status = _cairo_clip_path_to_region (clip_path->prev); - if (status) { - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_clip_path_to_region_geometric (clip_path); - - return status; - } - - prev = clip_path->prev->region; - } - - /* now extract the region for ourselves */ - clip_path->region = - _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path, - clip_path->fill_rule, - &clip_path->extents); - assert (clip_path->region != NULL); - - status = clip_path->region->status; - if (unlikely (status)) - return status; - - if (prev != NULL) { - status = cairo_region_intersect (clip_path->region, prev); - if (unlikely (status)) - return status; - } - - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; -} - -static inline int -pot (int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - -/* XXX there is likely a faster method! ;-) */ -static cairo_status_t -_region_clip_to_boxes (const cairo_region_t *region, - cairo_box_t **boxes, - int *num_boxes, - int *size_boxes) -{ - cairo_traps_t traps; - cairo_status_t status; - int n, num_rects; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, *boxes, *num_boxes); - traps.is_rectilinear = TRUE; - traps.is_rectangular = TRUE; - - num_rects = cairo_region_num_rectangles (region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - cairo_point_t p1, p2; - - cairo_region_get_rectangle (region, n, &rect); - - p1.x = _cairo_fixed_from_int (rect.x); - p1.y = _cairo_fixed_from_int (rect.y); - p2.x = _cairo_fixed_from_int (rect.x + rect.width); - p2.y = _cairo_fixed_from_int (rect.y + rect.height); - - status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2); - if (unlikely (status)) - goto CLEANUP; - } - - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - n = *size_boxes; - if (n < 0) - n = -n; - - if (traps.num_traps > n) { - cairo_box_t *new_boxes; - - new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (*size_boxes > 0) - free (*boxes); - - *boxes = new_boxes; - *size_boxes = traps.num_traps; - } - - for (n = 0; n < traps.num_traps; n++) { - (*boxes)[n].p1.x = traps.traps[n].left.p1.x; - (*boxes)[n].p1.y = traps.traps[n].top; - (*boxes)[n].p2.x = traps.traps[n].right.p1.x; - (*boxes)[n].p2.y = traps.traps[n].bottom; - } - *num_boxes = n; - - CLEANUP: - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_status_t -_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_box_t **boxes, - int *num_boxes, - int *size_boxes) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_status_t status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, *boxes, *num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, *boxes, *num_boxes); - - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - if (status == CAIRO_STATUS_SUCCESS) - goto BOXES; - - /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) { - *num_boxes = 0; - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - int i; - - BOXES: - i = *size_boxes; - if (i < 0) - i = -i; - - if (traps.num_traps > i) { - cairo_box_t *new_boxes; - int new_size; - - new_size = pot (traps.num_traps); - new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (*size_boxes > 0) - free (*boxes); - - *boxes = new_boxes; - *size_boxes = new_size; - } - - for (i = 0; i < traps.num_traps; i++) { - (*boxes)[i].p1.x = traps.traps[i].left.p1.x; - (*boxes)[i].p1.y = traps.traps[i].top; - (*boxes)[i].p2.x = traps.traps[i].right.p1.x; - (*boxes)[i].p2.y = traps.traps[i].bottom; - } - *num_boxes = i; - } - } - - CLEANUP: - _cairo_polygon_fini (&polygon); - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_int_status_t -_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path, - cairo_box_t **boxes, - int *count) -{ - int size = -*count; - int num_boxes = 0; - cairo_status_t status; - - if (clip_path->region != NULL) { - int num_rects, n; - - num_rects = cairo_region_num_rectangles (clip_path->region); - if (num_rects > -size) { - cairo_box_t *new_boxes; - - new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *boxes = new_boxes; - } - - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_path->region, n, &rect); - (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x); - (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y); - (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width); - (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height); - } - - *count = num_rects; - return CAIRO_STATUS_SUCCESS; - } - - /* keep it simple at first */ - if (! _clip_paths_are_rectilinear (clip_path)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - assert (-size >= 1); - if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) { - num_boxes = 1; - } else { - status = _rectilinear_clip_to_boxes (&clip_path->path, - clip_path->fill_rule, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - } - - while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) { - cairo_box_t box; - - if (clip_path->region != NULL) { - status = _region_clip_to_boxes (clip_path->region, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - - break; - } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) { - int i, j; - - for (i = j = 0; i < num_boxes; i++) { - if (j != i) - (*boxes)[j] = (*boxes)[i]; - - if (box.p1.x > (*boxes)[j].p1.x) - (*boxes)[j].p1.x = box.p1.x; - if (box.p2.x < (*boxes)[j].p2.x) - (*boxes)[j].p2.x = box.p2.x; - - if (box.p1.y > (*boxes)[j].p1.y) - (*boxes)[j].p1.y = box.p1.y; - if (box.p2.y < (*boxes)[j].p2.y) - (*boxes)[j].p2.y = box.p2.y; - - j += (*boxes)[j].p2.x > (*boxes)[j].p1.x && - (*boxes)[j].p2.y > (*boxes)[j].p1.y; - } - - num_boxes = j; - } else { - status = _rectilinear_clip_to_boxes (&clip_path->path, - clip_path->fill_rule, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - } - } - - *count = num_boxes; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path, - cairo_surface_t *target, - int *tx, int *ty) -{ - const cairo_rectangle_int_t *clip_extents = &clip_path->extents; - cairo_bool_t need_translate; - cairo_surface_t *surface; - cairo_clip_path_t *prev; - cairo_status_t status; - - while (clip_path->prev != NULL && - clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - clip_path = clip_path->prev; - } - - clip_extents = &clip_path->extents; - if (clip_path->surface != NULL && - clip_path->surface->backend == target->backend) - { - *tx = clip_extents->x; - *ty = clip_extents->y; - return clip_path->surface; - } - - surface = _cairo_surface_create_similar_scratch (target, - CAIRO_CONTENT_ALPHA, - clip_extents->width, - clip_extents->height); - if (surface == NULL) { - surface = cairo_image_surface_create (CAIRO_FORMAT_A8, - clip_extents->width, - clip_extents->height); - } - if (unlikely (surface->status)) - return surface; - - need_translate = clip_extents->x | clip_extents->y; - if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_SOURCE, - &_cairo_pattern_white.base, - NULL); - if (unlikely (status)) - goto BAIL; - } - else - { - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_CLEAR, - &_cairo_pattern_clear.base, - NULL); - if (unlikely (status)) - goto BAIL; - - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (-clip_extents->x), - _cairo_fixed_from_int (-clip_extents->y)); - } - status = _cairo_surface_fill (surface, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (clip_extents->x), - _cairo_fixed_from_int (clip_extents->y)); - } - - if (unlikely (status)) - goto BAIL; - } - - prev = clip_path->prev; - while (prev != NULL) { - if (prev->flags & CAIRO_CLIP_PATH_IS_BOX && - prev->path.maybe_fill_region) - { - /* a simple box only affects the extents */ - } - else if (prev->path.is_rectilinear || - prev->surface == NULL || - prev->surface->backend != target->backend) - { - if (need_translate) { - _cairo_path_fixed_translate (&prev->path, - _cairo_fixed_from_int (-clip_extents->x), - _cairo_fixed_from_int (-clip_extents->y)); - } - status = _cairo_surface_fill (surface, - CAIRO_OPERATOR_IN, - &_cairo_pattern_white.base, - &prev->path, - prev->fill_rule, - prev->tolerance, - prev->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&prev->path, - _cairo_fixed_from_int (clip_extents->x), - _cairo_fixed_from_int (clip_extents->y)); - } - - if (unlikely (status)) - goto BAIL; - } - else - { - cairo_surface_pattern_t pattern; - cairo_surface_t *prev_surface; - int prev_tx, prev_ty; - - prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty); - status = prev_surface->status; - if (unlikely (status)) - goto BAIL; - - _cairo_pattern_init_for_surface (&pattern, prev_surface); - pattern.base.filter = CAIRO_FILTER_NEAREST; - cairo_matrix_init_translate (&pattern.base.matrix, - clip_extents->x - prev_tx, - clip_extents->y - prev_ty); - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_IN, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) - goto BAIL; - - break; - } - - prev = prev->prev; - } - - *tx = clip_extents->x; - *ty = clip_extents->y; - cairo_surface_destroy (clip_path->surface); - return clip_path->surface = surface; - - BAIL: - cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (status); -} - -cairo_bool_t -_cairo_clip_contains_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rect) -{ - cairo_clip_path_t *clip_path; - - if (clip == NULL) - return FALSE; - - clip_path = clip->path; - if (clip_path->extents.x > rect->x || - clip_path->extents.y > rect->y || - clip_path->extents.x + clip_path->extents.width < rect->x + rect->width || - clip_path->extents.y + clip_path->extents.height < rect->y + rect->height) - { - return FALSE; - } - - do { - cairo_box_t box; - - if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) - return FALSE; - - if (! _cairo_path_fixed_is_box (&clip_path->path, &box)) - return FALSE; - - if (box.p1.x > _cairo_fixed_from_int (rect->x) || - box.p1.y > _cairo_fixed_from_int (rect->y) || - box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) || - box.p2.y < _cairo_fixed_from_int (rect->y + rect->height)) - { - return FALSE; - } - } while ((clip_path = clip_path->prev) != NULL); - - return TRUE; -} - -cairo_bool_t -_cairo_clip_contains_extents (cairo_clip_t *clip, - const cairo_composite_rectangles_t *extents) -{ - const cairo_rectangle_int_t *rect; - - if (clip == NULL) - return FALSE; - - rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; - return _cairo_clip_contains_rectangle (clip, rect); -} - -void -_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip) -{ - cairo_clip_path_t *clip_path; - - if (clip == NULL) { - fprintf (stream, "no clip\n"); - return; - } - - if (clip->all_clipped) { - fprintf (stream, "clip: all-clipped\n"); - return; - } - - if (clip->path == NULL) { - fprintf (stream, "clip: empty\n"); - return; - } - - fprintf (stream, "clip:\n"); - - clip_path = clip->path; - do { - fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ", - clip_path->region == NULL ? "no" : "yes", - clip_path->surface == NULL ? "no" : "yes", - clip_path->antialias, - clip_path->tolerance, - clip_path->fill_rule); - _cairo_debug_print_path (stream, &clip_path->path); - fprintf (stream, "\n"); - } while ((clip_path = clip_path->prev) != NULL); -} - -cairo_surface_t * -_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty) -{ - /* XXX is_clear -> all_clipped */ - assert (clip->path != NULL); - return _cairo_clip_path_get_surface (clip->path, target, tx, ty); -} - -cairo_status_t -_cairo_clip_combine_with_surface (cairo_clip_t *clip, - cairo_surface_t *dst, - int dst_x, int dst_y) -{ - cairo_clip_path_t *clip_path = clip->path; - cairo_bool_t need_translate; - cairo_status_t status; - - assert (clip_path != NULL); - - need_translate = dst_x | dst_y; - do { - if (clip_path->surface != NULL && - clip_path->surface->backend == dst->backend) - { - cairo_surface_pattern_t pattern; - - _cairo_pattern_init_for_surface (&pattern, clip_path->surface); - cairo_matrix_init_translate (&pattern.base.matrix, - dst_x - clip_path->extents.x, - dst_y - clip_path->extents.y); - pattern.base.filter = CAIRO_FILTER_NEAREST; - status = _cairo_surface_paint (dst, - CAIRO_OPERATOR_IN, - &pattern.base, - NULL); - - _cairo_pattern_fini (&pattern.base); - - return status; - } - - if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - continue; - } - - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (-dst_x), - _cairo_fixed_from_int (-dst_y)); - } - status = _cairo_surface_fill (dst, - CAIRO_OPERATOR_IN, - &_cairo_pattern_white.base, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (dst_x), - _cairo_fixed_from_int (dst_y)); - } - - if (unlikely (status)) - return status; - } while ((clip_path = clip_path->prev) != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 }; - -const cairo_rectangle_int_t * -_cairo_clip_get_extents (const cairo_clip_t *clip) -{ - if (clip->all_clipped) - return &_cairo_empty_rectangle_int; - - if (clip->path == NULL) - return NULL; - - return &clip->path->extents; -} - -void -_cairo_clip_drop_cache (cairo_clip_t *clip) -{ - cairo_clip_path_t *clip_path; - - if (clip->path == NULL) - return; - - clip_path = clip->path; - do { - if (clip_path->region != NULL) { - cairo_region_destroy (clip_path->region); - clip_path->region = NULL; - } - - if (clip_path->surface != NULL) { - cairo_surface_destroy (clip_path->surface); - clip_path->surface = NULL; - } - - clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION; - } while ((clip_path = clip_path->prev) != NULL); -} - -const cairo_rectangle_list_t _cairo_rectangles_nil = - { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; -static const cairo_rectangle_list_t _cairo_rectangles_not_representable = - { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 }; - -static cairo_bool_t -_cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, - cairo_rectangle_int_t *clip_rect, - cairo_rectangle_t *user_rect) -{ - cairo_bool_t is_tight; - - double x1 = clip_rect->x; - double y1 = clip_rect->y; - double x2 = clip_rect->x + (int) clip_rect->width; - double y2 = clip_rect->y + (int) clip_rect->height; - - _cairo_gstate_backend_to_user_rectangle (gstate, - &x1, &y1, &x2, &y2, - &is_tight); - - user_rect->x = x1; - user_rect->y = y1; - user_rect->width = x2 - x1; - user_rect->height = y2 - y1; - - return is_tight; -} - -cairo_int_status_t -_cairo_clip_get_region (cairo_clip_t *clip, - cairo_region_t **region) -{ - cairo_int_status_t status; - - if (clip->all_clipped) - goto CLIPPED; - - assert (clip->path != NULL); - - status = _cairo_clip_path_to_region (clip->path); - if (status) - return status; - - if (cairo_region_is_empty (clip->path->region)) { - _cairo_clip_set_all_clipped (clip); - goto CLIPPED; - } - - if (region) - *region = clip->path->region; - return CAIRO_STATUS_SUCCESS; - - CLIPPED: - if (region) - *region = NULL; - return CAIRO_INT_STATUS_NOTHING_TO_DO; -} - -cairo_int_status_t -_cairo_clip_get_boxes (cairo_clip_t *clip, - cairo_box_t **boxes, - int *count) -{ - cairo_int_status_t status; - - if (clip->all_clipped) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - assert (clip->path != NULL); - - status = _cairo_clip_path_to_boxes (clip->path, boxes, count); - if (status) - return status; - - if (*count == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -box_is_aligned (const cairo_box_t *box) -{ - return - _cairo_fixed_is_integer (box->p1.x) && - _cairo_fixed_is_integer (box->p1.y) && - _cairo_fixed_is_integer (box->p2.x) && - _cairo_fixed_is_integer (box->p2.y); -} - -static void -intersect_with_boxes (cairo_composite_rectangles_t *extents, - cairo_box_t *boxes, - int num_boxes) -{ - cairo_rectangle_int_t rect; - cairo_box_t box; - cairo_bool_t is_empty; - - box.p1.x = box.p1.y = INT_MIN; - box.p2.x = box.p2.y = INT_MAX; - while (num_boxes--) { - if (boxes->p1.x < box.p1.x) - box.p1.x = boxes->p1.x; - if (boxes->p1.y < box.p1.y) - box.p1.y = boxes->p1.y; - - if (boxes->p2.x > box.p2.x) - box.p2.x = boxes->p2.x; - if (boxes->p2.y > box.p2.y) - box.p2.y = boxes->p2.y; - } - - _cairo_box_round_to_rectangle (&box, &rect); - is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect); - is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect); -} - -cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t **clip, - cairo_composite_rectangles_t *extents, - cairo_box_t **boxes, - int *num_boxes) -{ - cairo_status_t status; - const cairo_rectangle_int_t *rect; - - rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; - - if (*clip == NULL) - goto EXTENTS; - - status = _cairo_clip_rectangle (*clip, rect); - if (unlikely (status)) - return status; - - status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); - switch ((int) status) { - case CAIRO_STATUS_SUCCESS: - intersect_with_boxes (extents, *boxes, *num_boxes); - if (rect->width == 0 || rect->height == 0 || - extents->is_bounded || - (*num_boxes == 1 && box_is_aligned (*boxes))) - { - *clip = NULL; - } - goto DONE; - - case CAIRO_INT_STATUS_UNSUPPORTED: - goto EXTENTS; - - default: - return status; - } - - EXTENTS: - status = CAIRO_STATUS_SUCCESS; - _cairo_box_from_rectangle (&(*boxes)[0], rect); - *num_boxes = 1; - DONE: - return status; -} - - -static cairo_rectangle_list_t * -_cairo_rectangle_list_create_in_error (cairo_status_t status) -{ - cairo_rectangle_list_t *list; - - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) - return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; - - list = malloc (sizeof (*list)); - if (unlikely (list == NULL)) { - _cairo_error_throw (status); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - } - - list->status = status; - list->rectangles = NULL; - list->num_rectangles = 0; - - return list; -} - -cairo_rectangle_list_t * -_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) -{ -#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)) - - cairo_rectangle_list_t *list; - cairo_rectangle_t *rectangles = NULL; - cairo_region_t *region = NULL; - cairo_int_status_t status; - int n_rects = 0; - int i; - - if (clip->all_clipped) - goto DONE; - - if (!clip->path) - return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - - status = _cairo_clip_get_region (clip, ®ion); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - goto DONE; - } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - } else if (unlikely (status)) { - return ERROR_LIST (status); - } - - n_rects = cairo_region_num_rectangles (region); - if (n_rects) { - rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); - if (unlikely (rectangles == NULL)) { - return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_rects; ++i) { - cairo_rectangle_int_t clip_rect; - - cairo_region_get_rectangle (region, i, &clip_rect); - - if (! _cairo_clip_int_rect_to_user (gstate, - &clip_rect, - &rectangles[i])) - { - free (rectangles); - return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - } - } - } - - DONE: - list = malloc (sizeof (cairo_rectangle_list_t)); - if (unlikely (list == NULL)) { - free (rectangles); - return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); - } - - list->status = CAIRO_STATUS_SUCCESS; - list->rectangles = rectangles; - list->num_rectangles = n_rects; - return list; - -#undef ERROR_LIST -} - -/** - * cairo_rectangle_list_destroy: - * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles() - * - * Unconditionally frees @rectangle_list and all associated - * references. After this call, the @rectangle_list pointer must not - * be dereferenced. - * - * Since: 1.4 - **/ -void -cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) -{ - if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil || - rectangle_list == &_cairo_rectangles_not_representable) - return; - - free (rectangle_list->rectangles); - free (rectangle_list); -} - -void -_cairo_clip_reset_static_data (void) -{ - _freed_pool_reset (&clip_path_pool); -} diff --git a/libs/cairo/cairo/src/cairo-color.c b/libs/cairo/cairo/src/cairo-color.c deleted file mode 100644 index b9dae237e..000000000 --- a/libs/cairo/cairo/src/cairo-color.c +++ /dev/null @@ -1,178 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -static cairo_color_t const cairo_color_white = { - 1.0, 1.0, 1.0, 1.0, - 0xffff, 0xffff, 0xffff, 0xffff -}; - -static cairo_color_t const cairo_color_black = { - 0.0, 0.0, 0.0, 1.0, - 0x0, 0x0, 0x0, 0xffff -}; - -static cairo_color_t const cairo_color_transparent = { - 0.0, 0.0, 0.0, 0.0, - 0x0, 0x0, 0x0, 0x0 -}; - -static cairo_color_t const cairo_color_magenta = { - 1.0, 0.0, 1.0, 1.0, - 0xffff, 0x0, 0xffff, 0xffff -}; - -const cairo_color_t * -_cairo_stock_color (cairo_stock_t stock) -{ - switch (stock) { - case CAIRO_STOCK_WHITE: - return &cairo_color_white; - case CAIRO_STOCK_BLACK: - return &cairo_color_black; - case CAIRO_STOCK_TRANSPARENT: - return &cairo_color_transparent; - - case CAIRO_STOCK_NUM_COLORS: - default: - ASSERT_NOT_REACHED; - /* If the user can get here somehow, give a color that indicates a - * problem. */ - return &cairo_color_magenta; - } -} - -void -_cairo_color_init (cairo_color_t *color) -{ - *color = cairo_color_white; -} - -void -_cairo_color_init_rgb (cairo_color_t *color, - double red, double green, double blue) -{ - _cairo_color_init_rgba (color, red, green, blue, 1.0); -} - -/* Convert a double in [0.0, 1.0] to an integer in [0, 65535] - * The conversion is designed to divide the input range into 65536 - * equally-sized regions. This is achieved by multiplying by 65536 and - * then special-casing the result of an input value of 1.0 so that it - * maps to 65535 instead of 65536. - */ -uint16_t -_cairo_color_double_to_short (double d) -{ - uint32_t i; - i = (uint32_t) (d * 65536); - i -= (i >> 16); - return i; -} - -static void -_cairo_color_compute_shorts (cairo_color_t *color) -{ - color->red_short = _cairo_color_double_to_short (color->red * color->alpha); - color->green_short = _cairo_color_double_to_short (color->green * color->alpha); - color->blue_short = _cairo_color_double_to_short (color->blue * color->alpha); - color->alpha_short = _cairo_color_double_to_short (color->alpha); -} - -void -_cairo_color_init_rgba (cairo_color_t *color, - double red, double green, double blue, - double alpha) -{ - color->red = red; - color->green = green; - color->blue = blue; - color->alpha = alpha; - - _cairo_color_compute_shorts (color); -} - -void -_cairo_color_multiply_alpha (cairo_color_t *color, - double alpha) -{ - color->alpha *= alpha; - - _cairo_color_compute_shorts (color); -} - -void -_cairo_color_get_rgba (cairo_color_t *color, - double *red, - double *green, - double *blue, - double *alpha) -{ - *red = color->red; - *green = color->green; - *blue = color->blue; - *alpha = color->alpha; -} - -void -_cairo_color_get_rgba_premultiplied (cairo_color_t *color, - double *red, - double *green, - double *blue, - double *alpha) -{ - *red = color->red * color->alpha; - *green = color->green * color->alpha; - *blue = color->blue * color->alpha; - *alpha = color->alpha; -} - -/* NB: This function works both for unmultiplied and premultiplied colors */ -cairo_bool_t -_cairo_color_equal (const cairo_color_t *color_a, - const cairo_color_t *color_b) -{ - if (color_a == color_b) - return TRUE; - - if (color_a->alpha_short != color_b->alpha_short) - return FALSE; - - if (color_a->alpha_short == 0) - return TRUE; - - return color_a->red_short == color_b->red_short && - color_a->green_short == color_b->green_short && - color_a->blue_short == color_b->blue_short; -} - -cairo_bool_t -_cairo_color_stop_equal (const cairo_color_stop_t *color_a, - const cairo_color_stop_t *color_b) -{ - if (color_a == color_b) - return TRUE; - - return color_a->alpha_short == color_b->alpha_short && - color_a->red_short == color_b->red_short && - color_a->green_short == color_b->green_short && - color_a->blue_short == color_b->blue_short; -} - -cairo_content_t -_cairo_color_get_content (const cairo_color_t *color) -{ - if (CAIRO_COLOR_IS_OPAQUE (color)) - return CAIRO_CONTENT_COLOR; - - if (color->red_short == 0 && - color->green_short == 0 && - color->blue_short == 0) - { - return CAIRO_CONTENT_ALPHA; - } - - return CAIRO_CONTENT_COLOR_ALPHA; -} diff --git a/libs/cairo/cairo/src/cairo-combsort-private.h b/libs/cairo/cairo/src/cairo-combsort-private.h deleted file mode 100644 index 3400a681c..000000000 --- a/libs/cairo/cairo/src/cairo-combsort-private.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This fragment implements a comb sort (specifically combsort11) */ -#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP -#define _HAVE_CAIRO_COMBSORT_NEWGAP -static inline unsigned int -_cairo_combsort_newgap (unsigned int gap) -{ - gap = 10 * gap / 13; - if (gap == 9 || gap == 10) - gap = 11; - if (gap < 1) - gap = 1; - return gap; -} -#endif - -#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \ -static void \ -NAME (TYPE *base, unsigned int nmemb) \ -{ \ - unsigned int gap = nmemb; \ - unsigned int i, j; \ - int swapped; \ - do { \ - gap = _cairo_combsort_newgap (gap); \ - swapped = gap > 1; \ - for (i = 0; i < nmemb-gap ; i++) { \ - j = i + gap; \ - if (CMP (base[i], base[j]) > 0 ) { \ - TYPE tmp; \ - tmp = base[i]; \ - base[i] = base[j]; \ - base[j] = tmp; \ - swapped = 1; \ - } \ - } \ - } while (swapped); \ -} diff --git a/libs/cairo/cairo/src/cairo-compiler-private.h b/libs/cairo/cairo/src/cairo-compiler-private.h deleted file mode 100644 index 34cfe0956..000000000 --- a/libs/cairo/cairo/src/cairo-compiler-private.h +++ /dev/null @@ -1,244 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_COMPILER_PRIVATE_H -#define CAIRO_COMPILER_PRIVATE_H - -#include "cairo.h" - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -/* Size in bytes of buffer to use off the stack per functions. - * Mostly used by text functions. For larger allocations, they'll - * malloc(). */ -#ifndef CAIRO_STACK_BUFFER_SIZE -#define CAIRO_STACK_BUFFER_SIZE (512 * sizeof (int)) -#endif - -#define CAIRO_STACK_ARRAY_LENGTH(T) (CAIRO_STACK_BUFFER_SIZE / sizeof(T)) - -/* - * The goal of this block is to define the following macros for - * providing faster linkage to functions in the public API for calls - * from within cairo. - * - * slim_hidden_proto(f) - * slim_hidden_proto_no_warn(f) - * - * Declares `f' as a library internal function and hides the - * function from the global symbol table. This macro must be - * expanded after `f' has been declared with a prototype but before - * any calls to the function are seen by the compiler. The no_warn - * variant inhibits warnings about the return value being unused at - * call sites. The macro works by renaming `f' to an internal name - * in the symbol table and hiding that. As far as cairo internal - * calls are concerned they're calling a library internal function - * and thus don't need to bounce via the PLT. - * - * slim_hidden_def(f) - * - * Exports `f' back to the global symbol table. This macro must be - * expanded right after the function definition and only for symbols - * hidden previously with slim_hidden_proto(). The macro works by - * adding a global entry to the symbol table which points at the - * internal name of `f' created by slim_hidden_proto(). - * - * Functions in the public API which aren't called by the library - * don't need to be hidden and re-exported using the slim hidden - * macros. - */ -#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun) -# define slim_hidden_proto(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private -# define slim_hidden_proto_no_warn(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private_no_warn -# define slim_hidden_def(name) slim_hidden_def1(name, slim_hidden_int_name(name)) -# define slim_hidden_int_name(name) INT_##name -# define slim_hidden_proto1(name, internal) \ - extern __typeof (name) name \ - __asm__ (slim_hidden_asmname (internal)) -# define slim_hidden_def1(name, internal) \ - extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \ - __attribute__((__alias__(slim_hidden_asmname(internal)))) -# define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__) -# define slim_hidden_ulp1(x) slim_hidden_ulp2(x) -# define slim_hidden_ulp2(x) #x -# define slim_hidden_asmname(name) slim_hidden_asmname1(name) -# define slim_hidden_asmname1(name) slim_hidden_ulp #name -#else -# define slim_hidden_proto(name) int _cairo_dummy_prototype(void) -# define slim_hidden_proto_no_warn(name) int _cairo_dummy_prototype(void) -# define slim_hidden_def(name) int _cairo_dummy_prototype(void) -#endif - -#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) -#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) \ - __attribute__((__format__(__printf__, fmt_index, va_index))) -#else -#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) -#endif - -/* slim_internal.h */ -#define CAIRO_HAS_HIDDEN_SYMBOLS 1 -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun) -#define cairo_private_no_warn __attribute__((__visibility__("hidden"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) -#define cairo_private_no_warn __hidden -#else /* not gcc >= 3.3 and not Sun Studio >= 8 */ -#define cairo_private_no_warn -#undef CAIRO_HAS_HIDDEN_SYMBOLS -#endif - -#ifndef WARN_UNUSED_RESULT -#define WARN_UNUSED_RESULT -#endif -/* Add attribute(warn_unused_result) if supported */ -#define cairo_warn WARN_UNUSED_RESULT -#define cairo_private cairo_private_no_warn cairo_warn - -/* This macro allow us to deprecate a function by providing an alias - for the old function name to the new function name. With this - macro, binary compatibility is preserved. The macro only works on - some platforms --- tough. - - Meanwhile, new definitions in the public header file break the - source code so that it will no longer link against the old - symbols. Instead it will give a descriptive error message - indicating that the old function has been deprecated by the new - function. -*/ -#if __GNUC__ >= 2 && defined(__ELF__) -# define CAIRO_FUNCTION_ALIAS(old, new) \ - extern __typeof (new) old \ - __asm__ ("" #old) \ - __attribute__((__alias__("" #new))) -#else -# define CAIRO_FUNCTION_ALIAS(old, new) -#endif - -/* - * Cairo uses the following function attributes in order to improve the - * generated code (effectively by manual inter-procedural analysis). - * - * 'cairo_pure': The function is only allowed to read from its arguments - * and global memory (i.e. following a pointer argument or - * accessing a shared variable). The return value should - * only depend on its arguments, and for an identical set of - * arguments should return the same value. - * - * 'cairo_const': The function is only allowed to read from its arguments. - * It is not allowed to access global memory. The return - * value should only depend its arguments, and for an - * identical set of arguments should return the same value. - * This is currently the most strict function attribute. - * - * Both these function attributes allow gcc to perform CSE and - * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents - * do not change across the function call. - */ -#if __GNUC__ >= 3 -#define cairo_pure __attribute__((pure)) -#define cairo_const __attribute__((const)) -#define cairo_always_inline inline __attribute__((always_inline)) -#else -#define cairo_pure -#define cairo_const -#define cairo_always_inline inline -#endif - -#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -#define _CAIRO_BOOLEAN_EXPR(expr) \ - __extension__ ({ \ - int _cairo_boolean_var_; \ - if (expr) \ - _cairo_boolean_var_ = 1; \ - else \ - _cairo_boolean_var_ = 0; \ - _cairo_boolean_var_; \ -}) -#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1)) -#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0)) -#else -#define likely(expr) (expr) -#define unlikely(expr) (expr) -#endif - -/* - * clang-cl supports __attribute__, but MSVC doesn't, so we need to make sure - * we do this if not GNUC but also if not clang either. - */ -#if !defined(__GNUC__) && !defined(__clang__) -#undef __attribute__ -#define __attribute__(x) -#endif - -#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) -#define snprintf _snprintf -#define popen _popen -#define pclose _pclose -#define hypot _hypot -#endif - -#ifdef _MSC_VER - -#define HAVE_WIN32_ATOMIC_PRIMITIVES 1 - -#ifndef __cplusplus -#undef inline -#define inline __inline -#endif - -/* there are currently linkage problems that arise when trying to include intrin.h in c++: - * D:\sdks\v7.0\include\winnt.h(3674) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed - * so avoid defining ffs in c++ code for now */ -#ifndef __cplusplus -/* Add a definition of ffs */ -#include -#pragma intrinsic(_BitScanForward) -static __forceinline int -ffs (int x) -{ - unsigned long i; - - if (_BitScanForward(&i, x) != 0) - return i + 1; - - return 0; -} -#endif - -#elif defined(__WIN32__) && defined(__GNUC__) - -#define ffs(x) __builtin_ffs(x) - -#endif - -#if defined(_MSC_VER) && defined(_M_IX86) -/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. - The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and - will never be folded into another one. Something like this might eventually - be needed for GCC but it seems fine for now. */ -#define CAIRO_ENSURE_UNIQUE \ - do { \ - char file[] = __FILE__; \ - __asm { \ - __asm jmp __internal_skip_line_no \ - __asm _emit (__COUNTER__ & 0xff) \ - __asm _emit ((__COUNTER__>>8) & 0xff) \ - __asm _emit ((__COUNTER__>>16) & 0xff)\ - __asm _emit ((__COUNTER__>>24) & 0xff)\ - __asm lea eax, dword ptr file \ - __asm __internal_skip_line_no: \ - }; \ - } while (0) -#else -#define CAIRO_ENSURE_UNIQUE do { } while (0) -#endif - -#ifdef __STRICT_ANSI__ -#undef inline -#define inline __inline__ -#endif - -#endif diff --git a/libs/cairo/cairo/src/cairo-composite-rectangles-private.h b/libs/cairo/cairo/src/cairo-composite-rectangles-private.h deleted file mode 100644 index ddbe6bb1a..000000000 --- a/libs/cairo/cairo/src/cairo-composite-rectangles-private.h +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H -#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H - -#include "cairo-types-private.h" - -CAIRO_BEGIN_DECLS - -/* Rectangles that take part in a composite operation. - * - * The source and mask track the extents of the respective patterns in device - * space. The unbounded rectangle is essentially the clip rectangle. And the - * intersection of all is the bounded rectangle, which is the minimum extents - * the operation may require. Whether or not the operation is actually bounded - * is tracked in the is_bounded boolean. - * - */ -struct _cairo_composite_rectangles { - cairo_rectangle_int_t source; - cairo_rectangle_int_t mask; - cairo_rectangle_int_t bounded; /* dst */ - cairo_rectangle_int_t unbounded; /* clip */ - uint32_t is_bounded; -}; - -cairo_private cairo_int_status_t -_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -cairo_private cairo_int_status_t -_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -cairo_private cairo_int_status_t -_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - cairo_clip_t *clip); - -cairo_private cairo_int_status_t -_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_clip_t *clip); - -cairo_private cairo_int_status_t -_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_clip_t *clip, - cairo_bool_t *overlap); - -#endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-composite-rectangles.c b/libs/cairo/cairo/src/cairo-composite-rectangles.c deleted file mode 100644 index a7b499cf4..000000000 --- a/libs/cairo/cairo/src/cairo-composite-rectangles.c +++ /dev/null @@ -1,164 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-composite-rectangles-private.h" - -/* A collection of routines to facilitate writing compositors. */ - -static inline cairo_bool_t -_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - extents->unbounded = *surface_extents; - - if (clip != NULL) { - const cairo_rectangle_int_t *clip_extents; - - clip_extents = _cairo_clip_get_extents (clip); - if (clip_extents == NULL) - return FALSE; - - if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents)) - return FALSE; - } - - extents->bounded = extents->unbounded; - extents->is_bounded = _cairo_operator_bounded_by_either (op); - - _cairo_pattern_get_extents (source, &extents->source); - if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) { - if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source)) - return FALSE; - } - - return TRUE; -} - -cairo_int_status_t -_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - extents->mask = extents->bounded; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents) -{ - cairo_bool_t ret; - - ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); - if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - _cairo_pattern_get_extents (mask, &extents->mask); - - return _cairo_composite_rectangles_intersect (extents); -} - -cairo_int_status_t -_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - cairo_clip_t *clip) -{ - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); - - return _cairo_composite_rectangles_intersect (extents); -} - -cairo_int_status_t -_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_clip_t *clip) -{ - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - _cairo_path_fixed_approximate_fill_extents (path, &extents->mask); - - return _cairo_composite_rectangles_intersect (extents); -} - -cairo_int_status_t -_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_clip_t *clip, - cairo_bool_t *overlap) -{ - cairo_status_t status; - - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, num_glyphs, - &extents->mask, - overlap); - if (unlikely (status)) - return status; - - return _cairo_composite_rectangles_intersect (extents); -} diff --git a/libs/cairo/cairo/src/cairo-d2d-private-fx.h b/libs/cairo/cairo/src/cairo-d2d-private-fx.h deleted file mode 100644 index 1756c57f6..000000000 --- a/libs/cairo/cairo/src/cairo-d2d-private-fx.h +++ /dev/null @@ -1,1164 +0,0 @@ -#if 0 -// -// FX Version: fx_4_0 -// Child effect (requires effect pool): false -// -// 1 local buffer(s) -// -cbuffer cb0 -{ - float4 QuadDesc; // Offset: 0, size: 16 - float4 TexCoords; // Offset: 16, size: 16 - float4 TextColor; // Offset: 32, size: 16 -} - -// -// 3 local object(s) -// -Texture2D tex; -BlendState bTextBlend -{ - AlphaToCoverageEnable = bool(FALSE /* 0 */); - BlendEnable[0] = bool(TRUE /* 1 */); - SrcBlend[0] = uint(SRC1_COLOR /* 16 */); - DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */); - BlendOp[0] = uint(ADD /* 1 */); - SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */); - DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */); - BlendOpAlpha[0] = uint(ADD /* 1 */); - RenderTargetWriteMask[0] = byte(0x0f); -}; -SamplerState sSampler -{ - Texture = tex; - AddressU = uint(CLAMP /* 3 */); - AddressV = uint(CLAMP /* 3 */); -}; - -// -// 2 technique(s) -// -technique10 SampleTexture -{ - pass P0 - { - VertexShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 - // float4 TexCoords; // Offset: 16 Size: 16 - // float4 TextColor; // Offset: 32 Size: 16 [unused] - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // POSITION 0 xyz 0 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float xyzw - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) - // - // - // Runtime generated constant mappings: - // - // Target Reg Constant Description - // ---------- -------------------------------------------------- - // c0 Vertex Shader position offset - // - // - // Level9 shader bytecode: - // - vs_2_x - def c3, 0, 1, 0, 0 - dcl_texcoord v0 - mad oT0.xy, v0, c2.zwzw, c2 - mad r0.xy, v0, c1.zwzw, c1 - add oPos.xy, r0, c0 - mov oPos.zw, c3.xyxy - - // approximately 4 instruction slots used - vs_4_0 - dcl_constantbuffer cb0[2], immediateIndexed - dcl_input v0.xy - dcl_output_siv o0.xyzw, position - dcl_output o1.xy - mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx - mov o0.zw, l(0,0,0,1.000000) - mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx - ret - // Approximately 4 instruction slots used - - }; - GeometryShader = NULL; - PixelShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // sSampler sampler NA NA 0 1 - // tex texture float4 2d 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Target 0 xyzw 0 TARGET float xyzw - // - // - // Sampler/Resource to DX9 shader sampler mappings: - // - // Target Sampler Source Sampler Source Resource - // -------------- --------------- ---------------- - // s0 s0 t0 - // - // - // Level9 shader bytecode: - // - ps_2_x - dcl t0.xy - dcl_2d s0 - texld r0, t0, s0 - mov oC0, r0 - - // approximately 2 instruction slots used (1 texture, 1 arithmetic) - ps_4_0 - dcl_sampler s0, mode_default - dcl_resource_texture2d (float,float,float,float) t0 - dcl_input_ps linear v1.xy - dcl_output o0.xyzw - sample o0.xyzw, v1.xyxx, t0.xyzw, s0 - ret - // Approximately 2 instruction slots used - - }; - } - -} - -technique10 SampleTextTexture -{ - pass P0 - { - AB_BlendFactor = float4(0, 0, 0, 0); - AB_SampleMask = uint(0xffffffff); - BlendState = bTextBlend; - VertexShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 - // float4 TexCoords; // Offset: 16 Size: 16 - // float4 TextColor; // Offset: 32 Size: 16 [unused] - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // POSITION 0 xyz 0 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float xyzw - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) - // - // - // Runtime generated constant mappings: - // - // Target Reg Constant Description - // ---------- -------------------------------------------------- - // c0 Vertex Shader position offset - // - // - // Level9 shader bytecode: - // - vs_2_x - def c3, 0, 1, 0, 0 - dcl_texcoord v0 - mad oT0.xy, v0, c2.zwzw, c2 - mad r0.xy, v0, c1.zwzw, c1 - add oPos.xy, r0, c0 - mov oPos.zw, c3.xyxy - - // approximately 4 instruction slots used - vs_4_0 - dcl_constantbuffer cb0[2], immediateIndexed - dcl_input v0.xy - dcl_output_siv o0.xyzw, position - dcl_output o1.xy - mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx - mov o0.zw, l(0,0,0,1.000000) - mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx - ret - // Approximately 4 instruction slots used - - }; - GeometryShader = NULL; - PixelShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 [unused] - // float4 TexCoords; // Offset: 16 Size: 16 [unused] - // float4 TextColor; // Offset: 32 Size: 16 - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // sSampler sampler NA NA 0 1 - // tex texture float4 2d 0 1 - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Target 0 xyzw 0 TARGET float xyzw - // SV_Target 1 xyzw 1 TARGET float xyzw - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c0 cb0 2 1 ( FLT, FLT, FLT, FLT) - // - // - // Sampler/Resource to DX9 shader sampler mappings: - // - // Target Sampler Source Sampler Source Resource - // -------------- --------------- ---------------- - // s0 s0 t0 - // - // - // Level9 shader bytecode: - // - ps_2_x - dcl t0.xy - dcl_2d s0 - mov oC0, c0 - texld r0, t0, s0 - mul r0, r0.zyxy, c0.w - mov oC1, r0 - - // approximately 4 instruction slots used (1 texture, 3 arithmetic) - ps_4_0 - dcl_constantbuffer cb0[3], immediateIndexed - dcl_sampler s0, mode_default - dcl_resource_texture2d (float,float,float,float) t0 - dcl_input_ps linear v1.xy - dcl_output o0.xyzw - dcl_output o1.xyzw - dcl_temps 1 - mov o0.xyzw, cb0[2].xyzw - sample r0.xyzw, v1.xyxx, t0.xyzw, s0 - mul o1.xyzw, r0.zyxy, cb0[2].wwww - ret - // Approximately 4 instruction slots used - - }; - } - -} - -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 53, 137, - 246, 90, 48, 255, 136, 62, - 98, 150, 163, 150, 147, 186, - 203, 53, 1, 0, 0, 0, - 225, 18, 0, 0, 1, 0, - 0, 0, 36, 0, 0, 0, - 70, 88, 49, 48, 181, 18, - 0, 0, 1, 16, 255, 254, - 1, 0, 0, 0, 3, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 57, 16, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 99, 98, - 48, 0, 102, 108, 111, 97, - 116, 52, 0, 8, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 16, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 10, 33, 0, - 0, 81, 117, 97, 100, 68, - 101, 115, 99, 0, 84, 101, - 120, 67, 111, 111, 114, 100, - 115, 0, 84, 101, 120, 116, - 67, 111, 108, 111, 114, 0, - 84, 101, 120, 116, 117, 114, - 101, 50, 68, 0, 72, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 12, 0, - 0, 0, 116, 101, 120, 0, - 66, 108, 101, 110, 100, 83, - 116, 97, 116, 101, 0, 114, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 98, 84, 101, - 120, 116, 66, 108, 101, 110, - 100, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 16, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 17, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 18, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 19, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 15, 0, - 0, 0, 83, 97, 109, 112, - 108, 101, 114, 83, 116, 97, - 116, 101, 0, 16, 1, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 21, 0, 0, - 0, 115, 83, 97, 109, 112, - 108, 101, 114, 0, 1, 0, - 0, 0, 2, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 2, 0, 0, 0, - 3, 0, 0, 0, 83, 97, - 109, 112, 108, 101, 84, 101, - 120, 116, 117, 114, 101, 0, - 80, 48, 0, 188, 3, 0, - 0, 68, 88, 66, 67, 211, - 96, 210, 105, 17, 130, 48, - 194, 178, 234, 96, 122, 215, - 146, 217, 132, 1, 0, 0, - 0, 188, 3, 0, 0, 6, - 0, 0, 0, 56, 0, 0, - 0, 228, 0, 0, 0, 168, - 1, 0, 0, 36, 2, 0, - 0, 48, 3, 0, 0, 100, - 3, 0, 0, 65, 111, 110, - 57, 164, 0, 0, 0, 164, - 0, 0, 0, 0, 2, 254, - 255, 112, 0, 0, 0, 52, - 0, 0, 0, 1, 0, 36, - 0, 0, 0, 48, 0, 0, - 0, 48, 0, 0, 0, 36, - 0, 1, 0, 48, 0, 0, - 0, 0, 0, 2, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 254, - 255, 81, 0, 0, 5, 3, - 0, 15, 160, 0, 0, 0, - 0, 0, 0, 128, 63, 0, - 0, 0, 0, 0, 0, 0, - 0, 31, 0, 0, 2, 5, - 0, 0, 128, 0, 0, 15, - 144, 4, 0, 0, 4, 0, - 0, 3, 224, 0, 0, 228, - 144, 2, 0, 238, 160, 2, - 0, 228, 160, 4, 0, 0, - 4, 0, 0, 3, 128, 0, - 0, 228, 144, 1, 0, 238, - 160, 1, 0, 228, 160, 2, - 0, 0, 3, 0, 0, 3, - 192, 0, 0, 228, 128, 0, - 0, 228, 160, 1, 0, 0, - 2, 0, 0, 12, 192, 3, - 0, 68, 160, 255, 255, 0, - 0, 83, 72, 68, 82, 188, - 0, 0, 0, 64, 0, 1, - 0, 47, 0, 0, 0, 89, - 0, 0, 4, 70, 142, 32, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 95, 0, 0, - 3, 50, 16, 16, 0, 0, - 0, 0, 0, 103, 0, 0, - 4, 242, 32, 16, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 50, - 32, 16, 0, 1, 0, 0, - 0, 50, 0, 0, 11, 50, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 0, - 0, 0, 0, 230, 138, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 70, 128, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, - 8, 194, 32, 16, 0, 0, - 0, 0, 0, 2, 64, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 63, 50, - 0, 0, 11, 50, 32, 16, - 0, 1, 0, 0, 0, 70, - 16, 16, 0, 0, 0, 0, - 0, 230, 138, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 70, 128, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 4, 1, 0, 0, 1, - 0, 0, 0, 64, 0, 0, - 0, 1, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 254, - 255, 0, 1, 0, 0, 208, - 0, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 99, - 98, 48, 0, 60, 0, 0, - 0, 3, 0, 0, 0, 88, - 0, 0, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 160, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 188, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 198, 0, 0, - 0, 32, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 81, 117, 97, - 100, 68, 101, 115, 99, 0, - 171, 171, 171, 1, 0, 3, - 0, 1, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 84, 101, 120, 67, 111, - 111, 114, 100, 115, 0, 84, - 101, 120, 116, 67, 111, 108, - 111, 114, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 57, 46, 50, 57, - 46, 57, 53, 50, 46, 51, - 49, 49, 49, 0, 171, 171, - 171, 73, 83, 71, 78, 44, - 0, 0, 0, 1, 0, 0, - 0, 8, 0, 0, 0, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 7, 3, 0, 0, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 171, 171, 171, 79, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 12, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 107, 1, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 188, - 2, 0, 0, 68, 88, 66, - 67, 57, 173, 135, 37, 0, - 15, 237, 50, 142, 80, 59, - 160, 81, 240, 60, 171, 1, - 0, 0, 0, 188, 2, 0, - 0, 6, 0, 0, 0, 56, - 0, 0, 0, 164, 0, 0, - 0, 16, 1, 0, 0, 140, - 1, 0, 0, 48, 2, 0, - 0, 136, 2, 0, 0, 65, - 111, 110, 57, 100, 0, 0, - 0, 100, 0, 0, 0, 0, - 2, 255, 255, 60, 0, 0, - 0, 40, 0, 0, 0, 0, - 0, 40, 0, 0, 0, 40, - 0, 0, 0, 40, 0, 1, - 0, 36, 0, 0, 0, 40, - 0, 0, 0, 0, 0, 1, - 2, 255, 255, 31, 0, 0, - 2, 0, 0, 0, 128, 0, - 0, 3, 176, 31, 0, 0, - 2, 0, 0, 0, 144, 0, - 8, 15, 160, 66, 0, 0, - 3, 0, 0, 15, 128, 0, - 0, 228, 176, 0, 8, 228, - 160, 1, 0, 0, 2, 0, - 8, 15, 128, 0, 0, 228, - 128, 255, 255, 0, 0, 83, - 72, 68, 82, 100, 0, 0, - 0, 64, 0, 0, 0, 25, - 0, 0, 0, 90, 0, 0, - 3, 0, 96, 16, 0, 0, - 0, 0, 0, 88, 24, 0, - 4, 0, 112, 16, 0, 0, - 0, 0, 0, 85, 85, 0, - 0, 98, 16, 0, 3, 50, - 16, 16, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 242, - 32, 16, 0, 0, 0, 0, - 0, 69, 0, 0, 9, 242, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 1, - 0, 0, 0, 70, 126, 16, - 0, 0, 0, 0, 0, 0, - 96, 16, 0, 0, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 156, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 255, - 255, 0, 1, 0, 0, 105, - 0, 0, 0, 92, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 101, - 0, 0, 0, 2, 0, 0, - 0, 5, 0, 0, 0, 4, - 0, 0, 0, 255, 255, 255, - 255, 0, 0, 0, 0, 1, - 0, 0, 0, 12, 0, 0, - 0, 115, 83, 97, 109, 112, - 108, 101, 114, 0, 116, 101, - 120, 0, 77, 105, 99, 114, - 111, 115, 111, 102, 116, 32, - 40, 82, 41, 32, 72, 76, - 83, 76, 32, 83, 104, 97, - 100, 101, 114, 32, 67, 111, - 109, 112, 105, 108, 101, 114, - 32, 57, 46, 50, 57, 46, - 57, 53, 50, 46, 51, 49, - 49, 49, 0, 171, 171, 73, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 3, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 79, 83, 71, - 78, 44, 0, 0, 0, 1, - 0, 0, 0, 8, 0, 0, - 0, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 15, 0, 0, - 0, 83, 86, 95, 84, 97, - 114, 103, 101, 116, 0, 171, - 171, 63, 5, 0, 0, 0, - 0, 0, 0, 83, 97, 109, - 112, 108, 101, 84, 101, 120, - 116, 84, 101, 120, 116, 117, - 114, 101, 0, 4, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 3, 0, 0, 0, 255, - 255, 255, 255, 188, 3, 0, - 0, 68, 88, 66, 67, 211, - 96, 210, 105, 17, 130, 48, - 194, 178, 234, 96, 122, 215, - 146, 217, 132, 1, 0, 0, - 0, 188, 3, 0, 0, 6, - 0, 0, 0, 56, 0, 0, - 0, 228, 0, 0, 0, 168, - 1, 0, 0, 36, 2, 0, - 0, 48, 3, 0, 0, 100, - 3, 0, 0, 65, 111, 110, - 57, 164, 0, 0, 0, 164, - 0, 0, 0, 0, 2, 254, - 255, 112, 0, 0, 0, 52, - 0, 0, 0, 1, 0, 36, - 0, 0, 0, 48, 0, 0, - 0, 48, 0, 0, 0, 36, - 0, 1, 0, 48, 0, 0, - 0, 0, 0, 2, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 254, - 255, 81, 0, 0, 5, 3, - 0, 15, 160, 0, 0, 0, - 0, 0, 0, 128, 63, 0, - 0, 0, 0, 0, 0, 0, - 0, 31, 0, 0, 2, 5, - 0, 0, 128, 0, 0, 15, - 144, 4, 0, 0, 4, 0, - 0, 3, 224, 0, 0, 228, - 144, 2, 0, 238, 160, 2, - 0, 228, 160, 4, 0, 0, - 4, 0, 0, 3, 128, 0, - 0, 228, 144, 1, 0, 238, - 160, 1, 0, 228, 160, 2, - 0, 0, 3, 0, 0, 3, - 192, 0, 0, 228, 128, 0, - 0, 228, 160, 1, 0, 0, - 2, 0, 0, 12, 192, 3, - 0, 68, 160, 255, 255, 0, - 0, 83, 72, 68, 82, 188, - 0, 0, 0, 64, 0, 1, - 0, 47, 0, 0, 0, 89, - 0, 0, 4, 70, 142, 32, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 95, 0, 0, - 3, 50, 16, 16, 0, 0, - 0, 0, 0, 103, 0, 0, - 4, 242, 32, 16, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 50, - 32, 16, 0, 1, 0, 0, - 0, 50, 0, 0, 11, 50, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 0, - 0, 0, 0, 230, 138, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 70, 128, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, - 8, 194, 32, 16, 0, 0, - 0, 0, 0, 2, 64, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 63, 50, - 0, 0, 11, 50, 32, 16, - 0, 1, 0, 0, 0, 70, - 16, 16, 0, 0, 0, 0, - 0, 230, 138, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 70, 128, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 4, 1, 0, 0, 1, - 0, 0, 0, 64, 0, 0, - 0, 1, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 254, - 255, 0, 1, 0, 0, 208, - 0, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 99, - 98, 48, 0, 60, 0, 0, - 0, 3, 0, 0, 0, 88, - 0, 0, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 160, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 188, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 198, 0, 0, - 0, 32, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 81, 117, 97, - 100, 68, 101, 115, 99, 0, - 171, 171, 171, 1, 0, 3, - 0, 1, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 84, 101, 120, 67, 111, - 111, 114, 100, 115, 0, 84, - 101, 120, 116, 67, 111, 108, - 111, 114, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 57, 46, 50, 57, - 46, 57, 53, 50, 46, 51, - 49, 49, 49, 0, 171, 171, - 171, 73, 83, 71, 78, 44, - 0, 0, 0, 1, 0, 0, - 0, 8, 0, 0, 0, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 7, 3, 0, 0, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 171, 171, 171, 79, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 12, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 73, 8, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 16, - 4, 0, 0, 68, 88, 66, - 67, 175, 129, 196, 242, 129, - 85, 126, 90, 106, 179, 87, - 12, 194, 2, 170, 102, 1, - 0, 0, 0, 16, 4, 0, - 0, 6, 0, 0, 0, 56, - 0, 0, 0, 204, 0, 0, - 0, 148, 1, 0, 0, 16, - 2, 0, 0, 108, 3, 0, - 0, 196, 3, 0, 0, 65, - 111, 110, 57, 140, 0, 0, - 0, 140, 0, 0, 0, 0, - 2, 255, 255, 88, 0, 0, - 0, 52, 0, 0, 0, 1, - 0, 40, 0, 0, 0, 52, - 0, 0, 0, 52, 0, 1, - 0, 36, 0, 0, 0, 52, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, - 2, 255, 255, 31, 0, 0, - 2, 0, 0, 0, 128, 0, - 0, 3, 176, 31, 0, 0, - 2, 0, 0, 0, 144, 0, - 8, 15, 160, 1, 0, 0, - 2, 0, 8, 15, 128, 0, - 0, 228, 160, 66, 0, 0, - 3, 0, 0, 15, 128, 0, - 0, 228, 176, 0, 8, 228, - 160, 5, 0, 0, 3, 0, - 0, 15, 128, 0, 0, 70, - 128, 0, 0, 255, 160, 1, - 0, 0, 2, 1, 8, 15, - 128, 0, 0, 228, 128, 255, - 255, 0, 0, 83, 72, 68, - 82, 192, 0, 0, 0, 64, - 0, 0, 0, 48, 0, 0, - 0, 89, 0, 0, 4, 70, - 142, 32, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 90, - 0, 0, 3, 0, 96, 16, - 0, 0, 0, 0, 0, 88, - 24, 0, 4, 0, 112, 16, - 0, 0, 0, 0, 0, 85, - 85, 0, 0, 98, 16, 0, - 3, 50, 16, 16, 0, 1, - 0, 0, 0, 101, 0, 0, - 3, 242, 32, 16, 0, 0, - 0, 0, 0, 101, 0, 0, - 3, 242, 32, 16, 0, 1, - 0, 0, 0, 104, 0, 0, - 2, 1, 0, 0, 0, 54, - 0, 0, 6, 242, 32, 16, - 0, 0, 0, 0, 0, 70, - 142, 32, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 69, - 0, 0, 9, 242, 0, 16, - 0, 0, 0, 0, 0, 70, - 16, 16, 0, 1, 0, 0, - 0, 70, 126, 16, 0, 0, - 0, 0, 0, 0, 96, 16, - 0, 0, 0, 0, 0, 56, - 0, 0, 8, 242, 32, 16, - 0, 1, 0, 0, 0, 102, - 4, 16, 0, 0, 0, 0, - 0, 246, 143, 32, 0, 0, - 0, 0, 0, 2, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 84, 1, 0, 0, 1, - 0, 0, 0, 144, 0, 0, - 0, 3, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 255, - 255, 0, 1, 0, 0, 32, - 1, 0, 0, 124, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 133, - 0, 0, 0, 2, 0, 0, - 0, 5, 0, 0, 0, 4, - 0, 0, 0, 255, 255, 255, - 255, 0, 0, 0, 0, 1, - 0, 0, 0, 12, 0, 0, - 0, 137, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 115, 83, 97, - 109, 112, 108, 101, 114, 0, - 116, 101, 120, 0, 99, 98, - 48, 0, 171, 171, 171, 137, - 0, 0, 0, 3, 0, 0, - 0, 168, 0, 0, 0, 48, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 240, - 0, 0, 0, 0, 0, 0, - 0, 16, 0, 0, 0, 0, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 12, - 1, 0, 0, 16, 0, 0, - 0, 16, 0, 0, 0, 0, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 22, - 1, 0, 0, 32, 0, 0, - 0, 16, 0, 0, 0, 2, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 81, - 117, 97, 100, 68, 101, 115, - 99, 0, 171, 171, 171, 1, - 0, 3, 0, 1, 0, 4, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 101, 120, - 67, 111, 111, 114, 100, 115, - 0, 84, 101, 120, 116, 67, - 111, 108, 111, 114, 0, 77, - 105, 99, 114, 111, 115, 111, - 102, 116, 32, 40, 82, 41, - 32, 72, 76, 83, 76, 32, - 83, 104, 97, 100, 101, 114, - 32, 67, 111, 109, 112, 105, - 108, 101, 114, 32, 57, 46, - 50, 57, 46, 57, 53, 50, - 46, 51, 49, 49, 49, 0, - 171, 171, 171, 73, 83, 71, - 78, 80, 0, 0, 0, 2, - 0, 0, 0, 8, 0, 0, - 0, 56, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 15, 0, 0, - 0, 68, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 1, - 0, 0, 0, 3, 3, 0, - 0, 83, 86, 95, 80, 111, - 115, 105, 116, 105, 111, 110, - 0, 84, 69, 88, 67, 79, - 79, 82, 68, 0, 171, 171, - 171, 79, 83, 71, 78, 68, - 0, 0, 0, 2, 0, 0, - 0, 8, 0, 0, 0, 56, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 15, 0, 0, 0, 56, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 1, 0, 0, - 0, 15, 0, 0, 0, 83, - 86, 95, 84, 97, 114, 103, - 101, 116, 0, 171, 171, 29, - 12, 0, 0, 0, 0, 0, - 0, 4, 0, 0, 0, 48, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 255, - 255, 255, 255, 0, 0, 0, - 0, 43, 0, 0, 0, 15, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 52, - 0, 0, 0, 15, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 62, 0, 0, - 0, 15, 0, 0, 0, 0, - 0, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 110, 0, 0, 0, 82, - 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 0, - 0, 0, 0, 153, 0, 0, - 0, 125, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, - 255, 9, 0, 0, 0, 36, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 164, - 0, 0, 0, 37, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 176, 0, 0, - 0, 38, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 188, 0, 0, 0, 39, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 200, - 0, 0, 0, 40, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 212, 0, 0, - 0, 41, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 224, 0, 0, 0, 42, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 236, - 0, 0, 0, 43, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 248, 0, 0, - 0, 44, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 4, 1, 0, 0, 0, - 0, 0, 0, 57, 1, 0, - 0, 29, 1, 0, 0, 0, - 0, 0, 0, 255, 255, 255, - 255, 3, 0, 0, 0, 55, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 110, - 0, 0, 0, 46, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 66, 1, 0, - 0, 47, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 78, 1, 0, 0, 0, - 0, 0, 0, 90, 1, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 104, 1, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 6, 0, 0, - 0, 0, 0, 0, 0, 7, - 0, 0, 0, 43, 5, 0, - 0, 8, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 51, 5, 0, 0, 7, - 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 255, - 7, 0, 0, 7, 8, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 104, 1, 0, - 0, 6, 0, 0, 0, 0, - 0, 0, 0, 10, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 25, 8, 0, - 0, 11, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 61, 8, 0, 0, 2, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 153, - 0, 0, 0, 6, 0, 0, - 0, 0, 0, 0, 0, 7, - 0, 0, 0, 9, 12, 0, - 0, 8, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 17, 12, 0, 0, 7, - 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 49, - 16, 0, 0 -}; diff --git a/libs/cairo/cairo/src/cairo-d2d-private.fx b/libs/cairo/cairo/src/cairo-d2d-private.fx deleted file mode 100644 index 8f05d693b..000000000 --- a/libs/cairo/cairo/src/cairo-d2d-private.fx +++ /dev/null @@ -1,96 +0,0 @@ -// We store vertex coordinates and the quad shape in a constant buffer, this is -// easy to update and allows us to use a single call to set the x, y, w, h of -// the quad. -// The QuadDesc and TexCoords both work as follows: -// The x component is the quad left point, the y component is the top point -// the z component is the width, and the w component is the height. The quad -// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f } -// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right -// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture -// space <0, 1.0f> left to right and top to bottom. The input vertices of the -// shader stage always form a rectangle from {0, 0} - {1, 1} -cbuffer cb0 -{ - float4 QuadDesc; - float4 TexCoords; - float4 TextColor; -} - -struct VS_OUTPUT -{ - float4 Position : SV_Position; - float2 TexCoord : TEXCOORD0; -}; - -struct PS_OUTPUT -{ - float4 color; - float4 alpha; -}; - -Texture2D tex; - -BlendState bTextBlend -{ - AlphaToCoverageEnable = FALSE; - BlendEnable[0] = TRUE; - SrcBlend = Src1_Color; - DestBlend = Inv_Src1_Color; - BlendOp = Add; - SrcBlendAlpha = Src1_Alpha; - DestBlendAlpha = Inv_Src1_Alpha; - BlendOpAlpha = Add; - RenderTargetWriteMask[0] = 0x0F; // All -}; - -sampler sSampler = sampler_state { - Texture = tex; - AddressU = Clamp; - AddressV = Clamp; -}; - -VS_OUTPUT SampleTextureVS(float3 pos : POSITION) -{ - VS_OUTPUT Output; - Output.Position.w = 1.0f; - Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x; - Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y; - Output.Position.z = 0; - Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x; - Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y; - return Output; -} - -float4 SampleTexturePS( VS_OUTPUT In) : SV_Target -{ - return tex.Sample(sSampler, In.TexCoord); -}; - -PS_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target -{ - PS_OUTPUT output; - output.color = TextColor; - output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a; - return output; -}; - -technique10 SampleTexture -{ - pass P0 - { - SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); - SetGeometryShader(NULL); - SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS())); - } -} - -technique10 SampleTextTexture -{ - pass P0 - { - SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); - SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); - SetGeometryShader(NULL); - SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS())); - } -} diff --git a/libs/cairo/cairo/src/cairo-d2d-private.h b/libs/cairo/cairo/src/cairo-d2d-private.h deleted file mode 100644 index 00244b497..000000000 --- a/libs/cairo/cairo/src/cairo-d2d-private.h +++ /dev/null @@ -1,160 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_D2D_PRIVATE_H -#define CAIRO_D2D_PRIVATE_H - -#ifdef CAIRO_HAS_D2D_SURFACE - -#include -#include -#include -#include - -#include "cairoint.h" -#include "cairo-surface-clipper-private.h" - -#include "cairo-win32-refptr.h" -#include "cairo-d2d-private-fx.h" -#include "cairo-win32.h" -#include "cairo-list-private.h" - -/* describes the type of the currently applied clip so that we can pop it */ -struct d2d_clip_t; - -#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1 - -struct _cairo_d2d_device -{ - cairo_device_t base; - - HMODULE mD3D10_1; - RefPtr mD3D10Device; - RefPtr mSampleEffect; - RefPtr mInputLayout; - RefPtr mQuadBuffer; - RefPtr mRasterizerState; - RefPtr mBlendStates[MAX_OPERATORS]; - /** Texture used for manual glyph rendering */ - RefPtr mTextTexture; - RefPtr mTextTextureView; - int mVRAMUsage; -}; - -const unsigned int TEXT_TEXTURE_WIDTH = 2048; -const unsigned int TEXT_TEXTURE_HEIGHT = 512; -typedef struct _cairo_d2d_device cairo_d2d_device_t; - -struct _cairo_d2d_surface { - _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), - textRenderingState(TEXT_RENDERING_UNINITIALIZED) - { - _cairo_clip_init (&this->clip); - cairo_list_init(&this->dependent_surfaces); - } - - ~_cairo_d2d_surface(); - - - cairo_surface_t base; - /* Device used by this surface - * NOTE: In upstream cairo this is in the surface base class */ - cairo_d2d_device_t *device; - - /** Render target of the texture we render to */ - RefPtr rt; - /** Surface containing our backstore */ - RefPtr surface; - /** - * Surface used to temporarily store our surface if a bitmap isn't available - * straight from our render target surface. - */ - RefPtr bufferTexture; - /** Backbuffer surface hwndrt renders to (NULL if not a window surface) */ - RefPtr backBuf; - /** Bitmap shared with texture and rendered to by rt */ - RefPtr surfaceBitmap; - /** Swap chain holding our backbuffer (NULL if not a window surface) */ - RefPtr dxgiChain; - /** Window handle of the window we belong to */ - HWND hwnd; - /** Format of the surface */ - cairo_format_t format; - - cairo_clip_t clip; - d2d_clip_t *d2d_clip; - - - /** Mask layer used by surface_mask to push opacity masks */ - RefPtr maskLayer; - /** - * Layer used for clipping when tiling, and also for clearing out geometries - * - lazily initialized - */ - RefPtr helperLayer; - /** If this layer currently is clipping, used to prevent excessive push/pops */ - bool clipping; - /** Brush used for bitmaps */ - RefPtr bitmapBrush; - /** Brush used for solid colors */ - RefPtr solidColorBrush; - /** Indicates if our render target is currently in drawing mode */ - bool isDrawing; - /** Indicates if text rendering is initialized */ - enum TextRenderingState { - TEXT_RENDERING_UNINITIALIZED, - TEXT_RENDERING_NO_CLEARTYPE, - TEXT_RENDERING_NORMAL, - TEXT_RENDERING_GDI_CLASSIC - }; - TextRenderingState textRenderingState; - - RefPtr buffer_rt_view; - RefPtr buffer_sr_view; - - // Other d2d surfaces which depend on this one and need to be flushed if - // it is drawn to. This is required for situations where this surface is - // drawn to another surface, but may be modified before the other surface - // has flushed. When the flush of the other surface then happens and the - // drawing command is actually executed, the contents of this surface will - // no longer be what it was when the drawing command was issued. - cairo_list_t dependent_surfaces; - //cairo_surface_clipper_t clipper; -}; -typedef struct _cairo_d2d_surface cairo_d2d_surface_t; - -struct _cairo_d2d_surface_entry -{ - cairo_list_t link; - cairo_d2d_surface_t *surface; -}; - -typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( - D2D1_FACTORY_TYPE factoryType, - REFIID iid, - CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, - void **factory -); - -typedef HRESULT (WINAPI*D3D10CreateDevice1Func)( - IDXGIAdapter *pAdapter, - D3D10_DRIVER_TYPE DriverType, - HMODULE Software, - UINT Flags, - D3D10_FEATURE_LEVEL1 HardwareLevel, - UINT SDKVersion, - ID3D10Device1 **ppDevice -); - -typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( - void *pData, - SIZE_T DataLength, - UINT FXFlags, - ID3D10Device *pDevice, - ID3D10EffectPool *pEffectPool, - ID3D10Effect **ppEffect -); - -#endif /* CAIRO_HAS_D2D_SURFACE */ -#endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-d2d-surface.cpp b/libs/cairo/cairo/src/cairo-d2d-surface.cpp deleted file mode 100644 index 6aa8a3503..000000000 --- a/libs/cairo/cairo/src/cairo-d2d-surface.cpp +++ /dev/null @@ -1,4835 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define INITGUID - -#include "cairoint.h" -#include "cairo-d2d-private.h" -#include "cairo-dwrite-private.h" - -#include "cairo-win32.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" - -// Required for using placement new. -#include - -#include "d2d1_1.h" - -#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS - -struct Vertex -{ - float position[2]; -}; - -// This factory is not device dependent, we can store it. But will clear it -// if there are no devices left needing it. -static ID2D1Factory *sD2DFactory = NULL; -static HMODULE sD2DModule; - -static void -_cairo_d2d_release_factory() -{ - int refcnt = sD2DFactory->Release(); - if (!refcnt) { - // Once the last reference goes, free the library. - sD2DFactory = NULL; - FreeLibrary(sD2DModule); - } -} - -/** - * Set a blending mode for an operator. This will also return a boolean that - * reports if for this blend mode the entire surface needs to be blended. This - * is true whenever the DEST blend is not ONE when src alpha is 0. - */ -static cairo_int_status_t -_cairo_d2d_set_operator(cairo_d2d_device_t *device, - cairo_operator_t op) -{ - assert(op < MAX_OPERATORS); - if (op >= MAX_OPERATORS) { - // Eep! Someone forgot to update MAX_OPERATORS probably. - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (device->mBlendStates[static_cast(op)]) { - device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); - return CAIRO_INT_STATUS_SUCCESS; - } - - D3D10_BLEND_DESC desc; - memset(&desc, 0, sizeof(desc)); - desc.BlendEnable[0] = TRUE; - desc.AlphaToCoverageEnable = FALSE; - desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; - - switch (op) { - case CAIRO_OPERATOR_OVER: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; - break; - case CAIRO_OPERATOR_ADD: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; - break; - case CAIRO_OPERATOR_IN: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; - break; - case CAIRO_OPERATOR_OUT: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - break; - case CAIRO_OPERATOR_ATOP: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; - break; - case CAIRO_OPERATOR_DEST: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; - break; - case CAIRO_OPERATOR_DEST_OVER: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - break; - case CAIRO_OPERATOR_DEST_IN: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; - break; - case CAIRO_OPERATOR_DEST_OUT: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; - break; - case CAIRO_OPERATOR_DEST_ATOP: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - break; - case CAIRO_OPERATOR_XOR: - desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; - desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; - break; - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - }; - device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]); - - device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); - return CAIRO_INT_STATUS_SUCCESS; -} - -cairo_device_t * -cairo_d2d_create_device_from_d3d10device(ID3D10Device1 *d3d10device) -{ - HRESULT hr; - D3D10_RASTERIZER_DESC rastDesc; - D3D10_INPUT_ELEMENT_DESC layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, - }; - D3D10_PASS_DESC passDesc; - ID3D10EffectTechnique *technique; - Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} }; - CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); - D3D10_SUBRESOURCE_DATA data; - CD3D10_TEXTURE2D_DESC textDesc(DXGI_FORMAT_B8G8R8A8_UNORM, - TEXT_TEXTURE_WIDTH, - TEXT_TEXTURE_HEIGHT, - 1, 1); - - cairo_d2d_device_t *device = new cairo_d2d_device_t; - - device->mD3D10Device = d3d10device; - - device->mD3D10_1 = LoadLibraryA("d3d10_1.dll"); - D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) - GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory"); - D2D1CreateFactoryFunc createD2DFactory; - - if (!createEffect) { - goto FAILED; - } - - if (!sD2DFactory) { - sD2DModule = LoadLibraryW(L"d2d1.dll"); - createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(sD2DModule, "D2D1CreateFactory"); - if (!createD2DFactory) { - goto FAILED; - } - D2D1_FACTORY_OPTIONS options; -#ifdef DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&sD2DFactory); - if (FAILED(hr)) { - goto FAILED; - } - } else { - sD2DFactory->AddRef(); - } - - device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); - createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect); - - technique = device->mSampleEffect->GetTechniqueByName("SampleTexture"); - technique->GetPassByIndex(0)->GetDesc(&passDesc); - - - hr = device->mD3D10Device->CreateInputLayout(layout, - sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), - passDesc.pIAInputSignature, - passDesc.IAInputSignatureSize, - &device->mInputLayout); - if (FAILED(hr)) { - goto FAILED; - } - - data.pSysMem = (void*)vertices; - hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer); - if (FAILED(hr)) { - goto FAILED; - } - - memset(&rastDesc, 0, sizeof(rastDesc)); - rastDesc.CullMode = D3D10_CULL_NONE; - rastDesc.FillMode = D3D10_FILL_SOLID; - rastDesc.DepthClipEnable = TRUE; - hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState); - if (FAILED(hr)) { - goto FAILED; - } - device->base.refcount = 1; - - // We start out with TEXT_TEXTURE roughly in VRAM usage. - device->mVRAMUsage = TEXT_TEXTURE_WIDTH * TEXT_TEXTURE_HEIGHT * 4; - - // We create this with USAGE_DEFAULT, our intention is to have VRAM reserved - // for text usage. We actually store glyph data in STAGING textures for the - // rendering pipeline to read and copy it to this VRAM texture. - textDesc.Usage = D3D10_USAGE_DEFAULT; - hr = device->mD3D10Device->CreateTexture2D(&textDesc, NULL, &device->mTextTexture); - if (FAILED(hr)) { - goto FAILED; - } - - hr = device->mD3D10Device->CreateShaderResourceView(device->mTextTexture, - NULL, - &device->mTextTextureView); - - return &device->base; -FAILED: - delete &device->base; - return NULL; -} - -cairo_device_t * -cairo_d2d_create_device() -{ - HMODULE d3d10module = LoadLibraryA("d3d10_1.dll"); - D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) - GetProcAddress(d3d10module, "D3D10CreateDevice1"); - - if (!createD3DDevice) { - return NULL; - } - - RefPtr d3ddevice; - - /** - * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: - * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - * can be misleading. In fact, that flag gives no such indication. I pointed this - * out to Bas in my email. However, Microsoft is in fact using this flag to - * indicate "light weight" DX applications. By light weight they are essentially - * referring to applications that are not games. The idea is that when you create - * a DX game, the driver assumes that you will pretty much have a single instance - * and therefore it doesn't try to hold back when it comes to GPU resource - * allocation as long as it can crank out performance. In other words, the - * priority in regular DX applications is to make that one application run as fast - * as you can. For "light weight" applications, including D2D applications, the - * priorities are a bit different. Now you are no longer going to have a single - * (or very few) instances. You can have a lot of them (say, for example, a - * separate DX context/device per browser tab). In such cases, the GPU resource - * allocation scheme changes. - */ - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_1, - D3D10_1_SDK_VERSION, - &d3ddevice); - if (FAILED(hr)) { - hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_0, - D3D10_1_SDK_VERSION, - &d3ddevice); - if (FAILED(hr)) { - /* This is not guaranteed to be too fast! */ - hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_9_3, - D3D10_1_SDK_VERSION, - &d3ddevice); - - } - } - if (FAILED(hr)) { - return NULL; - } - - cairo_device_t *device = cairo_d2d_create_device_from_d3d10device(d3ddevice); - - // Free our reference to the modules. The created device should have its own. - FreeLibrary(d3d10module); - return device; -} - -int -cairo_release_device(cairo_device_t *device) -{ - int newrefcnt = --device->refcount; - if (!newrefcnt) { - // Call the correct destructor - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - HMODULE d3d10_1 = d2d_device->mD3D10_1; - delete d2d_device; - _cairo_d2d_release_factory(); - FreeLibrary(d3d10_1); - } - return newrefcnt; -} - -int -cairo_addref_device(cairo_device_t *device) -{ - return ++device->refcount; -} - -void -cairo_d2d_finish_device(cairo_device_t *device) -{ - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - // Here it becomes interesting, this flush method is generally called when - // interop is going on between our device and another device. The - // synchronisation between these devices is not always that great. The - // device flush method may flush the device's command queue, but it gives - // no guarantee that the device will actually be done with those commands, - // and so the surface may still not be complete when the external device - // chooses to use it. The EVENT query will actually tell us when the GPU - // is completely done with our commands. - D3D10_QUERY_DESC queryDesc; - queryDesc.MiscFlags = 0; - queryDesc.Query = D3D10_QUERY_EVENT; - RefPtr query; - - d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query); - - // QUERY_EVENT does not use Begin(). It's disabled. - query->End(); - - BOOL done = FALSE; - while (!done) { - // This will return S_OK and done = FALSE when the GPU is not done, and - // S_OK and done = TRUE when the GPU is done. Any other return value - // means we need to break out or risk an infinite loop. - if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) { - break; - } - if (FAILED(d2d_device->mD3D10Device->GetDeviceRemovedReason())) { - break; - } - } -} - -ID3D10Device1* -cairo_d2d_device_get_device(cairo_device_t *device) -{ - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - return d2d_device->mD3D10Device; -} - -static void -_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) -{ - device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device->mD3D10Device->IASetInputLayout(device->mInputLayout); - - UINT stride = sizeof(Vertex); - UINT offset = 0; - ID3D10Buffer *buff = device->mQuadBuffer; - device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset); - - device->mD3D10Device->RSSetState(device->mRasterizerState); -} - -// Contains our cache usage - perhaps this should be made threadsafe. -static int cache_usage = 0; - -/** - * Create a similar surface which will blend effectively to - * another surface. For D2D, this will create another texture. - * Within the types we use blending is always easy. - * - * \param surface Surface this needs to be similar to - * \param content Content type of the new surface - * \param width Width of the new surface - * \param height Height of the new surface - * \return New surface - */ -static cairo_surface_t* -_cairo_d2d_create_similar(void *surface, - cairo_content_t content, - int width, - int height); - -/** - * Release all the data held by a surface, the surface structure - * itsself will be freed by cairo. - * - * \param surface Surface to clean up - */ -static cairo_status_t -_cairo_d2d_finish(void *surface); - -/** - * Get a read-only image surface that contains the pixel data - * of a D2D surface. - * - * \param abstract_surface D2D surface to acquire the image from - * \param image_out Pointer to where we should store the image surface pointer - * \param image_extra Pointer where to store extra data we want to know about - * at the point of release. - * \return CAIRO_STATUS_SUCCESS for success - */ -static cairo_status_t -_cairo_d2d_acquire_source_image(void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra); - -/** - * Release a read-only image surface that was obtained using acquire_source_image - * - * \param abstract_surface D2D surface to acquire the image from - * \param image_out Pointer to where we should store the image surface pointer - * \param image_extra Pointer where to store extra data we want to know about - * at the point of release. - * \return CAIRO_STATUS_SUCCESS for success - */ -static void -_cairo_d2d_release_source_image(void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra); - -/** - * Get a read-write image surface that contains the pixel data - * of a D2D surface. - * - * \param abstract_surface D2D surface to acquire the image from - * \param image_out Pointer to where we should store the image surface pointer - * \param image_extra Pointer where to store extra data we want to know about - * at the point of release. - * \return CAIRO_STATUS_SUCCESS for success - */ -static cairo_status_t -_cairo_d2d_acquire_dest_image(void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra); - -/** - * Release a read-write image surface that was obtained using acquire_source_image - * - * \param abstract_surface D2D surface to acquire the image from - * \param image_out Pointer to where we should store the image surface pointer - * \param image_extra Pointer where to store extra data we want to know about - * at the point of release. - * \return CAIRO_STATUS_SUCCESS for success - */ -static void -_cairo_d2d_release_dest_image(void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra); - -/** - * Flush this surface, only after this operation is the related hardware texture - * guaranteed to contain all the results of the executed drawing operations. - * - * \param surface D2D surface to flush - * \return CAIRO_STATUS_SUCCESS or CAIRO_SURFACE_TYPE_MISMATCH - */ -static cairo_status_t -_cairo_d2d_flush(void *surface); - -/** - * Fill a path on this D2D surface. - * - * \param surface The surface to apply this operation to, must be - * a D2D surface - * \param op The operator to use - * \param source The source pattern to fill this path with - * \param path The path to fill - * \param fill_rule The fill rule to uses on the path - * \param tolerance The tolerance applied to the filling - * \param antialias The anti-alias mode to use - * \param clip The clip of this operation - * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, - * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS - */ -static cairo_int_status_t -_cairo_d2d_fill(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -/** - * Paint this surface, applying the operation to the entire surface - * - * \param surface The surface to apply this operation to, must be - * a D2D surface - * \param op Operator to use when painting - * \param source The pattern to fill this surface with, source of the op - * \param clip The clip of this operation - * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, - * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS - */ -static cairo_int_status_t -_cairo_d2d_paint(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -/** - * Paint something on the surface applying a certain mask to that - * source. - * - * \param surface The surface to apply this oepration to, must be - * a D2D surface - * \param op Operator to use - * \param source Source for this operation - * \param mask Pattern to mask source with - * \param clip The clip of this operation - * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, - * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS - */ -static cairo_int_status_t -_cairo_d2d_mask(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -/** - * Show a glyph run on the target D2D surface. - * - * \param surface The surface to apply this oepration to, must be - * a D2D surface - * \param op Operator to use - * \param source Source for this operation - * \param glyphs Glyphs to draw - * \param num_gluphs Amount of glyphs stored at glyphs - * \param scaled_font Scaled font to draw - * \param remaining_glyphs Pointer to store amount of glyphs still - * requiring drawing. - * \param clip The clip of this operation - * \return CAIRO_ERROR_SURFACE_TYPE_MISMATCH, CAIRO_ERROR_FONT_TYPE_MISMATCH, - * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS - */ -static cairo_int_status_t -_cairo_d2d_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -/** - * Get the extents of this surface. - * - * \param surface D2D surface to get the extents for - * \param extents Pointer to where to store the extents - * \param CAIRO_ERROR_SURFACE_TYPE_MISTMATCH or CAIRO_STATUS_SUCCESS - */ -static cairo_bool_t -_cairo_d2d_getextents(void *surface, - cairo_rectangle_int_t *extents); - - -/** - * Stroke a path on this D2D surface. - * - * \param surface The surface to apply this operation to, must be - * a D2D surface - * \param op The operator to use - * \param source The source pattern to fill this path with - * \param path The path to stroke - * \param style The style of the stroke - * \param ctm A logical to device matrix, since the path might be in - * device space the miter angle and such are not, hence we need to - * be aware of the transformation to apply correct stroking. - * \param ctm_inverse Inverse of ctm, used to transform the path back - * to logical space. - * \param tolerance Tolerance to stroke with - * \param antialias Antialias mode to use - * \param clip The clip of this operation - * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, - * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS - */ -static cairo_int_status_t -_cairo_d2d_stroke(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -static const cairo_surface_backend_t cairo_d2d_surface_backend = { - CAIRO_SURFACE_TYPE_D2D, - _cairo_d2d_create_similar, /* create_similar */ - _cairo_d2d_finish, /* finish */ - _cairo_d2d_acquire_source_image, /* acquire_source_image */ - _cairo_d2d_release_source_image, /* release_source_image */ - _cairo_d2d_acquire_dest_image, /* acquire_dest_image */ - _cairo_d2d_release_dest_image, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_d2d_getextents, /* get_extents */ - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - _cairo_d2d_flush, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _cairo_d2d_paint, /* paint */ - _cairo_d2d_mask, /* mask */ - _cairo_d2d_stroke, /* stroke */ - _cairo_d2d_fill, /* fill */ - _cairo_d2d_show_glyphs, /* show_glyphs */ - NULL, /* snapshot */ - NULL -}; - -/* - * Helper functions. - */ - -/* Stack-based helper to manage region destruction. */ -struct cairo_region_auto_ptr -{ - cairo_region_auto_ptr() : region(NULL) - { } - cairo_region_auto_ptr(cairo_region_t *in_region) : region(in_region) - { } - - void set(cairo_region_t *in_region) { region = in_region; } - - ~cairo_region_auto_ptr() { if (region) cairo_region_destroy (region); } - - cairo_region_t *region; -}; - -/* This clears a new D2D surface in case the VRAM was reused from an existing surface - * and is therefor not empty, this must be called outside of drawing state! */ -static void -_d2d_clear_surface(cairo_d2d_surface_t *surf) -{ - surf->rt->BeginDraw(); - surf->rt->Clear(D2D1::ColorF(0, 0)); - surf->rt->EndDraw(); -} - -static cairo_rectangle_int_t -_cairo_rect_from_windows_rect(const RECT *rect) -{ - cairo_rectangle_int_t new_rect; - - new_rect.x = rect->left; - new_rect.y = rect->top; - new_rect.width = rect->right - rect->left; - new_rect.height = rect->bottom - rect->top; - - return new_rect; -} - -static D2D1_POINT_2F -_d2d_point_from_cairo_point(const cairo_point_t *point) -{ - return D2D1::Point2F(_cairo_fixed_to_float(point->x), - _cairo_fixed_to_float(point->y)); -} - -static D2D1_COLOR_F -_cairo_d2d_color_from_cairo_color(const cairo_color_t &color) -{ - return D2D1::ColorF((FLOAT)color.red, - (FLOAT)color.green, - (FLOAT)color.blue, - (FLOAT)color.alpha); -} - -static void -_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2) -{ - rect->x = (int)floor(x1); - rect->y = (int)floor(y1); - rect->width = (int)ceil(x2) - rect->x; - rect->height = (int)ceil(y2) - rect->y; -} - -static int -_cairo_d2d_compute_surface_mem_size(cairo_d2d_surface_t *surface) -{ - int size = surface->rt->GetPixelSize().width * surface->rt->GetPixelSize().height; - size *= surface->rt->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4; - return size; -} - -static D2D1_COLOR_F -_cairo_d2d_color_from_cairo_color_stop(const cairo_color_stop_t &color) -{ - return D2D1::ColorF((FLOAT)color.red, - (FLOAT)color.green, - (FLOAT)color.blue, - (FLOAT)color.alpha); -} - - -/** - * Gets the surface buffer texture for window surfaces whose backbuffer - * is not directly usable as a bitmap. - * - * \param surface D2D surface. - * \return Buffer texture - */ -static ID3D10Texture2D* -_cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface) -{ - if (!surface->bufferTexture) { - RefPtr surf; - DXGI_SURFACE_DESC surfDesc; - surface->surface->QueryInterface(&surf); - surf->GetDesc(&surfDesc); - CD3D10_TEXTURE2D_DESC softDesc(surfDesc.Format, surfDesc.Width, surfDesc.Height); - softDesc.MipLevels = 1; - softDesc.Usage = D3D10_USAGE_DEFAULT; - softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); - surface->device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(surface); - } - return surface->bufferTexture; -} - -/** - * Ensure that the surface has an up-to-date surface bitmap. Used for - * window surfaces which cannot have a surface bitmap directly related - * to their backbuffer for some reason. - * You cannot create a bitmap around a backbuffer surface for reason (it will - * fail with an E_INVALIDARG). Meaning they need a special texture to store - * their graphical data which is wrapped by a D2D bitmap if a window surface - * is ever used in a surface pattern. All other D2D surfaces use a texture as - * their backing store so can have a bitmap directly. - * - * \param surface D2D surface. - */ -static void _cairo_d2d_update_surface_bitmap(cairo_d2d_surface_t *d2dsurf) -{ - if (!d2dsurf->backBuf && d2dsurf->rt->GetPixelFormat().format != DXGI_FORMAT_A8_UNORM) { - return; - } - - if (!d2dsurf->surfaceBitmap) { - d2dsurf->rt->CreateBitmap(d2dsurf->rt->GetPixelSize(), - D2D1::BitmapProperties(d2dsurf->rt->GetPixelFormat()), - &d2dsurf->surfaceBitmap); - } - - d2dsurf->surfaceBitmap->CopyFromRenderTarget(NULL, d2dsurf->rt, NULL); -} - -/** - * Present the backbuffer for a surface create for an HWND. This needs - * to be called when the owner of the original window surface wants to - * actually present the executed drawing operations to the screen. - * - * \param surface D2D surface. - */ -void cairo_d2d_present_backbuffer(cairo_surface_t *surface) -{ - if (surface->type != CAIRO_SURFACE_TYPE_D2D) { - return; - } - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - _cairo_d2d_flush(d2dsurf); - if (d2dsurf->dxgiChain) { - d2dsurf->dxgiChain->Present(0, 0); - d2dsurf->device->mD3D10Device->Flush(); - } -} - -struct d2d_clip_t -{ - enum clip_type {LAYER, AXIS_ALIGNED_CLIP}; - d2d_clip_t * const prev; - const enum clip_type type; - d2d_clip_t(d2d_clip_t *prev, clip_type type) : prev(prev), type(type) { } -}; - -static RefPtr -_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - D2D1_FIGURE_BEGIN type); - - -static cairo_bool_t -box_is_integer (cairo_box_t *box) -{ - return _cairo_fixed_is_integer(box->p1.x) && - _cairo_fixed_is_integer(box->p1.y) && - _cairo_fixed_is_integer(box->p2.x) && - _cairo_fixed_is_integer(box->p2.y); -} - -static cairo_status_t -push_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_path_t *clip_path) -{ - cairo_box_t box; - if (_cairo_path_fixed_is_box(&clip_path->path, &box)) { - - assert(box.p1.y < box.p2.y); - - D2D1_ANTIALIAS_MODE mode; - if (box_is_integer (&box)) { - mode = D2D1_ANTIALIAS_MODE_ALIASED; - } else { - mode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; - } - d2dsurf->rt->PushAxisAlignedClip ( - D2D1::RectF( - _cairo_fixed_to_float(box.p1.x), - _cairo_fixed_to_float(box.p1.y), - _cairo_fixed_to_float(box.p2.x), - _cairo_fixed_to_float(box.p2.y)), - mode); - - d2dsurf->d2d_clip = new d2d_clip_t (d2dsurf->d2d_clip, d2d_clip_t::AXIS_ALIGNED_CLIP); - } else { - HRESULT hr; - RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&clip_path->path, - clip_path->fill_rule, - D2D1_FIGURE_BEGIN_FILLED); - RefPtr layer; - - hr = d2dsurf->rt->CreateLayer (&layer); - - D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; - D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE; - - if (d2dsurf->base.content == CAIRO_CONTENT_COLOR) { - options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; - options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA; - options1 = D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; - } - - RefPtr dc; - hr = d2dsurf->rt->QueryInterface(IID_ID2D1DeviceContext, (void**)&dc); - - if (FAILED(hr)) { - d2dsurf->rt->PushLayer(D2D1::LayerParameters( - D2D1::InfiniteRect(), - geom, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1::IdentityMatrix(), - 1.0, - 0, - options), - layer); - } else { - dc->PushLayer(D2D1::LayerParameters1( - D2D1::InfiniteRect(), - geom, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1::IdentityMatrix(), - 1.0, - 0, - options1), - layer); - } - - d2dsurf->d2d_clip = new d2d_clip_t(d2dsurf->d2d_clip, d2d_clip_t::LAYER); - } - if (!d2dsurf->d2d_clip) - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - return CAIRO_STATUS_SUCCESS; -} - -static void -pop_clip (cairo_d2d_surface_t *d2dsurf) -{ - d2d_clip_t *current_clip = d2dsurf->d2d_clip; - - /* pop the clip from the render target */ - if (current_clip->type == d2d_clip_t::LAYER) { - d2dsurf->rt->PopLayer(); - } else if (current_clip->type == d2d_clip_t::AXIS_ALIGNED_CLIP) { - d2dsurf->rt->PopAxisAlignedClip(); - } - - /* pop it from our own stack */ - d2dsurf->d2d_clip = current_clip->prev; - delete current_clip; -} - -/* intersect clip_paths until we reach head */ -static cairo_status_t -clipper_intersect_clip_path_recursive (cairo_d2d_surface_t *d2dsurf, - cairo_clip_path_t *head, - cairo_clip_path_t *clip_path) -{ - cairo_status_t status; - - if (clip_path->prev != head) { - status = - clipper_intersect_clip_path_recursive (d2dsurf, - head, - clip_path->prev); - if (unlikely (status)) - return status; - } - return push_clip(d2dsurf, clip_path); - -} - -/* pop all of the clipping layers and reset the clip */ -static void -reset_clip (cairo_d2d_surface_t *d2dsurf) -{ - cairo_clip_path_t *current_clip_path = d2dsurf->clip.path; - while (current_clip_path != NULL) { - pop_clip (d2dsurf); - current_clip_path = current_clip_path->prev; - } - - _cairo_clip_reset (&d2dsurf->clip); -} - -/* finds the lowest common ancestor of a and b */ -static cairo_clip_path_t * -find_common_ancestor(cairo_clip_path_t *a, cairo_clip_path_t *b) -{ - int a_depth = 0, b_depth = 0; - - cairo_clip_path_t *x; - - /* find the depths of the clip_paths */ - x = a; - while (x) { - a_depth++; - x = x->prev; - } - - x = b; - while (x) { - b_depth++; - x = x->prev; - } - - /* rewind the deeper chain to the depth of the shallowest chain */ - while (b_depth < a_depth && a) { - a = a->prev; - a_depth--; - } - - while (a_depth < b_depth && b) { - b = b->prev; - b_depth--; - } - - /* walk back until we find a common ancesstor */ - - /* b will be null if and only if a is null because the depths - * must match at this point */ - while (a) { - if (a == b) - return a; - - a = a->prev; - b = b->prev; - } - - /* a will be NULL */ - return a; -} - -static cairo_status_t -_cairo_d2d_set_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip) -{ - if (clip == NULL) { - reset_clip (d2dsurf); - return CAIRO_STATUS_SUCCESS; - } - - if (clip != NULL && clip->path == d2dsurf->clip.path) - return CAIRO_STATUS_SUCCESS; - - cairo_clip_path_t *current_clip_path = d2dsurf->clip.path; - cairo_clip_path_t *new_clip_path = clip->path; - cairo_clip_path_t *ancestor = find_common_ancestor (current_clip_path, new_clip_path); - - /* adjust the clip to the common ancestor */ - while (current_clip_path != ancestor) { - pop_clip (d2dsurf); - current_clip_path = current_clip_path->prev; - } - - /* we now have a common parent (current_clip_path) for the clip */ - - /* replace the old clip */ - _cairo_clip_reset (&d2dsurf->clip); - _cairo_clip_init_copy (&d2dsurf->clip, clip); - - /* push the new clip paths up to current_clip_path */ - if (current_clip_path != clip->path) - return clipper_intersect_clip_path_recursive (d2dsurf, current_clip_path, clip->path); - else - return CAIRO_STATUS_SUCCESS; -} - -static void _cairo_d2d_add_dependent_surface(cairo_d2d_surface_t *surf, cairo_d2d_surface_t *user) -{ - _cairo_d2d_surface_entry *entry = new _cairo_d2d_surface_entry; - entry->surface = user; - cairo_surface_reference(&user->base); - cairo_list_add(&entry->link, &surf->dependent_surfaces); -}; - -static void _cairo_d2d_flush_dependent_surfaces(cairo_d2d_surface_t *surf) -{ - _cairo_d2d_surface_entry *entry, *next; - cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &surf->dependent_surfaces, link) { - _cairo_d2d_flush(entry->surface); - cairo_surface_destroy(&entry->surface->base); - delete entry; - } - cairo_list_init(&surf->dependent_surfaces); -} - -/** - * Enter the state where the surface is ready for drawing. This will guarantee - * the surface is in the correct state, and the correct clipping area is pushed. - * - * \param surface D2D surface - */ -static void _begin_draw_state(cairo_d2d_surface_t* surface) -{ - if (!surface->isDrawing) { - _cairo_d2d_flush_dependent_surfaces(surface); - surface->rt->BeginDraw(); - surface->isDrawing = true; - } -} - -/** - * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo - * uses column vectors. Hence the transposition. - * - * \param Cairo matrix - * \return D2D matrix - */ -static D2D1::Matrix3x2F -_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix) -{ - return D2D1::Matrix3x2F((FLOAT)matrix->xx, - (FLOAT)matrix->yx, - (FLOAT)matrix->xy, - (FLOAT)matrix->yy, - (FLOAT)matrix->x0, - (FLOAT)matrix->y0); -} - -/** - * Returns the inverse matrix for a D2D1 matrix. We cannot use the Invert function - * on the Matrix3x2F function class since it's statically linked and we'd have to - * lookup the symbol in the library. Doing this ourselves is easier. - * - * \param mat matrix - * \return inverse of matrix mat - */ -static D2D1::Matrix3x2F -_cairo_d2d_invert_matrix(const D2D1::Matrix3x2F &mat) -{ - float inv_det = (1 / mat.Determinant()); - - return D2D1::Matrix3x2F(mat._22 * inv_det, - -mat._12 * inv_det, - -mat._21 * inv_det, - mat._11 * inv_det, - (mat._21 * mat._32 - mat._22 * mat._31) * inv_det, - -(mat._11 * mat._32 - mat._12 * mat._31) * inv_det); -} - -/** - * Create a D2D stroke style interface for a cairo stroke style object. Must be - * released when the calling function is finished with it. - * - * \param style Cairo stroke style object - * \return D2D StrokeStyle interface - */ -static RefPtr -_cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style) -{ - D2D1_CAP_STYLE line_cap = D2D1_CAP_STYLE_FLAT; - switch (style->line_cap) { - case CAIRO_LINE_CAP_BUTT: - line_cap = D2D1_CAP_STYLE_FLAT; - break; - case CAIRO_LINE_CAP_SQUARE: - line_cap = D2D1_CAP_STYLE_SQUARE; - break; - case CAIRO_LINE_CAP_ROUND: - line_cap = D2D1_CAP_STYLE_ROUND; - break; - } - - D2D1_LINE_JOIN line_join = D2D1_LINE_JOIN_MITER; - switch (style->line_join) { - case CAIRO_LINE_JOIN_MITER: - line_join = D2D1_LINE_JOIN_MITER_OR_BEVEL; - break; - case CAIRO_LINE_JOIN_ROUND: - line_join = D2D1_LINE_JOIN_ROUND; - break; - case CAIRO_LINE_JOIN_BEVEL: - line_join = D2D1_LINE_JOIN_BEVEL; - break; - } - - FLOAT *dashes = NULL; - if (style->num_dashes) { - dashes = new FLOAT[style->num_dashes]; - for (unsigned int i = 0; i < style->num_dashes; i++) { - /* D2D seems to specify dash lengths in units of - * line width instead of the more traditional approach - * that cairo and many other APIs use where the unit - * is in pixels or someother constant unit. */ - dashes[i] = (FLOAT) (style->dash[i] / style->line_width); - } - } - - D2D1_DASH_STYLE dashStyle = D2D1_DASH_STYLE_SOLID; - if (dashes) { - dashStyle = D2D1_DASH_STYLE_CUSTOM; - } - - RefPtr strokeStyle; - sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, - line_cap, - line_cap, - line_join, - (FLOAT)style->miter_limit, - dashStyle, - (FLOAT)style->dash_offset), - dashes, - style->num_dashes, - &strokeStyle); - delete [] dashes; - return strokeStyle; -} - -static int _d2d_compute_bitmap_mem_size(ID2D1Bitmap *bitmap) -{ - D2D1_SIZE_U size = bitmap->GetPixelSize(); - int bytes_per_pixel = bitmap->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4; - return size.width * size.height * bytes_per_pixel; -} - -cairo_user_data_key_t bitmap_key_nonextend; -cairo_user_data_key_t bitmap_key_extend; -cairo_user_data_key_t bitmap_key_snapshot; - -struct cached_bitmap { - cached_bitmap() - { - sD2DFactory->AddRef(); - } - - ~cached_bitmap() - { - // Clear bitmap out first because it depends on the factory. - bitmap = NULL; - _cairo_d2d_release_factory(); - } - - /* Device this cached bitmap was created with, we should really have a per - * device cache, see bug 607408 */ - cairo_d2d_device_t *device; - /** The cached bitmap */ - RefPtr bitmap; - /** The cached bitmap is dirty and needs its data refreshed */ - bool dirty; - /** Order of snapshot detach/release bitmap called not guaranteed, single threaded refcount for now */ - int refs; -}; - -/** - * This is called when user data on a surface is replaced or the surface is - * destroyed. - */ -static void _d2d_release_bitmap(void *bitmap) -{ - cached_bitmap *existingBitmap = (cached_bitmap*)bitmap; - if (!--existingBitmap->refs) { - cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap); - delete existingBitmap; - } -} - -/** - * Via a little trick this is just used to determine when a surface has been - * modified. - */ -static void _d2d_snapshot_detached(cairo_surface_t *surface) -{ - cached_bitmap *existingBitmap = (cached_bitmap*)cairo_surface_get_user_data(surface, &bitmap_key_snapshot); - if (existingBitmap) { - existingBitmap->dirty = true; - } - if (!--existingBitmap->refs) { - cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap); - delete existingBitmap; - } - cairo_surface_destroy(surface); -} - -/** - * This function will calculate the part of srcSurf which will possibly be used within - * the boundaries of d2dsurf given the current transformation mat. This is used to - * determine what the minimal part of a surface is that needs to be uploaded. - * - * \param d2dsurf D2D surface - * \param srcSurf Source surface for operation - * \param mat Transformation matrix applied to source - */ -static void -_cairo_d2d_calculate_visible_rect(cairo_d2d_surface_t *d2dsurf, cairo_image_surface_t *srcSurf, - cairo_matrix_t *mat, - int *x, int *y, unsigned int *width, unsigned int *height) -{ - /** Leave room for extend_none space, 2 pixels */ - UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2; - - /* Transform this surface to image surface space */ - cairo_matrix_t invMat = *mat; - if (_cairo_matrix_is_invertible(mat)) { - /* If this is not invertible it will be rank zero, and invMat = mat is fine */ - cairo_matrix_invert(&invMat); - } - - RefPtr surf; - d2dsurf->surface->QueryInterface(&surf); - DXGI_SURFACE_DESC desc; - surf->GetDesc(&desc); - - double leftMost = 0; - double rightMost = desc.Width; - double topMost = 0; - double bottomMost = desc.Height; - - _cairo_matrix_transform_bounding_box(&invMat, &leftMost, &topMost, &rightMost, &bottomMost, NULL); - - leftMost -= 1; - topMost -= 1; - rightMost += 1; - bottomMost += 1; - - /* Calculate the offsets into the source image and the width of the part required */ - if ((UINT32)srcSurf->width > maxSize) { - *x = (int)MAX(0, floor(leftMost)); - /* Ensure that we get atleast 1 column of pixels as source, this will make EXTEND_PAD work */ - if (*x < srcSurf->width) { - *width = (unsigned int)MIN(MAX(1, ceil(rightMost - *x)), srcSurf->width - *x); - } else { - *x = srcSurf->width - 1; - *width = 1; - } - } else { - *x = 0; - *width = srcSurf->width; - } - - if ((UINT32)srcSurf->height > maxSize) { - *y = (int)MAX(0, floor(topMost)); - /* Ensure that we get atleast 1 row of pixels as source, this will make EXTEND_PAD work */ - if (*y < srcSurf->height) { - *height = (unsigned int)MIN(MAX(1, ceil(bottomMost - *y)), srcSurf->height - *y); - } else { - *y = srcSurf->height - 1; - *height = 1; - } - } else { - *y = 0; - *height = srcSurf->height; - } -} - -static double -_cairo_d2d_point_dist(const cairo_point_double_t &p1, const cairo_point_double_t &p2) -{ - return hypot(p2.x - p1.x, p2.y - p1.y); -} - -static void -_cairo_d2d_normalize_point(cairo_point_double_t *p) -{ - double length = hypot(p->x, p->y); - p->x /= length; - p->y /= length; -} - -static cairo_point_double_t -_cairo_d2d_subtract_point(const cairo_point_double_t &p1, const cairo_point_double_t &p2) -{ - cairo_point_double_t p = {p1.x - p2.x, p1.y - p2.y}; - return p; -} - -static double -_cairo_d2d_dot_product(const cairo_point_double_t &p1, const cairo_point_double_t &p2) -{ - return p1.x * p2.x + p1.y * p2.y; -} - -static RefPtr -_cairo_d2d_create_radial_gradient_brush(cairo_d2d_surface_t *d2dsurf, - cairo_radial_pattern_t *source_pattern) -{ - cairo_matrix_t inv_mat = source_pattern->base.base.matrix; - if (_cairo_matrix_is_invertible(&inv_mat)) { - /* If this is not invertible it will be rank zero, and invMat = mat is fine */ - cairo_matrix_invert(&inv_mat); - } - - D2D1_BRUSH_PROPERTIES brushProps = - D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat)); - - if ((source_pattern->c1.x != source_pattern->c2.x || - source_pattern->c1.y != source_pattern->c2.y) && - source_pattern->r1 != 0) { - /** - * In this particular case there's no way to deal with this! - * \todo Create an image surface with the gradient and use that. - */ - return NULL; - } - - D2D_POINT_2F center = - _d2d_point_from_cairo_point(&source_pattern->c2); - D2D_POINT_2F origin = - _d2d_point_from_cairo_point(&source_pattern->c1); - origin.x -= center.x; - origin.y -= center.y; - - float outer_radius = _cairo_fixed_to_float(source_pattern->r2); - float inner_radius = _cairo_fixed_to_float(source_pattern->r1); - int num_stops = source_pattern->base.n_stops; - int repeat_count = 1; - D2D1_GRADIENT_STOP *stops; - - if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - bool reflected = false; - bool reflect = source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT; - - RefPtr surf; - d2dsurf->surface->QueryInterface(&surf); - DXGI_SURFACE_DESC desc; - surf->GetDesc(&desc); - - // Calculate the largest distance the origin could be from the edge after inverse - // transforming by the pattern transformation. - cairo_point_double_t top_left, top_right, bottom_left, bottom_right, gradient_center; - top_left.x = bottom_left.x = top_left.y = top_right.y = 0; - top_right.x = bottom_right.x = desc.Width; - bottom_right.y = bottom_left.y = desc.Height; - - gradient_center.x = _cairo_fixed_to_float(source_pattern->c1.x); - gradient_center.y = _cairo_fixed_to_float(source_pattern->c1.y); - - // Transform surface corners into pattern coordinates. - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y); - - // Find the corner furthest away from the gradient center in pattern space. - double largest = MAX(_cairo_d2d_point_dist(top_left, gradient_center), _cairo_d2d_point_dist(top_right, gradient_center)); - largest = MAX(largest, _cairo_d2d_point_dist(bottom_left, gradient_center)); - largest = MAX(largest, _cairo_d2d_point_dist(bottom_right, gradient_center)); - - unsigned int minSize = (unsigned int)ceil(largest); - - // Calculate how often we need to repeat on the inside (for filling the inner radius) - // and on the outside (for covering the entire surface) and create the appropriate number - // of stops. - float gradient_length = outer_radius - inner_radius; - int inner_repeat = (int)ceil(inner_radius / gradient_length); - int outer_repeat = (int)MAX(1, ceil((minSize - inner_radius) / gradient_length)); - num_stops *= (inner_repeat + outer_repeat); - stops = new D2D1_GRADIENT_STOP[num_stops]; - - // Change outer_radius to the true outer radius after taking into account the needed - // repeats. - outer_radius = (inner_repeat + outer_repeat) * gradient_length; - - float stop_scale = 1.0f / (inner_repeat + outer_repeat); - - float inner_position = (inner_repeat * gradient_length) / outer_radius; - if (reflect) { - // We start out reflected (meaning reflected starts as false since it will - // immediately be inverted) if the inner_repeats are uneven. - reflected = !(inner_repeat & 0x1); - - for (int i = 0; i < num_stops; i++) { - if (!(i % source_pattern->base.n_stops)) { - // Reflect here - reflected = !reflected; - } - // Calculate the repeat count. - int repeat = i / source_pattern->base.n_stops; - // Take the stop that we're using in the pattern. - int stop = i % source_pattern->base.n_stops; - if (reflected) { - // Take the stop from the opposite side when reflected. - stop = source_pattern->base.n_stops - stop - 1; - // When reflected take 1 - offset as the offset. - stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale); - } else { - stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale); - } - stops[i].color = - _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color); - } - } else { - // Simple case, we don't need to reflect. - for (int i = 0; i < num_stops; i++) { - // Calculate which repeat this is. - int repeat = i / source_pattern->base.n_stops; - // Calculate which stop this would be in the original pattern - cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops]; - stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale); - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - } - } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) { - float offset_factor = (outer_radius - inner_radius) / outer_radius; - float global_offset = inner_radius / outer_radius; - - stops = new D2D1_GRADIENT_STOP[num_stops]; - - // If the inner radius is not 0 we need to scale and offset the stops. - for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) { - cairo_gradient_stop_t *stop = &source_pattern->base.stops[i]; - stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor); - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) { - float offset_factor = (outer_radius - inner_radius) / outer_radius; - float global_offset = inner_radius / outer_radius; - - num_stops++; // Add a stop on the outer radius. - if (inner_radius != 0) { - num_stops++; // Add a stop on the inner radius. - } - - stops = new D2D1_GRADIENT_STOP[num_stops]; - - // If the inner radius is not 0 we need to scale and offset the stops and put a stop before the inner_radius - // of a transparent color. - int i = 0; - if (inner_radius != 0) { - stops[i].position = global_offset; - stops[i].color = D2D1::ColorF(0, 0); - i++; - } - for (unsigned int j = 0; j < source_pattern->base.n_stops; j++, i++) { - cairo_gradient_stop_t *stop = &source_pattern->base.stops[j]; - stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor); - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - stops[i].position = 1.0f; - stops[i].color = D2D1::ColorF(0, 0); - } else { - return NULL; - } - - RefPtr stopCollection; - d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection); - RefPtr brush; - - d2dsurf->rt->CreateRadialGradientBrush(D2D1::RadialGradientBrushProperties(center, - origin, - outer_radius, - outer_radius), - brushProps, - stopCollection, - &brush); - delete [] stops; - return brush; -} - -static RefPtr -_cairo_d2d_create_linear_gradient_brush(cairo_d2d_surface_t *d2dsurf, - cairo_path_fixed_t *fill_path, - cairo_linear_pattern_t *source_pattern) -{ - if (source_pattern->p1.x == source_pattern->p2.x && - source_pattern->p1.y == source_pattern->p2.y) { - // Cairo behavior in this situation is to draw a solid color the size of the last stop. - RefPtr brush; - d2dsurf->rt->CreateSolidColorBrush( - _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[source_pattern->base.n_stops - 1].color), - &brush); - return brush; - } - - cairo_matrix_t inv_mat = source_pattern->base.base.matrix; - /** - * Cairo views this matrix as the transformation of the destination - * when the pattern is imposed. We see this differently, D2D transformation - * moves the pattern over the destination. - */ - if (_cairo_matrix_is_invertible(&inv_mat)) { - /* If this is not invertible it will be rank zero, and invMat = mat is fine */ - cairo_matrix_invert(&inv_mat); - } - D2D1_BRUSH_PROPERTIES brushProps = - D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat)); - cairo_point_double_t p1, p2; - p1.x = _cairo_fixed_to_float(source_pattern->p1.x); - p1.y = _cairo_fixed_to_float(source_pattern->p1.y); - p2.x = _cairo_fixed_to_float(source_pattern->p2.x); - p2.y = _cairo_fixed_to_float(source_pattern->p2.y); - - D2D1_GRADIENT_STOP *stops; - int num_stops = source_pattern->base.n_stops; - if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - // Get this when the points are not transformed yet. - double gradient_length = _cairo_d2d_point_dist(p1, p2); - cairo_point_double_t top_left, top_right, bottom_left, bottom_right; - - if (fill_path) { - // Calculate the repeat count needed; - cairo_box_t fill_extents; - _cairo_path_fixed_extents (fill_path, &fill_extents); - - top_left.x = bottom_left.x = _cairo_fixed_to_double (fill_extents.p1.x); - top_left.y = top_right.y = _cairo_fixed_to_double (fill_extents.p1.y); - top_right.x = bottom_right.x = _cairo_fixed_to_double (fill_extents.p2.x); - bottom_right.y = bottom_left.y = _cairo_fixed_to_double (fill_extents.p2.y); - } else { - RefPtr surf; - d2dsurf->surface->QueryInterface(&surf); - DXGI_SURFACE_DESC desc; - surf->GetDesc(&desc); - - top_left.x = bottom_left.x = 0; - top_left.y = top_right.y = 0; - top_right.x = bottom_right.x = desc.Width; - bottom_right.y = bottom_left.y = desc.Height; - } - - // Transform the corners of our surface to pattern space. - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y); - cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y); - - cairo_point_double_t u; - // Unit vector of the gradient direction. - u = _cairo_d2d_subtract_point(p2, p1); - _cairo_d2d_normalize_point(&u); - - // (corner - p1) . u = |corner - p1| cos(a) where a is the angle between the two vectors. - // Coincidentally |corner - p1| cos(a) is actually also the distance our gradient needs to cover since - // at this point on the gradient line it will be perpendicular to the line running from the gradient - // line through the corner. - - double max_dist, min_dist; - max_dist = MAX(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), - _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); - max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); - max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); - min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), - _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); - min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); - min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); - - min_dist = MAX(-min_dist, 0); - - // Repeats after gradient start. - // It's possible for max_dist and min_dist to both be zero, in which case - // we'll set num_stops to 0 and crash D2D. Let's just ensure after_repeat - // is at least 1. - int after_repeat = MAX((int)ceil(max_dist / gradient_length), 1); - int before_repeat = (int)ceil(min_dist / gradient_length); - num_stops *= (after_repeat + before_repeat); - - p2.x = p1.x + u.x * after_repeat * gradient_length; - p2.y = p1.y + u.y * after_repeat * gradient_length; - p1.x = p1.x - u.x * before_repeat * gradient_length; - p1.y = p1.y - u.y * before_repeat * gradient_length; - - float stop_scale = 1.0f / (float)(after_repeat + before_repeat); - float begin_position = (float)before_repeat / (float)(after_repeat + before_repeat); - - stops = new D2D1_GRADIENT_STOP[num_stops]; - if (source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - // We start out reflected (meaning reflected starts as false since it will - // immediately be inverted) if the inner_repeats are uneven. - bool reflected = !(before_repeat & 0x1); - - for (int i = 0; i < num_stops; i++) { - if (!(i % source_pattern->base.n_stops)) { - // Reflect here - reflected = !reflected; - } - // Calculate the repeat count. - int repeat = i / source_pattern->base.n_stops; - // Take the stop that we're using in the pattern. - int stop = i % source_pattern->base.n_stops; - if (reflected) { - // Take the stop from the opposite side when reflected. - stop = source_pattern->base.n_stops - stop - 1; - // When reflected take 1 - offset as the offset. - stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale); - } else { - stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale); - } - stops[i].color = - _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color); - } - } else { - // Simple case, we don't need to reflect. - for (int i = 0; i < num_stops; i++) { - // Calculate which repeat this is. - int repeat = i / source_pattern->base.n_stops; - // Calculate which stop this would be in the original pattern - cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops]; - stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale); - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - } - } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) { - stops = new D2D1_GRADIENT_STOP[source_pattern->base.n_stops]; - for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) { - cairo_gradient_stop_t *stop = &source_pattern->base.stops[i]; - stops[i].position = (FLOAT)stop->offset; - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) { - num_stops += 2; - stops = new D2D1_GRADIENT_STOP[num_stops]; - stops[0].position = 0; - stops[0].color = D2D1::ColorF(0, 0); - for (unsigned int i = 1; i < source_pattern->base.n_stops + 1; i++) { - cairo_gradient_stop_t *stop = &source_pattern->base.stops[i - 1]; - stops[i].position = (FLOAT)stop->offset; - stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); - } - stops[source_pattern->base.n_stops + 1].position = 1.0f; - stops[source_pattern->base.n_stops + 1].color = D2D1::ColorF(0, 0); - } - RefPtr stopCollection; - d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection); - RefPtr brush; - d2dsurf->rt->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2D1::Point2F((FLOAT)p1.x, (FLOAT)p1.y), - D2D1::Point2F((FLOAT)p2.x, (FLOAT)p2.y)), - brushProps, - stopCollection, - &brush); - delete [] stops; - return brush; -} - -/** - * This creates an ID2D1Brush that will fill with the correct pattern. - * This function passes a -strong- reference to the caller, the brush - * needs to be released, even if it is not unique. - * - * \param d2dsurf Surface to create a brush for - * \param pattern The pattern to create a brush for - * \param unique We cache the bitmap/color brush for speed. If this - * needs a brush that is unique (i.e. when more than one is needed), - * this will make the function return a seperate brush. - * \return A brush object - */ -static RefPtr -_cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, - cairo_path_fixed_t *fill_path, - const cairo_pattern_t *pattern, - bool unique = false) -{ - HRESULT hr; - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *sourcePattern = - (cairo_solid_pattern_t*)pattern; - D2D1_COLOR_F color = _cairo_d2d_color_from_cairo_color(sourcePattern->color); - if (unique) { - RefPtr brush; - d2dsurf->rt->CreateSolidColorBrush(color, - &brush); - return brush; - } else { - if (d2dsurf->solidColorBrush->GetColor().a != color.a || - d2dsurf->solidColorBrush->GetColor().r != color.r || - d2dsurf->solidColorBrush->GetColor().g != color.g || - d2dsurf->solidColorBrush->GetColor().b != color.b) { - d2dsurf->solidColorBrush->SetColor(color); - } - return d2dsurf->solidColorBrush; - } - - } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *source_pattern = - (cairo_linear_pattern_t*)pattern; - return _cairo_d2d_create_linear_gradient_brush(d2dsurf, fill_path, source_pattern); - } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - cairo_radial_pattern_t *source_pattern = - (cairo_radial_pattern_t*)pattern; - return _cairo_d2d_create_radial_gradient_brush(d2dsurf, source_pattern); - } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_matrix_t mat = pattern->matrix; - cairo_matrix_invert(&mat); - - cairo_surface_pattern_t *surfacePattern = - (cairo_surface_pattern_t*)pattern; - D2D1_EXTEND_MODE extendMode; - - cairo_user_data_key_t *key = &bitmap_key_extend; - - if (pattern->extend == CAIRO_EXTEND_NONE) { - extendMode = D2D1_EXTEND_MODE_CLAMP; - key = &bitmap_key_nonextend; - /** - * We create a slightly larger bitmap with a transparent border - * around it for this case. Need to translate for that. - */ - cairo_matrix_translate(&mat, -1.0, -1.0); - } else if (pattern->extend == CAIRO_EXTEND_REPEAT) { - extendMode = D2D1_EXTEND_MODE_WRAP; - } else if (pattern->extend == CAIRO_EXTEND_REFLECT) { - extendMode = D2D1_EXTEND_MODE_MIRROR; - } else { - extendMode = D2D1_EXTEND_MODE_CLAMP; - } - - RefPtr sourceBitmap; - bool partial = false; - int xoffset = 0; - int yoffset = 0; - unsigned int width; - unsigned int height; - unsigned char *data = NULL; - unsigned int stride = 0; - - if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_D2D) { - /** - * \todo We need to somehow get a rectangular transparent - * border here too!! - */ - cairo_d2d_surface_t *srcSurf = - reinterpret_cast(surfacePattern->surface); - - if (srcSurf == d2dsurf) { - /* D2D cannot deal with self-copy. We should add an optimized - * codepath for self-copy in the easy cases that does ping-pong like - * scroll does. See bug 579215. For now fallback. - */ - return NULL; - } - if (srcSurf->device != d2dsurf->device) { - /* This code does not work if the source surface does not use - * the same device. Some work could be done to do something - * fairly efficient here, for now, fallback. - */ - return NULL; - } - - _cairo_d2d_update_surface_bitmap(srcSurf); - _cairo_d2d_flush(srcSurf); - - // Mark a dependency on the source surface. - _cairo_d2d_add_dependent_surface(srcSurf, d2dsurf); - - if (pattern->extend == CAIRO_EXTEND_NONE) { - ID2D1Bitmap *srcSurfBitmap = srcSurf->surfaceBitmap; - d2dsurf->rt->CreateBitmap( - D2D1::SizeU(srcSurfBitmap->GetPixelSize().width + 2, - srcSurfBitmap->GetPixelSize().height + 2), - D2D1::BitmapProperties(srcSurfBitmap->GetPixelFormat()), - &sourceBitmap); - D2D1_POINT_2U point = D2D1::Point2U(1, 1); - sourceBitmap->CopyFromBitmap(&point, srcSurfBitmap, NULL); - } else { - sourceBitmap = srcSurf->surfaceBitmap; - } - - } else if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { - cairo_image_surface_t *srcSurf = - reinterpret_cast(surfacePattern->surface); - D2D1_ALPHA_MODE alpha; - if (srcSurf->format == CAIRO_FORMAT_ARGB32 || - srcSurf->format == CAIRO_FORMAT_A8) { - alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; - } else { - alpha = D2D1_ALPHA_MODE_IGNORE; - } - - data = srcSurf->data; - stride = srcSurf->stride; - - /* This is used as a temporary surface for resampling surfaces larget than maxSize. */ - pixman_image_t *pix_image = NULL; - - DXGI_FORMAT format; - unsigned int Bpp; - if (srcSurf->format == CAIRO_FORMAT_ARGB32) { - format = DXGI_FORMAT_B8G8R8A8_UNORM; - Bpp = 4; - } else if (srcSurf->format == CAIRO_FORMAT_RGB24) { - format = DXGI_FORMAT_B8G8R8A8_UNORM; - Bpp = 4; - } else if (srcSurf->format == CAIRO_FORMAT_A8) { - format = DXGI_FORMAT_A8_UNORM; - Bpp = 1; - } else { - return NULL; - } - - /** Leave room for extend_none space, 2 pixels */ - UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2; - - if ((UINT32)srcSurf->width > maxSize || (UINT32)srcSurf->height > maxSize) { - if (pattern->extend == CAIRO_EXTEND_REPEAT || - pattern->extend == CAIRO_EXTEND_REFLECT) { - // XXX - we don't have code to deal with these yet. - return NULL; - } - - /* We cannot fit this image directly into a texture, start doing tricks to draw correctly anyway. */ - partial = true; - - /* First we check which part of the image is inside the viewable area. */ - _cairo_d2d_calculate_visible_rect(d2dsurf, srcSurf, &mat, &xoffset, &yoffset, &width, &height); - - cairo_matrix_translate(&mat, xoffset, yoffset); - - if (width > maxSize || height > maxSize) { - /* - * We cannot upload the required part of the surface directly, we're going to create - * a version which is downsampled to a smaller size by pixman and then uploaded. - * - * We need to size it to at least the diagonal size of this surface, in order to prevent ever - * upsampling this again when drawing it to the surface. We want the resized surface - * to be as small as possible to limit pixman required fill rate. - * - * Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and - * a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate - * this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination, - * the composition will require all 10 original columns to do the best possible sampling. - */ - RefPtr surf; - d2dsurf->surface->QueryInterface(&surf); - DXGI_SURFACE_DESC desc; - surf->GetDesc(&desc); - - unsigned int minSize = (unsigned int)ceil(sqrt(pow((float)desc.Width, 2) + pow((float)desc.Height, 2))); - - unsigned int newWidth = MIN(minSize, MIN(width, maxSize)); - unsigned int newHeight = MIN(minSize, MIN(height, maxSize)); - double xRatio = (double)width / newWidth; - double yRatio = (double)height / newHeight; - - if (newWidth > maxSize || newHeight > maxSize) { - /* - * Okay, the diagonal of our surface is big enough to require a sampling larger - * than the maximum texture size. This is where we give up. - */ - return NULL; - } - - /* Create a temporary surface to hold the downsampled image */ - pix_image = pixman_image_create_bits(srcSurf->pixman_format, - newWidth, - newHeight, - NULL, - -1); - - /* Set the transformation to downsample and call pixman_image_composite to downsample */ - pixman_transform_t transform; - pixman_transform_init_scale(&transform, pixman_double_to_fixed(xRatio), pixman_double_to_fixed(yRatio)); - pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(xoffset), pixman_int_to_fixed(yoffset)); - - pixman_image_set_transform(srcSurf->pixman_image, &transform); - pixman_image_composite(PIXMAN_OP_SRC, srcSurf->pixman_image, NULL, pix_image, 0, 0, 0, 0, 0, 0, newWidth, newHeight); - - /* Adjust the pattern transform to the used temporary surface */ - cairo_matrix_scale(&mat, xRatio, yRatio); - - data = (unsigned char*)pixman_image_get_data(pix_image); - stride = pixman_image_get_stride(pix_image); - - /* Into this image we actually have no offset */ - xoffset = 0; - yoffset = 0; - width = newWidth; - height = newHeight; - } - } else { - width = srcSurf->width; - height = srcSurf->height; - } - - cached_bitmap *cachebitmap = NULL; - - if (!partial) { - cachebitmap = - (cached_bitmap*)cairo_surface_get_user_data( - surfacePattern->surface, - key); - if (cachebitmap && cachebitmap->device != d2dsurf->device) { - cachebitmap = NULL; - } - } - - if (cachebitmap) { - sourceBitmap = cachebitmap->bitmap; - if (cachebitmap->dirty) { - D2D1_RECT_U rect; - /* No need to take partial uploading into account - partially uploaded surfaces are never cached. */ - if (pattern->extend == CAIRO_EXTEND_NONE) { - rect = D2D1::RectU(1, 1, srcSurf->width + 1, srcSurf->height + 1); - } else { - rect = D2D1::RectU(0, 0, srcSurf->width, srcSurf->height); - } - sourceBitmap->CopyFromMemory(&rect, - srcSurf->data, - srcSurf->stride); - cairo_surface_t *nullSurf = - cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA); - cachebitmap->refs++; - cachebitmap->dirty = false; - cairo_surface_set_user_data(nullSurf, - &bitmap_key_snapshot, - cachebitmap, - NULL); - cairo_surface_attach_snapshot(surfacePattern->surface, - nullSurf, - _d2d_snapshot_detached); - } - } else { - if (pattern->extend != CAIRO_EXTEND_NONE) { - hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(width, height), - data + yoffset * stride + xoffset * Bpp, - stride, - D2D1::BitmapProperties(D2D1::PixelFormat(format, - alpha)), - &sourceBitmap); - - if (FAILED(hr)) { - return NULL; - } - } else { - /** - * Trick here, we create a temporary rectangular - * surface with 1 pixel margin on each side. This - * provides a rectangular transparent border, that - * will ensure CLAMP acts as EXTEND_NONE. Perhaps - * this could be further optimized by not memsetting - * the whole array. - */ - unsigned int tmpWidth = width + 2; - unsigned int tmpHeight = height + 2; - unsigned char *tmp = new unsigned char[tmpWidth * tmpHeight * Bpp]; - memset(tmp, 0, tmpWidth * tmpHeight * Bpp); - for (unsigned int y = 0; y < height; y++) { - memcpy( - tmp + tmpWidth * Bpp * y + tmpWidth * Bpp + Bpp, - data + yoffset * stride + y * stride + xoffset * Bpp, - width * Bpp); - } - - hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(tmpWidth, tmpHeight), - tmp, - tmpWidth * Bpp, - D2D1::BitmapProperties(D2D1::PixelFormat(format, - D2D1_ALPHA_MODE_PREMULTIPLIED)), - &sourceBitmap); - - delete [] tmp; - if (FAILED(hr)) { - return NULL; - } - } - - if (!partial) { - cached_bitmap *cachebitmap = new cached_bitmap; - /* We can cache it if it isn't a partial bitmap */ - cachebitmap->dirty = false; - cachebitmap->bitmap = sourceBitmap; - cachebitmap->device = d2dsurf->device; - /* - * This will start out with two references, one on the snapshot - * and one more in the user data structure. - */ - cachebitmap->refs = 2; - cairo_surface_set_user_data(surfacePattern->surface, - key, - cachebitmap, - _d2d_release_bitmap); - cairo_surface_t *nullSurf = - cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA); - cairo_surface_set_user_data(nullSurf, - &bitmap_key_snapshot, - cachebitmap, - NULL); - cairo_surface_attach_snapshot(surfacePattern->surface, - nullSurf, - _d2d_snapshot_detached); - cache_usage += _d2d_compute_bitmap_mem_size(sourceBitmap); - } - if (pix_image) { - pixman_image_unref(pix_image); - } - } - } else { - return NULL; - } - D2D1_BITMAP_BRUSH_PROPERTIES bitProps; - - if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) { - bitProps = D2D1::BitmapBrushProperties(extendMode, - extendMode, - D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); - } else { - bitProps = D2D1::BitmapBrushProperties(extendMode, - extendMode, - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR); - } - if (unique) { - RefPtr bitBrush; - D2D1_BRUSH_PROPERTIES brushProps = - D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat)); - d2dsurf->rt->CreateBitmapBrush(sourceBitmap, - &bitProps, - &brushProps, - &bitBrush); - return bitBrush; - } else { - D2D1_MATRIX_3X2_F matrix = _cairo_d2d_matrix_from_matrix(&mat); - - if (d2dsurf->bitmapBrush) { - d2dsurf->bitmapBrush->SetTransform(matrix); - - if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) { - d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); - } else { - d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR); - } - - d2dsurf->bitmapBrush->SetBitmap(sourceBitmap); - d2dsurf->bitmapBrush->SetExtendModeX(extendMode); - d2dsurf->bitmapBrush->SetExtendModeY(extendMode); - } else { - D2D1_BRUSH_PROPERTIES brushProps = - D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat)); - d2dsurf->rt->CreateBitmapBrush(sourceBitmap, - &bitProps, - &brushProps, - &d2dsurf->bitmapBrush); - } - return d2dsurf->bitmapBrush; - } - } else { - return NULL; - } -} - - -/** Path Conversion */ - -/** - * Structure to use for the closure, containing all needed data. - */ -struct path_conversion { - /** Geometry sink that we need to write to */ - ID2D1GeometrySink *sink; - /** - * If this figure is active, cairo doesn't always send us a close. But - * we do need to end this figure if it didn't. - */ - bool figureActive; - /** - * Current point, D2D has no explicit move so we need to track moved for - * the next begin. - */ - cairo_point_t current_point; - /** The type of figure begin for this geometry instance */ - D2D1_FIGURE_BEGIN type; -}; - -static cairo_status_t -_cairo_d2d_path_move_to(void *closure, - const cairo_point_t *point) -{ - path_conversion *pathConvert = - static_cast(closure); - if (pathConvert->figureActive) { - pathConvert->sink->EndFigure(D2D1_FIGURE_END_OPEN); - pathConvert->figureActive = false; - } - - pathConvert->current_point = *point; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_d2d_path_line_to(void *closure, - const cairo_point_t *point) -{ - path_conversion *pathConvert = - static_cast(closure); - if (!pathConvert->figureActive) { - pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), - pathConvert->type); - pathConvert->figureActive = true; - } - - D2D1_POINT_2F d2dpoint = _d2d_point_from_cairo_point(point); - - pathConvert->sink->AddLine(d2dpoint); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_d2d_path_curve_to(void *closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - path_conversion *pathConvert = - static_cast(closure); - if (!pathConvert->figureActive) { - pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), - D2D1_FIGURE_BEGIN_FILLED); - pathConvert->figureActive = true; - } - - pathConvert->sink->AddBezier(D2D1::BezierSegment(_d2d_point_from_cairo_point(p0), - _d2d_point_from_cairo_point(p1), - _d2d_point_from_cairo_point(p2))); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_d2d_path_close(void *closure) -{ - path_conversion *pathConvert = - static_cast(closure); - - if (!pathConvert->figureActive) { - pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), - pathConvert->type); - /** - * In this case we mean a single point. For D2D this means we need to add an infinitely - * small line here to get that effect. - */ - pathConvert->sink->AddLine(_d2d_point_from_cairo_point(&pathConvert->current_point)); - } - - pathConvert->sink->EndFigure(D2D1_FIGURE_END_CLOSED); - pathConvert->figureActive = false; - return CAIRO_STATUS_SUCCESS; -} - -/** - * Create an ID2D1PathGeometry for a cairo_path_fixed_t - * - * \param path Path to create a geometry for - * \param fill_rule Fill rule to use - * \param type Figure begin type to use - * \return A D2D geometry - */ -static RefPtr -_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - D2D1_FIGURE_BEGIN type) -{ - RefPtr d2dpath; - sD2DFactory->CreatePathGeometry(&d2dpath); - RefPtr sink; - d2dpath->Open(&sink); - D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING; - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - fillMode = D2D1_FILL_MODE_WINDING; - } else if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD) { - fillMode = D2D1_FILL_MODE_ALTERNATE; - } - sink->SetFillMode(fillMode); - - path_conversion pathConvert; - pathConvert.type = type; - pathConvert.sink = sink; - pathConvert.figureActive = false; - _cairo_path_fixed_interpret(path, - CAIRO_DIRECTION_FORWARD, - _cairo_d2d_path_move_to, - _cairo_d2d_path_line_to, - _cairo_d2d_path_curve_to, - _cairo_d2d_path_close, - &pathConvert); - if (pathConvert.figureActive) { - sink->EndFigure(D2D1_FIGURE_END_OPEN); - } - sink->Close(); - return d2dpath; -} - -static cairo_bool_t -clip_contains_only_boxes (cairo_clip_t *clip) -{ - cairo_bool_t is_boxes = TRUE; - - if (clip) { - cairo_box_t clip_box; - cairo_clip_path_t *path = clip->path; - - while (path) { - is_boxes &= _cairo_path_fixed_is_box(&path->path, &clip_box); - path = path->prev; - } - } - return is_boxes; -} - -static cairo_int_status_t -_cairo_d2d_clear_box (cairo_d2d_surface_t *d2dsurf, - cairo_clip_t *clip, - cairo_box_t *box) -{ - if (clip_contains_only_boxes (clip)) { - /* clear the box using axis aligned clips */ - d2dsurf->rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(box->p1.x), - _cairo_fixed_to_float(box->p1.y), - _cairo_fixed_to_float(box->p2.x), - _cairo_fixed_to_float(box->p2.y)), - D2D1_ANTIALIAS_MODE_ALIASED); - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - d2dsurf->rt->PopAxisAlignedClip(); - - return CAIRO_INT_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf, - cairo_clip_t *clip) -{ - cairo_region_t *region; - cairo_int_status_t status; - - if (!clip) { - /* no clip so clear everything */ - _begin_draw_state(d2dsurf); - reset_clip(d2dsurf); - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - - return CAIRO_INT_STATUS_SUCCESS; - } - - status = _cairo_clip_get_region (clip, ®ion); - if (status) - return status; - - /* We now have a region, we'll clear it one rectangle at a time */ - _begin_draw_state(d2dsurf); - - reset_clip(d2dsurf); - - if (region) { - int num_rects; - int i; - - num_rects = cairo_region_num_rectangles (region); - - for (i = 0; i < num_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - d2dsurf->rt->PushAxisAlignedClip( - D2D1::RectF((FLOAT)rect.x, - (FLOAT)rect.y, - (FLOAT)rect.x + rect.width, - (FLOAT)rect.y + rect.height), - D2D1_ANTIALIAS_MODE_ALIASED); - - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - - d2dsurf->rt->PopAxisAlignedClip(); - } - - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, - const cairo_pattern_t *source) -{ - if (op == CAIRO_OPERATOR_SOURCE) { - /** Operator over is easier for D2D! If the source if opaque, change */ - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surfpattern = - reinterpret_cast(source); - if (surfpattern->surface->content == CAIRO_CONTENT_COLOR) { - return CAIRO_OPERATOR_OVER; - } - } else if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - const cairo_solid_pattern_t *solidpattern = - reinterpret_cast(source); - if (solidpattern->color.alpha == 1.0) { - return CAIRO_OPERATOR_OVER; - } - } - } - return op; -} - -void -_cairo_d2d_surface_init(cairo_d2d_surface_t *newSurf, cairo_d2d_device_t *d2d_device, cairo_format_t format) -{ - newSurf->format = format; - - newSurf->device = d2d_device; - cairo_addref_device(&d2d_device->base); - d2d_device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(newSurf); -} - -_cairo_d2d_surface::~_cairo_d2d_surface() -{ - _cairo_d2d_surface_entry *entry, *next; - cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &dependent_surfaces, link) { - // We do not need to flush, the contents of our texture has not changed, - // our users have their own reference and can just use it later. - cairo_surface_destroy(&entry->surface->base); - delete entry; - } - -} - -// Implementation -static cairo_surface_t* -_cairo_d2d_create_similar(void *surface, - cairo_content_t content, - int width, - int height) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); - - new (newSurf) cairo_d2d_surface_t(); - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); - - - D2D1_SIZE_U sizePixels; - D2D1_SIZE_F size; - HRESULT hr; - - sizePixels.width = width; - sizePixels.height = height; - FLOAT dpiX; - FLOAT dpiY; - - d2dsurf->rt->GetDpi(&dpiX, &dpiY); - - D2D1_ALPHA_MODE alpha; - - if (content == CAIRO_CONTENT_COLOR) { - alpha = D2D1_ALPHA_MODE_IGNORE; - } else { - alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; - } - - size.width = sizePixels.width * dpiX; - size.height = sizePixels.height * dpiY; - D2D1_BITMAP_PROPERTIES bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha)); - - if (sizePixels.width < 1) { - sizePixels.width = 1; - } - if (sizePixels.height < 1) { - sizePixels.height = 1; - } - RefPtr oldDxgiSurface; - d2dsurf->surface->QueryInterface(&oldDxgiSurface); - DXGI_SURFACE_DESC origDesc; - - oldDxgiSurface->GetDesc(&origDesc); - - CD3D10_TEXTURE2D_DESC desc(origDesc.Format, - sizePixels.width, - sizePixels.height); - - if (content == CAIRO_CONTENT_ALPHA) { - desc.Format = DXGI_FORMAT_A8_UNORM; - } - - desc.MipLevels = 1; - desc.Usage = D3D10_USAGE_DEFAULT; - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */ - if (desc.Format != DXGI_FORMAT_A8_UNORM) - desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; - - RefPtr texture; - RefPtr dxgiSurface; - - D2D1_RENDER_TARGET_USAGE usage = (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) ? - D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE - : D2D1_RENDER_TARGET_USAGE_NONE; - - hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); - if (FAILED(hr)) { - goto FAIL_CREATESIMILAR; - } - - newSurf->surface = texture; - - // Create the DXGI surface. - hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); - if (FAILED(hr)) { - goto FAIL_CREATESIMILAR; - } - - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha), - dpiX, - dpiY, - usage), - &newSurf->rt); - - if (FAILED(hr)) { - goto FAIL_CREATESIMILAR; - } - - if (desc.Format != DXGI_FORMAT_A8_UNORM) { - /* For some reason creation of shared bitmaps for A8 UNORM surfaces - * doesn't work even though the documentation suggests it does. The - * function will return an error if we try */ - hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, - dxgiSurface, - &bitProps, - &newSurf->surfaceBitmap); - if (FAILED(hr)) { - goto FAIL_CREATESIMILAR; - } - } - - newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); - - _d2d_clear_surface(newSurf); - - _cairo_d2d_surface_init(newSurf, d2dsurf->device, _cairo_format_from_content(content)); - - return reinterpret_cast(newSurf); - -FAIL_CREATESIMILAR: - /** Ensure we call our surfaces desctructor */ - newSurf->~cairo_d2d_surface_t(); - free(newSurf); - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); -} - -static cairo_status_t -_cairo_d2d_finish(void *surface) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - - d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf); - if (d2dsurf->bufferTexture) { - d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf); - } - - reset_clip(d2dsurf); - - // We need to release the device after calling the constructor, since the - // device destruction may release the D3D/D2D libraries. - cairo_device_t *device = &d2dsurf->device->base; - d2dsurf->~cairo_d2d_surface_t(); - cairo_release_device(device); - return CAIRO_STATUS_SUCCESS; -} - -/* The input types for src and dst don't match because in our particular use case, copying from a texture, - * those types don't match. */ -static void -_copy_data_to_different_stride(unsigned char *dst, int dst_stride, void *src, UINT src_stride, int height) -{ - - unsigned char *src_p = (unsigned char *)src; - int min_stride = MIN(dst_stride, src_stride); - while (height) { - memcpy(dst, src_p, min_stride); - height--; - dst += dst_stride; - src_p += src_stride; - } -} - -static cairo_status_t -_cairo_d2d_acquire_source_image(void *abstract_surface, - cairo_image_surface_t **image_out_ret, - void **image_extra) -{ - cairo_surface_t *image_out; - cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); - _cairo_d2d_flush(d2dsurf); - - HRESULT hr; - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - - RefPtr softTexture; - - RefPtr dxgiSurface; - d2dsurf->surface->QueryInterface(&dxgiSurface); - DXGI_SURFACE_DESC desc; - - dxgiSurface->GetDesc(&desc); - - CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height); - - /** - * We can't actually map our backing store texture, so we create one in CPU memory, and then - * tell D3D to copy the data from our surface there, readback is expensive, we -never- - * -ever- want this to happen. - */ - softDesc.MipLevels = 1; - softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; - softDesc.Usage = D3D10_USAGE_STAGING; - softDesc.BindFlags = 0; - hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); - if (FAILED(hr)) { - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - } - - d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); - - D3D10_MAPPED_TEXTURE2D data; - hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); - if (FAILED(hr)) { - return _cairo_error(CAIRO_STATUS_NO_DEVICE); - } - - if (_cairo_valid_stride_alignment(data.RowPitch)) { - image_out = cairo_image_surface_create_for_data((unsigned char*)data.pData, - d2dsurf->format, - size.width, - size.height, - data.RowPitch); - } else { - /* Slow path used when the stride doesn't match our requirements. - * This is possible on at least the Intel driver 8.15.10.2302. - * - * Create a new image surface and copy our data into it */ - image_out = cairo_image_surface_create(d2dsurf->format, - size.width, - size.height); - _copy_data_to_different_stride(cairo_image_surface_get_data(image_out), - cairo_image_surface_get_stride(image_out), - data.pData, - data.RowPitch, - size.height); - - } - /* these are the only surface statuses we expect */ - assert(cairo_surface_status(image_out) == CAIRO_STATUS_SUCCESS || - cairo_surface_status(image_out) == CAIRO_STATUS_NO_MEMORY); - - *image_extra = softTexture.forget().drop(); - *image_out_ret = (cairo_image_surface_t*)image_out; - - return cairo_surface_status(image_out); -} - -static void -_cairo_d2d_release_source_image(void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - if (((cairo_surface_t*)abstract_surface)->type != CAIRO_SURFACE_TYPE_D2D) { - return; - } - cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); - - cairo_surface_destroy(&image->base); - ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra; - - softTexture->Unmap(0); - softTexture->Release(); - softTexture = NULL; -} - -static cairo_status_t -_cairo_d2d_acquire_dest_image(void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); - _cairo_d2d_flush(d2dsurf); - - HRESULT hr; - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - - RefPtr softTexture; - - - RefPtr dxgiSurface; - d2dsurf->surface->QueryInterface(&dxgiSurface); - DXGI_SURFACE_DESC desc; - - dxgiSurface->GetDesc(&desc); - - CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height); - - image_rect->width = desc.Width; - image_rect->height = desc.Height; - image_rect->x = image_rect->y = 0; - - softDesc.MipLevels = 1; - softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; - softDesc.Usage = D3D10_USAGE_STAGING; - softDesc.BindFlags = 0; - hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); - if (FAILED(hr)) { - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - } - d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); - - D3D10_MAPPED_TEXTURE2D data; - hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); - if (FAILED(hr)) { - return _cairo_error(CAIRO_STATUS_NO_DEVICE); - } - *image_out = - (cairo_image_surface_t*)cairo_image_surface_create_for_data((unsigned char*)data.pData, - _cairo_format_from_content(d2dsurf->base.content), - size.width, - size.height, - data.RowPitch); - *image_extra = softTexture.forget().drop(); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_d2d_release_dest_image(void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); - - ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra; - D2D1_POINT_2U point; - point.x = 0; - point.y = 0; - D2D1_RECT_U rect; - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - rect.left = rect.top = 0; - rect.right = size.width; - rect.bottom = size.height; - - cairo_surface_destroy(&image->base); - - softTexture->Unmap(0); - d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture); - softTexture->Release(); -} - - -static cairo_status_t -_cairo_d2d_flush(void *surface) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - - if (d2dsurf->isDrawing) { - reset_clip(d2dsurf); - HRESULT hr = d2dsurf->rt->EndDraw(); - d2dsurf->isDrawing = false; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, - cairo_d2d_surface_t *src, - cairo_point_int_t *translation, - cairo_region_t *region) -{ - RefPtr dstSurface; - dst->surface->QueryInterface(&dstSurface); - RefPtr srcSurface; - src->surface->QueryInterface(&srcSurface); - DXGI_SURFACE_DESC srcDesc, dstDesc; - - srcSurface->GetDesc(&srcDesc); - dstSurface->GetDesc(&dstDesc); - - cairo_rectangle_int_t clip_rect; - clip_rect.x = 0; - clip_rect.y = 0; - clip_rect.width = dstDesc.Width; - clip_rect.height = dstDesc.Height; - - cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; - - _cairo_d2d_flush(dst); - ID3D10Resource *srcResource = src->surface; - if (src->surface.get() == dst->surface.get()) { - // Self-copy - srcResource = _cairo_d2d_get_buffer_texture(dst); - src->device->mD3D10Device->CopyResource(srcResource, src->surface); - } else { - // Need to flush the source too if it's a different surface. - _cairo_d2d_flush(src); - } - - // One copy for each rectangle in the final clipping region. - for (int i = 0; i < cairo_region_num_rectangles(region); i++) { - D3D10_BOX rect; - cairo_rectangle_int_t area_to_copy; - - cairo_region_get_rectangle(region, i, &area_to_copy); - - cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, - area_to_copy.y + translation->y, - area_to_copy.width, area_to_copy.height }; - cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; - - - if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { - /* We cannot do any sort of extend, in the future a little bit of extra code could - * allow us to support EXTEND_NONE. - */ - rv = CAIRO_INT_STATUS_UNSUPPORTED; - break; - } - - rect.front = 0; - rect.back = 1; - rect.left = transformed_rect.x; - rect.top = transformed_rect.y; - rect.right = transformed_rect.x + transformed_rect.width; - rect.bottom = transformed_rect.y + transformed_rect.height; - - src->device->mD3D10Device->CopySubresourceRegion(dst->surface, - 0, - area_to_copy.x, - area_to_copy.y, - 0, - srcResource, - 0, - &rect); - } - - return rv; -} - -static cairo_int_status_t -_cairo_d2d_blend_surface(cairo_d2d_surface_t *dst, - cairo_d2d_surface_t *src, - const cairo_matrix_t *transform, - cairo_box_t *box, - cairo_clip_t *clip, - cairo_filter_t filter, - float opacity) -{ - if (dst == src) { - // We cannot do self-blend. - return CAIRO_INT_STATUS_UNSUPPORTED; - } - cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; - - _begin_draw_state(dst); - _cairo_d2d_set_clip(dst, clip); - _cairo_d2d_flush(src); - D2D1_SIZE_U sourceSize = src->surfaceBitmap->GetPixelSize(); - - - double x1, x2, y1, y2; - if (box) { - _cairo_box_to_doubles(box, &x1, &y1, &x2, &y2); - } else { - x1 = y1 = 0; - x2 = dst->rt->GetSize().width; - y2 = dst->rt->GetSize().height; - } - - if (clip) { - const cairo_rectangle_int_t *clipExtent = _cairo_clip_get_extents(clip); - x1 = MAX(x1, clipExtent->x); - x2 = MIN(x2, clipExtent->x + clipExtent->width); - y1 = MAX(y1, clipExtent->y); - y2 = MIN(y2, clipExtent->y + clipExtent->height); - } - - // We should be in drawing state for this. - _begin_draw_state(dst); - _cairo_d2d_set_clip (dst, clip); - D2D1_RECT_F rectSrc; - rectSrc.left = (float)(x1 * transform->xx + transform->x0); - rectSrc.top = (float)(y1 * transform->yy + transform->y0); - rectSrc.right = (float)(x2 * transform->xx + transform->x0); - rectSrc.bottom = (float)(y2 * transform->yy + transform->y0); - - if (rectSrc.left < 0 || rectSrc.top < 0 || rectSrc.right < 0 || rectSrc.bottom < 0 || - rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height || - rectSrc.left > sourceSize.width || rectSrc.top > sourceSize.height) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - D2D1_RECT_F rectDst; - rectDst.left = (float)x1; - rectDst.top = (float)y1; - rectDst.right = (float)x2; - rectDst.bottom = (float)y2; - - // Bug 599658 - if the src rect is inverted in either axis D2D is fine with - // this but it does not actually invert the bitmap. This is an easy way - // of doing that. - D2D1_MATRIX_3X2_F matrix = D2D1::IdentityMatrix(); - bool needsTransform = false; - if (rectSrc.left > rectSrc.right) { - rectDst.left = -rectDst.left; - rectDst.right = -rectDst.right; - matrix._11 = -1.0; - needsTransform = true; - } - if (rectSrc.top > rectSrc.bottom) { - rectDst.top = -rectDst.top; - rectDst.bottom = -rectDst.bottom; - matrix._22 = -1.0; - needsTransform = true; - } - - _cairo_d2d_add_dependent_surface(src, dst); - - D2D1_BITMAP_INTERPOLATION_MODE interpMode = - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; - - if (needsTransform) { - dst->rt->SetTransform(matrix); - } - - if (filter == CAIRO_FILTER_NEAREST) { - interpMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - } - - dst->rt->DrawBitmap(src->surfaceBitmap, - rectDst, - opacity, - interpMode, - rectSrc); - if (needsTransform) { - dst->rt->SetTransform(D2D1::IdentityMatrix()); - } - - return rv; -} -/** - * This function will text if we can use GPU mem cpy to execute an operation with - * a surface pattern. If box is NULL it will operate on the entire dst surface. - */ -static cairo_int_status_t -_cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst, - cairo_surface_t *src, - cairo_box_t *box, - const cairo_matrix_t *matrix, - cairo_clip_t *clip, - cairo_operator_t op, - cairo_filter_t filter, - float opacity = 1.0f) -{ - if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) { - op = CAIRO_OPERATOR_SOURCE; - } - if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* For now we do only D2D sources */ - if (src->type != CAIRO_SURFACE_TYPE_D2D) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); - if (op == CAIRO_OPERATOR_OVER && matrix->xy == 0 && matrix->yx == 0) { - return _cairo_d2d_blend_surface(dst, d2dsrc, matrix, box, clip, filter, opacity); - } - - if (op == CAIRO_OPERATOR_OVER || opacity != 1.0f) { - // Past this point we will never get into a situation where we can - // support OVER. - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_point_int_t translation; - if ((box && !box_is_integer(box)) || - !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_rectangle_int_t rect; - if (box) { - _cairo_box_round_to_rectangle(box, &rect); - } else { - rect.x = rect.y = 0; - rect.width = dst->rt->GetPixelSize().width; - rect.height = dst->rt->GetPixelSize().height; - } - - if (d2dsrc->device != dst->device) { - // This doesn't work between different devices. - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Region we need to clip this operation to */ - cairo_region_t *clipping_region = NULL; - cairo_region_t *region; - cairo_region_auto_ptr region_ptr; - - if (clip) { - _cairo_clip_get_region(clip, &clipping_region); - - if (!clipping_region) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - region = cairo_region_copy(clipping_region); - region_ptr.set(region); - - cairo_region_intersect_rectangle(region, &rect); - - if (cairo_region_is_empty(region)) { - // Nothing to do. - return CAIRO_INT_STATUS_SUCCESS; - } - } else { - region = cairo_region_create_rectangle(&rect); - region_ptr.set(region); - - // Areas outside of the surface do not matter. - cairo_rectangle_int_t surface_rect = { 0, 0, - dst->rt->GetPixelSize().width, - dst->rt->GetPixelSize().height }; - cairo_region_intersect_rectangle(region, &surface_rect); - } - - cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); - - return rv; -} - -static RefPtr -_cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip) -{ - RefPtr texture = _cairo_d2d_get_buffer_texture(surf); - RefPtr new_rt; - RefPtr dxgiSurface; - texture->QueryInterface(&dxgiSurface); - HRESULT hr; - - _cairo_d2d_flush(surf); - - if (!surf) { - return NULL; - } - - D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, - props, - &new_rt); - - if (FAILED(hr)) { - return NULL; - } - - new_rt->BeginDraw(); - new_rt->Clear(D2D1::ColorF(0, 0)); - - // Since this is a fresh surface there's no point in doing clever things to - // keep the clip path around until a certain depth. So we just do a straight- - // forward push of all clip paths in the tree, similar to what the normal - // clip code does, but a little less clever. - if (clip) { - cairo_clip_path_t *path = clip->path; - while (path) { - cairo_box_t clip_box; - if (_cairo_path_fixed_is_box(&path->path, &clip_box)) { - // If this does not have a region it could be none-pixel aligned. - D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; - if (box_is_integer(&clip_box)) { - aaMode = D2D1_ANTIALIAS_MODE_ALIASED; - } - new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x), - _cairo_fixed_to_float(clip_box.p1.y), - _cairo_fixed_to_float(clip_box.p2.x), - _cairo_fixed_to_float(clip_box.p2.y)), - aaMode); - } else { - HRESULT hr; - RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&path->path, - path->fill_rule, - D2D1_FIGURE_BEGIN_FILLED); - RefPtr layer; - - hr = new_rt->CreateLayer (&layer); - - D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; - - new_rt->PushLayer(D2D1::LayerParameters( - D2D1::InfiniteRect(), - geom, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1::IdentityMatrix(), - 1.0, - 0, - options), - layer); - } - path = path->prev; - } - } - return new_rt; -} - -static cairo_int_status_t -_cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL) -{ - _cairo_d2d_flush_dependent_surfaces(surf); - - int numPaths = 0; - if (clip) { - cairo_clip_path_t *path = clip->path; - while (path) { - numPaths++; - path = path->prev; - } - - cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths]; - - numPaths = 0; - path = clip->path; - while (path) { - paths[numPaths++] = path; - path = path->prev; - } - - for (int i = numPaths - 1; i >= 0; i--) { - if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) { - rt->PopAxisAlignedClip(); - } else { - rt->PopLayer(); - } - } - delete [] paths; - } - rt->EndDraw(); - HRESULT hr; - - RefPtr srcTexture = _cairo_d2d_get_buffer_texture(surf); - RefPtr dstTexture; - - surf->surface->QueryInterface(&dstTexture); - ID3D10Device *device = surf->device->mD3D10Device; - - if (!surf->buffer_rt_view) { - hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (!surf->buffer_sr_view) { - hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - cairo_int_status_t status; - - status = _cairo_d2d_set_operator(surf->device, op); - - if (unlikely(status)) { - return status; - } - - D3D10_TEXTURE2D_DESC tDesc; - dstTexture->GetDesc(&tDesc); - D3D10_VIEWPORT vp; - vp.Height = tDesc.Height; - vp.MinDepth = 0; - vp.MaxDepth = 1.0; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - vp.Width = tDesc.Width; - device->RSSetViewports(1, &vp); - - ID3D10Effect *effect = surf->device->mSampleEffect; - - ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view; - device->OMSetRenderTargets(1, &rtViewPtr, 0); - ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); - ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); - - float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f }; - float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f }; - if (bounds && _cairo_operator_bounded_by_mask(op)) { - quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f; - quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f; - quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f; - quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f; - texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width; - texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height; - texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width; - texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height; - } - quadDesc->SetFloatVector(quadDescVal); - texCoords->SetFloatVector(texCoordsVal); - - _cairo_d2d_setup_for_blend(surf->device); - ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture"); - technique->GetPassByIndex(0)->Apply(0); - - ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view; - device->PSSetShaderResources(0, 1, &srViewPtr); - - device->Draw(4, 0); - -#ifdef DEBUG - // Quiet down some info messages from D3D10 debug layer - srViewPtr = NULL; - device->PSSetShaderResources(0, 1, &srViewPtr); - rtViewPtr = NULL; - device->OMSetRenderTargets(1, &rtViewPtr, 0); -#endif - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_paint(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - cairo_int_status_t status; - - op = _cairo_d2d_simplify_operator(op, source); - - if (op == CAIRO_OPERATOR_SOURCE) { - if (!clip) { - _cairo_d2d_clear(d2dsurf, NULL); - op = CAIRO_OPERATOR_OVER; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (op == CAIRO_OPERATOR_CLEAR) { - return _cairo_d2d_clear(d2dsurf, clip); - } - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surf_pattern = - reinterpret_cast(source); - - status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, - NULL, &source->matrix, clip, - op, source->filter); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - return status; - } - } - RefPtr target_rt = d2dsurf->rt; -#ifndef ALWAYS_MANUAL_COMPOSITE - if (op != CAIRO_OPERATOR_OVER) { -#endif - target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); - if (!target_rt) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } -#ifndef ALWAYS_MANUAL_COMPOSITE - } else { - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - } -#endif - - target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - - RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, - source); - - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - D2D1_SIZE_F size = target_rt->GetSize(); - target_rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - - if (target_rt.get() != d2dsurf->rt.get()) { - return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_mask(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - cairo_rectangle_int_t extents; - - cairo_clip_t *actual_clip = clip; - cairo_clip_t temporary_clip; - - cairo_int_status_t status; - - status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base, - op, source, - mask, - clip, &extents); - if (unlikely (status)) - return status; - - bool isSolidAlphaMask = false; - float solidAlphaValue = 1.0f; - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solidPattern = - (cairo_solid_pattern_t*)mask; - if (_cairo_color_get_content (&solidPattern->color) == CAIRO_CONTENT_ALPHA) { - isSolidAlphaMask = true; - solidAlphaValue = solidPattern->color.alpha; - } - } - - cairo_box_t box; - _cairo_box_from_rectangle(&box, &extents); - - if (clip && isSolidAlphaMask) { - // We do some work here to try and avoid pushing and popping clips for rectangular areas, - // if we do this fill rects will occur without rectangular clips being pushed and popped. - // This is faster for non-axis aligned clips in general and allows more efficient batching - // of the pop-clip calls. - int num_boxes = 1; - cairo_box_t box_stack; - cairo_box_t *boxes; - boxes = &box_stack; - - // This function assumes atleast a single box resides at 'boxes' and the - // amount of boxes that reside there are passed in under num_boxes. - status = _cairo_clip_get_boxes(clip, &boxes, &num_boxes); - - if (!status && num_boxes == 1) { - box.p1.x = MAX(box.p1.x, boxes->p1.x); - box.p2.x = MIN(box.p2.x, boxes->p2.x); - box.p1.y = MAX(box.p1.y, boxes->p1.y); - box.p2.y = MIN(box.p2.y, boxes->p2.y); - - if (clip->path != d2dsurf->clip.path) { - // If we have a clip set, but it's not the right one. We want to - // pop as much as we need to, to be sure the area affected by - // the operation is not clipped. To do this we set the clip path - // to the common ancestor of the currently set clip path and the - // clip path for this operation. This will cause - // _cairo_d2d_set_clip to pop to that common ancestor, but not - // needlessly push the additional clips we're trying to avoid. - temporary_clip.path = find_common_ancestor(clip->path, d2dsurf->clip.path); - - // We're not going to be using this down the line so it doesn't - // really matter what the value is. If all -was- clipped this - // call shouldn't even have reached the surface backend. - temporary_clip.all_clipped = FALSE; - - actual_clip = &temporary_clip; - } - } - - if (boxes != &box_stack) { - // If the function changed the boxes pointer, we need to free it. - free(boxes); - } - } - - if (isSolidAlphaMask) { - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surf_pattern = - reinterpret_cast(source); - cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, - surf_pattern->surface, - &box, - &source->matrix, - clip, - op, - source->filter, - solidAlphaValue); - if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { - return rv; - } - } - } - - RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, source); - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - RefPtr target_rt = d2dsurf->rt; -#ifndef ALWAYS_MANUAL_COMPOSITE - if (op != CAIRO_OPERATOR_OVER) { -#endif - target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); - if (!target_rt) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } -#ifndef ALWAYS_MANUAL_COMPOSITE - } else { - _begin_draw_state(d2dsurf); - - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, actual_clip); - if (unlikely(status)) - return status; - } -#endif - - D2D1_RECT_F rect = D2D1::RectF(_cairo_fixed_to_float(box.p1.x), - _cairo_fixed_to_float(box.p1.y), - _cairo_fixed_to_float(box.p2.x), - _cairo_fixed_to_float(box.p2.y)); - - if (isSolidAlphaMask) { - brush->SetOpacity(solidAlphaValue); - target_rt->FillRectangle(rect, - brush); - brush->SetOpacity(1.0); - - if (target_rt.get() != d2dsurf->rt.get()) { - return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); - } - return CAIRO_INT_STATUS_SUCCESS; - } - - RefPtr opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, mask, true); - if (!opacityBrush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (!d2dsurf->maskLayer) { - d2dsurf->rt->CreateLayer(&d2dsurf->maskLayer); - } - target_rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), - 0, - D2D1_ANTIALIAS_MODE_ALIASED, - D2D1::IdentityMatrix(), - 1.0, - opacityBrush), - d2dsurf->maskLayer); - - target_rt->FillRectangle(rect, - brush); - target_rt->PopLayer(); - - if (target_rt.get() != d2dsurf->rt.get()) { - return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); - } - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_stroke(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_int_status_t status; - - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - - op = _cairo_d2d_simplify_operator(op, source); - - if (op == CAIRO_OPERATOR_SOURCE) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - RefPtr target_rt = d2dsurf->rt; -#ifndef ALWAYS_MANUAL_COMPOSITE - if (op != CAIRO_OPERATOR_OVER) { -#endif - target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); - if (!target_rt) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } -#ifndef ALWAYS_MANUAL_COMPOSITE - } else { - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - } -#endif - - if (antialias == CAIRO_ANTIALIAS_NONE) { - target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - } else { - target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - RefPtr strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style); - - if (!strokeStyle) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, - CAIRO_FILL_RULE_WINDING, - D2D1_FIGURE_BEGIN_FILLED); - - bool transformed = true; - - if (_cairo_matrix_is_identity(ctm)) { - transformed = false; - } - - RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, - source); - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - D2D1::Matrix3x2F mat; - if (transformed) { - // If we are transformed we will draw the geometry multiplied by the - // inverse transformation and apply the transform to our render target. - // This way the transformation will also be applied to the strokestyle. - mat = _cairo_d2d_matrix_from_matrix(ctm); - D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat); - - RefPtr trans_geom; - sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); - - // If we are setting a transform on the render target, we've multiplied - // the geometry by the inverse transform, we should also multiply the - // brush matrix by this inverse transform then to map the brush to the - // correct place. - D2D1_MATRIX_3X2_F brushMatrix; - brush->GetTransform(&brushMatrix); - brushMatrix = brushMatrix * inverse_mat; - brush->SetTransform(brushMatrix); - target_rt->SetTransform(mat); - d2dpath = trans_geom; - } else { - mat = D2D1::Matrix3x2F::Identity(); - } - - target_rt->DrawGeometry(d2dpath, brush, (FLOAT)style->line_width, strokeStyle); - - if (transformed) { - target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); - } - - if (target_rt.get() != d2dsurf->rt.get()) { - D2D1_RECT_F bounds; - d2dpath->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, mat, &bounds); - cairo_rectangle_int_t bound_rect; - _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom); - return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_fill(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_int_status_t status; - - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - cairo_box_t box; - bool is_box = _cairo_path_fixed_is_box(path, &box); - - if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surf_pattern = - reinterpret_cast(source); - cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, - &box, &source->matrix, clip, op, - source->filter); - - if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { - return rv; - } - } - - op = _cairo_d2d_simplify_operator(op, source); - - if (op == CAIRO_OPERATOR_SOURCE) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (op == CAIRO_OPERATOR_CLEAR) { - if (_cairo_path_fixed_is_box(path, &box)) { - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - - return _cairo_d2d_clear_box (d2dsurf, clip, &box); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - RefPtr target_rt = d2dsurf->rt; - -#ifndef ALWAYS_MANUAL_COMPOSITE - if (op != CAIRO_OPERATOR_OVER) { -#endif - target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); - if (!target_rt) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } -#ifndef ALWAYS_MANUAL_COMPOSITE - } else { - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - } -#endif - - if (antialias == CAIRO_ANTIALIAS_NONE) { - target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - } else { - target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - - if (is_box) { - float x1 = _cairo_fixed_to_float(box.p1.x); - float y1 = _cairo_fixed_to_float(box.p1.y); - float x2 = _cairo_fixed_to_float(box.p2.x); - float y2 = _cairo_fixed_to_float(box.p2.y); - RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, - path, source); - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - target_rt->FillRectangle(D2D1::RectF(x1, - y1, - x2, - y2), - brush); - } else { - RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED); - - RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, - path, source); - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - target_rt->FillGeometry(d2dpath, brush); - } - - if (target_rt.get() != d2dsurf->rt.get()) { - double x1, y1, x2, y2; - cairo_box_t box; - _cairo_path_fixed_extents (path, &box); - x1 = _cairo_fixed_to_double (box.p1.x); - y1 = _cairo_fixed_to_double (box.p1.y); - x2 = _cairo_fixed_to_double (box.p2.x); - y2 = _cairo_fixed_to_double (box.p2.y); - cairo_rectangle_int_t bounds; - _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2); - return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface, - cairo_operator_t op, - const cairo_solid_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_dwrite_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); - if (!dwritesf->manual_show_glyphs_allowed) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); - cairo_d2d_surface_t *dst = reinterpret_cast(surface); - - BOOL transform = FALSE; - HRESULT hr; - - cairo_region_t *clip_region = NULL; - - // We can only draw axis and pixel aligned rectangular quads, this means we - // can only support clips which form regions, since the intersection with - // our text area will then always be a set of rectangular axis and pixel - // aligned quads. - if (clip) { - _cairo_clip_get_region(clip, &clip_region); - if (!clip_region) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (!dst->isDrawing) { - _cairo_d2d_flush_dependent_surfaces(dst); - } - - _cairo_d2d_set_clip(dst, NULL); - dst->rt->Flush(); - - AutoDWriteGlyphRun run; - _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, scaled_font, &run, &transform); - - RefPtr analysis; - DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); - - RefPtr params; - dst->rt->GetTextRenderingParams(¶ms); - - DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; - if (params) { - hr = dwriteff->dwriteface->GetRecommendedRenderingMode( - (FLOAT)scaled_font->base.font_matrix.yy, - 1.0f, - DWRITE_MEASURING_MODE_NATURAL, - params, - &renderMode); - if (FAILED(hr)) { - // this probably never happens, but let's play it safe - renderMode = DWRITE_RENDERING_MODE_DEFAULT; - } - } - - // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. - switch (renderMode) { - case DWRITE_RENDERING_MODE_ALIASED: - // ClearType texture creation will fail in this mode, so bail out - return CAIRO_INT_STATUS_UNSUPPORTED; - case DWRITE_RENDERING_MODE_DEFAULT: - // As per DWRITE_RENDERING_MODE documentation, pick Natural for font - // sizes under 16 ppem - if (scaled_font->base.font_matrix.yy < 16.0f) { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; - } else { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; - } - break; - case DWRITE_RENDERING_MODE_OUTLINE: - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; - break; - default: - break; - } - - DWRITE_MEASURING_MODE measureMode = - renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : - renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : - DWRITE_MEASURING_MODE_NATURAL; - - hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, - 1.0f, - transform ? &dwmat : 0, - renderMode, - measureMode, - 0, - 0, - &analysis); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - RECT bounds; - hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, - &bounds); - if (FAILED(hr) || - // with bitmap sizes of asian fonts, GetAlphaTextureBounds returns - // an empty rect, so we need to detect that and fall back - (bounds.top == 0 && bounds.bottom == 0 && num_glyphs > 0)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_rectangle_int_t cairo_bounds = - _cairo_rect_from_windows_rect(&bounds); - - cairo_region_t *region; - if (clip) { - region = cairo_region_copy(clip_region); - - cairo_region_intersect_rectangle(region, &cairo_bounds); - } else { - region = cairo_region_create_rectangle(&cairo_bounds); - } - - cairo_region_auto_ptr region_ptr(region); - - if (cairo_region_is_empty(region)) { - // Nothing to do. - return CAIRO_INT_STATUS_SUCCESS; - } - - int bufferSize = cairo_bounds.width * cairo_bounds.height * 3; - - if (!bufferSize) { - // width == 0 || height == 0 - return CAIRO_INT_STATUS_SUCCESS; - } - - // We add one byte so we can safely read an entire 32-bit int when copying - // the last 3 bytes of the alpha texture. - BYTE *texture = new BYTE[bufferSize + 1]; - hr = analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, - &bounds, texture, bufferSize); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - RefPtr srView; - ID3D10Device1 *device = dst->device->mD3D10Device; - - int textureWidth, textureHeight; - - if (cairo_bounds.width < TEXT_TEXTURE_WIDTH && - cairo_bounds.height < TEXT_TEXTURE_HEIGHT) - { - // Use our cached TextTexture when it is big enough. - RefPtr tmpTexture; - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - cairo_bounds.width, cairo_bounds.height, - 1, 1, 0); - - desc.Usage = D3D10_USAGE_STAGING; - desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; - - hr = device->CreateTexture2D(&desc, NULL, &tmpTexture); - - D3D10_MAPPED_TEXTURE2D texMap; - hr = tmpTexture->Map(0, D3D10_MAP_WRITE, 0, &texMap); - - if (FAILED(hr)) { - delete [] texture; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - BYTE *alignedTextureData = (BYTE*)texMap.pData; - for (int y = 0; y < cairo_bounds.height; y++) { - for (int x = 0; x < cairo_bounds.width; x++) { - // Copy 3 Bpp source to 4 Bpp texture. - // - // Since we don't care what ends up in the alpha pixel of the - // destination, therefor we can simply copy a normal 32 bit - // integer each time, filling the alpha pixel of the destination - // with the first subpixel of the next pixel from the source. - *((int*)(alignedTextureData + (y * texMap.RowPitch) + x * 4)) = - *((int*)(texture + (y * cairo_bounds.width + x) * 3)); - } - } - - tmpTexture->Unmap(0); - - delete [] texture; - - D3D10_BOX box; - box.front = box.top = box.left = 0; - box.back = 1; - box.right = cairo_bounds.width; - box.bottom = cairo_bounds.height; - - device->CopySubresourceRegion(dst->device->mTextTexture, 0, 0, 0, 0, tmpTexture, 0, &box); - - srView = dst->device->mTextTextureView; - - textureWidth = TEXT_TEXTURE_WIDTH; - textureHeight = TEXT_TEXTURE_HEIGHT; - } else { - int alignedBufferSize = cairo_bounds.width * cairo_bounds.height * 4; - - // Create a one-off immutable texture from system memory. - BYTE *alignedTextureData = new BYTE[alignedBufferSize]; - for (int y = 0; y < cairo_bounds.height; y++) { - for (int x = 0; x < cairo_bounds.width; x++) { - // Copy 3 Bpp source to 4 Bpp destination memory used for - // texture creation. D3D10 has no 3 Bpp texture format we can - // use. - // - // Since we don't care what ends up in the alpha pixel of the - // destination, therefor we can simply copy a normal 32 bit - // integer each time, filling the alpha pixel of the destination - // with the first subpixel of the next pixel from the source. - *((int*)(alignedTextureData + (y * cairo_bounds.width + x) * 4)) = - *((int*)(texture + (y * cairo_bounds.width + x) * 3)); - } - } - - D3D10_SUBRESOURCE_DATA data; - - CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, - cairo_bounds.width, cairo_bounds.height, - 1, 1); - desc.Usage = D3D10_USAGE_IMMUTABLE; - - data.SysMemPitch = cairo_bounds.width * 4; - data.pSysMem = alignedTextureData; - - RefPtr tex; - hr = device->CreateTexture2D(&desc, &data, &tex); - - delete [] alignedTextureData; - delete [] texture; - - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - hr = device->CreateShaderResourceView(tex, NULL, &srView); - - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - textureWidth = cairo_bounds.width; - textureHeight = cairo_bounds.height; - } - - // Prepare destination surface for rendering. - RefPtr dstTexture; - - dst->surface->QueryInterface(&dstTexture); - - if (!dst->buffer_rt_view) { - hr = device->CreateRenderTargetView(dstTexture, NULL, &dst->buffer_rt_view); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - D3D10_TEXTURE2D_DESC tDesc; - dstTexture->GetDesc(&tDesc); - D3D10_VIEWPORT vp; - vp.Height = tDesc.Height; - vp.MinDepth = 0; - vp.MaxDepth = 1.0; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - vp.Width = tDesc.Width; - device->RSSetViewports(1, &vp); - - ID3D10Effect *effect = dst->device->mSampleEffect; - - ID3D10RenderTargetView *rtViewPtr = dst->buffer_rt_view; - - device->OMSetRenderTargets(1, &rtViewPtr, 0); - - ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTextTexture"); - - ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); - ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); - ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector(); - - float colorVal[] = { float(source->color.red * source->color.alpha), - float(source->color.green * source->color.alpha), - float(source->color.blue * source->color.alpha), - float(source->color.alpha) }; - textColor->SetFloatVector(colorVal); - - float quadDescVal[4]; - float texCoordsVal[4]; - - // Draw a quad for each rectangle in the intersection of the clip and the - // text area. - for (int i = 0; i < cairo_region_num_rectangles(region); i++) { - cairo_rectangle_int_t quad; - cairo_region_get_rectangle(region, i, &quad); - - quadDescVal[0] = -1.0f + ((float)quad.x / (float)tDesc.Width) * 2.0f; - quadDescVal[1] = 1.0f - ((float)quad.y / (float)tDesc.Height) * 2.0f; - quadDescVal[2] = ((float)quad.width / (float)tDesc.Width) * 2.0f; - quadDescVal[3] = -((float)quad.height / (float)tDesc.Height) * 2.0f; - - texCoordsVal[0] = (float)(quad.x - cairo_bounds.x) / textureWidth; - texCoordsVal[1] = (float)(quad.y - cairo_bounds.y) / textureHeight; - texCoordsVal[2] = (float)quad.width / textureWidth; - texCoordsVal[3] = (float)quad.height / textureHeight; - - quadDesc->SetFloatVector(quadDescVal); - texCoords->SetFloatVector(texCoordsVal); - - _cairo_d2d_setup_for_blend(dst->device); - technique->GetPassByIndex(0)->Apply(0); - - ID3D10ShaderResourceView *srViewPtr = srView; - device->PSSetShaderResources(0, 1, &srViewPtr); - - device->Draw(4, 0); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_int_status_t status; - - // TODO: Check font & surface for types. - cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); - cairo_d2d_surface_t *dst = reinterpret_cast(surface); - - /* We can only handle dwrite fonts */ - //XXX: this is checked by at least one caller - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - op = _cairo_d2d_simplify_operator(op, source); - - /* We cannot handle operator SOURCE or CLEAR */ - if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (op == CAIRO_OPERATOR_OVER && source->type == CAIRO_PATTERN_TYPE_SOLID && - dst->base.content != CAIRO_CONTENT_COLOR && - dst->base.permit_subpixel_antialiasing && - dwritesf->antialias_mode == CAIRO_ANTIALIAS_SUBPIXEL) - { - // The D2D/DWrite drawing API's will not allow drawing subpixel AA to - // an RGBA surface. We do however want to do this if we know all text - // on a surface will be over opaque pixels, when this is the case - // we set the permit_subpixel_antialiasing flag on a surface. We then - // proceed to manually composite the glyphs to the surface. - - const cairo_solid_pattern_t *solid_src = - reinterpret_cast(source); - status = _cairo_dwrite_manual_show_glyphs_on_d2d_surface(surface, - op, - solid_src, - glyphs, - num_glyphs, - dwritesf, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - return status; - } - } - - RefPtr target_rt = dst->rt; - cairo_rectangle_int_t fontArea; -#ifndef ALWAYS_MANUAL_COMPOSITE - if (op != CAIRO_OPERATOR_OVER) { -#endif - target_rt = _cairo_d2d_get_temp_rt(dst, clip); - - if (!target_rt) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } -#ifndef ALWAYS_MANUAL_COMPOSITE - } else { - _begin_draw_state(dst); - status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip); - - if (unlikely(status)) - return status; - } -#endif - - D2D1_TEXT_ANTIALIAS_MODE cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - - // If we're rendering to a temporary surface we cannot do sub-pixel AA. - if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) { - cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - } - - RefPtr params; - target_rt->GetTextRenderingParams(¶ms); - - DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; - if (params) { - HRESULT hr = dwriteff->dwriteface->GetRecommendedRenderingMode( - (FLOAT)dwritesf->base.font_matrix.yy, - 1.0f, - DWRITE_MEASURING_MODE_NATURAL, - params, - &renderMode); - if (FAILED(hr)) { - // this probably never happens, but let's play it safe - renderMode = DWRITE_RENDERING_MODE_DEFAULT; - } - } - - // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept - switch (renderMode) { - case DWRITE_RENDERING_MODE_DEFAULT: - // As per DWRITE_RENDERING_MODE documentation, pick Natural for font - // sizes under 16 ppem - if (dwritesf->base.font_matrix.yy < 16.0f) { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; - } else { - renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; - } - break; - case DWRITE_RENDERING_MODE_OUTLINE: - return CAIRO_INT_STATUS_UNSUPPORTED; - default: - break; - } - - switch (dwritesf->antialias_mode) { - case CAIRO_ANTIALIAS_NONE: - cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; - break; - case CAIRO_ANTIALIAS_GRAY: - cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - break; - case CAIRO_ANTIALIAS_SUBPIXEL: - break; - } - - if (renderMode == DWRITE_RENDERING_MODE_ALIASED) { - cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; - } - - target_rt->SetTextAntialiasMode(cleartype_quality); - - DWRITE_MEASURING_MODE measureMode = - renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : - renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : - DWRITE_MEASURING_MODE_NATURAL; - - cairo_bool_t transform = FALSE; - - AutoDWriteGlyphRun run; - _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform); - - D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat); - - if (transform) { - target_rt->SetTransform(mat); - } - - if (dst->rt.get() != target_rt.get()) { - RefPtr analysis; - DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); - DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, - 1.0f, - transform ? &dwmat : 0, - renderMode, - measureMode, - 0, - 0, - &analysis); - - RECT bounds; - analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ? - DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, - &bounds); - fontArea.x = bounds.left; - fontArea.y = bounds.top; - fontArea.width = bounds.right - bounds.left; - fontArea.height = bounds.bottom - bounds.top; - } - - RefPtr brush = _cairo_d2d_create_brush_for_pattern(dst, NULL, - source); - - if (!brush) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (transform) { - D2D1::Matrix3x2F mat_inverse = _cairo_d2d_matrix_from_matrix(&dwritesf->mat_inverse); - D2D1::Matrix3x2F mat_brush; - - // The brush matrix needs to be multiplied with the inverted matrix - // as well, to move the brush into the space of the glyphs. Before - // the render target transformation. - brush->GetTransform(&mat_brush); - mat_brush = mat_brush * mat_inverse; - brush->SetTransform(&mat_brush); - } - - target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, measureMode); - - if (transform) { - target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); - } - - if (target_rt.get() != dst->rt.get()) { - return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_d2d_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D || - scaled_font->backend->type != CAIRO_FONT_TYPE_DWRITE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - cairo_d2d_surface_t::TextRenderingState textRenderingState = - reinterpret_cast(scaled_font)->rendering_mode; - if (d2dsurf->textRenderingState != textRenderingState) { - RefPtr params = - DWriteFactory::RenderingParams(textRenderingState); - d2dsurf->rt->SetTextRenderingParams(params); - d2dsurf->textRenderingState = textRenderingState; - } - cairo_int_status_t status = (cairo_int_status_t) - _cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); - - return status; -} - - -static cairo_bool_t -_cairo_d2d_getextents(void *surface, - cairo_rectangle_int_t *extents) -{ - cairo_d2d_surface_t *d2dsurf = static_cast(surface); - extents->x = 0; - extents->y = 0; - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - extents->width = size.width; - extents->height = size.height; - return TRUE; -} - - -/** Helper functions. */ - - - -cairo_surface_t* -cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device, - HWND wnd, - cairo_content_t content) -{ - cairo_d2d_device_t *d2d_device = reinterpret_cast(cairo_device); - cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); - new (newSurf) cairo_d2d_surface_t(); - - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); - - RECT rc; - HRESULT hr; - - newSurf->isDrawing = false; - ::GetClientRect(wnd, &rc); - - FLOAT dpiX; - FLOAT dpiY; - D2D1_SIZE_U sizePixels; - D2D1_SIZE_F size; - - dpiX = 96; - dpiY = 96; - - - sizePixels.width = rc.right - rc.left; - sizePixels.height = rc.bottom - rc.top; - - if (!sizePixels.width) { - sizePixels.width = 1; - } - if (!sizePixels.height) { - sizePixels.height = 1; - } - ID3D10Device1 *device = d2d_device->mD3D10Device; - RefPtr dxgiDevice; - RefPtr dxgiAdapter; - RefPtr dxgiFactory; - D2D1_RENDER_TARGET_PROPERTIES props; - D2D1_BITMAP_PROPERTIES bitProps; - - device->QueryInterface(&dxgiDevice); - dxgiDevice->GetAdapter(&dxgiAdapter); - dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)); - - DXGI_SWAP_CHAIN_DESC swapDesc; - ::ZeroMemory(&swapDesc, sizeof(swapDesc)); - - swapDesc.BufferDesc.Width = sizePixels.width; - swapDesc.BufferDesc.Height = sizePixels.height; - swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - swapDesc.BufferDesc.RefreshRate.Numerator = 60; - swapDesc.BufferDesc.RefreshRate.Denominator = 1; - swapDesc.SampleDesc.Count = 1; - swapDesc.SampleDesc.Quality = 0; - swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapDesc.BufferCount = 1; - swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE; - swapDesc.OutputWindow = wnd; - swapDesc.Windowed = TRUE; - - /** - * Create a swap chain, this swap chain will contain the backbuffer for - * the window we draw to. The front buffer is the full screen front - * buffer. - */ - hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, &newSurf->dxgiChain); - - /** - * We do not want DXGI to intercept alt-enter events and make the window go - * fullscreen! This shouldn't be in the cairo backend but controlled through - * the device. See comments on mozilla bug 553603. - */ - dxgiFactory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES); - - if (FAILED(hr)) { - goto FAIL_HWND; - } - /** Get the backbuffer surface from the swap chain */ - hr = newSurf->dxgiChain->GetBuffer(0, - IID_PPV_ARGS(&newSurf->surface)); - - if (FAILED(hr)) { - goto FAIL_HWND; - } - - newSurf->surface->QueryInterface(&newSurf->backBuf); - - size.width = sizePixels.width * dpiX; - size.height = sizePixels.height * dpiY; - - props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), - dpiX, - dpiY, - D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, - props, - &newSurf->rt); - if (FAILED(hr)) { - goto FAIL_HWND; - } - - bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - D2D1_ALPHA_MODE_PREMULTIPLIED)); - - newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); - - _d2d_clear_surface(newSurf); - - _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); - - return reinterpret_cast(newSurf); - -FAIL_HWND: - newSurf->~cairo_d2d_surface_t(); - free(newSurf); - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); -} - - - -cairo_surface_t * -cairo_d2d_surface_create(cairo_device_t *device, - cairo_format_t format, - int width, - int height) -{ - if (width == 0 || height == 0) { - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)); - } - - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); - new (newSurf) cairo_d2d_surface_t(); - - DXGI_FORMAT dxgiformat = DXGI_FORMAT_B8G8R8A8_UNORM; - D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; - if (format == CAIRO_FORMAT_ARGB32) { - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR_ALPHA); - } else if (format == CAIRO_FORMAT_RGB24) { - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR); - alpha = D2D1_ALPHA_MODE_IGNORE; - } else { - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA); - dxgiformat = DXGI_FORMAT_A8_UNORM; - } - - - newSurf->format = format; - - D2D1_SIZE_U sizePixels; - HRESULT hr; - - sizePixels.width = width; - sizePixels.height = height; - - CD3D10_TEXTURE2D_DESC desc( - dxgiformat, - sizePixels.width, - sizePixels.height - ); - desc.MipLevels = 1; - desc.Usage = D3D10_USAGE_DEFAULT; - desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - - /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */ - if (desc.Format != DXGI_FORMAT_A8_UNORM) - desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; - - RefPtr texture; - RefPtr dxgiSurface; - D2D1_BITMAP_PROPERTIES bitProps; - D2D1_RENDER_TARGET_PROPERTIES props; - - hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); - - if (FAILED(hr)) { - goto FAIL_CREATE; - } - - newSurf->surface = texture; - - /** Create the DXGI surface. */ - hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); - if (FAILED(hr)) { - goto FAIL_CREATE; - } - - props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); - - if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) - props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; - - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, - props, - &newSurf->rt); - - if (FAILED(hr)) { - goto FAIL_CREATE; - } - - bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha)); - - if (dxgiformat != DXGI_FORMAT_A8_UNORM) { - /* For some reason creation of shared bitmaps for A8 UNORM surfaces - * doesn't work even though the documentation suggests it does. The - * function will return an error if we try */ - hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, - dxgiSurface, - &bitProps, - &newSurf->surfaceBitmap); - - if (FAILED(hr)) { - goto FAIL_CREATE; - } - } - - newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); - - _d2d_clear_surface(newSurf); - - _cairo_d2d_surface_init(newSurf, d2d_device, format); - - return reinterpret_cast(newSurf); - -FAIL_CREATE: - newSurf->~cairo_d2d_surface_t(); - free(newSurf); - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); -} - -cairo_surface_t * -cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content) -{ - if (!device) { - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } - - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); - new (newSurf) cairo_d2d_surface_t(); - - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - HRESULT hr; - RefPtr texture; - RefPtr dxgiSurface; - D2D1_BITMAP_PROPERTIES bitProps; - D2D1_RENDER_TARGET_PROPERTIES props; - DXGI_FORMAT format; - DXGI_SURFACE_DESC desc; - D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; - - hr = d2d_device->mD3D10Device->OpenSharedResource(handle, - __uuidof(ID3D10Resource), - (void**)&newSurf->surface); - - if (FAILED(hr)) { - goto FAIL_CREATEHANDLE; - } - - hr = newSurf->surface->QueryInterface(&dxgiSurface); - - if (FAILED(hr)) { - goto FAIL_CREATEHANDLE; - } - - dxgiSurface->GetDesc(&desc); - format = desc.Format; - - if (format == DXGI_FORMAT_B8G8R8A8_UNORM) { - if (content == CAIRO_CONTENT_ALPHA) { - status = CAIRO_STATUS_INVALID_CONTENT; - goto FAIL_CREATEHANDLE; - } - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); - if (content == CAIRO_CONTENT_COLOR) { - alpha = D2D1_ALPHA_MODE_IGNORE; - } - } else if (format == DXGI_FORMAT_A8_UNORM) { - if (content != CAIRO_CONTENT_ALPHA) { - status = CAIRO_STATUS_INVALID_CONTENT; - goto FAIL_CREATEHANDLE; - } - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA); - } else { - status = CAIRO_STATUS_INVALID_FORMAT; - // We don't know how to support this format! - goto FAIL_CREATEHANDLE; - } - - props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); - - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, - props, - &newSurf->rt); - - if (FAILED(hr)) { - goto FAIL_CREATEHANDLE; - } - - bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha)); - - if (format != DXGI_FORMAT_A8_UNORM) { - /* For some reason creation of shared bitmaps for A8 UNORM surfaces - * doesn't work even though the documentation suggests it does. The - * function will return an error if we try */ - hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, - dxgiSurface, - &bitProps, - &newSurf->surfaceBitmap); - - if (FAILED(hr)) { - goto FAIL_CREATEHANDLE; - } - } - - newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); - - _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); - - return &newSurf->base; - -FAIL_CREATEHANDLE: - newSurf->~cairo_d2d_surface_t(); - free(newSurf); - return _cairo_surface_create_in_error(_cairo_error(status)); -} - -cairo_surface_t * -cairo_d2d_surface_create_for_texture(cairo_device_t *device, - ID3D10Texture2D *texture, - cairo_content_t content) -{ - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); - new (newSurf) cairo_d2d_surface_t(); - - D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; - if (content == CAIRO_CONTENT_COLOR) { - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR); - alpha = D2D1_ALPHA_MODE_IGNORE; - } else { - _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); - } - - D2D1_SIZE_U sizePixels; - HRESULT hr; - - D3D10_TEXTURE2D_DESC desc; - RefPtr dxgiSurface; - D2D1_BITMAP_PROPERTIES bitProps; - D2D1_RENDER_TARGET_PROPERTIES props; - - texture->GetDesc(&desc); - - sizePixels.width = desc.Width; - sizePixels.height = desc.Height; - - newSurf->surface = texture; - - /** Create the DXGI surface. */ - hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); - if (FAILED(hr)) { - goto FAIL_CREATE; - } - - props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); - - if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) - props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; - - hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, - props, - &newSurf->rt); - - if (FAILED(hr)) { - goto FAIL_CREATE; - } - - bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha)); - - if (content != CAIRO_CONTENT_ALPHA) { - /* For some reason creation of shared bitmaps for A8 UNORM surfaces - * doesn't work even though the documentation suggests it does. The - * function will return an error if we try */ - hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, - dxgiSurface, - &bitProps, - &newSurf->surfaceBitmap); - - if (FAILED(hr)) { - goto FAIL_CREATE; - } - } - - newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); - - _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); - - return reinterpret_cast(newSurf); - -FAIL_CREATE: - newSurf->~cairo_d2d_surface_t(); - free(newSurf); - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); -} - -ID3D10Texture2D* -cairo_d2d_surface_get_texture(cairo_surface_t *surface) -{ - if (surface->type != CAIRO_SURFACE_TYPE_D2D) { - return NULL; - } - - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - - RefPtr texture; - d2dsurf->surface->QueryInterface(&texture); - - return texture; -} - -void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) -{ - if (surface->type != CAIRO_SURFACE_TYPE_D2D) { - return; - } - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - - /** For now, we invalidate our storing texture with this operation. */ - D2D1_POINT_2U point; - D3D10_BOX rect; - rect.front = 0; - rect.back = 1; - - RefPtr dxgiSurface; - d2dsurf->surface->QueryInterface(&dxgiSurface); - DXGI_SURFACE_DESC desc; - - dxgiSurface->GetDesc(&desc); - - /** - * It's important we constrain the size of the clip region to the area of - * the surface. If we don't we might get a box that goes outside the - * surface, and CopySubresourceRegion will become very angry with us. - * It will cause a device failure and subsequent drawing will break. - */ - clip->x = MAX(clip->x, 0); - clip->y = MAX(clip->y, 0); - clip->width = MIN(clip->width, desc.Width - clip->x); - clip->height = MIN(clip->height, desc.Height - clip->y); - - if (x < 0) { - point.x = (UINT32)clip->x; - rect.left = (UINT)(clip->x - x); - rect.right = (UINT)(clip->x + clip->width); - } else { - point.x = (UINT32)(clip->x + x); - rect.left = (UINT)clip->x; - rect.right = (UINT32)(clip->x + clip->width - x); - } - if (y < 0) { - point.y = (UINT32)clip->y; - rect.top = (UINT)(clip->y - y); - rect.bottom = (UINT)(clip->y + clip->height); - } else { - point.y = (UINT32)(clip->y + y); - rect.top = (UINT)clip->y; - rect.bottom = (UINT)(clip->y + clip->height - y); - } - ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf); - - d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface); - d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface, - 0, - point.x, - point.y, - 0, - texture, - 0, - &rect); - -} - -HDC -cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) -{ - if (surface->type != CAIRO_SURFACE_TYPE_D2D) { - return NULL; - } - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - - /* We'll pop the clip here manually so that we'll stay in drawing state if we - * already are, we need to ensure d2dsurf->isDrawing manually then though - */ - - /* Clips aren't allowed as per MSDN docs */ - reset_clip(d2dsurf); - - if (!d2dsurf->isDrawing) { - /* GetDC must be called between BeginDraw/EndDraw */ - d2dsurf->rt->BeginDraw(); - d2dsurf->isDrawing = true; - } - - RefPtr interopRT; - - /* This QueryInterface call will always succeed even if the - * the render target doesn't support the ID2D1GdiInteropRenderTarget - * interface */ - d2dsurf->rt->QueryInterface(&interopRT); - - HDC dc; - HRESULT rv; - - rv = interopRT->GetDC(retain_contents ? D2D1_DC_INITIALIZE_MODE_COPY : - D2D1_DC_INITIALIZE_MODE_CLEAR, &dc); - - if (FAILED(rv)) { - return NULL; - } - - return dc; -} - -void -cairo_d2d_release_dc(cairo_surface_t *surface, const cairo_rectangle_int_t *updated_rect) -{ - if (surface->type != CAIRO_SURFACE_TYPE_D2D) { - return; - } - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - - RefPtr interopRT; - - d2dsurf->rt->QueryInterface(&interopRT); - - if (!updated_rect) { - interopRT->ReleaseDC(NULL); - return; - } - - RECT r; - r.left = updated_rect->x; - r.top = updated_rect->y; - r.right = r.left + updated_rect->width; - r.bottom = r.top + updated_rect->height; - - interopRT->ReleaseDC(&r); -} - -int -cairo_d2d_get_image_surface_cache_usage() -{ - return _cairo_atomic_int_get(&cache_usage); -} - -int -cairo_d2d_get_surface_vram_usage(cairo_device_t *device) -{ - cairo_d2d_device_t *d2d_device = reinterpret_cast(device); - return d2d_device->mVRAMUsage; -} - -int -cairo_d2d_surface_get_width(cairo_surface_t *surface) -{ - if (surface->backend != &cairo_d2d_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - return size.width; -} - -int -cairo_d2d_surface_get_height(cairo_surface_t *surface) -{ - if (surface->backend != &cairo_d2d_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); - D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); - return size.height; -} diff --git a/libs/cairo/cairo/src/cairo-debug.c b/libs/cairo/cairo/src/cairo-debug.c deleted file mode 100644 index 49bf31594..000000000 --- a/libs/cairo/cairo/src/cairo-debug.c +++ /dev/null @@ -1,221 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -/** - * cairo_debug_reset_static_data: - * - * Resets all static data within cairo to its original state, - * (ie. identical to the state at the time of program invocation). For - * example, all caches within cairo will be flushed empty. - * - * This function is intended to be useful when using memory-checking - * tools such as valgrind. When valgrind's memcheck analyzes a - * cairo-using program without a call to cairo_debug_reset_static_data(), - * it will report all data reachable via cairo's static objects as - * "still reachable". Calling cairo_debug_reset_static_data() just prior - * to program termination will make it easier to get squeaky clean - * reports from valgrind. - * - * WARNING: It is only safe to call this function when there are no - * active cairo objects remaining, (ie. the appropriate destroy - * functions have been called as necessary). If there are active cairo - * objects, this call is likely to cause a crash, (eg. an assertion - * failure due to a hash table being destroyed when non-empty). - **/ -void -cairo_debug_reset_static_data (void) -{ - CAIRO_MUTEX_INITIALIZE (); - - _cairo_scaled_font_map_destroy (); - - _cairo_toy_font_face_reset_static_data (); - -#if CAIRO_HAS_FT_FONT - _cairo_ft_font_reset_static_data (); -#endif - -#if CAIRO_HAS_WIN32_FONT - _cairo_win32_font_reset_static_data (); -#endif - - _cairo_intern_string_reset_static_data (); - - _cairo_scaled_font_reset_static_data (); - - _cairo_pattern_reset_static_data (); - - _cairo_clip_reset_static_data (); - - _cairo_image_reset_static_data (); - -#if CAIRO_HAS_DRM_SURFACE - _cairo_drm_device_reset_static_data (); -#endif - - _cairo_reset_static_data (); - - CAIRO_MUTEX_FINALIZE (); -} - -#if HAVE_VALGRIND -void -_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) -{ - const cairo_image_surface_t *image = (cairo_image_surface_t *) surface; - const uint8_t *bits; - int row, width; - - if (surface == NULL) - return; - - if (! RUNNING_ON_VALGRIND) - return; - - bits = image->data; - switch (image->format) { - case CAIRO_FORMAT_A1: - width = (image->width + 7)/8; - break; - case CAIRO_FORMAT_A8: - width = image->width; - break; - case CAIRO_FORMAT_RGB16_565: - width = image->width*2; - break; - case CAIRO_FORMAT_RGB24: - case CAIRO_FORMAT_ARGB32: - width = image->width*4; - break; - case CAIRO_FORMAT_INVALID: - default: - /* XXX compute width from pixman bpp */ - return; - } - - for (row = 0; row < image->height; row++) { - VALGRIND_CHECK_MEM_IS_DEFINED (bits, width); - /* and then silence any future valgrind warnings */ - VALGRIND_MAKE_MEM_DEFINED (bits, width); - bits += image->stride; - } -} -#endif - - -#if 0 -void -_cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn) -{ - char *fmt; - if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) - fmt = "P6"; - else if (isurf->format == CAIRO_FORMAT_A8) - fmt = "P5"; - else - return; - - FILE *fp = fopen(fn, "wb"); - if (!fp) - return; - - fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height); - for (int j = 0; j < isurf->height; j++) { - unsigned char *row = isurf->data + isurf->stride * j; - for (int i = 0; i < isurf->width; i++) { - if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) { - unsigned char r = *row++; - unsigned char g = *row++; - unsigned char b = *row++; - *row++; - putc(r, fp); - putc(g, fp); - putc(b, fp); - } else { - unsigned char a = *row++; - putc(a, fp); - } - } - } - - fclose (fp); - - fprintf (stderr, "Wrote %s\n", fn); -} -#endif - -static cairo_status_t -_print_move_to (void *closure, - const cairo_point_t *point) -{ - fprintf (closure, - " %f %f m", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_print_line_to (void *closure, - const cairo_point_t *point) -{ - fprintf (closure, - " %f %f l", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_print_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - fprintf (closure, - " %f %f %f %f %f %f c", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y), - _cairo_fixed_to_double (p2->x), - _cairo_fixed_to_double (p2->y), - _cairo_fixed_to_double (p3->x), - _cairo_fixed_to_double (p3->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_print_close (void *closure) -{ - fprintf (closure, " h"); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) -{ - cairo_status_t status; - - printf ("path: extents=(%f, %f), (%f, %f)\n", - _cairo_fixed_to_double (path->extents.p1.x), - _cairo_fixed_to_double (path->extents.p1.y), - _cairo_fixed_to_double (path->extents.p2.x), - _cairo_fixed_to_double (path->extents.p2.y)); - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _print_move_to, - _print_line_to, - _print_curve_to, - _print_close, - stream); - assert (status == CAIRO_STATUS_SUCCESS); - - printf ("\n"); -} diff --git a/libs/cairo/cairo/src/cairo-deflate-stream.c b/libs/cairo/cairo/src/cairo-deflate-stream.c deleted file mode 100644 index b6d10b12a..000000000 --- a/libs/cairo/cairo/src/cairo-deflate-stream.c +++ /dev/null @@ -1,119 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" -#include - -#define BUFFER_SIZE 16384 - -typedef struct _cairo_deflate_stream { - cairo_output_stream_t base; - cairo_output_stream_t *output; - z_stream zlib_stream; - unsigned char input_buf[BUFFER_SIZE]; - unsigned char output_buf[BUFFER_SIZE]; -} cairo_deflate_stream_t; - -static void -cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush) -{ - int ret; - cairo_bool_t finished; - - do { - ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH); - if (flush || stream->zlib_stream.avail_out == 0) - { - _cairo_output_stream_write (stream->output, - stream->output_buf, - BUFFER_SIZE - stream->zlib_stream.avail_out); - stream->zlib_stream.next_out = stream->output_buf; - stream->zlib_stream.avail_out = BUFFER_SIZE; - } - - finished = TRUE; - if (stream->zlib_stream.avail_in != 0) - finished = FALSE; - if (flush && ret != Z_STREAM_END) - finished = FALSE; - - } while (!finished); - - stream->zlib_stream.next_in = stream->input_buf; -} - -static cairo_status_t -_cairo_deflate_stream_write (cairo_output_stream_t *base, - const unsigned char *data, - unsigned int length) -{ - cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; - unsigned int count; - const unsigned char *p = data; - - while (length) { - count = length; - if (count > BUFFER_SIZE - stream->zlib_stream.avail_in) - count = BUFFER_SIZE - stream->zlib_stream.avail_in; - memcpy (stream->input_buf + stream->zlib_stream.avail_in, p, count); - p += count; - stream->zlib_stream.avail_in += count; - length -= count; - - if (stream->zlib_stream.avail_in == BUFFER_SIZE) - cairo_deflate_stream_deflate (stream, FALSE); - } - - return _cairo_output_stream_get_status (stream->output); -} - -static cairo_status_t -_cairo_deflate_stream_close (cairo_output_stream_t *base) -{ - cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; - - cairo_deflate_stream_deflate (stream, TRUE); - deflateEnd (&stream->zlib_stream); - - return _cairo_output_stream_get_status (stream->output); -} - -cairo_output_stream_t * -_cairo_deflate_stream_create (cairo_output_stream_t *output) -{ - cairo_deflate_stream_t *stream; - - if (output->status) - return _cairo_output_stream_create_in_error (output->status); - - stream = malloc (sizeof (cairo_deflate_stream_t)); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - _cairo_deflate_stream_write, - NULL, - _cairo_deflate_stream_close); - stream->output = output; - - stream->zlib_stream.zalloc = Z_NULL; - stream->zlib_stream.zfree = Z_NULL; - stream->zlib_stream.opaque = Z_NULL; - - if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { - free (stream); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - stream->zlib_stream.next_in = stream->input_buf; - stream->zlib_stream.avail_in = 0; - stream->zlib_stream.next_out = stream->output_buf; - stream->zlib_stream.avail_out = BUFFER_SIZE; - - return &stream->base; -} diff --git a/libs/cairo/cairo/src/cairo-deprecated.h b/libs/cairo/cairo/src/cairo-deprecated.h deleted file mode 100644 index 04b5d264d..000000000 --- a/libs/cairo/cairo/src/cairo-deprecated.h +++ /dev/null @@ -1,92 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-device-private.h b/libs/cairo/cairo/src/cairo-device-private.h deleted file mode 100644 index 371f66714..000000000 --- a/libs/cairo/cairo/src/cairo-device-private.h +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _CAIRO_DEVICE_PRIVATE_H_ -#define _CAIRO_DEVICE_PRIVATE_H_ - -#include "cairo-compiler-private.h" -#include "cairo-mutex-private.h" -#include "cairo-reference-count-private.h" -#include "cairo-types-private.h" - -struct _cairo_device { - cairo_reference_count_t ref_count; - cairo_status_t status; - cairo_user_data_array_t user_data; - - const cairo_device_backend_t *backend; - - cairo_recursive_mutex_t mutex; - unsigned mutex_depth; - - cairo_bool_t finished; -}; - -struct _cairo_device_backend { - cairo_device_type_t type; - - void (*lock) (void *device); - void (*unlock) (void *device); - - cairo_warn cairo_status_t (*flush) (void *device); - void (*finish) (void *device); - void (*destroy) (void *device); -}; - -cairo_private cairo_device_t * -_cairo_device_create_in_error (cairo_status_t status); - -cairo_private void -_cairo_device_init (cairo_device_t *device, - const cairo_device_backend_t *backend); - -cairo_private cairo_status_t -_cairo_device_set_error (cairo_device_t *device, - cairo_status_t error); - -slim_hidden_proto_no_warn (cairo_device_reference); -slim_hidden_proto (cairo_device_acquire); -slim_hidden_proto (cairo_device_release); -slim_hidden_proto (cairo_device_flush); -slim_hidden_proto (cairo_device_finish); -slim_hidden_proto (cairo_device_destroy); - -#endif /* _CAIRO_DEVICE_PRIVATE_H_ */ diff --git a/libs/cairo/cairo/src/cairo-device.c b/libs/cairo/cairo/src/cairo-device.c deleted file mode 100644 index d24dba94c..000000000 --- a/libs/cairo/cairo/src/cairo-device.c +++ /dev/null @@ -1,502 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-device-private.h" -#include "cairo-error-private.h" - -/** - * SECTION:cairo-device - * @Title: cairo_device_t - * @Short_Description: interface to underlying rendering system - * @See_Also: #cairo_surface_t - * - * Devices are the abstraction Cairo employs for the rendering system - * used by a #cairo_surface_t. You can get the device of a surface using - * cairo_surface_get_device(). - * - * Devices are created using custom functions specific to the rendering - * system you want to use. See the documentation for the surface types - * for those functions. - * - * An important function that devices fulfill is sharing access to the - * rendering system between Cairo and your application. If you want to - * access a device directly that you used to draw to with Cairo, you must - * first call cairo_device_flush() to ensure that Cairo finishes all - * operations on the device and resets it to a clean state. - * - * Cairo also provides the functions cairo_device_acquire() and - * cairo_device_release() to synchronize access to the rendering system - * in a multithreaded environment. This is done internally, but can also - * be used by applications. - * - * Putting this all together, a function that works with devices should - * look something like this: - * - * void - * my_device_modifying_function (cairo_device_t *device) - * { - * cairo_status_t status; - * - * // Ensure the device is properly reset - * cairo_device_flush (device); - * // Try to acquire the device - * status = cairo_device_acquire (device); - * if (status != CAIRO_STATUS_SUCCESS) { - * printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status)); - * return; - * } - * - * // Do the custom operations on the device here. - * // But do not call any Cairo functions that might acquire devices. - * - * // Release the device when done. - * cairo_device_release (device); - * } - * - * - * Please refer to the documentation of each backend for - * additional usage requirements, guarantees provided, and - * interactions with existing surface API of the device functions for - * surfaces of that type. - * - */ - -static const cairo_device_t _nil_device = { - CAIRO_REFERENCE_COUNT_INVALID, - CAIRO_STATUS_NO_MEMORY, -}; - -static const cairo_device_t _mismatch_device = { - CAIRO_REFERENCE_COUNT_INVALID, - CAIRO_STATUS_DEVICE_TYPE_MISMATCH, -}; - -static const cairo_device_t _invalid_device = { - CAIRO_REFERENCE_COUNT_INVALID, - CAIRO_STATUS_DEVICE_ERROR, -}; - -cairo_device_t * -_cairo_device_create_in_error (cairo_status_t status) -{ - switch (status) { - case CAIRO_STATUS_NO_MEMORY: - return (cairo_device_t *) &_nil_device; - case CAIRO_STATUS_DEVICE_ERROR: - return (cairo_device_t *) &_invalid_device; - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: - return (cairo_device_t *) &_mismatch_device; - - case CAIRO_STATUS_SUCCESS: - case CAIRO_STATUS_LAST_STATUS: - ASSERT_NOT_REACHED; - /* fall-through */ - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: - case CAIRO_STATUS_INVALID_STATUS: - case CAIRO_STATUS_INVALID_FORMAT: - case CAIRO_STATUS_INVALID_VISUAL: - case CAIRO_STATUS_READ_ERROR: - case CAIRO_STATUS_WRITE_ERROR: - case CAIRO_STATUS_FILE_NOT_FOUND: - case CAIRO_STATUS_TEMP_FILE_ERROR: - case CAIRO_STATUS_INVALID_STRIDE: - case CAIRO_STATUS_INVALID_SIZE: - case CAIRO_STATUS_INVALID_RESTORE: - case CAIRO_STATUS_INVALID_POP_GROUP: - case CAIRO_STATUS_NO_CURRENT_POINT: - case CAIRO_STATUS_INVALID_MATRIX: - case CAIRO_STATUS_NULL_POINTER: - case CAIRO_STATUS_INVALID_STRING: - case CAIRO_STATUS_INVALID_PATH_DATA: - case CAIRO_STATUS_SURFACE_FINISHED: - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: - case CAIRO_STATUS_INVALID_DASH: - case CAIRO_STATUS_INVALID_DSC_COMMENT: - case CAIRO_STATUS_INVALID_INDEX: - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: - case CAIRO_STATUS_FONT_TYPE_MISMATCH: - case CAIRO_STATUS_USER_FONT_IMMUTABLE: - case CAIRO_STATUS_USER_FONT_ERROR: - case CAIRO_STATUS_NEGATIVE_COUNT: - case CAIRO_STATUS_INVALID_CLUSTERS: - case CAIRO_STATUS_INVALID_SLANT: - case CAIRO_STATUS_INVALID_WEIGHT: - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: - case CAIRO_STATUS_INVALID_CONTENT: - default: - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_device_t *) &_nil_device; - } -} - -void -_cairo_device_init (cairo_device_t *device, - const cairo_device_backend_t *backend) -{ - CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1); - device->status = CAIRO_STATUS_SUCCESS; - - device->backend = backend; - - CAIRO_RECURSIVE_MUTEX_INIT (device->mutex); - device->mutex_depth = 0; - - device->finished = FALSE; - - _cairo_user_data_array_init (&device->user_data); -} - -/** - * cairo_device_reference: - * @device: a #cairo_device_t - * - * Increases the reference count on @device by one. This prevents - * @device from being destroyed until a matching call to - * cairo_device_destroy() is made. - * - * The number of references to a #cairo_device_t can be get using - * cairo_device_get_reference_count(). - * - * Return value: the referenced #cairo_device_t. - * - * Since: 1.10 - **/ -cairo_device_t * -cairo_device_reference (cairo_device_t *device) -{ - if (device == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - { - return device; - } - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); - _cairo_reference_count_inc (&device->ref_count); - - return device; -} -slim_hidden_def (cairo_device_reference); - -/** - * cairo_device_status: - * @device: a #cairo_device_t - * - * Checks whether an error has previously occurred for this - * device. - * - * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if - * the device is in an error state. - * - * Since: 1.10 - **/ -cairo_status_t -cairo_device_status (cairo_device_t *device) -{ - if (device == NULL) - return CAIRO_STATUS_NULL_POINTER; - - return device->status; -} - -/** - * cairo_device_flush: - * @device: a #cairo_device_t - * - * Finish any pending operations for the device and also restore any - * temporary modifications cairo has made to the device's state. - * This function must be called before switching from using the - * device with Cairo to operating on it directly with native APIs. - * If the device doesn't support direct access, then this function - * does nothing. - * - * This function may acquire devices. - * - * Since: 1.10 - **/ -void -cairo_device_flush (cairo_device_t *device) -{ - cairo_status_t status; - - if (device == NULL || device->status) - return; - - if (device->backend->flush != NULL) { - status = device->backend->flush (device); - if (unlikely (status)) - status = _cairo_device_set_error (device, status); - } -} -slim_hidden_def (cairo_device_flush); - -/** - * cairo_device_finish: - * @device: the #cairo_device_t to finish - * - * This function finishes the device and drops all references to - * external resources. All surfaces, fonts and other objects created - * for this @device will be finished, too. - * Further operations on the @device will not affect the @device but - * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error. - * - * When the last call to cairo_device_destroy() decreases the - * reference count to zero, cairo will call cairo_device_finish() if - * it hasn't been called already, before freeing the resources - * associated with the device. - * - * This function may acquire devices. - * - * Since: 1.10 - **/ -void -cairo_device_finish (cairo_device_t *device) -{ - if (device == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - { - return; - } - - if (device->finished) - return; - - cairo_device_flush (device); - - device->finished = TRUE; - - if (device->backend->finish != NULL) - device->backend->finish (device); -} -slim_hidden_def (cairo_device_finish); - -/** - * cairo_device_destroy: - * @device: a #cairo_device_t - * - * Decreases the reference count on @device by one. If the result is - * zero, then @device and all associated resources are freed. See - * cairo_device_reference(). - * - * This function may acquire devices if the last reference was dropped. - * - * Since: 1.10 - **/ -void -cairo_device_destroy (cairo_device_t *device) -{ - cairo_user_data_array_t user_data; - - if (device == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - { - return; - } - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); - if (! _cairo_reference_count_dec_and_test (&device->ref_count)) - return; - - cairo_device_finish (device); - - assert (device->mutex_depth == 0); - CAIRO_MUTEX_FINI (device->mutex); - - user_data = device->user_data; - - device->backend->destroy (device); - - _cairo_user_data_array_fini (&user_data); - -} -slim_hidden_def (cairo_device_destroy); - -/** - * cairo_device_get_type: - * @device: a #cairo_device_t - * - * This function returns the type of the device. See #cairo_device_type_t - * for available types. - * - * Return value: The type of @device. - * - * Since: 1.10 - **/ -cairo_device_type_t -cairo_device_get_type (cairo_device_t *device) -{ - if (device == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - { - return (cairo_device_type_t) -1; - } - - return device->backend->type; -} - -/** - * cairo_device_acquire: - * @device: a #cairo_device_t - * - * Acquires the @device for the current thread. This function will block - * until no other thread has acquired the device. - * - * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the - * device. From now on your thread owns the device and no other thread will be - * able to acquire it until a matching call to cairo_device_release(). It is - * allowed to recursively acquire the device multiple times from the same - * thread. - * - * You must never acquire two different devices at the same time - * unless this is explicitly allowed. Otherwise the possibility of deadlocks - * exist. - * - * As various Cairo functions can acquire devices when called, these functions - * may also cause deadlocks when you call them with an acquired device. So you - * must not have a device acquired when calling them. These functions are - * marked in the documentation. - * - * - * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if - * the device is in an error state and could not be - * acquired. After a successful call to cairo_device_acquire(), - * a matching call to cairo_device_release() is required. - * - * Since: 1.10 - **/ -cairo_status_t -cairo_device_acquire (cairo_device_t *device) -{ - if (device == NULL) - return CAIRO_STATUS_SUCCESS; - - if (unlikely (device->status)) - return device->status; - - if (unlikely (device->finished)) - return _cairo_device_set_error (device, CAIRO_STATUS_SURFACE_FINISHED); /* XXX */ - - CAIRO_MUTEX_LOCK (device->mutex); - if (device->mutex_depth++ == 0) { - if (device->backend->lock != NULL) - device->backend->lock (device); - } - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_device_acquire); - -/** - * cairo_device_release: - * @device: a #cairo_device_t - * - * Releases a @device previously acquired using cairo_device_acquire(). See - * that function for details. - * - * Since: 1.10 - **/ -void -cairo_device_release (cairo_device_t *device) -{ - if (device == NULL) - return; - - assert (device->mutex_depth > 0); - - if (--device->mutex_depth == 0) { - if (device->backend->unlock != NULL) - device->backend->unlock (device); - } - - CAIRO_MUTEX_UNLOCK (device->mutex); -} -slim_hidden_def (cairo_device_release); - -cairo_status_t -_cairo_device_set_error (cairo_device_t *device, - cairo_status_t status) -{ - if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&device->status, status); - - return _cairo_error (status); -} - -/** - * cairo_device_get_reference_count: - * @device: a #cairo_device_t - * - * Returns the current reference count of @device. - * - * Return value: the current reference count of @device. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.10 - **/ -unsigned int -cairo_device_get_reference_count (cairo_device_t *device) -{ - if (device == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count); -} - -/** - * cairo_device_get_user_data: - * @device: a #cairo_device_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @device using the - * specified key. If no user data has been attached with the given - * key this function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - * - * Since: 1.10 - **/ -void * -cairo_device_get_user_data (cairo_device_t *device, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&device->user_data, - key); -} - -/** - * cairo_device_set_user_data: - * @device: a #cairo_device_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the #cairo_device_t - * @destroy: a #cairo_destroy_func_t which will be called when the - * #cairo_t is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @device. To remove user data from a surface, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - * - * Since: 1.10 - **/ -cairo_status_t -cairo_device_set_user_data (cairo_device_t *device, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) - return device->status; - - return _cairo_user_data_array_set_data (&device->user_data, - key, user_data, destroy); -} diff --git a/libs/cairo/cairo/src/cairo-directfb-surface.c b/libs/cairo/cairo/src/cairo-directfb-surface.c deleted file mode 100644 index 6387fee94..000000000 --- a/libs/cairo/cairo/src/cairo-directfb-surface.c +++ /dev/null @@ -1,1931 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-directfb.h" - -#include "cairo-clip-private.h" -#include "cairo-error-private.h" - -#include - -#include -#include -#include -#include -#include - -/* - * Rectangle works fine. - * Bugs 361377, 359553, 359243 in Gnome BTS are caused - * by GDK/DirectFB, not by Cairo/DirectFB. - */ -#define DFB_RECTANGLES 1 - -/* - * Composite works fine. - */ -#define DFB_COMPOSITE 1 - -/* - * CompositeTrapezoids works (without antialiasing). - */ -#define DFB_COMPOSITE_TRAPEZOIDS 1 - -/* - * ShowGlyphs works fine. - */ -#define DFB_SHOW_GLYPHS 1 - -#define PIXMAN_invalid (pixman_format_code_t) 0 - - -D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire"); -D_DEBUG_DOMAIN (CairoDFB_Clip, "CairoDFB/Clip", "Cairo DirectFB Clipping"); -D_DEBUG_DOMAIN (CairoDFB_Font, "CairoDFB/Font", "Cairo DirectFB Font Rendering"); -D_DEBUG_DOMAIN (CairoDFB_Render, "CairoDFB/Render", "Cairo DirectFB Rendering"); -D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface"); - -/*****************************************************************************/ - -typedef struct _cairo_directfb_surface { - cairo_surface_t base; - - pixman_format_code_t pixman_format; - cairo_bool_t supported_destination; - - IDirectFB *dfb; - IDirectFBSurface *dfbsurface; - IDirectFBSurface *tmpsurface; - pixman_format_code_t tmpformat; - - int width; - int height; - - unsigned local : 1; - unsigned blit_premultiplied : 1; -} cairo_directfb_surface_t; - - -typedef struct _cairo_directfb_font_cache { - IDirectFB *dfb; - IDirectFBSurface *dfbsurface; - - int width; - int height; - - /* coordinates within the surface - * of the last loaded glyph */ - int x; - int y; -} cairo_directfb_font_cache_t; - -static cairo_surface_backend_t _cairo_directfb_surface_backend; - -/*****************************************************************************/ - -static int _directfb_argb_font = 0; - -/*****************************************************************************/ - -#define RUN_CLIPPED(surface, clip_region, clip, func) {\ - if ((clip_region) != NULL) {\ - int n_clips = cairo_region_num_rectangles (clip_region), n; \ - for (n = 0; n < n_clips; n++) {\ - if (clip) {\ - DFBRegion reg, *cli = (clip); \ - cairo_rectangle_int_t rect; \ - cairo_region_get_rectangle (clip_region, n, &rect); \ - reg.x1 = rect.x; \ - reg.y1 = rect.y; \ - reg.x2 = rect.x + rect.width - 1; \ - reg.y2 = rect.y + rect.height - 1; \ - if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ - reg.x1 > cli->x2 || reg.y1 > cli->y2)\ - continue;\ - if (reg.x1 < cli->x1)\ - reg.x1 = cli->x1;\ - if (reg.y1 < cli->y1)\ - reg.y1 = cli->y1;\ - if (reg.x2 > cli->x2)\ - reg.x2 = cli->x2;\ - if (reg.y2 > cli->y2)\ - reg.y2 = cli->y2;\ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ - } else {\ - DFBRegion reg; \ - cairo_rectangle_int_t rect; \ - cairo_region_get_rectangle (clip_region, n, &rect); \ - reg.x1 = rect.x; \ - reg.y1 = rect.y; \ - reg.x2 = rect.x + rect.width - 1; \ - reg.y2 = rect.y + rect.height - 1; \ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ - }\ - func;\ - }\ - } else {\ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\ - func;\ - }\ -} - -#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \ - double _x = (x); \ - double _y = (y); \ - (ret_x) = (_x * (m).xx + (m).x0); \ - (ret_y) = (_y * (m).yy + (m).y0); \ -} while (0) - -#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \ - double _x = (x); \ - double _y = (y); \ - (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \ - (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \ -} while (0) - -/* XXX: A1 has a different bits ordering in cairo. - * Probably we should drop it. - */ - -static cairo_content_t -_directfb_format_to_content (DFBSurfacePixelFormat format) -{ - if (DFB_PIXELFORMAT_HAS_ALPHA (format)) { - if (DFB_COLOR_BITS_PER_PIXEL (format)) - return CAIRO_CONTENT_COLOR_ALPHA; - - return CAIRO_CONTENT_ALPHA; - } - - return CAIRO_CONTENT_COLOR; -} - -static inline DFBSurfacePixelFormat -_cairo_to_directfb_format (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_RGB24: - return DSPF_RGB32; - case CAIRO_FORMAT_ARGB32: - return DSPF_ARGB; - case CAIRO_FORMAT_A8: - return DSPF_A8; - case CAIRO_FORMAT_A1: - return DSPF_A1; - default: - break; - } - - return -1; -} - -static inline pixman_format_code_t -_directfb_to_pixman_format (DFBSurfacePixelFormat format) -{ - switch (format) { - case DSPF_UNKNOWN: return PIXMAN_invalid; - case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; - case DSPF_RGB16: return PIXMAN_r5g6b5; - case DSPF_RGB24: return PIXMAN_r8g8b8; - case DSPF_RGB32: return PIXMAN_x8r8g8b8; - case DSPF_ARGB: return PIXMAN_a8r8g8b8; - case DSPF_A8: return PIXMAN_a8; - case DSPF_YUY2: return PIXMAN_yuy2; - case DSPF_RGB332: return PIXMAN_r3g3b2; - case DSPF_UYVY: return PIXMAN_invalid; - case DSPF_I420: return PIXMAN_invalid; - case DSPF_YV12: return PIXMAN_yv12; - case DSPF_LUT8: return PIXMAN_invalid; - case DSPF_ALUT44: return PIXMAN_invalid; - case DSPF_AiRGB: return PIXMAN_invalid; - case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */ - case DSPF_NV12: return PIXMAN_invalid; - case DSPF_NV16: return PIXMAN_invalid; - case DSPF_ARGB2554: return PIXMAN_invalid; - case DSPF_ARGB4444: return PIXMAN_a4r4g4b4; - case DSPF_NV21: return PIXMAN_invalid; - case DSPF_AYUV: return PIXMAN_invalid; - case DSPF_A4: return PIXMAN_a4; - case DSPF_ARGB1666: return PIXMAN_invalid; - case DSPF_ARGB6666: return PIXMAN_invalid; - case DSPF_RGB18: return PIXMAN_invalid; - case DSPF_LUT2: return PIXMAN_invalid; - case DSPF_RGB444: return PIXMAN_x4r4g4b4; - case DSPF_RGB555: return PIXMAN_x1r5g5b5; -#if DFB_NUM_PIXELFORMATS >= 29 - case DSPF_BGR555: return PIXMAN_x1b5g5r5; -#endif - } - return PIXMAN_invalid; -} - -static inline DFBSurfacePixelFormat -_directfb_from_pixman_format (pixman_format_code_t format) -{ - switch ((int) format) { - case PIXMAN_a1r5g5b5: return DSPF_ARGB1555; - case PIXMAN_r5g6b5: return DSPF_RGB16; - case PIXMAN_r8g8b8: return DSPF_RGB24; - case PIXMAN_x8r8g8b8: return DSPF_RGB32; - case PIXMAN_a8r8g8b8: return DSPF_ARGB; - case PIXMAN_a8: return DSPF_A8; - case PIXMAN_yuy2: return DSPF_YUY2; - case PIXMAN_r3g3b2: return DSPF_RGB332; - case PIXMAN_yv12: return DSPF_YV12; - case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */ - case PIXMAN_a4r4g4b4: return DSPF_ARGB4444; - case PIXMAN_a4: return DSPF_A4; - case PIXMAN_x4r4g4b4: return DSPF_RGB444; - case PIXMAN_x1r5g5b5: return DSPF_RGB555; -#if DFB_NUM_PIXELFORMATS >= 29 - case PIXMAN_x1b5g5r5: return DSPF_BGR555; -#endif - default: return 0; - } -} - -static cairo_bool_t -_directfb_get_operator (cairo_operator_t operator, - DFBSurfaceBlendFunction *ret_srcblend, - DFBSurfaceBlendFunction *ret_dstblend) -{ - DFBSurfaceBlendFunction srcblend = DSBF_ONE; - DFBSurfaceBlendFunction dstblend = DSBF_ZERO; - - switch (operator) { - case CAIRO_OPERATOR_CLEAR: - srcblend = DSBF_ZERO; - dstblend = DSBF_ZERO; - break; - case CAIRO_OPERATOR_SOURCE: - srcblend = DSBF_ONE; - dstblend = DSBF_ZERO; - break; - case CAIRO_OPERATOR_OVER: - srcblend = DSBF_ONE; - dstblend = DSBF_INVSRCALPHA; - break; - case CAIRO_OPERATOR_IN: - srcblend = DSBF_DESTALPHA; - dstblend = DSBF_ZERO; - break; - case CAIRO_OPERATOR_OUT: - srcblend = DSBF_INVDESTALPHA; - dstblend = DSBF_ZERO; - break; - case CAIRO_OPERATOR_ATOP: - srcblend = DSBF_DESTALPHA; - dstblend = DSBF_INVSRCALPHA; - break; - case CAIRO_OPERATOR_DEST: - srcblend = DSBF_ZERO; - dstblend = DSBF_ONE; - break; - case CAIRO_OPERATOR_DEST_OVER: - srcblend = DSBF_INVDESTALPHA; - dstblend = DSBF_ONE; - break; - case CAIRO_OPERATOR_DEST_IN: - srcblend = DSBF_ZERO; - dstblend = DSBF_SRCALPHA; - break; - case CAIRO_OPERATOR_DEST_OUT: - srcblend = DSBF_ZERO; - dstblend = DSBF_INVSRCALPHA; - break; - case CAIRO_OPERATOR_DEST_ATOP: - srcblend = DSBF_INVDESTALPHA; - dstblend = DSBF_SRCALPHA; - break; - case CAIRO_OPERATOR_XOR: - srcblend = DSBF_INVDESTALPHA; - dstblend = DSBF_INVSRCALPHA; - break; - case CAIRO_OPERATOR_ADD: - srcblend = DSBF_ONE; - dstblend = DSBF_ONE; - break; - case CAIRO_OPERATOR_SATURATE: - /* XXX This does not work. */ -#if 0 - srcblend = DSBF_SRCALPHASAT; - dstblend = DSBF_ONE; - break; -#endif - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - return FALSE; - } - - *ret_srcblend = srcblend; - *ret_dstblend = dstblend; - - return TRUE; -} - -static cairo_status_t -_directfb_buffer_surface_create (IDirectFB *dfb, - DFBSurfacePixelFormat format, - int width, - int height, - IDirectFBSurface **out) -{ - IDirectFBSurface *buffer; - DFBSurfaceDescription dsc; - DFBResult ret; - - dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT; - dsc.caps = DSCAPS_PREMULTIPLIED; - dsc.width = width; - dsc.height = height; - dsc.pixelformat = format; - - ret = dfb->CreateSurface (dfb, &dsc, &buffer); - if (ret) { - DirectFBError ("IDirectFB::CreateSurface()", ret); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *out = buffer; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_directfb_acquire_surface (cairo_directfb_surface_t *surface, - cairo_rectangle_int_t *intrest_rec, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra, - DFBSurfaceLockFlags lock_flags) -{ - IDirectFBSurface *buffer = NULL; - DFBRectangle source_rect; - cairo_surface_t *image; - pixman_image_t *pixman_image; - pixman_format_code_t pixman_format; - cairo_status_t status; - void *data; - int pitch; - - if (surface->pixman_format == PIXMAN_invalid) { - if (intrest_rec != NULL) { - source_rect.x = intrest_rec->x; - source_rect.y = intrest_rec->y; - source_rect.w = intrest_rec->width; - source_rect.h = intrest_rec->height; - } else { - source_rect.x = 0; - source_rect.y = 0; - surface->dfbsurface->GetSize (surface->dfbsurface, - &source_rect.w, &source_rect.h); - } - - if (surface->tmpsurface != NULL) { - int w, h; - - surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h); - if (w < source_rect.w || h < source_rect.h) { - surface->tmpsurface->Release (surface->tmpsurface); - surface->tmpsurface = NULL; - surface->tmpformat = PIXMAN_invalid; - } - } - - if (surface->tmpsurface == NULL) { - DFBSurfacePixelFormat format; - - D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface); - - format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content)); - status = - _directfb_buffer_surface_create (surface->dfb, format, - source_rect.w, source_rect.h, - &surface->tmpsurface); - if (unlikely (status)) - goto ERROR; - - surface->tmpformat = _directfb_to_pixman_format (format); - } - buffer = surface->tmpsurface; - pixman_format = surface->tmpformat; - - -/* surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps); - DFBSurfaceCapabilities caps; - if (caps & DSCAPS_FLIPPING) { - DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y, - .x2 = source_rect.x + source_rect.w - 1, - .y2 = source_rect.y + source_rect.h - 1 }; - surface->dfbsurface->Flip (surface->dfbsurface, ®ion, DSFLIP_BLIT); - } */ - buffer->Blit (buffer, surface->dfbsurface, &source_rect, 0, 0); - } else { - /*might be a subsurface get the offset*/ - surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect); - pixman_format = surface->pixman_format; - buffer = surface->dfbsurface; - } - - if (buffer->Lock (buffer, lock_flags, &data, &pitch)) { - D_DEBUG_AT (CairoDFB_Acquire, "Couldn't lock surface!\n"); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto ERROR; - } - - pixman_image = pixman_image_create_bits (pixman_format, - source_rect.w, source_rect.h, - data, pitch); - if (pixman_image == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto ERROR; - } - - image = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - status = image->status; - if (status) - goto ERROR; - - if (image_rect_out) { - image_rect_out->x = source_rect.x; - image_rect_out->y = source_rect.y; - image_rect_out->width = source_rect.w; - image_rect_out->height = source_rect.h; - } else { - /* lock for read */ - /* might be a subsurface */ - if (buffer == surface->dfbsurface) { - cairo_surface_set_device_offset (image, - source_rect.x, source_rect.y); - } - } - - *image_extra = buffer; - *image_out = (cairo_image_surface_t *) image; - return CAIRO_STATUS_SUCCESS; - -ERROR: - if (buffer) { - buffer->Unlock (buffer); - if (buffer != surface->dfbsurface) - buffer->Release (buffer); - } - return status; -} - -static cairo_surface_t * -_cairo_directfb_surface_create_internal (IDirectFB *dfb, - DFBSurfacePixelFormat format, - cairo_content_t content, - int width, - int height) -{ - cairo_directfb_surface_t *surface; - cairo_status_t status; - - surface = calloc (1, sizeof (cairo_directfb_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->dfb = dfb; - - if (width < 8 || height < 8) { - IDirectFBSurface *tmp; - DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height }; - - /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */ - status = _directfb_buffer_surface_create (dfb, format, - MAX (width, 8), MAX (height, 8), - &tmp); - if (status) { - free (surface); - return _cairo_surface_create_in_error (status); - } - - tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface); - tmp->Release (tmp); - } else { - status = _directfb_buffer_surface_create (dfb, format, - width, height, - &surface->dfbsurface); - if (status) { - free (surface); - return _cairo_surface_create_in_error (status); - } - } - - _cairo_surface_init (&surface->base, - &_cairo_directfb_surface_backend, - NULL, /* device */ - content); - surface->pixman_format = _directfb_to_pixman_format (format); - surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); - - surface->width = width; - surface->height = height; - surface->local = TRUE; - surface->blit_premultiplied = TRUE; - - return &surface->base; -} - -static cairo_surface_t * -_cairo_directfb_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_directfb_surface_t *other = abstract_src; - DFBSurfacePixelFormat format; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( src=%p, content=0x%x, width=%d, height=%d).\n", - __FUNCTION__, other, content, width, height); - - width = (width <= 0) ? 1 : width; - height = (height<= 0) ? 1 : height; - - format = _cairo_to_directfb_format (_cairo_format_from_content (content)); - return _cairo_directfb_surface_create_internal (other->dfb, format, - content, width, height); -} - -static cairo_status_t -_cairo_directfb_surface_finish (void *data) -{ - cairo_directfb_surface_t *surface = (cairo_directfb_surface_t *)data; - - D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface); - - if (surface->tmpsurface) { - surface->tmpsurface->Release (surface->tmpsurface); - surface->tmpsurface = NULL; - } - - if (surface->dfbsurface) { - surface->dfbsurface->Release (surface->dfbsurface); - surface->dfbsurface = NULL; - } - - if (surface->dfb) - surface->dfb = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_directfb_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p ).\n", __FUNCTION__, surface); - - return _directfb_acquire_surface (surface, NULL, image_out, - NULL, image_extra, DSLF_READ); -} - -static void -_cairo_directfb_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - IDirectFBSurface *buffer = image_extra; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( release=%p ).\n", __FUNCTION__, abstract_surface); - - buffer->Unlock (buffer); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_directfb_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p (%dx%d), interest_rect={ %u %u %u %u } ).\n", - __FUNCTION__, surface, surface->width, surface->height, - interest_rect ? interest_rect->x : 0, - interest_rect ? interest_rect->y : 0, - interest_rect ? interest_rect->width : (unsigned) surface->width, - interest_rect ? interest_rect->height : (unsigned) surface->height); - - return _directfb_acquire_surface (surface, interest_rect, image_out, - image_rect_out, image_extra, - DSLF_READ | DSLF_WRITE); -} - -static void -_cairo_directfb_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_directfb_surface_t *surface = abstract_surface; - IDirectFBSurface *buffer = image_extra; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p ).\n", __FUNCTION__, surface); - - buffer->Unlock (buffer); - - if (surface->dfbsurface != buffer) { - DFBRegion region = { - .x1 = interest_rect->x, - .y1 = interest_rect->y, - .x2 = interest_rect->x + interest_rect->width - 1, - .y2 = interest_rect->y + interest_rect->height - 1 - }; - surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX); - surface->dfbsurface->SetClip (surface->dfbsurface, ®ion); - surface->dfbsurface->Blit (surface->dfbsurface, - buffer, NULL, - image_rect->x, image_rect->y); - } - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_directfb_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_directfb_surface_t *surface = abstract_surface; - cairo_directfb_surface_t *clone; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src); - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - DFBSurfacePixelFormat format; - DFBResult ret; - pixman_image_t *pixman_image; - void *data; - int pitch; - - format = _directfb_from_pixman_format (image_src->pixman_format); - if (format == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clone = (cairo_directfb_surface_t *) - _cairo_directfb_surface_create_internal (surface->dfb, format, - image_src->base.content, - width, height); - if (unlikely (clone->base.status)) - return clone->base.status; - - ret = clone->dfbsurface->Lock (clone->dfbsurface, - DSLF_WRITE, (void *)&data, &pitch); - if (ret) { - DirectFBError ("IDirectFBSurface::Lock()", ret); - cairo_surface_destroy (&clone->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image = pixman_image_create_bits (clone->pixman_format, - width, height, - data, pitch); - if (unlikely (pixman_image == NULL)) { - DirectFBError ("IDirectFBSurface::Lock()", ret); - cairo_surface_destroy (&clone->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - image_src->pixman_image, - NULL, - pixman_image, - src_x, src_y, - 0, 0, - 0, 0, - width, height); - - pixman_image_unref (pixman_image); - - clone->dfbsurface->Unlock (clone->dfbsurface); - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -#if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS -static cairo_int_status_t -_directfb_prepare_composite (cairo_directfb_surface_t *dst, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - cairo_operator_t op, - int *src_x, int *src_y, - int *mask_x, int *mask_y, - unsigned int width, - unsigned int height, - cairo_directfb_surface_t **ret_src, - cairo_surface_attributes_t *ret_src_attr) -{ - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_status_t status; - DFBSurfaceBlittingFlags flags; - DFBSurfaceBlendFunction sblend; - DFBSurfaceBlendFunction dblend; - const cairo_color_t *color; - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_source (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _directfb_get_operator (op, &sblend, &dblend)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask_pattern) { - return CAIRO_INT_STATUS_UNSUPPORTED; - if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - const cairo_pattern_t *tmp; - int tmp_x, tmp_y; - - if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID || - sblend == DSBF_INVDESTALPHA) /* Doesn't work correctly */ - return CAIRO_INT_STATUS_UNSUPPORTED; - - D_DEBUG_AT (CairoDFB_Render, "Replacing src pattern by mask pattern.\n"); - - tmp = src_pattern; - tmp_x = *src_x; tmp_y = *src_y; - - src_pattern = mask_pattern; - *src_x = *mask_x; *src_y = *mask_y; - - mask_pattern = tmp; - *mask_x = tmp_x; *mask_y = tmp_y; - - if (sblend == DSBF_ONE) { - sblend = DSBF_SRCALPHA; - /*dblend = DSBF_INVSRCALPHA;*/ - } - } - - color = &((cairo_solid_pattern_t *) mask_pattern)->color; - } else { - color = _cairo_stock_color (CAIRO_STOCK_WHITE); - } - - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - *src_x, *src_y, width, height, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT, - (cairo_surface_t **) &src, - &src_attr); - if (status) - return status; - - if (src->base.backend != &_cairo_directfb_surface_backend || - src->dfb != dst->dfb) - { - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) { - if (sblend == DSBF_SRCALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVSRCALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_SRCALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVSRCALPHA) - dblend = DSBF_ZERO; - } - - if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { - if (sblend == DSBF_DESTALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVDESTALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_DESTALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVDESTALPHA) - dblend = DSBF_ZERO; - } - - flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) - ? DSBLIT_NOFX : DSBLIT_BLEND_ALPHACHANNEL; - if (! CAIRO_COLOR_IS_OPAQUE (color)) - flags |= DSBLIT_BLEND_COLORALPHA; - if (! _cairo_color_equal (color, _cairo_stock_color (CAIRO_STOCK_WHITE))) - flags |= DSBLIT_COLORIZE; - - dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); - - if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) { - dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); - dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); - } - - if (flags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE)) { - if (dst->blit_premultiplied) { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red_short >> 8, - color->green_short >> 8, - color->blue_short >> 8, - color->alpha_short >> 8); - } else { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red * 0xff, - color->green * 0xff, - color->blue * 0xff, - color->alpha * 0xff); - } - } - - *ret_src = src; - *ret_src_attr = src_attr; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_directfb_finish_composite (cairo_directfb_surface_t *dst, - const cairo_pattern_t *src_pattern, - cairo_surface_t *src, - cairo_surface_attributes_t *src_attr) -{ - _cairo_pattern_release_surface (src_pattern, src, src_attr); -} -#endif /* DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS */ - -#if DFB_COMPOSITE -static DFBAccelerationMask -_directfb_categorize_operation (cairo_surface_attributes_t *src_attr) -{ - cairo_matrix_t *m = &src_attr->matrix; - - if (m->xy != 0 || m->yx != 0 || m->xx < 0 || m->yy < 0) { - if (src_attr->extend != CAIRO_EXTEND_NONE) - return DFXL_NONE; - - return DFXL_TEXTRIANGLES; - } - - if (m->xx != 1 || m->yy != 1) { - if (src_attr->extend != CAIRO_EXTEND_NONE) - return DFXL_NONE; - - return DFXL_STRETCHBLIT; - } - - switch (src_attr->extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, - NULL, NULL)) - { - return DFXL_BLIT; - } - else - { - return DFXL_STRETCHBLIT; - } - - default: - case CAIRO_EXTEND_REFLECT: - case CAIRO_EXTEND_PAD: - return DFXL_NONE; - } -} - -static cairo_int_status_t -_cairo_directfb_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, int src_y, - int mask_x, int mask_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_directfb_surface_t *dst = abstract_dst; - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_bool_t is_integer_translation; - DFBAccelerationMask accel, mask; - cairo_int_status_t status; - int tx, ty; - - D_DEBUG_AT (CairoDFB_Render, - "%s( op=%d, src_pattern=%p, mask_pattern=%p, dst=%p," - " src_x=%d, src_y=%d, mask_x=%d, mask_y=%d, dst_x=%d," - " dst_y=%d, width=%u, height=%u ).\n", - __FUNCTION__, op, src_pattern, mask_pattern, dst, - src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op, - &src_x, &src_y, &mask_x, &mask_y, - width, height, &src, &src_attr); - if (status) - return status; - - accel = _directfb_categorize_operation (&src_attr); - if (accel == DFXL_NONE) { - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, - src->dfbsurface, - &mask); - if ((mask & accel) == 0) { - D_DEBUG_AT (CairoDFB_Render, "No acceleration (%08x)!\n", accel); - if (accel != DFXL_BLIT) { - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - src_x += src_attr.x_offset; - src_y += src_attr.y_offset; - - switch ((int) accel) { - case DFXL_BLIT: - { - DFBRectangle sr; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, - &tx, &ty); - assert (is_integer_translation); - - sr.x = src_x + tx; - sr.y = src_y + ty; - sr.w = width; - sr.h = height; - - if (src_attr.extend == CAIRO_EXTEND_NONE) { - D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->Blit (dst->dfbsurface, - src->dfbsurface, - &sr, dst_x, dst_y)); - } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) { - DFBRegion clip; - - clip.x1 = dst_x; - clip.y1 = dst_y; - clip.x2 = dst_x + width - 1; - clip.y2 = dst_y + height - 1; - - D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n"); - - RUN_CLIPPED (dst, clip_region, &clip, - dst->dfbsurface->TileBlit (dst->dfbsurface, - src->dfbsurface, - &sr, dst_x, dst_y)); - } - break; - } - - case DFXL_STRETCHBLIT: - { - DFBRectangle sr, dr; - double x1, y1, x2, y2; - - TRANSFORM_POINT2X (src_attr.matrix, - src_x, src_y, x1, y1); - TRANSFORM_POINT2X (src_attr.matrix, - src_x+width, src_y+height, x2, y2); - - sr.x = floor (x1); - sr.y = floor (y1); - sr.w = ceil (x2) - sr.x; - sr.h = ceil (y2) - sr.y; - - dr.x = dst_x; - dr.y = dst_y; - dr.w = width; - dr.h = height; - - D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->StretchBlit (dst->dfbsurface, - src->dfbsurface, - &sr, &dr)); - break; - } - - case DFXL_TEXTRIANGLES: - { - DFBRegion clip; - DFBVertex v[4]; - float x1, y1, x2, y2; - int w, h; - - status = cairo_matrix_invert (&src_attr.matrix); - /* guaranteed by cairo_pattern_set_matrix (); */ - assert (status == CAIRO_STATUS_SUCCESS); - - x1 = src_x; - y1 = src_y; - x2 = width + x1; - y2 = height + y1; - - src->dfbsurface->GetSize (src->dfbsurface, &w, &h); - - TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y); - v[0].z = 0; - v[0].w = 1; - v[0].s = x1 / w; - v[0].t = y1 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y); - v[1].z = 0; - v[1].w = 1; - v[1].s = x2 / w; - v[1].t = y1 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y); - v[2].z = 0; - v[2].w = 1; - v[2].s = x2 / w; - v[2].t = y2 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y); - v[3].z = 0; - v[3].w = 1; - v[3].s = x1 / w; - v[3].t = y2 / h; - - clip.x1 = dst_x; - clip.y1 = dst_y; - clip.x2 = dst_x + width - 1; - clip.y2 = dst_y + height - 1; - - D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - - RUN_CLIPPED (dst, clip_region, &clip, - dst->dfbsurface->TextureTriangles (dst->dfbsurface, - src->dfbsurface, - v, NULL, - 4, DTTF_FAN)); - break; - } - - default: - D_BUG ("Unexpected operation"); - break; - } - - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - - return CAIRO_STATUS_SUCCESS; -} -#endif /* DFB_COMPOSITE */ - -#if DFB_RECTANGLES -static cairo_int_status_t -_cairo_directfb_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int n_rects) -{ - cairo_directfb_surface_t *dst = abstract_surface; - DFBSurfaceDrawingFlags flags; - DFBSurfaceBlendFunction sblend; - DFBSurfaceBlendFunction dblend; - DFBRectangle r[n_rects]; - int i; - - D_DEBUG_AT (CairoDFB_Render, - "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n", - __FUNCTION__, dst, op, color, rects, n_rects); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _directfb_get_operator (op, &sblend, &dblend)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (CAIRO_COLOR_IS_OPAQUE (color)) { - if (sblend == DSBF_SRCALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVSRCALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_SRCALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVSRCALPHA) - dblend = DSBF_ZERO; - } - if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { - if (sblend == DSBF_DESTALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVDESTALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_DESTALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVDESTALPHA) - dblend = DSBF_ZERO; - } - - flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND; - dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags); - if (flags & DSDRAW_BLEND) { - dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); - dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); - } - - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red_short >> 8, - color->green_short >> 8, - color->blue_short >> 8, - color->alpha_short >> 8); - - for (i = 0; i < n_rects; i++) { - r[i].x = rects[i].x; - r[i].y = rects[i].y; - r[i].w = rects[i].width; - r[i].h = rects[i].height; - } - - RUN_CLIPPED (dst, NULL, NULL, - dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects)); - - return CAIRO_STATUS_SUCCESS; -} -#endif - -#if DFB_COMPOSITE_TRAPEZOIDS -static cairo_int_status_t -_cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_directfb_surface_t *dst = abstract_dst; - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_status_t status; - DFBAccelerationMask accel; - - D_DEBUG_AT (CairoDFB_Render, - "%s( op=%d, pattern=%p, dst=%p, antialias=%d," - " src_x=%d, src_y=%d, dst_x=%d, dst_y=%d," - " width=%u, height=%u, traps=%p, num_traps=%d ).\n", - __FUNCTION__, op, pattern, dst, antialias, - src_x, src_y, dst_x, dst_y, width, height, traps, num_traps); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (antialias != CAIRO_ANTIALIAS_NONE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Textures are not supported yet. */ - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _directfb_prepare_composite (dst, pattern, NULL, op, - &src_x, &src_y, NULL, NULL, - width, height, &src, &src_attr); - if (status) - return status; - - dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, - src->dfbsurface, - &accel); - - status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (accel & DFXL_TEXTRIANGLES) { - DFBVertex vertex[6*num_traps]; - DFBVertex *v = &vertex[0]; - int n = 0; - -#define ADD_TRI_V(V, X, Y) do { \ - (V)->x = (X); (V)->y = (Y); (V)->w = 1; (V)->z = (V)->s = (V)->t = 0; \ -} while (0) -#define ADD_TRI(id, x1, y1, x2, y2, x3, y3) do {\ - const int p = (id)*3;\ - ADD_TRI_V (v+p+0, x1, y1); \ - ADD_TRI_V (v+p+1, x2, y2); \ - ADD_TRI_V (v+p+2, x3, y3); \ -} while (0) - while (num_traps--) { - double lx1, ly1, lx2, ly2; - double rx1, ry1, rx2, ry2; - - lx1 = _cairo_fixed_to_double (traps->left.p1.x); - ly1 = _cairo_fixed_to_double (traps->left.p1.y); - lx2 = _cairo_fixed_to_double (traps->left.p2.x); - ly2 = _cairo_fixed_to_double (traps->left.p2.y); - rx1 = _cairo_fixed_to_double (traps->right.p1.x); - ry1 = _cairo_fixed_to_double (traps->right.p1.y); - rx2 = _cairo_fixed_to_double (traps->right.p2.x); - ry2 = _cairo_fixed_to_double (traps->right.p2.y); - - if (traps->left.p1.y < traps->top) { - double y = _cairo_fixed_to_double (traps->top); - if (lx2 != lx1) - lx1 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; - ly1 = y; - } - if (traps->left.p2.y > traps->bottom) { - double y = _cairo_fixed_to_double (traps->bottom); - if (lx2 != lx1) - lx2 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; - ly2 = y; - } - - if (traps->right.p1.y < traps->top) { - double y = _cairo_fixed_to_double (traps->top); - if (rx2 != rx1) - rx1 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; - ry1 = y; - } - if (traps->right.p2.y > traps->bottom) { - double y = _cairo_fixed_to_double (traps->bottom); - if (rx2 != rx1) - rx2 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; - ry2 = y; - } - - if (lx1 == rx1 && ly1 == ry1) { - ADD_TRI (0, lx2, ly2, lx1, ly1, rx2, ry2); - v += 3; - n += 3; - } else if (lx2 == rx2 && ly2 == ry2) { - ADD_TRI (0, lx1, ly1, lx2, ly2, rx1, ry1); - v += 3; - n += 3; - } else { - ADD_TRI (0, lx1, ly1, rx1, ry1, lx2, ly2); - ADD_TRI (1, lx2, ly2, rx1, ry1, rx2, ry2); - v += 6; - n += 6; - } - - traps++; - } -#undef ADD_TRI -#undef ADD_TRI_V - - D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->TextureTriangles (dst->dfbsurface, - src->dfbsurface, - vertex, NULL, n, - DTTF_LIST)); - - status = CAIRO_STATUS_SUCCESS; - } - - _directfb_finish_composite (dst, pattern, &src->base, &src_attr); - - return status; -} -#endif /* DFB_COMPOSITE_TRAPEZOIDS */ - -static cairo_bool_t -_cairo_directfb_abstract_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( surface=%p, rectangle=%p ).\n", - __FUNCTION__, surface, rectangle); - - if (!surface->local) { - surface->dfbsurface->GetSize (surface->dfbsurface, - &surface->width, &surface->height); - } - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -#if DFB_SHOW_GLYPHS -static cairo_status_t -_directfb_allocate_font_cache (IDirectFB *dfb, - int width, int height, - cairo_directfb_font_cache_t **out) -{ - cairo_directfb_font_cache_t *cache; - cairo_status_t status; - - cache = calloc (1, sizeof (cairo_directfb_font_cache_t)); - if (cache == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cache->dfb = dfb; - status = _directfb_buffer_surface_create (dfb, - _directfb_argb_font ? DSPF_ARGB : DSPF_A8, - width, height, - &cache->dfbsurface); - if (status) { - free (cache); - return status; - } - - cache->width = width; - cache->height = height; - *out = cache; - return CAIRO_STATUS_SUCCESS; -} - -static void -_directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache) -{ - cache->dfbsurface->Release (cache->dfbsurface); - free (cache); -} - -/* XXX hook into rtree font cache from drm */ -static cairo_status_t -_directfb_acquire_font_cache (cairo_directfb_surface_t *surface, - cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_directfb_font_cache_t **ret_cache, - DFBRectangle *rects, - DFBPoint *points, - int *ret_num) -{ - cairo_status_t status; - cairo_scaled_glyph_t *chars[num_glyphs]; - int num_chars = 0; - cairo_directfb_font_cache_t *cache = NULL; - int n = 0; - int x = 0; - int y = 0; - int w = 8; - int h = 8; - int i; - - D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs ); - - _cairo_scaled_font_freeze_cache (scaled_font); - - if (scaled_font->surface_private) { - cache = scaled_font->surface_private; - x = cache->x; - y = cache->y; - } - - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - cairo_image_surface_t *img; - - D_DEBUG_AT (CairoDFB_Font, " -> [%2d] = %4lu\n", i, glyphs[i].index ); - - status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - img = scaled_glyph->surface; - switch (img->format) { - case CAIRO_FORMAT_A1: - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_ARGB32: - break; - case CAIRO_FORMAT_RGB24: - default: - D_DEBUG_AT (CairoDFB_Font, - " -> Unsupported font format %d!\n", img->format); - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - points[n].x = _cairo_lround (glyphs[i].x - img->base.device_transform.x0); - points[n].y = _cairo_lround (glyphs[i].y - img->base.device_transform.y0); - - // D_DEBUG_AT (CairoDFB_Font, " (%4d,%4d) [%2d]\n", points[n].x, points[n].y, n ); - - if (points[n].x >= surface->width || - points[n].y >= surface->height || - points[n].x+img->width <= 0 || - points[n].y+img->height <= 0) - { - continue; - } - - if (scaled_glyph->surface_private == NULL) { - DFBRectangle *rect; - - if (x+img->width > 2048) { - x = 0; - y = h; - h = 0; - } - - rects[n].x = x; - rects[n].y = y; - rects[n].w = img->width; - rects[n].h = img->height; - - x += img->width; - h = MAX (h, img->height); - w = MAX (w, x); - - /* Remember glyph location */ - rect = malloc (sizeof (DFBRectangle)); - if (rect == NULL) { - _cairo_scaled_font_thaw_cache (scaled_font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - *rect = rects[n]; - - scaled_glyph->surface_private = rect; - chars[num_chars++] = scaled_glyph; - - D_DEBUG_AT (CairoDFB_Font, " -> loading at %4d,%2d <- rect %p, img %p, entry %p\n", - rects[n].x, rects[n].y, rect, scaled_glyph->surface, scaled_glyph); - } else { - rects[n] = *(DFBRectangle *) scaled_glyph->surface_private; - - D_DEBUG_AT (CairoDFB_Font, " -> exists at %4d,%2d\n", rects[n].x, rects[n].y); - } - - n++; - } - - if (n == 0) { - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - h += y; - w = MAX (w, 8); - h = MAX (h, 8); - - /* XXX query maximum surface size */ - if (w > 2048 || h > 2048) { - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (cache) { - if (cache->width < w || cache->height < h) { - cairo_directfb_font_cache_t *new_cache; - - w = MAX (w, cache->width); - h = MAX (h, cache->height); - - D_DEBUG_AT (CairoDFB_Font, " -> Reallocating font cache (%dx%d).\n", w, h); - - status = _directfb_allocate_font_cache (surface->dfb, - w, h, - &new_cache); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - new_cache->dfbsurface->Blit (new_cache->dfbsurface, - cache->dfbsurface, NULL, 0, 0); - - _directfb_destroy_font_cache (cache); - scaled_font->surface_private = cache = new_cache; - } - } else { - D_DEBUG_AT (CairoDFB_Font, " -> Allocating font cache (%dx%d).\n", w, h); - - status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - scaled_font->surface_backend = &_cairo_directfb_surface_backend; - scaled_font->surface_private = cache; - } - - if (num_chars) { - unsigned char *data; - int pitch; - - if (cache->dfbsurface->Lock (cache->dfbsurface, - DSLF_WRITE, (void *)&data, &pitch)) - { - _cairo_scaled_font_thaw_cache (scaled_font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - D_DEBUG_AT (CairoDFB_Font, " => %d chars to load, cache %dx%d\n", num_chars, cache->width, cache->height); - - for (i = 0; i < num_chars; i++) { - cairo_image_surface_t *img = chars[i]->surface; - DFBRectangle *rect = chars[i]->surface_private; - unsigned char *dst = data; - unsigned char *src; - int j; - - D_DEBUG_AT (CairoDFB_Font, " -> loading [%2d] <- rect %p, img %p, entry %p\n", i, rect, img, chars[i]); - - src = img->data; - - D_DEBUG_AT (CairoDFB_Font, " from %p\n", src); - - dst += rect->y * pitch + (_directfb_argb_font ? (rect->x<<2) : rect->x); - - D_DEBUG_AT (CairoDFB_Font, " to %4d,%2d (%p)\n", rect->x, rect->y, dst); - - if (img->format == CAIRO_FORMAT_A1) { - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - for (j = 0; j < rect->w; j++) - ((uint32_t *) dst)[j] = (src[j>>3] & (1 << (j&7))) ? 0xffffffff : 0; - } else { - for (j = 0; j < rect->w; j++) - dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0; - } - - dst += pitch; - src += img->stride; - } - } else if (img->format == CAIRO_FORMAT_A8) { - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - for (j = 0; j < rect->w; j++) - ((uint32_t *) dst)[j] = src[j] * 0x01010101; - } else { - direct_memcpy (dst, src, rect->w); - } - - dst += pitch; - src += img->stride; - } - } else { /* ARGB32 */ - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - direct_memcpy (dst, src, rect->w<<2); - } else { - for (j = 0; j < rect->w; j++) - dst[j] = ((uint32_t *) src)[j] >> 24; - } - - dst += pitch; - src += img->stride; - } - } - } - - cache->dfbsurface->Unlock (cache->dfbsurface); - } - - _cairo_scaled_font_thaw_cache (scaled_font); - - cache->x = x; - cache->y = y; - - D_DEBUG_AT (CairoDFB_Font, " => cache %d,%d, %p [%d]\n", x, y, cache, n); - - *ret_cache = cache; - *ret_num = n; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_directfb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_directfb_font_cache_t *cache = scaled_font->surface_private; - - D_DEBUG_AT (CairoDFB_Font, - "%s( scaled_font=%p ).\n", __FUNCTION__, scaled_font); - - if (cache != NULL) { - _directfb_destroy_font_cache (cache); - scaled_font->surface_private = NULL; - } -} - -static void -_cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - D_DEBUG_AT (CairoDFB_Font, - "%s( scaled_glyph=%p, scaled_font=%p ).\n", - __FUNCTION__, scaled_glyph, scaled_font); - - if (scaled_glyph->surface_private != NULL) { - free (scaled_glyph->surface_private); - scaled_glyph->surface_private = NULL; - } -} - -static cairo_int_status_t -_cairo_directfb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_directfb_surface_t *dst = abstract_dst; - cairo_directfb_font_cache_t *cache; - cairo_status_t status; - DFBSurfaceBlittingFlags flags; - DFBSurfaceBlendFunction sblend; - DFBSurfaceBlendFunction dblend; - DFBRectangle rects[num_glyphs]; - DFBPoint points[num_glyphs]; - int num; - const cairo_color_t *color; - cairo_region_t *clip_region = NULL; - - D_DEBUG_AT (CairoDFB_Font, - "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n", - __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Fallback if we need to emulate clip regions */ - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - } - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _directfb_get_operator (op, &sblend, &dblend) || - sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _directfb_acquire_font_cache (dst, scaled_font, glyphs, num_glyphs, - &cache, &rects[0], &points[0], &num); - if (status) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - status = CAIRO_STATUS_SUCCESS; - return status; - } - - color = &((cairo_solid_pattern_t *) pattern)->color; - - flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE; - if (! CAIRO_COLOR_IS_OPAQUE (color)) - flags |= DSBLIT_BLEND_COLORALPHA; - - if (!_directfb_argb_font) { - if (sblend == DSBF_ONE) { - sblend = DSBF_SRCALPHA; - if (dblend == DSBF_ZERO) - dblend = DSBF_INVSRCALPHA; - } - } - - dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); - dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); - dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); - if (dst->blit_premultiplied) { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red_short >> 8, - color->green_short >> 8, - color->blue_short >> 8, - color->alpha_short >> 8); - } else { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red * 0xff, - color->green * 0xff, - color->blue * 0xff, - color->alpha * 0xff); - } - - D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->BatchBlit (dst->dfbsurface, - cache->dfbsurface, rects, points, num)); - - return CAIRO_STATUS_SUCCESS; -} -#endif /* DFB_SHOW_GLYPHS */ - - -static cairo_bool_t -_cairo_directfb_surface_is_similar (void *surface_a, void *surface_b) -{ - cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a; - cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b; - - return a->dfb == b->dfb; -} - -static cairo_surface_backend_t -_cairo_directfb_surface_backend = { - CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/ - _cairo_directfb_surface_create_similar,/*create_similar*/ - _cairo_directfb_surface_finish, /*finish*/ - _cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/ - _cairo_directfb_surface_release_source_image,/*release_source_image*/ - _cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/ - _cairo_directfb_surface_release_dest_image,/*release_dest_image*/ - _cairo_directfb_surface_clone_similar,/*clone_similar*/ -#if DFB_COMPOSITE - _cairo_directfb_surface_composite,/*composite*/ -#else - NULL,/*composite*/ -#endif -#if DFB_RECTANGLES - _cairo_directfb_surface_fill_rectangles,/*fill_rectangles*/ -#else - NULL,/*fill_rectangles*/ -#endif -#if DFB_COMPOSITE_TRAPEZOIDS - _cairo_directfb_surface_composite_trapezoids,/*composite_trapezoids*/ -#else - NULL,/*composite_trapezoids*/ -#endif - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_directfb_abstract_surface_get_extents,/* get_extents */ - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_scaled_font_fini,/* scaled_font_fini */ - _cairo_directfb_surface_scaled_glyph_fini,/* scaled_glyph_fini */ -#else - NULL, - NULL, -#endif - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_show_glyphs,/* show_glyphs */ -#else - NULL, /* show_glyphs */ -#endif - NULL, /* snapshot */ - _cairo_directfb_surface_is_similar, -}; - - -static void -cairo_directfb_surface_backend_init (IDirectFB *dfb) -{ - static int done = 0; - - if (done) - return; - - if (getenv ("CAIRO_DIRECTFB_NO_ACCEL")) { -#if DFB_RECTANGLES - _cairo_directfb_surface_backend.fill_rectangles = NULL; -#endif -#if DFB_COMPOSITE - _cairo_directfb_surface_backend.composite = NULL; -#endif -#if DFB_COMPOSITE_TRAPEZOIDS - _cairo_directfb_surface_backend.composite_trapezoids = NULL; -#endif -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_backend.scaled_font_fini = NULL; - _cairo_directfb_surface_backend.scaled_glyph_fini = NULL; - _cairo_directfb_surface_backend.show_glyphs = NULL; -#endif - D_DEBUG_AT (CairoDFB_Surface, "Acceleration disabled.\n"); - } else { - DFBGraphicsDeviceDescription dsc; - - dfb->GetDeviceDescription (dfb, &dsc); - -#if DFB_COMPOSITE - // if (!(dsc.acceleration_mask & DFXL_BLIT)) - // _cairo_directfb_surface_backend.composite = NULL; -#endif - -#if DFB_COMPOSITE_TRAPEZOIDS - // if (!(dsc.acceleration_mask & DFXL_TEXTRIANGLES)) - // _cairo_directfb_surface_backend.composite_trapezoids = NULL; -#endif - } - - if (getenv ("CAIRO_DIRECTFB_ARGB_FONT")) { - _directfb_argb_font = 1; - D_DEBUG_AT (CairoDFB_Surface, "Using ARGB fonts.\n"); - } - - done = 1; -} - -cairo_surface_t * -cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) -{ - cairo_directfb_surface_t *surface; - DFBSurfacePixelFormat format; - DFBSurfaceCapabilities caps; - - D_ASSERT (dfb != NULL); - D_ASSERT (dfbsurface != NULL); - - cairo_directfb_surface_backend_init (dfb); - - surface = calloc (1, sizeof (cairo_directfb_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - dfbsurface->AddRef (dfbsurface); - dfbsurface->GetPixelFormat (dfbsurface, &format); - dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height); - surface->dfb = dfb; - surface->dfbsurface = dfbsurface; - surface->pixman_format = _directfb_to_pixman_format (format); - surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); - - dfbsurface->GetCapabilities (dfbsurface, &caps); - if (caps & DSCAPS_PREMULTIPLIED) - surface->blit_premultiplied = TRUE; - - _cairo_surface_init (&surface->base, - &_cairo_directfb_surface_backend, - NULL, /* device */ - _directfb_format_to_content (format)); - - return &surface->base; -} diff --git a/libs/cairo/cairo/src/cairo-directfb.h b/libs/cairo/cairo/src/cairo-directfb.h deleted file mode 100644 index 029b43ef7..000000000 --- a/libs/cairo/cairo/src/cairo-directfb.h +++ /dev/null @@ -1,35 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Environment variables affecting the backend: - * - * %CAIRO_DIRECTFB_NO_ACCEL (boolean) - * if found, disables acceleration at all - * - * %CAIRO_DIRECTFB_ARGB_FONT (boolean) - * if found, enables using ARGB fonts instead of A8 - */ - -#ifndef CAIRO_DIRECTFB_H -#define CAIRO_DIRECTFB_H - -#include "cairo.h" - -#if CAIRO_HAS_DIRECTFB_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *surface); - -CAIRO_END_DECLS - -#else /*CAIRO_HAS_DIRECTFB_SURFACE*/ -# error Cairo was not compiled with support for the directfb backend -#endif /*CAIRO_HAS_DIRECTFB_SURFACE*/ - -#endif /*CAIRO_DIRECTFB_H*/ diff --git a/libs/cairo/cairo/src/cairo-drm.h b/libs/cairo/cairo/src/cairo-drm.h deleted file mode 100644 index bbdb28ba2..000000000 --- a/libs/cairo/cairo/src/cairo-drm.h +++ /dev/null @@ -1,92 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-dwrite-font.cpp b/libs/cairo/cairo/src/cairo-dwrite-font.cpp deleted file mode 100644 index 391f2e8a7..000000000 --- a/libs/cairo/cairo/src/cairo-dwrite-font.cpp +++ /dev/null @@ -1,1590 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-win32-private.h" -#include "cairo-surface-private.h" -#include "cairo-clip-private.h" - -#include "cairo-d2d-private.h" -#include "cairo-dwrite-private.h" -#include "cairo-truetype-subset-private.h" -#include - -typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( - D2D1_FACTORY_TYPE factoryType, - REFIID iid, - CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, - void **factory -); - -#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS - -// Forward declarations -cairo_int_status_t -_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, - DWRITE_MATRIX *transform, - DWRITE_GLYPH_RUN *run, - COLORREF color, - const RECT &area); - -cairo_int_status_t -_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, - DWRITE_MATRIX *transform, - DWRITE_GLYPH_RUN *run, - COLORREF color, - cairo_dwrite_scaled_font_t *scaled_font, - const RECT &area); - -class D2DFactory -{ -public: - static ID2D1Factory *Instance() - { - if (!mFactoryInstance) { - D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); - if (createD2DFactory) { - D2D1_FACTORY_OPTIONS options; - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; - createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&mFactoryInstance); - } - } - return mFactoryInstance; - } - - static ID2D1DCRenderTarget *RenderTarget() - { - if (!mRenderTarget) { - if (!Instance()) { - return NULL; - } - // Create a DC render target. - D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( - D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED), - 0, - 0, - D2D1_RENDER_TARGET_USAGE_NONE, - D2D1_FEATURE_LEVEL_DEFAULT - ); - - Instance()->CreateDCRenderTarget(&props, &mRenderTarget); - } - return mRenderTarget; - } - -private: - static ID2D1Factory *mFactoryInstance; - static ID2D1DCRenderTarget *mRenderTarget; -}; - -IDWriteFactory *DWriteFactory::mFactoryInstance = NULL; -IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL; -IDWriteRenderingParams *DWriteFactory::mDefaultRenderingParams = NULL; -IDWriteRenderingParams *DWriteFactory::mCustomClearTypeRenderingParams = NULL; -IDWriteRenderingParams *DWriteFactory::mForceGDIClassicRenderingParams = NULL; -FLOAT DWriteFactory::mGamma = -1.0; -FLOAT DWriteFactory::mEnhancedContrast = -1.0; -FLOAT DWriteFactory::mClearTypeLevel = -1.0; -int DWriteFactory::mPixelGeometry = -1; -int DWriteFactory::mRenderingMode = -1; - -ID2D1Factory *D2DFactory::mFactoryInstance = NULL; -ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL; - -/* Functions cairo_font_face_backend_t */ -static cairo_status_t -_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face); -static void -_cairo_dwrite_font_face_destroy (void *font_face); - -static cairo_status_t -_cairo_dwrite_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font); - -const cairo_font_face_backend_t _cairo_dwrite_font_face_backend = { - CAIRO_FONT_TYPE_DWRITE, - _cairo_dwrite_font_face_create_for_toy, - _cairo_dwrite_font_face_destroy, - _cairo_dwrite_font_face_scaled_font_create -}; - -/* Functions cairo_scaled_font_backend_t */ - -void _cairo_dwrite_scaled_font_fini(void *scaled_font); - -static cairo_warn cairo_int_status_t -_cairo_dwrite_scaled_glyph_init(void *scaled_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info); - -cairo_warn cairo_int_status_t -_cairo_dwrite_scaled_show_glyphs(void *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs); - -cairo_int_status_t -_cairo_dwrite_load_truetype_table(void *scaled_font, - unsigned long tag, - long offset, - unsigned char *buffer, - unsigned long *length); - -unsigned long -_cairo_dwrite_ucs4_to_index(void *scaled_font, - uint32_t ucs4); - -const cairo_scaled_font_backend_t _cairo_dwrite_scaled_font_backend = { - CAIRO_FONT_TYPE_DWRITE, - _cairo_dwrite_scaled_font_fini, - _cairo_dwrite_scaled_glyph_init, - NULL, - _cairo_dwrite_ucs4_to_index, - _cairo_dwrite_scaled_show_glyphs, - _cairo_dwrite_load_truetype_table, - NULL, -}; - -/* Helper conversion functions */ - -/** - * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo - * uses column vectors. Hence the transposition. - * - * \param Cairo matrix - * \return D2D matrix - */ -static D2D1::Matrix3x2F -_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix) -{ - return D2D1::Matrix3x2F((FLOAT)matrix->xx, - (FLOAT)matrix->yx, - (FLOAT)matrix->xy, - (FLOAT)matrix->yy, - (FLOAT)matrix->x0, - (FLOAT)matrix->y0); -} - - -/** - * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row - * vectors where cairo uses column vectors. Hence the transposition. - * - * \param Cairo matrix - * \return DirectWrite matrix - */ -DWRITE_MATRIX -_cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix) -{ - DWRITE_MATRIX dwmat; - dwmat.m11 = (FLOAT)matrix->xx; - dwmat.m12 = (FLOAT)matrix->yx; - dwmat.m21 = (FLOAT)matrix->xy; - dwmat.m22 = (FLOAT)matrix->yy; - dwmat.dx = (FLOAT)matrix->x0; - dwmat.dy = (FLOAT)matrix->y0; - return dwmat; -} - -/* Helper functions for cairo_dwrite_scaled_glyph_init */ -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_metrics - (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); - -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_surface - (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); - -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_path - (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); - -/* implement the font backend interface */ - -static cairo_status_t -_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - WCHAR *face_name; - int face_name_len; - - if (!DWriteFactory::Instance()) { - return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; - } - - face_name_len = MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, NULL, 0); - face_name = new WCHAR[face_name_len]; - MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); - - IDWriteFontFamily *family = DWriteFactory::FindSystemFontFamily(face_name); - delete face_name; - if (!family) { - *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; - return CAIRO_STATUS_FONT_TYPE_MISMATCH; - } - - DWRITE_FONT_WEIGHT weight; - switch (toy_face->weight) { - case CAIRO_FONT_WEIGHT_BOLD: - weight = DWRITE_FONT_WEIGHT_BOLD; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - weight = DWRITE_FONT_WEIGHT_NORMAL; - break; - } - - DWRITE_FONT_STYLE style; - switch (toy_face->slant) { - case CAIRO_FONT_SLANT_ITALIC: - style = DWRITE_FONT_STYLE_ITALIC; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - style = DWRITE_FONT_STYLE_OBLIQUE; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - style = DWRITE_FONT_STYLE_NORMAL; - break; - } - - cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t*)malloc(sizeof(cairo_dwrite_font_face_t)); - HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &face->font); - if (SUCCEEDED(hr)) { - // Cannot use C++ style new since cairo deallocates this. - *font_face = (cairo_font_face_t*)face; - _cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend); - } else { - free(face); - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_dwrite_font_face_destroy (void *font_face) -{ - cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); - if (dwrite_font_face->dwriteface) - dwrite_font_face->dwriteface->Release(); - if (dwrite_font_face->font) - dwrite_font_face->font->Release(); -} - - -static inline unsigned short -read_short(const char *buf) -{ - return be16_to_cpu(*(unsigned short*)buf); -} - -void -_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, - int num_glyphs, - cairo_dwrite_scaled_font_t *scaled_font, - AutoDWriteGlyphRun *run, - cairo_bool_t *transformed) -{ - run->allocate(num_glyphs); - - UINT16 *indices = const_cast(run->glyphIndices); - FLOAT *advances = const_cast(run->glyphAdvances); - DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); - - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); - - run->bidiLevel = 0; - run->fontFace = dwriteff->dwriteface; - run->glyphCount = num_glyphs; - run->isSideways = FALSE; - - if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 && - scaled_font->mat.xx == scaled_font->base.font_matrix.xx && - scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { - // Fast route, don't actually use a transform but just - // set the correct font size. - *transformed = 0; - - run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; - - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - - offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y); - offsets[i].advanceOffset = (FLOAT)(glyphs[i].x); - advances[i] = 0.0; - } - } else { - *transformed = 1; - // Transforming positions by the inverse matrix, then by the original - // matrix later may introduce small errors, especially because the - // D2D matrix is single-precision whereas the cairo one is double. - // This is a problem when glyph positions were originally at exactly - // half-pixel locations, which eventually round to whole pixels for - // GDI rendering - the errors introduced here cause them to round in - // unpredictable directions, instead of all rounding in a consistent - // way, leading to poor glyph spacing (bug 675383). - // To mitigate this, nudge the positions by a tiny amount to try and - // ensure that after the two transforms, they'll still round in a - // consistent direction. - const double EPSILON = 0.0001; - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - double x = glyphs[i].x + EPSILON; - double y = glyphs[i].y; - cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); - // Since we will multiply by our ctm matrix later for rotation effects - // and such, adjust positions by the inverse matrix now. Y-axis is - // inverted! Therefor the offset is -y. - offsets[i].ascenderOffset = -(FLOAT)y; - offsets[i].advanceOffset = (FLOAT)x; - advances[i] = 0.0; - } - // The font matrix takes care of the scaling if we have a transform, - // emSize should be 1. - run->fontEmSize = 1.0f; - } -} - -#define GASP_TAG 0x70736167 -#define GASP_DOGRAY 0x2 - -static cairo_bool_t -do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) -{ - void *tableContext; - char *tableData; - UINT32 tableSize; - BOOL exists; - dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); - - if (exists) { - if (tableSize < 4) { - dwface->ReleaseFontTable(tableContext); - return true; - } - struct gaspRange { - unsigned short maxPPEM; // Stored big-endian - unsigned short behavior; // Stored big-endian - }; - unsigned short numRanges = read_short(tableData + 2); - if (tableSize < (UINT)4 + numRanges * 4) { - dwface->ReleaseFontTable(tableContext); - return true; - } - gaspRange *ranges = (gaspRange *)(tableData + 4); - for (int i = 0; i < numRanges; i++) { - if (be16_to_cpu(ranges[i].maxPPEM) > ppem) { - if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) { - dwface->ReleaseFontTable(tableContext); - return false; - } - break; - } - } - dwface->ReleaseFontTable(tableContext); - } - return true; -} - -static cairo_status_t -_cairo_dwrite_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font) -{ - cairo_dwrite_font_face_t *font_face = static_cast(abstract_face); - - // Must do malloc and not C++ new, since Cairo frees this. - cairo_dwrite_scaled_font_t *dwriteFont = (cairo_dwrite_scaled_font_t*)malloc(sizeof(cairo_dwrite_scaled_font_t)); - *font = reinterpret_cast(dwriteFont); - _cairo_scaled_font_init(&dwriteFont->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend); - - cairo_font_extents_t extents; - - DWRITE_FONT_METRICS metrics; - font_face->dwriteface->GetMetrics(&metrics); - - extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; - extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; - extents.height = (FLOAT)(metrics.ascent + metrics.descent + metrics.lineGap) / metrics.designUnitsPerEm; - extents.max_x_advance = 14.0; - extents.max_y_advance = 0.0; - - dwriteFont->mat = dwriteFont->base.ctm; - cairo_matrix_multiply(&dwriteFont->mat, &dwriteFont->mat, font_matrix); - dwriteFont->mat_inverse = dwriteFont->mat; - cairo_matrix_invert (&dwriteFont->mat_inverse); - - cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; - - dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - - // The following code detects the system quality at scaled_font creation time, - // this means that if cleartype settings are changed but the scaled_fonts - // are re-used, they might not adhere to the new system setting until re- - // creation. - switch (cairo_win32_get_system_text_quality()) { - case CLEARTYPE_QUALITY: - default_quality = CAIRO_ANTIALIAS_SUBPIXEL; - break; - case ANTIALIASED_QUALITY: - default_quality = CAIRO_ANTIALIAS_GRAY; - dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; - break; - case DEFAULT_QUALITY: - // _get_system_quality() seems to think aliased is default! - default_quality = CAIRO_ANTIALIAS_NONE; - dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; - break; - } - - if (default_quality == CAIRO_ANTIALIAS_GRAY) { - if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { - default_quality = CAIRO_ANTIALIAS_NONE; - } - } - - if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) { - dwriteFont->antialias_mode = default_quality; - } else { - dwriteFont->antialias_mode = options->antialias; - } - - dwriteFont->manual_show_glyphs_allowed = TRUE; - dwriteFont->rendering_mode = - default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? - cairo_d2d_surface_t::TEXT_RENDERING_NORMAL : cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE; - - return _cairo_scaled_font_set_metrics (*font, &extents); -} - -/* Implementation cairo_dwrite_scaled_font_backend_t */ -void -_cairo_dwrite_scaled_font_fini(void *scaled_font) -{ -} - -static cairo_int_status_t -_cairo_dwrite_scaled_glyph_init(void *scaled_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) -{ - cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast(scaled_font); - cairo_int_status_t status; - - if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { - status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); - if (status) - return status; - } - - if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { - status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); - if (status) - return status; - } - - if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { - status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); - if (status) - return status; - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -unsigned long -_cairo_dwrite_ucs4_to_index(void *scaled_font, - uint32_t ucs4) -{ - cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); - cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); - - UINT16 index; - face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); - return index; -} - -cairo_warn cairo_int_status_t -_cairo_dwrite_scaled_show_glyphs(void *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; - cairo_int_status_t status; - - if (width == 0 || height == 0) - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - - if (_cairo_surface_is_win32 (generic_surface) && - surface->format == CAIRO_FORMAT_RGB24 && - op == CAIRO_OPERATOR_OVER) { - - //XXX: we need to set the clip region here - - status = (cairo_int_status_t)_cairo_dwrite_show_glyphs_on_surface (surface, op, pattern, - glyphs, num_glyphs, - (cairo_scaled_font_t*)scaled_font, NULL); - - return status; - } else { - cairo_dwrite_scaled_font_t *dwritesf = - static_cast(scaled_font); - BOOL transform = FALSE; - - AutoDWriteGlyphRun run; - run.allocate(num_glyphs); - UINT16 *indices = const_cast(run.glyphIndices); - FLOAT *advances = const_cast(run.glyphAdvances); - DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); - - run.bidiLevel = 0; - run.fontFace = ((cairo_dwrite_font_face_t*)dwritesf->base.font_face)->dwriteface; - run.isSideways = FALSE; - IDWriteGlyphRunAnalysis *analysis; - - if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && - dwritesf->mat.xx == dwritesf->base.font_matrix.xx && - dwritesf->mat.yy == dwritesf->base.font_matrix.yy) { - - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - // Since we will multiply by our ctm matrix later for rotation effects - // and such, adjust positions by the inverse matrix now. - offsets[i].ascenderOffset = (FLOAT)dest_y - (FLOAT)glyphs[i].y; - offsets[i].advanceOffset = (FLOAT)glyphs[i].x - dest_x; - advances[i] = 0.0; - } - run.fontEmSize = (FLOAT)dwritesf->base.font_matrix.yy; - } else { - transform = TRUE; - - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - double x = glyphs[i].x - dest_x; - double y = glyphs[i].y - dest_y; - cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); - // Since we will multiply by our ctm matrix later for rotation effects - // and such, adjust positions by the inverse matrix now. - offsets[i].ascenderOffset = -(FLOAT)y; - offsets[i].advanceOffset = (FLOAT)x; - advances[i] = 0.0; - } - run.fontEmSize = 1.0f; - } - - HRESULT hr; - if (!transform) { - hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, - 1.0f, - NULL, - DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, - DWRITE_MEASURING_MODE_NATURAL, - 0, - 0, - &analysis); - } else { - DWRITE_MATRIX dwmatrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); - hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, - 1.0f, - &dwmatrix, - DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, - DWRITE_MEASURING_MODE_NATURAL, - 0, - 0, - &analysis); - } - - if (FAILED(hr) || !analysis) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - RECT r; - r.left = 0; - r.top = 0; - r.right = width; - r.bottom = height; - - BYTE *surface = new BYTE[width * height * 3]; - - analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &r, surface, width * height * 3); - - cairo_image_surface_t *mask_surface = - (cairo_image_surface_t*)cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - - cairo_surface_flush(&mask_surface->base); - - for (unsigned int y = 0; y < height; y++) { - for (unsigned int x = 0; x < width; x++) { - mask_surface->data[y * mask_surface->stride + x * 4] = surface[y * width * 3 + x * 3 + 1]; - mask_surface->data[y * mask_surface->stride + x * 4 + 1] = surface[y * width * 3 + x * 3 + 1]; - mask_surface->data[y * mask_surface->stride + x * 4 + 2] = surface[y * width * 3 + x * 3 + 1]; - mask_surface->data[y * mask_surface->stride + x * 4 + 3] = surface[y * width * 3 + x * 3 + 1]; - } - } - cairo_surface_mark_dirty(&mask_surface->base); - - pixman_image_set_component_alpha(mask_surface->pixman_image, 1); - - cairo_surface_pattern_t mask; - _cairo_pattern_init_for_surface (&mask, &mask_surface->base); - - status = (cairo_int_status_t)_cairo_surface_composite (op, pattern, - &mask.base, - generic_surface, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height, - clip_region); - - _cairo_pattern_fini (&mask.base); - - analysis->Release(); - delete [] surface; - - cairo_surface_destroy (&mask_surface->base); - *remaining_glyphs = 0; - - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - } -} - -/* cairo_dwrite_scaled_glyph_init helper function bodies */ -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - UINT16 charIndex = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); - cairo_dwrite_font_face_t *font_face = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; - cairo_text_extents_t extents; - - DWRITE_GLYPH_METRICS metrics; - DWRITE_FONT_METRICS fontMetrics; - font_face->dwriteface->GetMetrics(&fontMetrics); - HRESULT hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); - if (FAILED(hr)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - // TODO: Treat swap_xy. - extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / - fontMetrics.designUnitsPerEm; - extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / - fontMetrics.designUnitsPerEm; - extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; - extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; - extents.y_advance = 0.0; - extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / - fontMetrics.designUnitsPerEm; - - // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics - // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, - // and therefore it does not always cover all pixels that will actually be touched. - if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && - extents.width > 0 && extents.height > 0) { - extents.width += scaled_font->mat_inverse.xx * 2; - extents.x_bearing -= scaled_font->mat_inverse.xx; - } - - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &scaled_font->base, - &extents); - return CAIRO_INT_STATUS_SUCCESS; -} - -/** - * Stack-based helper implementing IDWriteGeometrySink. - * Used to determine the path of the glyphs. - */ - -class GeometryRecorder : public IDWriteGeometrySink -{ -public: - GeometryRecorder(cairo_path_fixed_t *aCairoPath) - : mCairoPath(aCairoPath) {} - - // IUnknown interface - IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) - { - if (iid != __uuidof(IDWriteGeometrySink)) - return E_NOINTERFACE; - - *ppObject = static_cast(this); - - return S_OK; - } - - IFACEMETHOD_(ULONG, AddRef)() - { - return 1; - } - - IFACEMETHOD_(ULONG, Release)() - { - return 1; - } - - IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) - { - return; - } - - STDMETHODIMP Close() - { - return S_OK; - } - - IFACEMETHODIMP_(void) SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) - { - return; - } - - cairo_fixed_t GetFixedX(const D2D1_POINT_2F &point) - { - unsigned int control_word; - _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); - return _cairo_fixed_from_double(point.x); - } - - cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point) - { - unsigned int control_word; - _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); - return _cairo_fixed_from_double(point.y); - } - - IFACEMETHODIMP_(void) BeginFigure( - D2D1_POINT_2F startPoint, - D2D1_FIGURE_BEGIN figureBegin) - { - mStartPoint = startPoint; - cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, - GetFixedX(startPoint), - GetFixedY(startPoint)); - } - - IFACEMETHODIMP_(void) EndFigure( - D2D1_FIGURE_END figureEnd) - { - if (figureEnd == D2D1_FIGURE_END_CLOSED) { - cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, - GetFixedX(mStartPoint), - GetFixedY(mStartPoint)); - } - } - - IFACEMETHODIMP_(void) AddBeziers( - const D2D1_BEZIER_SEGMENT *beziers, - UINT beziersCount) - { - for (unsigned int i = 0; i < beziersCount; i++) { - cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, - GetFixedX(beziers[i].point1), - GetFixedY(beziers[i].point1), - GetFixedX(beziers[i].point2), - GetFixedY(beziers[i].point2), - GetFixedX(beziers[i].point3), - GetFixedY(beziers[i].point3)); - } - } - - IFACEMETHODIMP_(void) AddLines( - const D2D1_POINT_2F *points, - UINT pointsCount) - { - for (unsigned int i = 0; i < pointsCount; i++) { - cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, - GetFixedX(points[i]), - GetFixedY(points[i])); - } - } - -private: - cairo_path_fixed_t *mCairoPath; - D2D1_POINT_2F mStartPoint; -}; - -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_path_fixed_t *path; - path = _cairo_path_fixed_create(); - GeometryRecorder recorder(path); - - DWRITE_GLYPH_OFFSET offset; - offset.advanceOffset = 0; - offset.ascenderOffset = 0; - UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); - FLOAT advance = 0.0; - cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; - dwriteff->dwriteface->GetGlyphRunOutline((FLOAT)scaled_font->base.font_matrix.yy, - &glyphId, - &advance, - &offset, - 1, - FALSE, - FALSE, - &recorder); - _cairo_path_fixed_close_path(path); - - /* Now apply our transformation to the drawn path. */ - _cairo_path_fixed_transform(path, &scaled_font->base.ctm); - - _cairo_scaled_glyph_set_path (scaled_glyph, - &scaled_font->base, - path); - return CAIRO_INT_STATUS_SUCCESS; -} - -/* Helper function also stolen from cairo-win32-font.c */ - -/* Compute an alpha-mask from a monochrome RGB24 image - */ -static cairo_surface_t * -_compute_a8_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; - cairo_image_surface_t *image8; - int i, j; - - if (image24->base.status) - return cairo_surface_reference (&image24->base); - - image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, - image24->width, image24->height); - if (image8->base.status) - return &image8->base; - - for (i = 0; i < image24->height; i++) { - uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); - unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); - - for (j = 0; j < image24->width; j++) { - *q = 255 - ((*p & 0x0000ff00) >> 8); - p++; - q++; - } - } - - return &image8->base; -} - -cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_int_status_t status; - cairo_glyph_t glyph; - cairo_win32_surface_t *surface; - cairo_t *cr; - cairo_surface_t *image; - int width, height; - double x1, y1, x2, y2; - - x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); - y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); - y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); - width = (int)(x2 - x1); - height = (int)(y2 - y1); - - glyph.index = _cairo_scaled_glyph_index (scaled_glyph); - glyph.x = -x1; - glyph.y = -y1; - - DWRITE_GLYPH_RUN run; - FLOAT advance = 0; - UINT16 index = (UINT16)glyph.index; - DWRITE_GLYPH_OFFSET offset; - double x = glyph.x; - double y = glyph.y; - RECT area; - DWRITE_MATRIX matrix; - - surface = (cairo_win32_surface_t *) - cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); - - cr = cairo_create (&surface->base); - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_paint (cr); - status = (cairo_int_status_t)cairo_status (cr); - cairo_destroy(cr); - if (status) - goto FAIL; - - /** - * We transform by the inverse transformation here. This will put our glyph - * locations in the space in which we draw. Which is later transformed by - * the transformation matrix that we use. This will transform the - * glyph positions back to where they were before when drawing, but the - * glyph shapes will be transformed by the transformation matrix. - */ - cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); - offset.advanceOffset = (FLOAT)x; - /** Y-axis is inverted */ - offset.ascenderOffset = -(FLOAT)y; - - area.top = 0; - area.bottom = height; - area.left = 0; - area.right = width; - - run.glyphCount = 1; - run.glyphAdvances = &advance; - run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface; - run.fontEmSize = 1.0f; - run.bidiLevel = 0; - run.glyphIndices = &index; - run.isSideways = FALSE; - run.glyphOffsets = &offset; - - matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); - - status = _dwrite_draw_glyphs_to_gdi_surface_gdi (surface, &matrix, &run, - RGB(0,0,0), scaled_font, area); - if (status) - goto FAIL; - - GdiFlush(); - - image = _compute_a8_mask (surface); - status = (cairo_int_status_t)image->status; - if (status) - goto FAIL; - - cairo_surface_set_device_offset (image, -x1, -y1); - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - (cairo_image_surface_t *) image); - - FAIL: - cairo_surface_destroy (&surface->base); - - return status; -} - -cairo_int_status_t -_cairo_dwrite_load_truetype_table(void *scaled_font, - unsigned long tag, - long offset, - unsigned char *buffer, - unsigned long *length) -{ - cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); - cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); - - const void *data; - UINT32 size; - void *tableContext; - BOOL exists; - face->dwriteface->TryGetFontTable(be32_to_cpu (tag), - &data, - &size, - &tableContext, - &exists); - - if (!exists) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (buffer && *length && (UINT32)offset < size) { - size = MIN(size - (UINT32)offset, *length); - memcpy(buffer, (const char*)data + offset, size); - } - *length = size; - - if (tableContext) { - face->dwriteface->ReleaseFontTable(tableContext); - } - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; -} - -// WIN32 Helper Functions -cairo_font_face_t* -cairo_dwrite_font_face_create_for_dwrite_fontface(void* dwrite_font, void* dwrite_font_face) -{ - IDWriteFont *dwritefont = static_cast(dwrite_font); - IDWriteFontFace *dwriteface = static_cast(dwrite_font_face); - cairo_dwrite_font_face_t *face = new cairo_dwrite_font_face_t; - cairo_font_face_t *font_face; - - dwriteface->AddRef(); - - face->dwriteface = dwriteface; - face->font = NULL; - - font_face = (cairo_font_face_t*)face; - - _cairo_font_face_init (&((cairo_dwrite_font_face_t*)font_face)->base, &_cairo_dwrite_font_face_backend); - - return font_face; -} - -void -cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed) -{ - cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); - font->manual_show_glyphs_allowed = allowed; -} - -void -cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) -{ - cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); - if (force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL) { - font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC; - } else if (!force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) { - font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_NORMAL; - } -} - -cairo_bool_t -cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font) -{ - cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); - return font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC; -} - -void -cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, - int geometry, int mode) -{ - DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode); -} - -int -cairo_dwrite_get_cleartype_rendering_mode() -{ - return DWriteFactory::GetClearTypeRenderingMode(); -} - -cairo_int_status_t -_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, - DWRITE_MATRIX *transform, - DWRITE_GLYPH_RUN *run, - COLORREF color, - cairo_dwrite_scaled_font_t *scaled_font, - const RECT &area) -{ - IDWriteGdiInterop *gdiInterop; - DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); - IDWriteBitmapRenderTarget *rt; - HRESULT rv; - - cairo_d2d_surface_t::TextRenderingState renderingState = - scaled_font->rendering_mode; - - rv = gdiInterop->CreateBitmapRenderTarget(surface->dc, - area.right - area.left, - area.bottom - area.top, - &rt); - - if (FAILED(rv)) { - if (rv == E_OUTOFMEMORY) { - return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if ((renderingState == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL || - renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) && - !surface->base.permit_subpixel_antialiasing) { - renderingState = cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE; - IDWriteBitmapRenderTarget1* rt1; - rv = rt->QueryInterface(&rt1); - - if (SUCCEEDED(rv) && rt1) { - rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE); - rt1->Release(); - } - } - - IDWriteRenderingParams *params = - DWriteFactory::RenderingParams(renderingState); - - /** - * We set the number of pixels per DIP to 1.0. This is because we always want - * to draw in device pixels, and not device independent pixels. On high DPI - * systems this value will be higher than 1.0 and automatically upscale - * fonts, we don't want this since we do our own upscaling for various reasons. - */ - rt->SetPixelsPerDip(1.0); - - if (transform) { - rt->SetCurrentTransform(transform); - } - BitBlt(rt->GetMemoryDC(), - 0, 0, - area.right - area.left, area.bottom - area.top, - surface->dc, - area.left, area.top, - SRCCOPY | NOMIRRORBITMAP); - DWRITE_MEASURING_MODE measureMode; - switch (renderingState) { - case cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC: - case cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE: - measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; - break; - default: - measureMode = DWRITE_MEASURING_MODE_NATURAL; - break; - } - HRESULT hr = rt->DrawGlyphRun(0, 0, measureMode, run, params, color); - BitBlt(surface->dc, - area.left, area.top, - area.right - area.left, area.bottom - area.top, - rt->GetMemoryDC(), - 0, 0, - SRCCOPY | NOMIRRORBITMAP); - params->Release(); - rt->Release(); - gdiInterop->Release(); - return CAIRO_INT_STATUS_SUCCESS; -} - -cairo_int_status_t -_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, - DWRITE_MATRIX *transform, - DWRITE_GLYPH_RUN *run, - COLORREF color, - const RECT &area) -{ - HRESULT rv; - - ID2D1DCRenderTarget *rt = D2DFactory::RenderTarget(); - - // XXX don't we need to set RenderingParams on this RenderTarget? - - rv = rt->BindDC(surface->dc, &area); - - printf("Rendering to surface: %p\n", surface->dc); - - if (FAILED(rv)) { - rt->Release(); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - // D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF. - color = (color & 0xFF) << 16 | - (color & 0xFF00) | - (color & 0xFF0000) >> 16; - ID2D1SolidColorBrush *brush; - rv = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush); - - if (FAILED(rv)) { - rt->Release(); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (transform) { - rt->SetTransform(D2D1::Matrix3x2F(transform->m11, - transform->m12, - transform->m21, - transform->m22, - transform->dx, - transform->dy)); - } - rt->BeginDraw(); - rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush); - rt->EndDraw(); - if (transform) { - rt->SetTransform(D2D1::Matrix3x2F::Identity()); - } - brush->Release(); - if (FAILED(rv)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -/* Surface helper function */ -cairo_int_status_t -_cairo_dwrite_show_glyphs_on_surface(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - // TODO: Check font & surface for types. - cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); - cairo_win32_surface_t *dst = reinterpret_cast(surface); - cairo_int_status_t status; - /* We can only handle dwrite fonts */ - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle opaque solid color sources */ - if (!_cairo_pattern_is_opaque_solid(source)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* If we have a fallback mask clip set on the dst, we have - * to go through the fallback path */ - if (clip != NULL) { - if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { - cairo_region_t *clip_region; - cairo_int_status_t status; - - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - - _cairo_win32_surface_set_clip_region (dst, clip_region); - } - } else { - _cairo_win32_surface_set_clip_region (surface, NULL); - } - - /* It is vital that dx values for dxy_buf are calculated from the delta of - * _logical_ x coordinates (not user x coordinates) or else the sum of all - * previous dx values may start to diverge from the current glyph's x - * coordinate due to accumulated rounding error. As a result strings could - * be painted shorter or longer than expected. */ - - AutoDWriteGlyphRun run; - run.allocate(num_glyphs); - - UINT16 *indices = const_cast(run.glyphIndices); - FLOAT *advances = const_cast(run.glyphAdvances); - DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); - - BOOL transform = FALSE; - /* Needed to calculate bounding box for efficient blitting */ - INT32 smallestX = INT_MAX; - INT32 largestX = 0; - INT32 smallestY = INT_MAX; - INT32 largestY = 0; - for (int i = 0; i < num_glyphs; i++) { - if (glyphs[i].x < smallestX) { - smallestX = (INT32)glyphs[i].x; - } - if (glyphs[i].x > largestX) { - largestX = (INT32)glyphs[i].x; - } - if (glyphs[i].y < smallestY) { - smallestY = (INT32)glyphs[i].y; - } - if (glyphs[i].y > largestY) { - largestY = (INT32)glyphs[i].y; - } - } - /** - * Here we try to get a rough estimate of the area that this glyph run will - * cover on the surface. Since we use GDI interop to draw we will be copying - * data around the size of the area of the surface that we map. We will want - * to map an area as small as possible to prevent large surfaces to be - * copied around. We take the X/Y-size of the font as margin on the left/top - * twice the X/Y-size of the font as margin on the right/bottom. - * This should always cover the entire area where the glyphs are. - */ - RECT fontArea; - fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx); - fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2); - fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy); - fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2); - if (fontArea.left < 0) - fontArea.left = 0; - if (fontArea.top < 0) - fontArea.top = 0; - if (fontArea.bottom > dst->extents.height) { - fontArea.bottom = dst->extents.height; - } - if (fontArea.right > dst->extents.width) { - fontArea.right = dst->extents.width; - } - if (fontArea.right <= fontArea.left || - fontArea.bottom <= fontArea.top) { - return CAIRO_INT_STATUS_SUCCESS; - } - if (fontArea.right > dst->extents.width) { - fontArea.right = dst->extents.width; - } - if (fontArea.bottom > dst->extents.height) { - fontArea.bottom = dst->extents.height; - } - - run.bidiLevel = 0; - run.fontFace = dwriteff->dwriteface; - run.isSideways = FALSE; - if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && - dwritesf->mat.xx == scaled_font->font_matrix.xx && - dwritesf->mat.yy == scaled_font->font_matrix.yy) { - - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - // Since we will multiply by our ctm matrix later for rotation effects - // and such, adjust positions by the inverse matrix now. - offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y); - offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); - advances[i] = 0.0; - } - run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; - } else { - transform = TRUE; - // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs - const double EPSILON = 0.0001; - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - double x = glyphs[i].x - fontArea.left + EPSILON; - double y = glyphs[i].y - fontArea.top; - cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); - /** - * Since we will multiply by our ctm matrix later for rotation effects - * and such, adjust positions by the inverse matrix now. The Y-axis - * is inverted so the offset becomes negative. - */ - offsets[i].ascenderOffset = -(FLOAT)y; - offsets[i].advanceOffset = (FLOAT)x; - advances[i] = 0.0; - } - run.fontEmSize = 1.0f; - } - - cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; - COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, - ((int)solid_pattern->color.green_short) >> 8, - ((int)solid_pattern->color.blue_short) >> 8); - - DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); - - DWRITE_MATRIX *mat; - if (transform) { - mat = &matrix; - } else { - mat = NULL; - } - - RECT area; - area.left = dst->extents.x; - area.top = dst->extents.y; - area.right = area.left + dst->extents.width; - area.bottom = area.top + dst->extents.height; - -#ifdef CAIRO_TRY_D2D_TO_GDI - status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst, - mat, - &run, - color, - fontArea); - - if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { -#endif - status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, - mat, - &run, - color, - dwritesf, - fontArea); - -#ifdef CAIRO_TRY_D2D_TO_GDI - } -#endif - - return status; -} - -#define ENHANCED_CONTRAST_REGISTRY_KEY \ - HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel" - -void -DWriteFactory::CreateRenderingParams() -{ - if (!Instance()) { - return; - } - - Instance()->CreateRenderingParams(&mDefaultRenderingParams); - - // For EnhancedContrast, we override the default if the user has not set it - // in the registry (by using the ClearType Tuner). - FLOAT contrast; - if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) { - contrast = mEnhancedContrast; - } else { - HKEY hKey; - if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY, - 0, KEY_READ, &hKey) == ERROR_SUCCESS) - { - contrast = mDefaultRenderingParams->GetEnhancedContrast(); - RegCloseKey(hKey); - } else { - contrast = 1.0; - } - } - - // For parameters that have not been explicitly set via the SetRenderingParams API, - // we copy values from default params (or our overridden value for contrast) - FLOAT gamma = - mGamma >= 1.0 && mGamma <= 2.2 ? - mGamma : mDefaultRenderingParams->GetGamma(); - FLOAT clearTypeLevel = - mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? - mClearTypeLevel : mDefaultRenderingParams->GetClearTypeLevel(); - DWRITE_PIXEL_GEOMETRY pixelGeometry = - mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ? - (DWRITE_PIXEL_GEOMETRY)mPixelGeometry : mDefaultRenderingParams->GetPixelGeometry(); - DWRITE_RENDERING_MODE renderingMode = - mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ? - (DWRITE_RENDERING_MODE)mRenderingMode : mDefaultRenderingParams->GetRenderingMode(); - Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, - pixelGeometry, renderingMode, - &mCustomClearTypeRenderingParams); - Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, - pixelGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, - &mForceGDIClassicRenderingParams); -} - -static cairo_bool_t -_name_tables_match (cairo_scaled_font_t *font1, - cairo_scaled_font_t *font2) -{ - unsigned long size1; - unsigned long size2; - cairo_int_status_t status1; - cairo_int_status_t status2; - unsigned char *buffer1; - unsigned char *buffer2; - cairo_bool_t result = false; - - if (!font1->backend || !font2->backend || - !font1->backend->load_truetype_table || - !font2->backend->load_truetype_table) - return false; - - status1 = font1->backend->load_truetype_table (font1, - TT_TAG_name, 0, NULL, &size1); - status2 = font2->backend->load_truetype_table (font2, - TT_TAG_name, 0, NULL, &size2); - if (status1 || status2) - return false; - if (size1 != size2) - return false; - - buffer1 = (unsigned char*)malloc (size1); - buffer2 = (unsigned char*)malloc (size2); - - if (buffer1 && buffer2) { - status1 = font1->backend->load_truetype_table (font1, - TT_TAG_name, 0, buffer1, &size1); - status2 = font2->backend->load_truetype_table (font2, - TT_TAG_name, 0, buffer2, &size2); - if (!status1 && !status2) { - result = memcmp (buffer1, buffer2, size1) == 0; - } - } - - free (buffer1); - free (buffer2); - return result; -} - -// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent -// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing -// paths or blitting glyph bitmaps. -cairo_int_status_t -_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, - cairo_scaled_font_t **new_font) -{ - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); - cairo_dwrite_font_face_t *dwface = reinterpret_cast(face); - - RefPtr gdiInterop; - DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); - if (!gdiInterop) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - LOGFONTW logfont; - if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - // DW must have been using an outline font, so we want GDI to use the same, - // even if there's also a bitmap face available - logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; - - cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont); - if (!win32_face) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - cairo_matrix_t font_matrix; - cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); - - cairo_matrix_t ctm; - cairo_scaled_font_get_ctm (scaled_font, &ctm); - - cairo_font_options_t options; - cairo_scaled_font_get_font_options (scaled_font, &options); - - cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, - &font_matrix, - &ctm, - &options); - cairo_font_face_destroy (win32_face); - - if (!font) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (!_name_tables_match (font, scaled_font)) { - // If the font name tables aren't equal, then GDI may have failed to - // find the right font and substituted a different font. - cairo_scaled_font_destroy (font); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *new_font = font; - return CAIRO_INT_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-dwrite-private.h b/libs/cairo/cairo/src/cairo-dwrite-private.h deleted file mode 100644 index 1eaa46113..000000000 --- a/libs/cairo/cairo/src/cairo-dwrite-private.h +++ /dev/null @@ -1,193 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include - -// DirectWrite is not available on all platforms. -typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( - DWRITE_FACTORY_TYPE factoryType, - REFIID iid, - IUnknown **factory -); - -/* cairo_scaled_font_t implementation */ -struct _cairo_dwrite_scaled_font { - cairo_scaled_font_t base; - cairo_matrix_t mat; - cairo_matrix_t mat_inverse; - cairo_antialias_t antialias_mode; - DWRITE_MEASURING_MODE measuring_mode; - cairo_bool_t manual_show_glyphs_allowed; - cairo_d2d_surface_t::TextRenderingState rendering_mode; -}; -typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; - -class DWriteFactory -{ -public: - static IDWriteFactory *Instance() - { - if (!mFactoryInstance) { - DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); - if (createDWriteFactory) { - HRESULT hr = createDWriteFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast(&mFactoryInstance)); - assert(SUCCEEDED(hr)); - } - } - return mFactoryInstance; - } - - static IDWriteFontCollection *SystemCollection() - { - if (!mSystemCollection) { - if (Instance()) { - HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection); - assert(SUCCEEDED(hr)); - } - } - return mSystemCollection; - } - - static IDWriteFontFamily *FindSystemFontFamily(const WCHAR *aFamilyName) - { - UINT32 idx; - BOOL found; - if (!SystemCollection()) { - return NULL; - } - SystemCollection()->FindFamilyName(aFamilyName, &idx, &found); - if (!found) { - return NULL; - } - - IDWriteFontFamily *family; - SystemCollection()->GetFontFamily(idx, &family); - return family; - } - - static IDWriteRenderingParams *RenderingParams(cairo_d2d_surface_t::TextRenderingState mode) - { - if (!mDefaultRenderingParams || - !mForceGDIClassicRenderingParams || - !mCustomClearTypeRenderingParams) - { - CreateRenderingParams(); - } - IDWriteRenderingParams *params; - if (mode == cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE) { - params = mDefaultRenderingParams; - } else if (mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC && mRenderingMode < 0) { - params = mForceGDIClassicRenderingParams; - } else { - params = mCustomClearTypeRenderingParams; - } - if (params) { - params->AddRef(); - } - return params; - } - - static void SetRenderingParams(FLOAT aGamma, - FLOAT aEnhancedContrast, - FLOAT aClearTypeLevel, - int aPixelGeometry, - int aRenderingMode) - { - mGamma = aGamma; - mEnhancedContrast = aEnhancedContrast; - mClearTypeLevel = aClearTypeLevel; - mPixelGeometry = aPixelGeometry; - mRenderingMode = aRenderingMode; - // discard any current RenderingParams objects - if (mCustomClearTypeRenderingParams) { - mCustomClearTypeRenderingParams->Release(); - mCustomClearTypeRenderingParams = NULL; - } - if (mForceGDIClassicRenderingParams) { - mForceGDIClassicRenderingParams->Release(); - mForceGDIClassicRenderingParams = NULL; - } - if (mDefaultRenderingParams) { - mDefaultRenderingParams->Release(); - mDefaultRenderingParams = NULL; - } - } - - static int GetClearTypeRenderingMode() { - return mRenderingMode; - } - -private: - static void CreateRenderingParams(); - - static IDWriteFactory *mFactoryInstance; - static IDWriteFontCollection *mSystemCollection; - static IDWriteRenderingParams *mDefaultRenderingParams; - static IDWriteRenderingParams *mCustomClearTypeRenderingParams; - static IDWriteRenderingParams *mForceGDIClassicRenderingParams; - static FLOAT mGamma; - static FLOAT mEnhancedContrast; - static FLOAT mClearTypeLevel; - static int mPixelGeometry; - static int mRenderingMode; -}; - -class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN -{ - static const int kNumAutoGlyphs = 256; - -public: - AutoDWriteGlyphRun() { - glyphCount = 0; - } - - ~AutoDWriteGlyphRun() { - if (glyphCount > kNumAutoGlyphs) { - delete[] glyphIndices; - delete[] glyphAdvances; - delete[] glyphOffsets; - } - } - - void allocate(int aNumGlyphs) { - glyphCount = aNumGlyphs; - if (aNumGlyphs <= kNumAutoGlyphs) { - glyphIndices = &mAutoIndices[0]; - glyphAdvances = &mAutoAdvances[0]; - glyphOffsets = &mAutoOffsets[0]; - } else { - glyphIndices = new UINT16[aNumGlyphs]; - glyphAdvances = new FLOAT[aNumGlyphs]; - glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; - } - } - -private: - DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; - FLOAT mAutoAdvances[kNumAutoGlyphs]; - UINT16 mAutoIndices[kNumAutoGlyphs]; -}; - -/* cairo_font_face_t implementation */ -struct _cairo_dwrite_font_face { - cairo_font_face_t base; - IDWriteFont *font; - IDWriteFontFace *dwriteface; -}; -typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t; - -DWRITE_MATRIX _cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix); - -// This will initialize a DWrite glyph run from cairo glyphs and a scaled_font. -void -_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, - int num_glyphs, - cairo_dwrite_scaled_font_t *scaled_font, - AutoDWriteGlyphRun *run, - cairo_bool_t *transformed); diff --git a/libs/cairo/cairo/src/cairo-eagle-context.c b/libs/cairo/cairo/src/cairo-eagle-context.c deleted file mode 100644 index 5f59f5239..000000000 --- a/libs/cairo/cairo/src/cairo-eagle-context.c +++ /dev/null @@ -1,147 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include /* XXX dummy surface for glewInit() */ -#include - -typedef struct _cairo_eagle_context { - cairo_gl_context_t base; - - EGLDisplay display; - EGLContext context; -} cairo_eagle_context_t; - -typedef struct _cairo_eagle_surface { - cairo_gl_surface_t base; - - EGLSurface eagle; -} cairo_eagle_surface_t; - -static void -_eagle_make_current (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_eagle_context_t *ctx = abstract_ctx; - cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; - - eagleMakeCurrent (ctx->display, surface->eagle, surface->eagle, ctx->context); -} - -static void -_eagle_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_eagle_context_t *ctx = abstract_ctx; - cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; - - eagleSwapBuffers (ctx->display, surface->eagle); -} - -static void -_eagle_destroy (void *abstract_ctx) -{ -} - -static cairo_bool_t -_eagle_init (EGLDisplay display, EGLContext context) -{ - const EGLint config_attribs[] = { - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_NONE - }; - const EGLint surface_attribs[] = { - EGL_RENDER_BUFFER, EGL_BACK_BUFFER, - EGL_NONE - }; - EGLConfig config; - EGLSurface dummy; - struct drm_i915_gem_create create; - struct drm_gem_flink flink; - int fd; - GLenum err; - - if (! eagleChooseConfig (display, config_attribs, &config, 1, NULL)) { - fprintf (stderr, "Unable to choose config\n"); - return FALSE; - } - - /* XXX */ - fd = eagleGetDisplayFD (display); - - create.size = 4096; - if (ioctl (fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) { - fprintf (stderr, "gem create failed: %m\n"); - return FALSE; - } - flink.handle = create.handle; - if (ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) { - fprintf (stderr, "gem flink failed: %m\n"); - return FALSE; - } - - dummy = eagleCreateSurfaceForName (display, config, flink.name, - 1, 1, 4, surface_attribs); - if (dummy == NULL) { - fprintf (stderr, "Failed to create dummy surface\n"); - return FALSE; - } - - eagleMakeCurrent (display, dummy, dummy, context); -} - -cairo_gl_context_t * -cairo_eagle_context_create (EGLDisplay display, EGLContext context) -{ - cairo_eagle_context_t *ctx; - cairo_status_t status; - - if (! _eagle_init (display, context)) { - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - - ctx = calloc (1, sizeof (cairo_eagle_context_t)); - if (ctx == NULL) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - ctx->display = display; - ctx->context = context; - - ctx->base.make_current = _eagle_make_current; - ctx->base.swap_buffers = _eagle_swap_buffers; - ctx->base.destroy = _eagle_destroy; - - status = _cairo_gl_context_init (&ctx->base); - if (status) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - return &ctx->base; -} - -cairo_surface_t * -cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx, - EGLSurface eagle, - int width, - int height) -{ - cairo_eagle_surface_t *surface; - - if (ctx->status) - return _cairo_surface_create_in_error (ctx->status); - - surface = calloc (1, sizeof (cairo_eagle_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (ctx, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->eagle = eagle; - - return &surface->base.base; -} diff --git a/libs/cairo/cairo/src/cairo-error-private.h b/libs/cairo/cairo/src/cairo-error-private.h deleted file mode 100644 index c7a9f7098..000000000 --- a/libs/cairo/cairo/src/cairo-error-private.h +++ /dev/null @@ -1,27 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _CAIRO_ERROR_PRIVATE_H_ -#define _CAIRO_ERROR_PRIVATE_H_ - -#include "cairo.h" -#include "cairo-compiler-private.h" - -CAIRO_BEGIN_DECLS - -#define _cairo_status_is_error(status) \ - (status != CAIRO_STATUS_SUCCESS && status <= CAIRO_STATUS_LAST_STATUS) - -cairo_private cairo_status_t -_cairo_error (cairo_status_t status); - -/* hide compiler warnings when discarding the return value */ -#define _cairo_error_throw(status) do { \ - cairo_status_t status__ = _cairo_error (status); \ - (void) status__; \ -} while (0) - -CAIRO_END_DECLS - -#endif /* _CAIRO_ERROR_PRIVATE_H_ */ diff --git a/libs/cairo/cairo/src/cairo-features-win32.h b/libs/cairo/cairo/src/cairo-features-win32.h deleted file mode 100644 index ef202fc14..000000000 --- a/libs/cairo/cairo/src/cairo-features-win32.h +++ /dev/null @@ -1,16 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_FEATURES_H -#define CAIRO_FEATURES_H - -#define HAVE_WINDOWS_H 1 - -#define CAIRO_HAS_WIN32_SURFACE 1 -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 - -#endif diff --git a/libs/cairo/cairo/src/cairo-features.h.in b/libs/cairo/cairo/src/cairo-features.h.in deleted file mode 100644 index 9692c7cb7..000000000 --- a/libs/cairo/cairo/src/cairo-features.h.in +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FEATURES_H -#define CAIRO_FEATURES_H - -#include "cairo-platform.h" - -#ifdef __cplusplus -# define CAIRO_BEGIN_DECLS extern "C" { -# define CAIRO_END_DECLS } -#else -# define CAIRO_BEGIN_DECLS -# define CAIRO_END_DECLS -#endif - -#ifndef cairo_public -# define cairo_public -#endif - -#define CAIRO_VERSION_MAJOR 1 -#define CAIRO_VERSION_MINOR 10 -#define CAIRO_VERSION_MICRO 28 - -@PS_SURFACE_FEATURE@ - -@PDF_SURFACE_FEATURE@ - -@SVG_SURFACE_FEATURE@ - -@XLIB_SURFACE_FEATURE@ - -@XLIB_XRENDER_SURFACE_FEATURE@ - -@QUARTZ_SURFACE_FEATURE@ - -@QUARTZ_IMAGE_SURFACE_FEATURE@ - -@WIN32_SURFACE_FEATURE@ - -@OS2_SURFACE_FEATURE@ - -@DIRECTFB_SURFACE_FEATURE@ - -@QT_SURFACE_FEATURE@ - -@FT_FONT_FEATURE@ - -@WIN32_FONT_FEATURE@ - -@WIN32_DWRITE_FONT_FEATURE@ - -@WIN32_D2D_SURFACE_FEATURE@ - -@QUARTZ_FONT_FEATURE@ - -@TEE_SURFACE_FEATURE@ - -@PNG_FUNCTIONS_FEATURE@ - -@FC_FONT_FEATURE@ -#endif diff --git a/libs/cairo/cairo/src/cairo-fixed-private.h b/libs/cairo/cairo/src/cairo-fixed-private.h deleted file mode 100644 index a00e99cf3..000000000 --- a/libs/cairo/cairo/src/cairo-fixed-private.h +++ /dev/null @@ -1,326 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FIXED_PRIVATE_H -#define CAIRO_FIXED_PRIVATE_H - -#include "cairo-fixed-type-private.h" - -#include "cairo-wideint-private.h" - -/* Implementation */ - -#if (CAIRO_FIXED_BITS != 32) -# error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type. -# error To remove this limitation, you will have to fix the tesselator. -#endif - -#define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS)) -#define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS)) -#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS)) -#define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1)) - -#define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS))) -#define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK) - -static inline cairo_fixed_t -_cairo_fixed_from_int (int i) -{ - return i << CAIRO_FIXED_FRAC_BITS; -} - -/* This is the "magic number" approach to converting a double into fixed - * point as described here: - * - * http://www.stereopsis.com/sree/fpu2006.html (an overview) - * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail) - * - * The basic idea is to add a large enough number to the double that the - * literal floating point is moved up to the extent that it forces the - * double's value to be shifted down to the bottom of the mantissa (to make - * room for the large number being added in). Since the mantissa is, at a - * given moment in time, a fixed point integer itself, one can convert a - * float to various fixed point representations by moving around the point - * of a floating point number through arithmetic operations. This behavior - * is reliable on most modern platforms as it is mandated by the IEEE-754 - * standard for floating point arithmetic. - * - * For our purposes, a "magic number" must be carefully selected that is - * both large enough to produce the desired point-shifting effect, and also - * has no lower bits in its representation that would interfere with our - * value at the bottom of the mantissa. The magic number is calculated as - * follows: - * - * (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5 - * - * where in our case: - * - MANTISSA_SIZE for 64-bit doubles is 52 - * - FRACTIONAL_SIZE for 16.16 fixed point is 16 - * - * Although this approach provides a very large speedup of this function - * on a wide-array of systems, it does come with two caveats: - * - * 1) It uses banker's rounding as opposed to arithmetic rounding. - * 2) It doesn't function properly if the FPU is in single-precision - * mode. - */ - -/* The 16.16 number must always be available */ -#define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0) - -#if CAIRO_FIXED_BITS <= 32 -#define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5) - -/* For 32-bit fixed point numbers */ -static inline cairo_fixed_t -_cairo_fixed_from_double (double d) -{ - union { - double d; - int32_t i[2]; - } u; - - u.d = d + CAIRO_MAGIC_NUMBER_FIXED; -#ifdef FLOAT_WORDS_BIGENDIAN - return u.i[1]; -#else - return u.i[0]; -#endif -} - -#else -# error Please define a magic number for your fixed point type! -# error See cairo-fixed-private.h for details. -#endif - -static inline cairo_fixed_t -_cairo_fixed_from_26_6 (uint32_t i) -{ -#if CAIRO_FIXED_FRAC_BITS > 6 - return i << (CAIRO_FIXED_FRAC_BITS - 6); -#else - return i >> (6 - CAIRO_FIXED_FRAC_BITS); -#endif -} - -static inline cairo_fixed_t -_cairo_fixed_from_16_16 (uint32_t i) -{ -#if CAIRO_FIXED_FRAC_BITS > 16 - return i << (CAIRO_FIXED_FRAC_BITS - 16); -#else - return i >> (16 - CAIRO_FIXED_FRAC_BITS); -#endif -} - -static inline double -_cairo_fixed_to_double (cairo_fixed_t f) -{ - return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; -} - -static inline float -_cairo_fixed_to_float (cairo_fixed_t f) -{ - return ((float) f) / CAIRO_FIXED_ONE_FLOAT; -} - -static inline int -_cairo_fixed_is_integer (cairo_fixed_t f) -{ - return (f & CAIRO_FIXED_FRAC_MASK) == 0; -} - -static inline cairo_fixed_t -_cairo_fixed_floor (cairo_fixed_t f) -{ - return f & ~CAIRO_FIXED_FRAC_MASK; -} - -static inline cairo_fixed_t -_cairo_fixed_round (cairo_fixed_t f) -{ - return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2); -} - -static inline cairo_fixed_t -_cairo_fixed_round_down (cairo_fixed_t f) -{ - return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2); -} - -static inline int -_cairo_fixed_integer_part (cairo_fixed_t f) -{ - return f >> CAIRO_FIXED_FRAC_BITS; -} - -static inline int -_cairo_fixed_integer_round (cairo_fixed_t f) -{ - return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2); -} - -static inline int -_cairo_fixed_integer_round_down (cairo_fixed_t f) -{ - return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2); -} - -static inline int -_cairo_fixed_fractional_part (cairo_fixed_t f) -{ - return f & CAIRO_FIXED_FRAC_MASK; -} - -static inline int -_cairo_fixed_integer_floor (cairo_fixed_t f) -{ - if (f >= 0) - return f >> CAIRO_FIXED_FRAC_BITS; - else - return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1; -} - -static inline int -_cairo_fixed_integer_ceil (cairo_fixed_t f) -{ - if (f > 0) - return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1; - else - return - (-f >> CAIRO_FIXED_FRAC_BITS); -} - -/* A bunch of explicit 16.16 operators; we need these - * to interface with pixman and other backends that require - * 16.16 fixed point types. - */ -static inline cairo_fixed_16_16_t -_cairo_fixed_to_16_16 (cairo_fixed_t f) -{ -#if (CAIRO_FIXED_FRAC_BITS == 16) && (CAIRO_FIXED_BITS == 32) - return f; -#elif CAIRO_FIXED_FRAC_BITS > 16 - /* We're just dropping the low bits, so we won't ever got over/underflow here */ - return f >> (CAIRO_FIXED_FRAC_BITS - 16); -#else - cairo_fixed_16_16_t x; - - /* Handle overflow/underflow by clamping to the lowest/highest - * value representable as 16.16 - */ - if ((f >> CAIRO_FIXED_FRAC_BITS) < INT16_MIN) { - x = INT32_MIN; - } else if ((f >> CAIRO_FIXED_FRAC_BITS) > INT16_MAX) { - x = INT32_MAX; - } else { - x = f << (16 - CAIRO_FIXED_FRAC_BITS); - } - - return x; -#endif -} - -static inline cairo_fixed_16_16_t -_cairo_fixed_16_16_from_double (double d) -{ - union { - double d; - int32_t i[2]; - } u; - - u.d = d + CAIRO_MAGIC_NUMBER_FIXED_16_16; -#ifdef FLOAT_WORDS_BIGENDIAN - return u.i[1]; -#else - return u.i[0]; -#endif -} - -static inline int -_cairo_fixed_16_16_floor (cairo_fixed_16_16_t f) -{ - if (f >= 0) - return f >> 16; - else - return -((-f - 1) >> 16) - 1; -} - -static inline double -_cairo_fixed_16_16_to_double (cairo_fixed_16_16_t f) -{ - return ((double) f) / (double) (1 << 16); -} - -#if CAIRO_FIXED_BITS == 32 - -static inline cairo_fixed_t -_cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b) -{ - cairo_int64_t temp = _cairo_int32x32_64_mul (a, b); - return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS)); -} - -/* computes round (a * b / c) */ -static inline cairo_fixed_t -_cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) -{ - cairo_int64_t ab = _cairo_int32x32_64_mul (a, b); - cairo_int64_t c64 = _cairo_int32_to_int64 (c); - return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo); -} - -/* computes floor (a * b / c) */ -static inline cairo_fixed_t -_cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) -{ - return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c); -} - - -static inline cairo_fixed_t -_cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_fixed_t x) -{ - cairo_fixed_t y, dx; - - if (x == p1->x) - return p1->y; - if (x == p2->x) - return p2->y; - - y = p1->y; - dx = p2->x - p1->x; - if (dx != 0) - y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx); - - return y; -} - -static inline cairo_fixed_t -_cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_fixed_t y) -{ - cairo_fixed_t x, dy; - - if (y == p1->y) - return p1->x; - if (y == p2->y) - return p2->x; - - x = p1->x; - dy = p2->y - p1->y; - if (dy != 0) - x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy); - - return x; -} - -#else -# error Please define multiplication and other operands for your fixed-point type size -#endif - -#endif /* CAIRO_FIXED_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-fixed-type-private.h b/libs/cairo/cairo/src/cairo-fixed-type-private.h deleted file mode 100644 index d2bf6cb66..000000000 --- a/libs/cairo/cairo/src/cairo-fixed-type-private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-fixed.c b/libs/cairo/cairo/src/cairo-fixed.c deleted file mode 100644 index 75805f3d3..000000000 --- a/libs/cairo/cairo/src/cairo-fixed.c +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-fixed-private.h" diff --git a/libs/cairo/cairo/src/cairo-font-face-twin-data.c b/libs/cairo/cairo/src/cairo-font-face-twin-data.c deleted file mode 100644 index ff09cb2be..000000000 --- a/libs/cairo/cairo/src/cairo-font-face-twin-data.c +++ /dev/null @@ -1,1072 +0,0 @@ -/* 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/libs/cairo/cairo/src/cairo-font-face-twin.c b/libs/cairo/cairo/src/cairo-font-face-twin.c deleted file mode 100644 index da85cb08e..000000000 --- a/libs/cairo/cairo/src/cairo-font-face-twin.c +++ /dev/null @@ -1,729 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -#include - -/* - * This file implements a user-font rendering the descendant of the Hershey - * font coded by Keith Packard for use in the Twin window system. - * The actual font data is in cairo-font-face-twin-data.c - * - * Ported to cairo user font and extended by Behdad Esfahbod. - */ - - - -static cairo_user_data_key_t twin_properties_key; - - -/* - * Face properties - */ - -/* We synthesize multiple faces from the twin data. Here is the parameters. */ - -/* The following tables and matching code are copied from Pango */ - -/* CSS weight */ -typedef enum { - TWIN_WEIGHT_THIN = 100, - TWIN_WEIGHT_ULTRALIGHT = 200, - TWIN_WEIGHT_LIGHT = 300, - TWIN_WEIGHT_BOOK = 380, - TWIN_WEIGHT_NORMAL = 400, - TWIN_WEIGHT_MEDIUM = 500, - TWIN_WEIGHT_SEMIBOLD = 600, - TWIN_WEIGHT_BOLD = 700, - TWIN_WEIGHT_ULTRABOLD = 800, - TWIN_WEIGHT_HEAVY = 900, - TWIN_WEIGHT_ULTRAHEAVY = 1000 -} twin_face_weight_t; - -/* CSS stretch */ -typedef enum { - TWIN_STRETCH_ULTRA_CONDENSED, - TWIN_STRETCH_EXTRA_CONDENSED, - TWIN_STRETCH_CONDENSED, - TWIN_STRETCH_SEMI_CONDENSED, - TWIN_STRETCH_NORMAL, - TWIN_STRETCH_SEMI_EXPANDED, - TWIN_STRETCH_EXPANDED, - TWIN_STRETCH_EXTRA_EXPANDED, - TWIN_STRETCH_ULTRA_EXPANDED -} twin_face_stretch_t; - -typedef struct -{ - int value; - const char str[16]; -} FieldMap; - -static const FieldMap slant_map[] = { - { CAIRO_FONT_SLANT_NORMAL, "" }, - { CAIRO_FONT_SLANT_NORMAL, "Roman" }, - { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, - { CAIRO_FONT_SLANT_ITALIC, "Italic" } -}; - -static const FieldMap smallcaps_map[] = { - { FALSE, "" }, - { TRUE, "Small-Caps" } -}; - -static const FieldMap weight_map[] = { - { TWIN_WEIGHT_THIN, "Thin" }, - { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, - { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, - { TWIN_WEIGHT_LIGHT, "Light" }, - { TWIN_WEIGHT_BOOK, "Book" }, - { TWIN_WEIGHT_NORMAL, "" }, - { TWIN_WEIGHT_NORMAL, "Regular" }, - { TWIN_WEIGHT_MEDIUM, "Medium" }, - { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, - { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, - { TWIN_WEIGHT_BOLD, "Bold" }, - { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, - { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, - { TWIN_WEIGHT_HEAVY, "Heavy" }, - { TWIN_WEIGHT_HEAVY, "Black" }, - { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, - { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, - { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, - { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } -}; - -static const FieldMap stretch_map[] = { - { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, - { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, - { TWIN_STRETCH_CONDENSED, "Condensed" }, - { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, - { TWIN_STRETCH_NORMAL, "" }, - { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, - { TWIN_STRETCH_EXPANDED, "Expanded" }, - { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, - { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } -}; - -static const FieldMap monospace_map[] = { - { FALSE, "" }, - { TRUE, "Mono" }, - { TRUE, "Monospace" } -}; - - -typedef struct _twin_face_properties { - cairo_font_slant_t slant; - twin_face_weight_t weight; - twin_face_stretch_t stretch; - - /* lets have some fun */ - cairo_bool_t monospace; - cairo_bool_t smallcaps; -} twin_face_properties_t; - -static cairo_bool_t -field_matches (const char *s1, - const char *s2, - int len) -{ - int c1, c2; - - while (len && *s1 && *s2) - { -#define TOLOWER(c) \ - (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) - - c1 = TOLOWER (*s1); - c2 = TOLOWER (*s2); - if (c1 != c2) { - if (c1 == '-') { - s1++; - continue; - } - return FALSE; - } - s1++; s2++; - len--; - } - - return len == 0 && *s1 == '\0'; -} - -static cairo_bool_t -parse_int (const char *word, - size_t wordlen, - int *out) -{ - char *end; - long val = strtol (word, &end, 10); - int i = val; - - if (end != word && (end == word + wordlen) && val >= 0 && val == i) - { - if (out) - *out = i; - - return TRUE; - } - - return FALSE; -} - -static cairo_bool_t -find_field (const char *what, - const FieldMap *map, - int n_elements, - const char *str, - int len, - int *val) -{ - int i; - cairo_bool_t had_prefix = FALSE; - - if (what) - { - i = strlen (what); - if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') - { - str += i + 1; - len -= i + 1; - had_prefix = TRUE; - } - } - - for (i=0; iNAME)) \ - return; \ - - FIELD (weight); - FIELD (slant); - FIELD (stretch); - FIELD (smallcaps); - FIELD (monospace); - -#undef FIELD -} - -static void -face_props_parse (twin_face_properties_t *props, - const char *s) -{ - const char *start, *end; - - for (start = end = s; *end; end++) { - if (*end != ' ' && *end != ':') - continue; - - if (start < end) - parse_field (props, start, end - start); - start = end + 1; - } - if (start < end) - parse_field (props, start, end - start); -} - -static cairo_status_t -twin_font_face_create_properties (cairo_font_face_t *twin_face, - twin_face_properties_t **props_out) -{ - twin_face_properties_t *props; - cairo_status_t status; - - props = malloc (sizeof (twin_face_properties_t)); - if (unlikely (props == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - props->stretch = TWIN_STRETCH_NORMAL; - props->slant = CAIRO_FONT_SLANT_NORMAL; - props->weight = TWIN_WEIGHT_NORMAL; - props->monospace = FALSE; - props->smallcaps = FALSE; - - status = cairo_font_face_set_user_data (twin_face, - &twin_properties_key, - props, free); - if (unlikely (status)) { - free (props); - return status; - } - - if (props_out) - *props_out = props; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, - cairo_toy_font_face_t *toy_face) -{ - cairo_status_t status; - twin_face_properties_t *props; - - status = twin_font_face_create_properties (twin_face, &props); - if (unlikely (status)) - return status; - - props->slant = toy_face->slant; - props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? - TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; - face_props_parse (props, toy_face->family); - - return CAIRO_STATUS_SUCCESS; -} - - -/* - * Scaled properties - */ - -typedef struct _twin_scaled_properties { - twin_face_properties_t *face_props; - - cairo_bool_t snap; /* hint outlines */ - - double weight; /* unhinted pen width */ - double penx, peny; /* hinted pen width */ - double marginl, marginr; /* hinted side margins */ - - double stretch; /* stretch factor */ -} twin_scaled_properties_t; - -static void -compute_hinting_scale (cairo_t *cr, - double x, double y, - double *scale, double *inv) -{ - cairo_user_to_device_distance (cr, &x, &y); - *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); - *inv = 1 / *scale; -} - -static void -compute_hinting_scales (cairo_t *cr, - double *x_scale, double *x_scale_inv, - double *y_scale, double *y_scale_inv) -{ - double x, y; - - x = 1; y = 0; - compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); - - x = 0; y = 1; - compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); -} - -#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) -#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) - -/* This controls the global font size */ -#define F(g) ((g) / 72.) - -static void -twin_hint_pen_and_margins(cairo_t *cr, - double *penx, double *peny, - double *marginl, double *marginr) -{ - double x_scale, x_scale_inv; - double y_scale, y_scale_inv; - double margin; - - compute_hinting_scales (cr, - &x_scale, &x_scale_inv, - &y_scale, &y_scale_inv); - - *penx = SNAPXI (*penx); - if (*penx < x_scale_inv) - *penx = x_scale_inv; - - *peny = SNAPYI (*peny); - if (*peny < y_scale_inv) - *peny = y_scale_inv; - - margin = *marginl + *marginr; - *marginl = SNAPXI (*marginl); - if (*marginl < x_scale_inv) - *marginl = x_scale_inv; - - *marginr = margin - *marginl; - if (*marginr < 0) - *marginr = 0; - *marginr = SNAPXI (*marginr); -} - -static cairo_status_t -twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, - cairo_t *cr) -{ - cairo_status_t status; - twin_scaled_properties_t *props; - - props = malloc (sizeof (twin_scaled_properties_t)); - if (unlikely (props == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - - props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), - &twin_properties_key); - - props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; - - /* weight */ - props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); - - /* pen & margins */ - props->penx = props->peny = props->weight; - props->marginl = props->marginr = F (4); - if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) - twin_hint_pen_and_margins(cr, - &props->penx, &props->peny, - &props->marginl, &props->marginr); - - /* stretch */ - props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); - - - /* Save it */ - status = cairo_scaled_font_set_user_data (scaled_font, - &twin_properties_key, - props, free); - if (unlikely (status)) - goto FREE_PROPS; - - return CAIRO_STATUS_SUCCESS; - -FREE_PROPS: - free (props); - return status; -} - - -/* - * User-font implementation - */ - -static cairo_status_t -twin_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_t *cr, - cairo_font_extents_t *metrics) -{ - metrics->ascent = F (54); - metrics->descent = 1 - metrics->ascent; - - return twin_scaled_font_compute_properties (scaled_font, cr); -} - -#define TWIN_GLYPH_MAX_SNAP_X 4 -#define TWIN_GLYPH_MAX_SNAP_Y 7 - -typedef struct { - int n_snap_x; - int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; - double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; - int n_snap_y; - int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; - double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; -} twin_snap_info_t; - -#define twin_glyph_left(g) ((g)[0]) -#define twin_glyph_right(g) ((g)[1]) -#define twin_glyph_ascent(g) ((g)[2]) -#define twin_glyph_descent(g) ((g)[3]) - -#define twin_glyph_n_snap_x(g) ((g)[4]) -#define twin_glyph_n_snap_y(g) ((g)[5]) -#define twin_glyph_snap_x(g) (&g[6]) -#define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) -#define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) - -static void -twin_compute_snap (cairo_t *cr, - twin_snap_info_t *info, - const signed char *b) -{ - int s, n; - const signed char *snap; - double x_scale, x_scale_inv; - double y_scale, y_scale_inv; - - compute_hinting_scales (cr, - &x_scale, &x_scale_inv, - &y_scale, &y_scale_inv); - - snap = twin_glyph_snap_x (b); - n = twin_glyph_n_snap_x (b); - info->n_snap_x = n; - assert (n <= TWIN_GLYPH_MAX_SNAP_X); - for (s = 0; s < n; s++) { - info->snap_x[s] = snap[s]; - info->snapped_x[s] = SNAPXI (F (snap[s])); - } - - snap = twin_glyph_snap_y (b); - n = twin_glyph_n_snap_y (b); - info->n_snap_y = n; - assert (n <= TWIN_GLYPH_MAX_SNAP_Y); - for (s = 0; s < n; s++) { - info->snap_y[s] = snap[s]; - info->snapped_y[s] = SNAPYI (F (snap[s])); - } -} - -static double -twin_snap (int8_t v, int n, int8_t *snap, double *snapped) -{ - int s; - - if (!n) - return F(v); - - if (snap[0] == v) - return snapped[0]; - - for (s = 0; s < n - 1; s++) - { - if (snap[s+1] == v) - return snapped[s+1]; - - if (snap[s] <= v && v <= snap[s+1]) - { - int before = snap[s]; - int after = snap[s+1]; - int dist = after - before; - double snap_before = snapped[s]; - double snap_after = snapped[s+1]; - double dist_before = v - before; - return snap_before + (snap_after - snap_before) * dist_before / dist; - } - } - return F(v); -} - -#define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) -#define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) - -static cairo_status_t -twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, - unsigned long glyph, - cairo_t *cr, - cairo_text_extents_t *metrics) -{ - double x1, y1, x2, y2, x3, y3; - double marginl; - twin_scaled_properties_t *props; - twin_snap_info_t info; - const int8_t *b; - const int8_t *g; - int8_t w; - double gw; - - props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); - - /* Save glyph space, we need it when stroking */ - cairo_save (cr); - - /* center the pen */ - cairo_translate (cr, props->penx * .5, -props->peny * .5); - - /* small-caps */ - if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { - glyph += 'A' - 'a'; - /* 28 and 42 are small and capital letter heights of the glyph data */ - cairo_scale (cr, 1, 28. / 42); - } - - /* slant */ - if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { - cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; - cairo_transform (cr, &shear); - } - - b = _cairo_twin_outlines + - _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; - g = twin_glyph_draw(b); - w = twin_glyph_right(b); - gw = F(w); - - marginl = props->marginl; - - /* monospace */ - if (props->face_props->monospace) { - double monow = F(24); - double extra = props->penx + props->marginl + props->marginr; - cairo_scale (cr, (monow + extra) / (gw + extra), 1); - gw = monow; - - /* resnap margin for new transform */ - { - double x, y, x_scale, x_scale_inv; - x = 1; y = 0; - compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); - marginl = SNAPXI (marginl); - } - } - - cairo_translate (cr, marginl, 0); - - /* stretch */ - cairo_scale (cr, props->stretch, 1); - - if (props->snap) - twin_compute_snap (cr, &info, b); - else - info.n_snap_x = info.n_snap_y = 0; - - /* advance width */ - metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; - - /* glyph shape */ - for (;;) { - switch (*g++) { - case 'M': - cairo_close_path (cr); - /* fall through */ - case 'm': - x1 = SNAPX(*g++); - y1 = SNAPY(*g++); - cairo_move_to (cr, x1, y1); - continue; - case 'L': - cairo_close_path (cr); - /* fall through */ - case 'l': - x1 = SNAPX(*g++); - y1 = SNAPY(*g++); - cairo_line_to (cr, x1, y1); - continue; - case 'C': - cairo_close_path (cr); - /* fall through */ - case 'c': - x1 = SNAPX(*g++); - y1 = SNAPY(*g++); - x2 = SNAPX(*g++); - y2 = SNAPY(*g++); - x3 = SNAPX(*g++); - y3 = SNAPY(*g++); - cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); - continue; - case 'E': - cairo_close_path (cr); - /* fall through */ - case 'e': - cairo_restore (cr); /* restore glyph space */ - cairo_set_tolerance (cr, 0.01); - cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_width (cr, 1); - cairo_scale (cr, props->penx, props->peny); - cairo_stroke (cr); - break; - case 'X': - /* filler */ - continue; - } - break; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, - unsigned long unicode, - unsigned long *glyph) -{ - /* We use an identity charmap. Which means we could live - * with no unicode_to_glyph method too. But we define this - * to map all unknown chars to a single unknown glyph to - * reduce pressure on cache. */ - - if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) - *glyph = unicode; - else - *glyph = 0; - - return CAIRO_STATUS_SUCCESS; -} - - -/* - * Face constructor - */ - -static cairo_font_face_t * -_cairo_font_face_twin_create_internal (void) -{ - cairo_font_face_t *twin_font_face; - - twin_font_face = cairo_user_font_face_create (); - cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); - cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); - cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); - - return twin_font_face; -} - -cairo_font_face_t * -_cairo_font_face_twin_create_fallback (void) -{ - cairo_font_face_t *twin_font_face; - cairo_status_t status; - - twin_font_face = _cairo_font_face_twin_create_internal (); - status = twin_font_face_create_properties (twin_font_face, NULL); - if (status) { - cairo_font_face_destroy (twin_font_face); - return (cairo_font_face_t *) &_cairo_font_face_nil; - } - - return twin_font_face; -} - -cairo_status_t -_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - cairo_status_t status; - cairo_font_face_t *twin_font_face; - - twin_font_face = _cairo_font_face_twin_create_internal (); - status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); - if (status) { - cairo_font_face_destroy (twin_font_face); - return status; - } - - *font_face = twin_font_face; - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-font-face.c b/libs/cairo/cairo/src/cairo-font-face.c deleted file mode 100644 index ab17a8cb2..000000000 --- a/libs/cairo/cairo/src/cairo-font-face.c +++ /dev/null @@ -1,272 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -/** - * SECTION:cairo-font-face - * @Title: cairo_font_face_t - * @Short_Description: Base class for font faces - * @See_Also: #cairo_scaled_font_t - * - * #cairo_font_face_t represents a particular font at a particular weight, - * slant, and other characteristic but no size, transformation, or size. - * - * Font faces are created using font-backend-specific - * constructors, typically of the form - * cairo_backend_font_face_create(), or implicitly - * using the toy text API by way of - * cairo_select_font_face(). The resulting face can be accessed using - * cairo_get_font_face(). - */ - -/* #cairo_font_face_t */ - -const cairo_font_face_t _cairo_font_face_nil = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_NO_MEMORY, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL -}; - -cairo_status_t -_cairo_font_face_set_error (cairo_font_face_t *font_face, - cairo_status_t status) -{ - if (status == CAIRO_STATUS_SUCCESS) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&font_face->status, status); - - return _cairo_error (status); -} - -void -_cairo_font_face_init (cairo_font_face_t *font_face, - const cairo_font_face_backend_t *backend) -{ - CAIRO_MUTEX_INITIALIZE (); - - font_face->status = CAIRO_STATUS_SUCCESS; - CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1); - font_face->backend = backend; - - _cairo_user_data_array_init (&font_face->user_data); -} - -/** - * cairo_font_face_reference: - * @font_face: a #cairo_font_face_t, (may be %NULL in which case this - * function does nothing). - * - * Increases the reference count on @font_face by one. This prevents - * @font_face from being destroyed until a matching call to - * cairo_font_face_destroy() is made. - * - * The number of references to a #cairo_font_face_t can be get using - * cairo_font_face_get_reference_count(). - * - * Return value: the referenced #cairo_font_face_t. - **/ -cairo_font_face_t * -cairo_font_face_reference (cairo_font_face_t *font_face) -{ - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) - return font_face; - - /* We would normally assert that we have a reference here but we - * can't get away with that due to the zombie case as documented - * in _cairo_ft_font_face_destroy. */ - - _cairo_reference_count_inc (&font_face->ref_count); - - return font_face; -} -slim_hidden_def (cairo_font_face_reference); - -/** - * cairo_font_face_destroy: - * @font_face: a #cairo_font_face_t - * - * Decreases the reference count on @font_face by one. If the result - * is zero, then @font_face and all associated resources are freed. - * See cairo_font_face_reference(). - **/ -void -cairo_font_face_destroy (cairo_font_face_t *font_face) -{ - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&font_face->ref_count)) - return; - - if (font_face->backend->destroy) - font_face->backend->destroy (font_face); - - /* We allow resurrection to deal with some memory management for the - * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t - * need to effectively mutually reference each other - */ - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)) - return; - - _cairo_user_data_array_fini (&font_face->user_data); - - free (font_face); -} -slim_hidden_def (cairo_font_face_destroy); - -/** - * cairo_font_face_get_type: - * @font_face: a font face - * - * This function returns the type of the backend used to create - * a font face. See #cairo_font_type_t for available types. - * - * Return value: The type of @font_face. - * - * Since: 1.2 - **/ -cairo_font_type_t -cairo_font_face_get_type (cairo_font_face_t *font_face) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) - return CAIRO_FONT_TYPE_TOY; - - return font_face->backend->type; -} - -/** - * cairo_font_face_get_reference_count: - * @font_face: a #cairo_font_face_t - * - * Returns the current reference count of @font_face. - * - * Return value: the current reference count of @font_face. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.4 - **/ -unsigned int -cairo_font_face_get_reference_count (cairo_font_face_t *font_face) -{ - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count); -} - -/** - * cairo_font_face_status: - * @font_face: a #cairo_font_face_t - * - * Checks whether an error has previously occurred for this - * font face - * - * Return value: %CAIRO_STATUS_SUCCESS or another error such as - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -cairo_font_face_status (cairo_font_face_t *font_face) -{ - return font_face->status; -} - -/** - * cairo_font_face_get_user_data: - * @font_face: a #cairo_font_face_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @font_face using the specified - * key. If no user data has been attached with the given key this - * function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - **/ -void * -cairo_font_face_get_user_data (cairo_font_face_t *font_face, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&font_face->user_data, - key); -} -slim_hidden_def (cairo_font_face_get_user_data); - -/** - * cairo_font_face_set_user_data: - * @font_face: a #cairo_font_face_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the font face - * @destroy: a #cairo_destroy_func_t which will be called when the - * font face is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @font_face. To remove user data from a font face, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - **/ -cairo_status_t -cairo_font_face_set_user_data (cairo_font_face_t *font_face, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) - return font_face->status; - - return _cairo_user_data_array_set_data (&font_face->user_data, - key, user_data, destroy); -} -slim_hidden_def (cairo_font_face_set_user_data); - -void -_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, - const cairo_unscaled_font_backend_t *backend) -{ - CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1); - unscaled_font->backend = backend; -} - -cairo_unscaled_font_t * -_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font) -{ - if (unscaled_font == NULL) - return NULL; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); - - _cairo_reference_count_inc (&unscaled_font->ref_count); - - return unscaled_font; -} - -void -_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) -{ - if (unscaled_font == NULL) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count)) - return; - - unscaled_font->backend->destroy (unscaled_font); - - free (unscaled_font); -} diff --git a/libs/cairo/cairo/src/cairo-font-options.c b/libs/cairo/cairo/src/cairo-font-options.c deleted file mode 100644 index 17a892160..000000000 --- a/libs/cairo/cairo/src/cairo-font-options.c +++ /dev/null @@ -1,481 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -/** - * SECTION:cairo-font-options - * @Title: cairo_font_options_t - * @Short_Description: How a font should be rendered - * @See_Also: #cairo_scaled_font_t - * - * The font options specify how fonts should be rendered. Most of the - * time the font options implied by a surface are just right and do not - * need any changes, but for pixel-based targets tweaking font options - * may result in superior output on a particular display. - */ - -static const cairo_font_options_t _cairo_font_options_nil = { - CAIRO_ANTIALIAS_DEFAULT, - CAIRO_SUBPIXEL_ORDER_DEFAULT, - CAIRO_LCD_FILTER_DEFAULT, - CAIRO_HINT_STYLE_DEFAULT, - CAIRO_HINT_METRICS_DEFAULT, - CAIRO_ROUND_GLYPH_POS_DEFAULT -}; - -/** - * _cairo_font_options_init_default: - * @options: a #cairo_font_options_t - * - * Initializes all fields of the font options object to default values. - **/ -void -_cairo_font_options_init_default (cairo_font_options_t *options) -{ - options->antialias = CAIRO_ANTIALIAS_DEFAULT; - options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT; - options->hint_style = CAIRO_HINT_STYLE_DEFAULT; - options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT; - options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT; -} - -void -_cairo_font_options_init_copy (cairo_font_options_t *options, - const cairo_font_options_t *other) -{ - options->antialias = other->antialias; - options->subpixel_order = other->subpixel_order; - options->lcd_filter = other->lcd_filter; - options->hint_style = other->hint_style; - options->hint_metrics = other->hint_metrics; - options->round_glyph_positions = other->round_glyph_positions; -} - -/** - * cairo_font_options_create: - * - * Allocates a new font options object with all options initialized - * to default values. - * - * Return value: a newly allocated #cairo_font_options_t. Free with - * cairo_font_options_destroy(). This function always returns a - * valid pointer; if memory cannot be allocated, then a special - * error object is returned where all operations on the object do nothing. - * You can check for this with cairo_font_options_status(). - **/ -cairo_font_options_t * -cairo_font_options_create (void) -{ - cairo_font_options_t *options; - - options = malloc (sizeof (cairo_font_options_t)); - if (!options) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_options_t *) &_cairo_font_options_nil; - } - - _cairo_font_options_init_default (options); - - return options; -} - -/** - * cairo_font_options_copy: - * @original: a #cairo_font_options_t - * - * Allocates a new font options object copying the option values from - * @original. - * - * Return value: a newly allocated #cairo_font_options_t. Free with - * cairo_font_options_destroy(). This function always returns a - * valid pointer; if memory cannot be allocated, then a special - * error object is returned where all operations on the object do nothing. - * You can check for this with cairo_font_options_status(). - **/ -cairo_font_options_t * -cairo_font_options_copy (const cairo_font_options_t *original) -{ - cairo_font_options_t *options; - - if (cairo_font_options_status ((cairo_font_options_t *) original)) - return (cairo_font_options_t *) &_cairo_font_options_nil; - - options = malloc (sizeof (cairo_font_options_t)); - if (!options) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_options_t *) &_cairo_font_options_nil; - } - - _cairo_font_options_init_copy (options, original); - - return options; -} - -/** - * cairo_font_options_destroy: - * @options: a #cairo_font_options_t - * - * Destroys a #cairo_font_options_t object created with - * cairo_font_options_create() or cairo_font_options_copy(). - **/ -void -cairo_font_options_destroy (cairo_font_options_t *options) -{ - if (cairo_font_options_status (options)) - return; - - free (options); -} - -/** - * cairo_font_options_status: - * @options: a #cairo_font_options_t - * - * Checks whether an error has previously occurred for this - * font options object - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - **/ -cairo_status_t -cairo_font_options_status (cairo_font_options_t *options) -{ - if (options == NULL) - return CAIRO_STATUS_NULL_POINTER; - else if (options == (cairo_font_options_t *) &_cairo_font_options_nil) - return CAIRO_STATUS_NO_MEMORY; - else - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_font_options_status); - -/** - * cairo_font_options_merge: - * @options: a #cairo_font_options_t - * @other: another #cairo_font_options_t - * - * Merges non-default options from @other into @options, replacing - * existing values. This operation can be thought of as somewhat - * similar to compositing @other onto @options with the operation - * of %CAIRO_OPERATION_OVER. - **/ -void -cairo_font_options_merge (cairo_font_options_t *options, - const cairo_font_options_t *other) -{ - if (cairo_font_options_status (options)) - return; - - if (cairo_font_options_status ((cairo_font_options_t *) other)) - return; - - if (other->antialias != CAIRO_ANTIALIAS_DEFAULT) - options->antialias = other->antialias; - if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) - options->subpixel_order = other->subpixel_order; - if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) - options->lcd_filter = other->lcd_filter; - if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT) - options->hint_style = other->hint_style; - if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT) - options->hint_metrics = other->hint_metrics; - if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) - options->round_glyph_positions = other->round_glyph_positions; -} -slim_hidden_def (cairo_font_options_merge); - -/** - * cairo_font_options_equal: - * @options: a #cairo_font_options_t - * @other: another #cairo_font_options_t - * - * Compares two font options objects for equality. - * - * Return value: %TRUE if all fields of the two font options objects match. - * Note that this function will return %FALSE if either object is in - * error. - **/ -cairo_bool_t -cairo_font_options_equal (const cairo_font_options_t *options, - const cairo_font_options_t *other) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return FALSE; - if (cairo_font_options_status ((cairo_font_options_t *) other)) - return FALSE; - - if (options == other) - return TRUE; - - return (options->antialias == other->antialias && - options->subpixel_order == other->subpixel_order && - options->lcd_filter == other->lcd_filter && - options->hint_style == other->hint_style && - options->hint_metrics == other->hint_metrics && - options->round_glyph_positions == other->round_glyph_positions); -} -slim_hidden_def (cairo_font_options_equal); - -/** - * cairo_font_options_hash: - * @options: a #cairo_font_options_t - * - * Compute a hash for the font options object; this value will - * be useful when storing an object containing a #cairo_font_options_t - * in a hash table. - * - * Return value: the hash value for the font options object. - * The return value can be cast to a 32-bit type if a - * 32-bit hash value is needed. - **/ -unsigned long -cairo_font_options_hash (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - options = &_cairo_font_options_nil; /* force default values */ - - return ((options->antialias) | - (options->subpixel_order << 4) | - (options->lcd_filter << 8) | - (options->hint_style << 12) | - (options->hint_metrics << 16)); -} -slim_hidden_def (cairo_font_options_hash); - -/** - * cairo_font_options_set_antialias: - * @options: a #cairo_font_options_t - * @antialias: the new antialiasing mode - * - * Sets the antialiasing mode for the font options object. This - * specifies the type of antialiasing to do when rendering text. - **/ -void -cairo_font_options_set_antialias (cairo_font_options_t *options, - cairo_antialias_t antialias) -{ - if (cairo_font_options_status (options)) - return; - - options->antialias = antialias; -} -slim_hidden_def (cairo_font_options_set_antialias); - -/** - * cairo_font_options_get_antialias: - * @options: a #cairo_font_options_t - * - * Gets the antialiasing mode for the font options object. - * - * Return value: the antialiasing mode - **/ -cairo_antialias_t -cairo_font_options_get_antialias (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_ANTIALIAS_DEFAULT; - - return options->antialias; -} - -/** - * cairo_font_options_set_subpixel_order: - * @options: a #cairo_font_options_t - * @subpixel_order: the new subpixel order - * - * Sets the subpixel order for the font options object. The subpixel - * order specifies the order of color elements within each pixel on - * the display device when rendering with an antialiasing mode of - * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for - * #cairo_subpixel_order_t for full details. - **/ -void -cairo_font_options_set_subpixel_order (cairo_font_options_t *options, - cairo_subpixel_order_t subpixel_order) -{ - if (cairo_font_options_status (options)) - return; - - options->subpixel_order = subpixel_order; -} -slim_hidden_def (cairo_font_options_set_subpixel_order); - -/** - * cairo_font_options_get_subpixel_order: - * @options: a #cairo_font_options_t - * - * Gets the subpixel order for the font options object. - * See the documentation for #cairo_subpixel_order_t for full details. - * - * Return value: the subpixel order for the font options object - **/ -cairo_subpixel_order_t -cairo_font_options_get_subpixel_order (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_SUBPIXEL_ORDER_DEFAULT; - - return options->subpixel_order; -} - -/** - * _cairo_font_options_set_lcd_filter: - * @options: a #cairo_font_options_t - * @lcd_filter: the new LCD filter - * - * Sets the LCD filter for the font options object. The LCD filter - * specifies how pixels are filtered when rendered with an antialiasing - * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for - * #cairo_lcd_filter_t for full details. - * - * Since: 1.8 - **/ -void -_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, - cairo_lcd_filter_t lcd_filter) -{ - if (cairo_font_options_status (options)) - return; - - options->lcd_filter = lcd_filter; -} - -/** - * _cairo_font_options_get_lcd_filter: - * @options: a #cairo_font_options_t - * - * Gets the LCD filter for the font options object. - * See the documentation for #cairo_lcd_filter_t for full details. - * - * Return value: the LCD filter for the font options object - * - * Since: 1.8 - **/ -cairo_lcd_filter_t -_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_LCD_FILTER_DEFAULT; - - return options->lcd_filter; -} - -/** - * _cairo_font_options_set_round_glyph_positions: - * @options: a #cairo_font_options_t - * @round: the new rounding value - * - * Sets the rounding options for the font options object. If rounding is set, a - * glyph's position will be rounded to integer values. - * - * Since: 1.12 - **/ -void -_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, - cairo_round_glyph_positions_t round) -{ - if (cairo_font_options_status (options)) - return; - - options->round_glyph_positions = round; -} - -/** - * _cairo_font_options_get_round_glyph_positions: - * @options: a #cairo_font_options_t - * - * Gets the glyph position rounding option for the font options object. - * - * Return value: The round glyph posistions flag for the font options object. - * - * Since: 1.12 - **/ -cairo_round_glyph_positions_t -_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_ROUND_GLYPH_POS_DEFAULT; - - return options->round_glyph_positions; -} - -/** - * cairo_font_options_set_hint_style: - * @options: a #cairo_font_options_t - * @hint_style: the new hint style - * - * Sets the hint style for font outlines for the font options object. - * This controls whether to fit font outlines to the pixel grid, - * and if so, whether to optimize for fidelity or contrast. - * See the documentation for #cairo_hint_style_t for full details. - **/ -void -cairo_font_options_set_hint_style (cairo_font_options_t *options, - cairo_hint_style_t hint_style) -{ - if (cairo_font_options_status (options)) - return; - - options->hint_style = hint_style; -} -slim_hidden_def (cairo_font_options_set_hint_style); - -/** - * cairo_font_options_get_hint_style: - * @options: a #cairo_font_options_t - * - * Gets the hint style for font outlines for the font options object. - * See the documentation for #cairo_hint_style_t for full details. - * - * Return value: the hint style for the font options object - **/ -cairo_hint_style_t -cairo_font_options_get_hint_style (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_HINT_STYLE_DEFAULT; - - return options->hint_style; -} - -/** - * cairo_font_options_set_hint_metrics: - * @options: a #cairo_font_options_t - * @hint_metrics: the new metrics hinting mode - * - * Sets the metrics hinting mode for the font options object. This - * controls whether metrics are quantized to integer values in - * device units. - * See the documentation for #cairo_hint_metrics_t for full details. - **/ -void -cairo_font_options_set_hint_metrics (cairo_font_options_t *options, - cairo_hint_metrics_t hint_metrics) -{ - if (cairo_font_options_status (options)) - return; - - options->hint_metrics = hint_metrics; -} -slim_hidden_def (cairo_font_options_set_hint_metrics); - -/** - * cairo_font_options_get_hint_metrics: - * @options: a #cairo_font_options_t - * - * Gets the metrics hinting mode for the font options object. - * See the documentation for #cairo_hint_metrics_t for full details. - * - * Return value: the metrics hinting mode for the font options object - **/ -cairo_hint_metrics_t -cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return CAIRO_HINT_METRICS_DEFAULT; - - return options->hint_metrics; -} diff --git a/libs/cairo/cairo/src/cairo-fontconfig-private.h b/libs/cairo/cairo/src/cairo-fontconfig-private.h deleted file mode 100644 index 110304b18..000000000 --- a/libs/cairo/cairo/src/cairo-fontconfig-private.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _CAIRO_FONTCONFIG_PRIVATE_H -#define _CAIRO_FONTCONFIG_PRIVATE_H - -#include "cairo.h" - -#if CAIRO_HAS_FC_FONT -#include -#include -#endif - -/* sub-pixel order */ -#ifndef FC_RGBA_UNKNOWN -#define FC_RGBA_UNKNOWN 0 -#define FC_RGBA_RGB 1 -#define FC_RGBA_BGR 2 -#define FC_RGBA_VRGB 3 -#define FC_RGBA_VBGR 4 -#define FC_RGBA_NONE 5 -#endif - -/* hinting style */ -#ifndef FC_HINT_NONE -#define FC_HINT_NONE 0 -#define FC_HINT_SLIGHT 1 -#define FC_HINT_MEDIUM 2 -#define FC_HINT_FULL 3 -#endif - -/* LCD filter */ -#ifndef FC_LCD_NONE -#define FC_LCD_NONE 0 -#define FC_LCD_DEFAULT 1 -#define FC_LCD_LIGHT 2 -#define FC_LCD_LEGACY 3 -#endif - -#endif /* _CAIRO_FONTCONFIG_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-freed-pool-private.h b/libs/cairo/cairo/src/cairo-freed-pool-private.h deleted file mode 100644 index c23e5a03a..000000000 --- a/libs/cairo/cairo/src/cairo-freed-pool-private.h +++ /dev/null @@ -1,97 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FREED_POOL_H -#define CAIRO_FREED_POOL_H - -#include "cairoint.h" -#include "cairo-atomic-private.h" - -#if HAS_ATOMIC_OPS -/* Keep a stash of recently freed clip_paths, since we need to - * reallocate them frequently. - */ -#define MAX_FREED_POOL_SIZE 4 -typedef struct { - void *pool[MAX_FREED_POOL_SIZE]; - cairo_atomic_int_t top; -} freed_pool_t; - -static cairo_always_inline void * -_atomic_fetch (void **slot) -{ - void *ptr; - - do { - ptr = _cairo_atomic_ptr_get (slot); - } while (! _cairo_atomic_ptr_cmpxchg (slot, ptr, NULL)); - - return ptr; -} - -static cairo_always_inline cairo_bool_t -_atomic_store (void **slot, void *ptr) -{ - return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr); -} - -cairo_private void * -_freed_pool_get_search (freed_pool_t *pool); - -static inline void * -_freed_pool_get (freed_pool_t *pool) -{ - void *ptr; - int i; - - i = _cairo_atomic_int_get_relaxed (&pool->top) - 1; - if (i < 0) - i = 0; - - ptr = _atomic_fetch (&pool->pool[i]); - if (likely (ptr != NULL)) { - _cairo_atomic_int_set_relaxed (&pool->top, i); - return ptr; - } - - /* either empty or contended */ - return _freed_pool_get_search (pool); -} - -cairo_private void -_freed_pool_put_search (freed_pool_t *pool, void *ptr); - -static inline void -_freed_pool_put (freed_pool_t *pool, void *ptr) -{ - int i; - - i = _cairo_atomic_int_get_relaxed (&pool->top); - if (likely (i < ARRAY_LENGTH (pool->pool) && - _atomic_store (&pool->pool[i], ptr))) - { - _cairo_atomic_int_set_relaxed (&pool->top, i + 1); - return; - } - - /* either full or contended */ - _freed_pool_put_search (pool, ptr); -} - -cairo_private void -_freed_pool_reset (freed_pool_t *pool); - -#define HAS_FREED_POOL 1 - -#else - -typedef int freed_pool_t; - -#define _freed_pool_get(pool) NULL -#define _freed_pool_put(pool, ptr) free(ptr) -#define _freed_pool_reset(ptr) - -#endif - -#endif /* CAIRO_FREED_POOL_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-freed-pool.c b/libs/cairo/cairo/src/cairo-freed-pool.c deleted file mode 100644 index c65f4626d..000000000 --- a/libs/cairo/cairo/src/cairo-freed-pool.c +++ /dev/null @@ -1,60 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-freed-pool-private.h" - -#if HAS_FREED_POOL - -void * -_freed_pool_get_search (freed_pool_t *pool) -{ - void *ptr; - int i; - - for (i = ARRAY_LENGTH (pool->pool); i--;) { - ptr = _atomic_fetch (&pool->pool[i]); - if (ptr != NULL) { - _cairo_atomic_int_set_relaxed (&pool->top, i); - return ptr; - } - } - - /* empty */ - _cairo_atomic_int_set_relaxed (&pool->top, 0); - return NULL; -} - -void -_freed_pool_put_search (freed_pool_t *pool, void *ptr) -{ - int i; - - for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { - if (_atomic_store (&pool->pool[i], ptr)) { - _cairo_atomic_int_set_relaxed (&pool->top, i + 1); - return; - } - } - - /* full */ - _cairo_atomic_int_set_relaxed (&pool->top, i); - free (ptr); -} - -void -_freed_pool_reset (freed_pool_t *pool) -{ - int i; - - for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { - free (pool->pool[i]); - pool->pool[i] = NULL; - } - - _cairo_atomic_int_set_relaxed (&pool->top, 0); -} - -#endif diff --git a/libs/cairo/cairo/src/cairo-freelist-private.h b/libs/cairo/cairo/src/cairo-freelist-private.h deleted file mode 100644 index 703181b56..000000000 --- a/libs/cairo/cairo/src/cairo-freelist-private.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright © 2006 Joonas Pihlaja - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ -#ifndef CAIRO_FREELIST_H -#define CAIRO_FREELIST_H - -#include "cairo-types-private.h" -#include "cairo-compiler-private.h" -#include "cairo-freelist-type-private.h" - -/* for stand-alone compilation*/ -#ifndef VG -#define VG(x) -#endif - -#ifndef NULL -#define NULL (void *) 0 -#endif - -/* Initialise a freelist that will be responsible for allocating - * nodes of size nodesize. */ -cairo_private void -_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize); - -/* Deallocate any nodes in the freelist. */ -cairo_private void -_cairo_freelist_fini (cairo_freelist_t *freelist); - -/* Allocate a new node from the freelist. If the freelist contains no - * nodes, a new one will be allocated using malloc(). The caller is - * responsible for calling _cairo_freelist_free() or free() on the - * returned node. Returns %NULL on memory allocation error. */ -cairo_private void * -_cairo_freelist_alloc (cairo_freelist_t *freelist); - -/* Allocate a new node from the freelist. If the freelist contains no - * nodes, a new one will be allocated using calloc(). The caller is - * responsible for calling _cairo_freelist_free() or free() on the - * returned node. Returns %NULL on memory allocation error. */ -cairo_private void * -_cairo_freelist_calloc (cairo_freelist_t *freelist); - -/* Return a node to the freelist. This does not deallocate the memory, - * but makes it available for later reuse by - * _cairo_freelist_alloc(). */ -cairo_private void -_cairo_freelist_free (cairo_freelist_t *freelist, void *node); - - -cairo_private void -_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize); - -cairo_private void -_cairo_freepool_fini (cairo_freepool_t *freepool); - -static inline void -_cairo_freepool_reset (cairo_freepool_t *freepool) -{ - while (freepool->pools != &freepool->embedded_pool) { - cairo_freelist_pool_t *pool = freepool->pools; - freepool->pools = pool->next; - pool->next = freepool->freepools; - freepool->freepools = pool; - } - - freepool->embedded_pool.rem = sizeof (freepool->embedded_data); - freepool->embedded_pool.data = freepool->embedded_data; -} - -cairo_private void * -_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool); - -static inline void * -_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool) -{ - cairo_freelist_pool_t *pool; - uint8_t *ptr; - - pool = freepool->pools; - if (unlikely (freepool->nodesize > pool->rem)) - return _cairo_freepool_alloc_from_new_pool (freepool); - - ptr = pool->data; - pool->data += freepool->nodesize; - pool->rem -= freepool->nodesize; - VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize)); - return ptr; -} - -static inline void * -_cairo_freepool_alloc (cairo_freepool_t *freepool) -{ - cairo_freelist_node_t *node; - - node = freepool->first_free_node; - if (unlikely (node == NULL)) - return _cairo_freepool_alloc_from_pool (freepool); - - VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); - freepool->first_free_node = node->next; - VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); - - return node; -} - -cairo_private cairo_status_t -_cairo_freepool_alloc_array (cairo_freepool_t *freepool, - int count, - void **array); - -static inline void -_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) -{ - cairo_freelist_node_t *node = ptr; - - node->next = freepool->first_free_node; - freepool->first_free_node = node; - VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize)); -} - -#endif /* CAIRO_FREELIST_H */ diff --git a/libs/cairo/cairo/src/cairo-freelist-type-private.h b/libs/cairo/cairo/src/cairo-freelist-type-private.h deleted file mode 100644 index 4dd056461..000000000 --- a/libs/cairo/cairo/src/cairo-freelist-type-private.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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/libs/cairo/cairo/src/cairo-freelist.c b/libs/cairo/cairo/src/cairo-freelist.c deleted file mode 100644 index d596eab81..000000000 --- a/libs/cairo/cairo/src/cairo-freelist.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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/libs/cairo/cairo/src/cairo-ft-font.c b/libs/cairo/cairo/src/cairo-ft-font.c deleted file mode 100644 index 1a2799b86..000000000 --- a/libs/cairo/cairo/src/cairo-ft-font.c +++ /dev/null @@ -1,3319 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for strdup() */ -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-ft-private.h" - -#include - -#include "cairo-fontconfig-private.h" - -#include -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_IMAGE_H -#include FT_BITMAP_H -#include FT_TRUETYPE_TABLES_H -#if HAVE_FT_GLYPHSLOT_EMBOLDEN -#include FT_SYNTHESIS_H -#endif - -#if HAVE_FT_LIBRARY_SETLCDFILTER -#include FT_LCD_FILTER_H -#endif - -#define _GNU_SOURCE /* for RTLD_DEFAULT */ -#include - -#ifndef RTLD_DEFAULT -#define RTLD_DEFAULT ((void *) 0) -#endif - -/* Fontconfig version older than 2.6 didn't have these options */ -#ifndef FC_LCD_FILTER -#define FC_LCD_FILTER "lcdfilter" -#endif -/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */ -#ifndef FC_LCD_NONE -#define FC_LCD_NONE 0 -#define FC_LCD_DEFAULT 1 -#define FC_LCD_LIGHT 2 -#define FC_LCD_LEGACY 3 -#endif - -/* FreeType version older than 2.3.5(?) didn't have these options */ -#ifndef FT_LCD_FILTER_NONE -#define FT_LCD_FILTER_NONE 0 -#define FT_LCD_FILTER_DEFAULT 1 -#define FT_LCD_FILTER_LIGHT 2 -#define FT_LCD_FILTER_LEGACY 16 -#endif - -typedef FT_Error (*setLcdFilterFunc)(FT_Library, int); -static setLcdFilterFunc setLcdFilter; - -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) -#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) -#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) - -/* This is the max number of FT_face objects we keep open at once - */ -#define MAX_OPEN_FACES 10 -/* This is the maximum font size we allow to be passed to FT_Set_Char_Size - */ -#define MAX_FONT_SIZE 1000 - -/** - * SECTION:cairo-ft - * @Title: FreeType Fonts - * @Short_Description: Font support for FreeType - * @See_Also: #cairo_font_face_t - * - * The FreeType font backend is primarily used to render text on GNU/Linux - * systems, but can be used on other platforms too. - */ - -/** - * CAIRO_HAS_FT_FONT: - * - * Defined if the FreeType font backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/** - * CAIRO_HAS_FC_FONT: - * - * Defined if the Fontconfig-specific functions of the FreeType font backend - * are available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/* - * The simple 2x2 matrix is converted into separate scale and shape - * factors so that hinting works right - */ - -typedef struct _cairo_ft_font_transform { - double x_scale, y_scale; - double shape[2][2]; -} cairo_ft_font_transform_t; - -/* - * We create an object that corresponds to a single font on the disk; - * (identified by a filename/id pair) these are shared between all - * fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we - * just create a one-off version with a permanent face value. - */ - -typedef struct _cairo_ft_font_face cairo_ft_font_face_t; - -struct _cairo_ft_unscaled_font { - cairo_unscaled_font_t base; - - cairo_bool_t from_face; /* was the FT_Face provided by user? */ - FT_Face face; /* provided or cached face */ - - /* only set if from_face is false */ - char *filename; - int id; - - /* We temporarily scale the unscaled font as needed */ - cairo_bool_t have_scale; - cairo_matrix_t current_scale; - double x_scale; /* Extracted X scale factor */ - double y_scale; /* Extracted Y scale factor */ - cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/ - cairo_matrix_t current_shape; - FT_Matrix Current_Shape; - - cairo_mutex_t mutex; - int lock_count; - - cairo_ft_font_face_t *faces; /* Linked list of faces for this font */ -}; - -static int -_cairo_ft_unscaled_font_keys_equal (const void *key_a, - const void *key_b); - -static void -_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); - -typedef enum _cairo_ft_extra_flags { - CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0), - CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1) -} cairo_ft_extra_flags_t; - -typedef struct _cairo_ft_options { - cairo_font_options_t base; - int load_flags; /* flags for FT_Load_Glyph */ - cairo_ft_extra_flags_t extra_flags; /* other flags that affect results */ -} cairo_ft_options_t; - -struct _cairo_ft_font_face { - cairo_font_face_t base; - - cairo_ft_unscaled_font_t *unscaled; - cairo_ft_options_t ft_options; - cairo_ft_font_face_t *next; - -#if CAIRO_HAS_FC_FONT - FcPattern *pattern; /* if pattern is set, the above fields will be NULL */ - cairo_font_face_t *resolved_font_face; - FcConfig *resolved_config; -#endif -}; - -static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; - -#if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_options_substitute (const cairo_font_options_t *options, - FcPattern *pattern); - -static cairo_font_face_t * -_cairo_ft_resolve_pattern (FcPattern *pattern, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options); - -#endif - -/* - * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t. - * The hash table itself isn't limited in size. However, we limit the - * number of FT_Face objects we keep around; when we've exceeded that - * limit and need to create a new FT_Face, we dump the FT_Face from a - * random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if - * there are any). - */ - -typedef struct _cairo_ft_unscaled_font_map { - cairo_hash_table_t *hash_table; - FT_Library ft_library; - int num_open_faces; -} cairo_ft_unscaled_font_map_t; - -static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL; - - -static void -_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map, - cairo_ft_unscaled_font_t *unscaled) -{ - if (unscaled->face) { - FT_Done_Face (unscaled->face); - unscaled->face = NULL; - unscaled->have_scale = FALSE; - - font_map->num_open_faces--; - } -} - -static cairo_status_t -_cairo_ft_unscaled_font_map_create (void) -{ - cairo_ft_unscaled_font_map_t *font_map; - - /* This function is only intended to be called from - * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can - * detect some other call path. */ - assert (cairo_ft_unscaled_font_map == NULL); - - font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t)); - if (unlikely (font_map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_map->hash_table = - _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal); - - if (unlikely (font_map->hash_table == NULL)) - goto FAIL; - - if (unlikely (FT_Init_FreeType (&font_map->ft_library))) - goto FAIL; - - font_map->num_open_faces = 0; - - cairo_ft_unscaled_font_map = font_map; - return CAIRO_STATUS_SUCCESS; - -FAIL: - if (font_map->hash_table) - _cairo_hash_table_destroy (font_map->hash_table); - free (font_map); - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); -} - - -static void -_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) -{ - cairo_ft_unscaled_font_t *unscaled = entry; - cairo_ft_unscaled_font_map_t *font_map = closure; - - _cairo_hash_table_remove (font_map->hash_table, - &unscaled->base.hash_entry); - - if (! unscaled->from_face) - _font_map_release_face_lock_held (font_map, unscaled); - - _cairo_ft_unscaled_font_fini (unscaled); - free (unscaled); -} - -static void -_cairo_ft_unscaled_font_map_destroy (void) -{ - cairo_ft_unscaled_font_map_t *font_map; - - CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); - font_map = cairo_ft_unscaled_font_map; - cairo_ft_unscaled_font_map = NULL; - CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); - - if (font_map != NULL) { - _cairo_hash_table_foreach (font_map->hash_table, - _cairo_ft_unscaled_font_map_pluck_entry, - font_map); - assert (font_map->num_open_faces == 0); - - FT_Done_FreeType (font_map->ft_library); - - _cairo_hash_table_destroy (font_map->hash_table); - - free (font_map); - } -} - -static cairo_ft_unscaled_font_map_t * -_cairo_ft_unscaled_font_map_lock (void) -{ - CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); - - if (unlikely (cairo_ft_unscaled_font_map == NULL)) { - if (unlikely (_cairo_ft_unscaled_font_map_create ())) { - CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); - return NULL; - } - } - - return cairo_ft_unscaled_font_map; -} - -static void -_cairo_ft_unscaled_font_map_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); -} - -static void -_cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key, - cairo_bool_t from_face, - char *filename, - int id, - FT_Face face) -{ - unsigned long hash; - - key->from_face = from_face; - key->filename = filename; - key->id = id; - key->face = face; - - hash = _cairo_hash_string (filename); - /* the constants are just arbitrary primes */ - hash += ((unsigned long) id) * 1607; - hash += ((unsigned long) face) * 2137; - - key->base.hash_entry.hash = hash; -} - -/** - * _cairo_ft_unscaled_font_init: - * - * Initialize a #cairo_ft_unscaled_font_t. - * - * There are two basic flavors of #cairo_ft_unscaled_font_t, one - * created from an FT_Face and the other created from a filename/id - * pair. These two flavors are identified as from_face and !from_face. - * - * To initialize a from_face font, pass filename==%NULL, id=0 and the - * desired face. - * - * To initialize a !from_face font, pass the filename/id as desired - * and face==%NULL. - * - * Note that the code handles these two flavors in very distinct - * ways. For example there is a hash_table mapping - * filename/id->#cairo_unscaled_font_t in the !from_face case, but no - * parallel in the from_face case, (where the calling code would have - * to do its own mapping to ensure similar sharing). - **/ -static cairo_status_t -_cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled, - cairo_bool_t from_face, - const char *filename, - int id, - FT_Face face) -{ - _cairo_unscaled_font_init (&unscaled->base, - &cairo_ft_unscaled_font_backend); - - if (from_face) { - unscaled->from_face = TRUE; - _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face); - } else { - char *filename_copy; - - unscaled->from_face = FALSE; - unscaled->face = NULL; - - filename_copy = strdup (filename); - if (unlikely (filename_copy == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL); - } - - unscaled->have_scale = FALSE; - CAIRO_MUTEX_INIT (unscaled->mutex); - unscaled->lock_count = 0; - - unscaled->faces = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_ft_unscaled_font_fini: - * - * Free all data associated with a #cairo_ft_unscaled_font_t. - * - * CAUTION: The unscaled->face field must be %NULL before calling this - * function. This is because the #cairo_ft_unscaled_font_t_map keeps a - * count of these faces (font_map->num_open_faces) so it maintains the - * unscaled->face field while it has its lock held. See - * _font_map_release_face_lock_held(). - **/ -static void -_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) -{ - assert (unscaled->face == NULL); - - if (unscaled->filename) { - free (unscaled->filename); - unscaled->filename = NULL; - } - - CAIRO_MUTEX_FINI (unscaled->mutex); -} - -static int -_cairo_ft_unscaled_font_keys_equal (const void *key_a, - const void *key_b) -{ - const cairo_ft_unscaled_font_t *unscaled_a = key_a; - const cairo_ft_unscaled_font_t *unscaled_b = key_b; - - if (unscaled_a->id == unscaled_b->id && - unscaled_a->from_face == unscaled_b->from_face) - { - if (unscaled_a->from_face) - return unscaled_a->face == unscaled_b->face; - - if (unscaled_a->filename == NULL && unscaled_b->filename == NULL) - return TRUE; - else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL) - return FALSE; - else - return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0); - } - - return FALSE; -} - -/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from - * pattern. Returns a new reference to the unscaled font. - */ -static cairo_status_t -_cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, - char *filename, - int id, - FT_Face font_face, - cairo_ft_unscaled_font_t **out) -{ - cairo_ft_unscaled_font_t key, *unscaled; - cairo_ft_unscaled_font_map_t *font_map; - cairo_status_t status; - - font_map = _cairo_ft_unscaled_font_map_lock (); - if (unlikely (font_map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); - - /* Return existing unscaled font if it exists in the hash table. */ - unscaled = _cairo_hash_table_lookup (font_map->hash_table, - &key.base.hash_entry); - if (unscaled != NULL) { - _cairo_unscaled_font_reference (&unscaled->base); - goto DONE; - } - - /* Otherwise create it and insert into hash table. */ - unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); - if (unlikely (unscaled == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto UNWIND_FONT_MAP_LOCK; - } - - status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); - if (unlikely (status)) - goto UNWIND_UNSCALED_MALLOC; - - assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash); - status = _cairo_hash_table_insert (font_map->hash_table, - &unscaled->base.hash_entry); - if (unlikely (status)) - goto UNWIND_UNSCALED_FONT_INIT; - -DONE: - _cairo_ft_unscaled_font_map_unlock (); - *out = unscaled; - return CAIRO_STATUS_SUCCESS; - -UNWIND_UNSCALED_FONT_INIT: - _cairo_ft_unscaled_font_fini (unscaled); -UNWIND_UNSCALED_MALLOC: - free (unscaled); -UNWIND_FONT_MAP_LOCK: - _cairo_ft_unscaled_font_map_unlock (); - return status; -} - - -#if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, - cairo_ft_unscaled_font_t **out) -{ - FT_Face font_face = NULL; - char *filename = NULL; - int id = 0; - FcResult ret; - - ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); - if (ret == FcResultMatch) - goto DONE; - if (ret == FcResultOutOfMemory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename); - if (ret == FcResultOutOfMemory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (ret == FcResultMatch) { - /* If FC_INDEX is not set, we just use 0 */ - ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); - if (ret == FcResultOutOfMemory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - goto DONE; - } - - /* The pattern contains neither a face nor a filename, resolve it later. */ - *out = NULL; - return CAIRO_STATUS_SUCCESS; - -DONE: - return _cairo_ft_unscaled_font_create_internal (font_face != NULL, - filename, id, font_face, - out); -} -#endif - -static cairo_status_t -_cairo_ft_unscaled_font_create_from_face (FT_Face face, - cairo_ft_unscaled_font_t **out) -{ - return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out); -} - -static void -_cairo_ft_unscaled_font_destroy (void *abstract_font) -{ - cairo_ft_unscaled_font_t *unscaled = abstract_font; - cairo_ft_unscaled_font_map_t *font_map; - - if (unscaled == NULL) - return; - - font_map = _cairo_ft_unscaled_font_map_lock (); - /* All created objects must have been mapped in the font map. */ - assert (font_map != NULL); - - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) { - /* somebody recreated the font whilst we waited for the lock */ - _cairo_ft_unscaled_font_map_unlock (); - return; - } - - _cairo_hash_table_remove (font_map->hash_table, - &unscaled->base.hash_entry); - - if (unscaled->from_face) { - /* See comments in _ft_font_face_destroy about the "zombie" state - * for a _ft_font_face. - */ - if (unscaled->faces && unscaled->faces->unscaled == NULL) { - assert (unscaled->faces->next == NULL); - cairo_font_face_destroy (&unscaled->faces->base); - } - } else { - _font_map_release_face_lock_held (font_map, unscaled); - } - unscaled->face = NULL; - - _cairo_ft_unscaled_font_map_unlock (); - - _cairo_ft_unscaled_font_fini (unscaled); -} - -static cairo_bool_t -_has_unlocked_face (const void *entry) -{ - const cairo_ft_unscaled_font_t *unscaled = entry; - - return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face); -} - -/* Ensures that an unscaled font has a face object. If we exceed - * MAX_OPEN_FACES, try to close some. - * - * This differs from _cairo_ft_scaled_font_lock_face in that it doesn't - * set the scale on the face, but just returns it at the last scale. - */ -cairo_warn FT_Face -_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) -{ - cairo_ft_unscaled_font_map_t *font_map; - FT_Face face = NULL; - - CAIRO_MUTEX_LOCK (unscaled->mutex); - unscaled->lock_count++; - - if (unscaled->face) - return unscaled->face; - - /* If this unscaled font was created from an FT_Face then we just - * returned it above. */ - assert (!unscaled->from_face); - - font_map = _cairo_ft_unscaled_font_map_lock (); - { - assert (font_map != NULL); - - while (font_map->num_open_faces >= MAX_OPEN_FACES) - { - cairo_ft_unscaled_font_t *entry; - - entry = _cairo_hash_table_random_entry (font_map->hash_table, - _has_unlocked_face); - if (entry == NULL) - break; - - _font_map_release_face_lock_held (font_map, entry); - } - } - _cairo_ft_unscaled_font_map_unlock (); - - if (FT_New_Face (font_map->ft_library, - unscaled->filename, - unscaled->id, - &face) != FT_Err_Ok) - { - unscaled->lock_count--; - CAIRO_MUTEX_UNLOCK (unscaled->mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - unscaled->face = face; - - font_map->num_open_faces++; - - return face; -} - - -/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face - */ -void -_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled) -{ - assert (unscaled->lock_count > 0); - - unscaled->lock_count--; - - CAIRO_MUTEX_UNLOCK (unscaled->mutex); -} - - -static cairo_status_t -_compute_transform (cairo_ft_font_transform_t *sf, - cairo_matrix_t *scale, - cairo_ft_unscaled_font_t *unscaled) -{ - cairo_status_t status; - double x_scale, y_scale; - cairo_matrix_t normalized = *scale; - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - status = _cairo_matrix_compute_basis_scale_factors (scale, - &x_scale, &y_scale, - 1); - if (unlikely (status)) - return status; - - /* FreeType docs say this about x_scale and y_scale: - * "A character width or height smaller than 1pt is set to 1pt;" - * So, we cap them from below at 1.0 and let the FT transform - * take care of sub-1.0 scaling. */ - if (x_scale < 1.0) - x_scale = 1.0; - if (y_scale < 1.0) - y_scale = 1.0; - - if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) { - double min_distance = DBL_MAX; - cairo_bool_t magnify = TRUE; - int i; - int best_i = 0; - double best_x_size = 0; - double best_y_size = 0; - - for (i = 0; i < unscaled->face->num_fixed_sizes; i++) { - double x_size = unscaled->face->available_sizes[i].x_ppem / 64.; - double y_size = unscaled->face->available_sizes[i].y_ppem / 64.; - double distance = y_size - y_scale; - - /* - * distance is positive if current strike is larger than desired - * size, and negative if smaller. - * - * We like to prefer down-scaling to upscaling. - */ - - if ((magnify && distance >= 0) || fabs (distance) <= min_distance) { - magnify = distance < 0; - min_distance = fabs (distance); - best_i = i; - best_x_size = x_size; - best_y_size = y_size; - } - } - - x_scale = best_x_size; - y_scale = best_y_size; - } - - sf->x_scale = x_scale; - sf->y_scale = y_scale; - - cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); - - _cairo_matrix_get_affine (&normalized, - &sf->shape[0][0], &sf->shape[0][1], - &sf->shape[1][0], &sf->shape[1][1], - NULL, NULL); - - return CAIRO_STATUS_SUCCESS; -} - -/* Temporarily scales an unscaled font to the give scale. We catch - * scaling to the same size, since changing a FT_Face is expensive. - */ -static cairo_status_t -_cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, - cairo_matrix_t *scale) -{ - cairo_status_t status; - cairo_ft_font_transform_t sf; - FT_Matrix mat; - FT_Error error; - double x_scale, y_scale; - - assert (unscaled->face != NULL); - - if (unscaled->have_scale && - scale->xx == unscaled->current_scale.xx && - scale->yx == unscaled->current_scale.yx && - scale->xy == unscaled->current_scale.xy && - scale->yy == unscaled->current_scale.yy) - return CAIRO_STATUS_SUCCESS; - - unscaled->have_scale = TRUE; - unscaled->current_scale = *scale; - - status = _compute_transform (&sf, scale, unscaled); - if (unlikely (status)) - return status; - - unscaled->x_scale = sf.x_scale; - unscaled->y_scale = sf.y_scale; - - mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); - mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); - mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); - mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); - - unscaled->have_shape = (mat.xx != 0x10000 || - mat.yx != 0x00000 || - mat.xy != 0x00000 || - mat.yy != 0x10000); - - unscaled->Current_Shape = mat; - cairo_matrix_init (&unscaled->current_shape, - sf.shape[0][0], sf.shape[0][1], - sf.shape[1][0], sf.shape[1][1], - 0.0, 0.0); - - FT_Set_Transform(unscaled->face, &mat, NULL); - - x_scale = MIN(sf.x_scale, MAX_FONT_SIZE); - y_scale = MIN(sf.y_scale, MAX_FONT_SIZE); - error = FT_Set_Char_Size (unscaled->face, - x_scale * 64.0 + .5, - y_scale * 64.0 + .5, - 0, 0); - if (error) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot - * into a different format. For example, we want to convert a - * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit - * ARGB or ABGR bitmap. - * - * this function prepares a target descriptor for this operation. - * - * input :: target bitmap descriptor. The function will set its - * 'width', 'rows' and 'pitch' fields, and only these - * - * slot :: the glyph slot containing the source bitmap. this - * function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP - * - * mode :: the requested final rendering mode. supported values are - * MONO, NORMAL (i.e. gray), LCD and LCD_V - * - * the function returns the size in bytes of the corresponding buffer, - * it's up to the caller to allocate the corresponding memory block - * before calling _fill_xrender_bitmap - * - * it also returns -1 in case of error (e.g. incompatible arguments, - * like trying to convert a gray bitmap into a monochrome one) - */ -static int -_compute_xrender_bitmap_size(FT_Bitmap *target, - FT_GlyphSlot slot, - FT_Render_Mode mode) -{ - FT_Bitmap *ftbit; - int width, height, pitch; - - if (slot->format != FT_GLYPH_FORMAT_BITMAP) - return -1; - - /* compute the size of the final bitmap */ - ftbit = &slot->bitmap; - - width = ftbit->width; - height = ftbit->rows; - pitch = (width + 3) & ~3; - - switch (ftbit->pixel_mode) { - case FT_PIXEL_MODE_MONO: - if (mode == FT_RENDER_MODE_MONO) { - pitch = (((width + 31) & ~31) >> 3); - break; - } - /* fall-through */ - - case FT_PIXEL_MODE_GRAY: - if (mode == FT_RENDER_MODE_LCD || - mode == FT_RENDER_MODE_LCD_V) - { - /* each pixel is replicated into a 32-bit ARGB value */ - pitch = width * 4; - } - break; - - case FT_PIXEL_MODE_LCD: - if (mode != FT_RENDER_MODE_LCD) - return -1; - - /* horz pixel triplets are packed into 32-bit ARGB values */ - width /= 3; - pitch = width * 4; - break; - - case FT_PIXEL_MODE_LCD_V: - if (mode != FT_RENDER_MODE_LCD_V) - return -1; - - /* vert pixel triplets are packed into 32-bit ARGB values */ - height /= 3; - pitch = width * 4; - break; - - default: /* unsupported source format */ - return -1; - } - - target->width = width; - target->rows = height; - target->pitch = pitch; - target->buffer = NULL; - - return pitch * height; -} - -/* this functions converts the glyph bitmap found in a FT_GlyphSlot - * into a different format (see _compute_xrender_bitmap_size) - * - * you should call this function after _compute_xrender_bitmap_size - * - * target :: target bitmap descriptor. Note that its 'buffer' pointer - * must point to memory allocated by the caller - * - * slot :: the glyph slot containing the source bitmap - * - * mode :: the requested final rendering mode - * - * bgr :: boolean, set if BGR or VBGR pixel ordering is needed - */ -static void -_fill_xrender_bitmap(FT_Bitmap *target, - FT_GlyphSlot slot, - FT_Render_Mode mode, - int bgr) -{ - FT_Bitmap *ftbit = &slot->bitmap; - unsigned char *srcLine = ftbit->buffer; - unsigned char *dstLine = target->buffer; - int src_pitch = ftbit->pitch; - int width = target->width; - int height = target->rows; - int pitch = target->pitch; - int subpixel; - int h; - - subpixel = (mode == FT_RENDER_MODE_LCD || - mode == FT_RENDER_MODE_LCD_V); - - if (src_pitch < 0) - srcLine -= src_pitch * (ftbit->rows - 1); - - target->pixel_mode = ftbit->pixel_mode; - - switch (ftbit->pixel_mode) { - case FT_PIXEL_MODE_MONO: - if (subpixel) { - /* convert mono to ARGB32 values */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { - int x; - - for (x = 0; x < width; x++) { - if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) - ((unsigned int *) dstLine)[x] = 0xffffffffU; - } - } - target->pixel_mode = FT_PIXEL_MODE_LCD; - - } else if (mode == FT_RENDER_MODE_NORMAL) { - /* convert mono to 8-bit gray */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { - int x; - - for (x = 0; x < width; x++) { - if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) - dstLine[x] = 0xff; - } - } - target->pixel_mode = FT_PIXEL_MODE_GRAY; - - } else { - /* copy mono to mono */ - - int bytes = (width + 7) >> 3; - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) - memcpy (dstLine, srcLine, bytes); - } - break; - - case FT_PIXEL_MODE_GRAY: - if (subpixel) { - /* convert gray to ARGB32 values */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { - int x; - unsigned int *dst = (unsigned int *) dstLine; - - for (x = 0; x < width; x++) { - unsigned int pix = srcLine[x]; - - pix |= (pix << 8); - pix |= (pix << 16); - - dst[x] = pix; - } - } - target->pixel_mode = FT_PIXEL_MODE_LCD; - } else { - /* copy gray into gray */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) - memcpy (dstLine, srcLine, width); - } - break; - - case FT_PIXEL_MODE_LCD: - if (!bgr) { - /* convert horizontal RGB into ARGB32 */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { - int x; - unsigned char *src = srcLine; - unsigned int *dst = (unsigned int *) dstLine; - - for (x = 0; x < width; x++, src += 3) { - unsigned int pix; - - pix = ((unsigned int)src[0] << 16) | - ((unsigned int)src[1] << 8) | - ((unsigned int)src[2] ) | - ((unsigned int)src[1] << 24) ; - - dst[x] = pix; - } - } - } else { - /* convert horizontal BGR into ARGB32 */ - - for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { - - int x; - unsigned char *src = srcLine; - unsigned int *dst = (unsigned int *) dstLine; - - for (x = 0; x < width; x++, src += 3) { - unsigned int pix; - - pix = ((unsigned int)src[2] << 16) | - ((unsigned int)src[1] << 8) | - ((unsigned int)src[0] ) | - ((unsigned int)src[1] << 24) ; - - dst[x] = pix; - } - } - } - break; - - default: /* FT_PIXEL_MODE_LCD_V */ - /* convert vertical RGB into ARGB32 */ - if (!bgr) { - - for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) { - int x; - unsigned char* src = srcLine; - unsigned int* dst = (unsigned int *) dstLine; - - for (x = 0; x < width; x++, src += 1) { - unsigned int pix; - pix = ((unsigned int)src[0] << 16) | - ((unsigned int)src[src_pitch] << 8) | - ((unsigned int)src[src_pitch*2] ) | - ((unsigned int)src[src_pitch] << 24) ; - dst[x] = pix; - } - } - } else { - - for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) { - int x; - unsigned char *src = srcLine; - unsigned int *dst = (unsigned int *) dstLine; - - for (x = 0; x < width; x++, src += 1) { - unsigned int pix; - - pix = ((unsigned int)src[src_pitch * 2] << 16) | - ((unsigned int)src[src_pitch] << 8) | - ((unsigned int)src[0] ) | - ((unsigned int)src[src_pitch] << 24) ; - - dst[x] = pix; - } - } - } - } -} - - -/* Fills in val->image with an image surface created from @bitmap - */ -static cairo_status_t -_get_bitmap_surface (FT_Bitmap *bitmap, - FT_Library library, - cairo_bool_t own_buffer, - cairo_font_options_t *font_options, - cairo_image_surface_t **surface) -{ - int width, height, stride; - unsigned char *data; - int format = CAIRO_FORMAT_A8; - cairo_image_surface_t *image; - cairo_bool_t component_alpha = FALSE; - - width = bitmap->width; - height = bitmap->rows; - - if (width == 0 || height == 0) { - *surface = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); - return (*surface)->base.status; - } - - switch (bitmap->pixel_mode) { - case FT_PIXEL_MODE_MONO: - stride = (((width + 31) & ~31) >> 3); - if (own_buffer) { - data = bitmap->buffer; - assert (stride == bitmap->pitch); - } else { - data = _cairo_malloc_ab (height, stride); - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (stride == bitmap->pitch) { - memcpy (data, bitmap->buffer, stride * height); - } else { - int i; - unsigned char *source, *dest; - int copy_len = MIN (stride, bitmap->pitch); - int pad_len = stride - bitmap->pitch; - - source = bitmap->buffer; - dest = data; - for (i = height; i; i--) { - memcpy (dest, source, copy_len); - source += bitmap->pitch; - dest += stride; - } - /* do we really care about zeroing any extra row padding in dest? */ - if (pad_len > 0) { - dest = data + copy_len; - for (i = height; i; i--) { - memset (dest, '\0', pad_len); - dest += stride; - } - } - } - } - -#ifndef WORDS_BIGENDIAN - { - uint8_t *d = data; - int count = stride * height; - - while (count--) { - *d = CAIRO_BITSWAP8 (*d); - d++; - } - } -#endif - format = CAIRO_FORMAT_A1; - break; - - case FT_PIXEL_MODE_LCD: - case FT_PIXEL_MODE_LCD_V: - case FT_PIXEL_MODE_GRAY: - if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL || - bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) - { - stride = bitmap->pitch; - - /* We don't support stride not multiple of 4. */ - if (stride & 3) - { - assert (!own_buffer); - goto convert; - } - - if (own_buffer) { - data = bitmap->buffer; - } else { - data = _cairo_malloc_ab (height, stride); - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (data, bitmap->buffer, stride * height); - } - - format = CAIRO_FORMAT_A8; - } else { - data = bitmap->buffer; - stride = bitmap->pitch; - format = CAIRO_FORMAT_ARGB32; - component_alpha = TRUE; - } - break; -#ifdef FT_LOAD_COLOR - case FT_PIXEL_MODE_BGRA: - stride = width * 4; - if (own_buffer) { - data = bitmap->buffer; - } else { - data = _cairo_malloc_ab (height, stride); - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (data, bitmap->buffer, stride * height); - } - format = CAIRO_FORMAT_ARGB32; - break; -#endif - case FT_PIXEL_MODE_GRAY2: - case FT_PIXEL_MODE_GRAY4: - convert: - if (!own_buffer && library) - { - /* This is pretty much the only case that we can get in here. */ - /* Convert to 8bit grayscale. */ - - FT_Bitmap tmp; - FT_Int align; - - format = CAIRO_FORMAT_A8; - - align = cairo_format_stride_for_width (format, bitmap->width); - - FT_Bitmap_New( &tmp ); - - if (FT_Bitmap_Convert( library, bitmap, &tmp, align )) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - FT_Bitmap_Done( library, bitmap ); - *bitmap = tmp; - - stride = bitmap->pitch; - data = _cairo_malloc_ab (height, stride); - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (bitmap->num_grays != 256) - { - unsigned int x, y; - unsigned int mul = 255 / (bitmap->num_grays - 1); - FT_Byte *p = bitmap->buffer; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - p[x] *= mul; - p += bitmap->pitch; - } - } - - memcpy (data, bitmap->buffer, stride * height); - break; - } - /* These could be triggered by very rare types of TrueType fonts */ - default: - if (own_buffer) - free (bitmap->buffer); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* XXX */ - *surface = image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (data, - format, - width, height, stride); - if (image->base.status) { - free (data); - return (*surface)->base.status; - } - - if (component_alpha) - pixman_image_set_component_alpha (image->pixman_image, TRUE); - - _cairo_image_surface_assume_ownership_of_data (image); - - _cairo_debug_check_image_surface_is_defined (&image->base); - - return CAIRO_STATUS_SUCCESS; -} - -/* Converts an outline FT_GlyphSlot into an image - * - * This could go through _render_glyph_bitmap as well, letting - * FreeType convert the outline to a bitmap, but doing it ourselves - * has two minor advantages: first, we save a copy of the bitmap - * buffer: we can directly use the buffer that FreeType renders - * into. - * - * Second, it may help when we add support for subpixel - * rendering: the Xft code does it this way. (Keith thinks that - * it may also be possible to get the subpixel rendering with - * FT_Render_Glyph: something worth looking into in more detail - * when we add subpixel support. If so, we may want to eliminate - * this version of the code path entirely. - */ -static cairo_status_t -_render_glyph_outline (FT_Face face, - cairo_font_options_t *font_options, - cairo_image_surface_t **surface) -{ - int rgba = FC_RGBA_UNKNOWN; - int lcd_filter = FT_LCD_FILTER_LEGACY; - FT_GlyphSlot glyphslot = face->glyph; - FT_Outline *outline = &glyphslot->outline; - FT_Bitmap bitmap; - FT_BBox cbox; - unsigned int width, height; - cairo_status_t status; - FT_Error fterror; - FT_Library library = glyphslot->library; - FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; - - switch (font_options->antialias) { - case CAIRO_ANTIALIAS_NONE: - render_mode = FT_RENDER_MODE_MONO; - break; - - case CAIRO_ANTIALIAS_SUBPIXEL: - switch (font_options->subpixel_order) { - case CAIRO_SUBPIXEL_ORDER_DEFAULT: - case CAIRO_SUBPIXEL_ORDER_RGB: - case CAIRO_SUBPIXEL_ORDER_BGR: - render_mode = FT_RENDER_MODE_LCD; - break; - - case CAIRO_SUBPIXEL_ORDER_VRGB: - case CAIRO_SUBPIXEL_ORDER_VBGR: - render_mode = FT_RENDER_MODE_LCD_V; - break; - } - - switch (font_options->lcd_filter) { - case CAIRO_LCD_FILTER_NONE: - lcd_filter = FT_LCD_FILTER_NONE; - break; - case CAIRO_LCD_FILTER_DEFAULT: - case CAIRO_LCD_FILTER_INTRA_PIXEL: - lcd_filter = FT_LCD_FILTER_LEGACY; - break; - case CAIRO_LCD_FILTER_FIR3: - lcd_filter = FT_LCD_FILTER_LIGHT; - break; - case CAIRO_LCD_FILTER_FIR5: - lcd_filter = FT_LCD_FILTER_DEFAULT; - break; - } - - break; - - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_GRAY: - render_mode = FT_RENDER_MODE_NORMAL; - } - - FT_Outline_Get_CBox (outline, &cbox); - - cbox.xMin &= -64; - cbox.yMin &= -64; - cbox.xMax = (cbox.xMax + 63) & -64; - cbox.yMax = (cbox.yMax + 63) & -64; - - width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); - height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); - - if (width * height == 0) { - cairo_format_t format; - /* Looks like fb handles zero-sized images just fine */ - switch (render_mode) { - case FT_RENDER_MODE_MONO: - format = CAIRO_FORMAT_A1; - break; - case FT_RENDER_MODE_LCD: - case FT_RENDER_MODE_LCD_V: - format= CAIRO_FORMAT_ARGB32; - break; - case FT_RENDER_MODE_LIGHT: - case FT_RENDER_MODE_NORMAL: - case FT_RENDER_MODE_MAX: - default: - format = CAIRO_FORMAT_A8; - break; - } - - (*surface) = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); - if ((*surface)->base.status) - return (*surface)->base.status; - } else { - - int bitmap_size; - static int initialized_setLcdFilter = 0; - - switch (render_mode) { - case FT_RENDER_MODE_LCD: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) { - rgba = FC_RGBA_BGR; - } else { - rgba = FC_RGBA_RGB; - } - break; - case FT_RENDER_MODE_LCD_V: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) { - rgba = FC_RGBA_VBGR; - } else { - rgba = FC_RGBA_VRGB; - } - break; - case FT_RENDER_MODE_MONO: - case FT_RENDER_MODE_LIGHT: - case FT_RENDER_MODE_NORMAL: - case FT_RENDER_MODE_MAX: - default: - break; - } - - if (!initialized_setLcdFilter) { - initialized_setLcdFilter = 1; -#ifdef HAVE_FT_LIBRARY_SETLCDFILTER - setLcdFilter = &FT_Library_SetLcdFilter; -#else - setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter"); -#endif - } - - if (setLcdFilter) - setLcdFilter (library, lcd_filter); - - fterror = FT_Render_Glyph (face->glyph, render_mode); - - if (setLcdFilter) - setLcdFilter (library, FT_LCD_FILTER_NONE); - - if (fterror != 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - bitmap_size = _compute_xrender_bitmap_size (&bitmap, - face->glyph, - render_mode); - if (bitmap_size < 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - bitmap.buffer = calloc (1, bitmap_size); - if (bitmap.buffer == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, - (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); - - /* Note: - * _get_bitmap_surface will free bitmap.buffer if there is an error - */ - status = _get_bitmap_surface (&bitmap, NULL, TRUE, font_options, surface); - if (unlikely (status)) - return status; - - /* Note: the font's coordinate system is upside down from ours, so the - * Y coordinate of the control box needs to be negated. Moreover, device - * offsets are position of glyph origin relative to top left while xMin - * and yMax are offsets of top left relative to origin. Another negation. - */ - cairo_surface_set_device_offset (&(*surface)->base, - (double)-glyphslot->bitmap_left, - (double)+glyphslot->bitmap_top); - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Converts a bitmap (or other) FT_GlyphSlot into an image */ -static cairo_status_t -_render_glyph_bitmap (FT_Face face, - cairo_font_options_t *font_options, - cairo_image_surface_t **surface) -{ - FT_GlyphSlot glyphslot = face->glyph; - cairo_status_t status; - FT_Error error; - - /* According to the FreeType docs, glyphslot->format could be - * something other than FT_GLYPH_FORMAT_OUTLINE or - * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType - * the opportunity to convert such to - * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since - * we avoid the FT_LOAD_NO_RECURSE flag. - */ - error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _get_bitmap_surface (&glyphslot->bitmap, - glyphslot->library, - FALSE, font_options, - surface); - if (unlikely (status)) - return status; - - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinate of the control box needs to be negated. Moreover, device - * offsets are position of glyph origin relative to top left while - * bitmap_left and bitmap_top are offsets of top left relative to origin. - * Another negation. - */ - cairo_surface_set_device_offset (&(*surface)->base, - -glyphslot->bitmap_left, - +glyphslot->bitmap_top); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_transform_glyph_bitmap (cairo_matrix_t * shape, - cairo_image_surface_t ** surface) -{ - cairo_matrix_t original_to_transformed; - cairo_matrix_t transformed_to_original; - cairo_image_surface_t *old_image; - cairo_surface_t *image; - double x[4], y[4]; - double origin_x, origin_y; - int orig_width, orig_height; - int i; - int x_min, y_min, x_max, y_max; - int width, height; - cairo_status_t status; - cairo_surface_pattern_t pattern; - - /* We want to compute a transform that takes the origin - * (device_x_offset, device_y_offset) to 0,0, then applies - * the "shape" portion of the font transform - */ - original_to_transformed = *shape; - - cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y); - orig_width = (*surface)->width; - orig_height = (*surface)->height; - - cairo_matrix_translate (&original_to_transformed, - -origin_x, -origin_y); - - /* Find the bounding box of the original bitmap under that - * transform - */ - x[0] = 0; y[0] = 0; - x[1] = orig_width; y[1] = 0; - x[2] = orig_width; y[2] = orig_height; - x[3] = 0; y[3] = orig_height; - - for (i = 0; i < 4; i++) - cairo_matrix_transform_point (&original_to_transformed, - &x[i], &y[i]); - - x_min = floor (x[0]); y_min = floor (y[0]); - x_max = ceil (x[0]); y_max = ceil (y[0]); - - for (i = 1; i < 4; i++) { - if (x[i] < x_min) - x_min = floor (x[i]); - else if (x[i] > x_max) - x_max = ceil (x[i]); - if (y[i] < y_min) - y_min = floor (y[i]); - else if (y[i] > y_max) - y_max = ceil (y[i]); - } - - /* Adjust the transform so that the bounding box starts at 0,0 ... - * this gives our final transform from original bitmap to transformed - * bitmap. - */ - original_to_transformed.x0 -= x_min; - original_to_transformed.y0 -= y_min; - - /* Create the transformed bitmap */ - width = x_max - x_min; - height = y_max - y_min; - - transformed_to_original = original_to_transformed; - status = cairo_matrix_invert (&transformed_to_original); - if (unlikely (status)) - return status; - - if (cairo_image_surface_get_format (*surface) == CAIRO_FORMAT_ARGB32 && - !pixman_image_get_component_alpha ((*surface)->pixman_image)) - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - else - image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - if (unlikely (image->status)) - return image->status; - - /* Draw the original bitmap transformed into the new bitmap - */ - _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); - cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); - - status = _cairo_surface_paint (image, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - /* Now update the cache entry for the new bitmap, recomputing - * the origin based on the final transform. - */ - cairo_matrix_transform_point (&original_to_transformed, - &origin_x, &origin_y); - - old_image = (*surface); - (*surface) = (cairo_image_surface_t *)image; - cairo_surface_destroy (&old_image->base); - - cairo_surface_set_device_offset (&(*surface)->base, - _cairo_lround (origin_x), - _cairo_lround (origin_y)); - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { - _cairo_ft_unscaled_font_destroy, -#if 0 - _cairo_ft_unscaled_font_create_glyph -#endif -}; - -/* #cairo_ft_scaled_font_t */ - -typedef struct _cairo_ft_scaled_font { - cairo_scaled_font_t base; - cairo_ft_unscaled_font_t *unscaled; - cairo_ft_options_t ft_options; -} cairo_ft_scaled_font_t; - -static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; - -#if CAIRO_HAS_FC_FONT -/* The load flags passed to FT_Load_Glyph control aspects like hinting and - * antialiasing. Here we compute them from the fields of a FcPattern. - */ -static void -_get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) -{ - FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden; - cairo_ft_options_t ft_options; - int rgba; -#ifdef FC_HINT_STYLE - int hintstyle; -#endif - - _cairo_font_options_init_default (&ft_options.base); - ft_options.load_flags = FT_LOAD_DEFAULT; - ft_options.extra_flags = 0; - -#ifndef FC_EMBEDDED_BITMAP -#define FC_EMBEDDED_BITMAP "embeddedbitmap" -#endif - - /* Check whether to force use of embedded bitmaps */ - if (FcPatternGetBool (pattern, - FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch) - bitmap = FcFalse; - - /* disable antialiasing if requested */ - if (FcPatternGetBool (pattern, - FC_ANTIALIAS, 0, &antialias) != FcResultMatch) - antialias = FcTrue; - - if (antialias) { - cairo_subpixel_order_t subpixel_order; - int lcd_filter; - - /* disable hinting if requested */ - if (FcPatternGetBool (pattern, - FC_HINTING, 0, &hinting) != FcResultMatch) - hinting = FcTrue; - - if (FcPatternGetInteger (pattern, - FC_RGBA, 0, &rgba) != FcResultMatch) - rgba = FC_RGBA_UNKNOWN; - - switch (rgba) { - case FC_RGBA_RGB: - subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; - break; - case FC_RGBA_BGR: - subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; - break; - case FC_RGBA_VRGB: - subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; - break; - case FC_RGBA_VBGR: - subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; - break; - case FC_RGBA_UNKNOWN: - case FC_RGBA_NONE: - default: - subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - break; - } - - if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) { - ft_options.base.subpixel_order = subpixel_order; - ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; - } - - if (FcPatternGetInteger (pattern, - FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch) - { - switch (lcd_filter) { - case FC_LCD_NONE: - ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE; - break; - case FC_LCD_DEFAULT: - ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5; - break; - case FC_LCD_LIGHT: - ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3; - break; - case FC_LCD_LEGACY: - ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; - break; - } - } - -#ifdef FC_HINT_STYLE - if (FcPatternGetInteger (pattern, - FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) - hintstyle = FC_HINT_FULL; - - if (!hinting) - hintstyle = FC_HINT_NONE; - - switch (hintstyle) { - case FC_HINT_NONE: - ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; - break; - case FC_HINT_SLIGHT: - ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT; - break; - case FC_HINT_MEDIUM: - default: - ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM; - break; - case FC_HINT_FULL: - ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL; - break; - } -#else /* !FC_HINT_STYLE */ - if (!hinting) { - ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; - } -#endif /* FC_HINT_STYLE */ - - /* Force embedded bitmaps off if no hinting requested */ - if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE) - bitmap = FcFalse; - - if (!bitmap) - ft_options.load_flags |= FT_LOAD_NO_BITMAP; - - } else { - ft_options.base.antialias = CAIRO_ANTIALIAS_NONE; - } - - /* force autohinting if requested */ - if (FcPatternGetBool (pattern, - FC_AUTOHINT, 0, &autohint) != FcResultMatch) - autohint = FcFalse; - - if (autohint) - ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT; - - if (FcPatternGetBool (pattern, - FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch) - vertical_layout = FcFalse; - - if (vertical_layout) - ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; - -#ifndef FC_EMBOLDEN -#define FC_EMBOLDEN "embolden" -#endif - if (FcPatternGetBool (pattern, - FC_EMBOLDEN, 0, &embolden) != FcResultMatch) - embolden = FcFalse; - - if (embolden) - ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN; - - *ret = ft_options; -} -#endif - -static void -_cairo_ft_options_merge (cairo_ft_options_t *options, - cairo_ft_options_t *other) -{ - int load_flags = other->load_flags; - int load_target = FT_LOAD_TARGET_NORMAL; - - /* clear load target mode */ - load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); - - if (load_flags & FT_LOAD_NO_HINTING) - other->base.hint_style = CAIRO_HINT_STYLE_NONE; - - if (other->base.antialias == CAIRO_ANTIALIAS_NONE || - options->base.antialias == CAIRO_ANTIALIAS_NONE) { - options->base.antialias = CAIRO_ANTIALIAS_NONE; - options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - } else if (options->base.antialias != CAIRO_ANTIALIAS_GRAY) { - /* The surface supports subpixel aa, so let the font face options - * choose whether to use subpixel aa. If the surface has - * CAIRO_ANTIALIAS_GRAY (e.g. PS, PDF, SVG, translucent part of a - * CONTENT_COLOR_ALPHA surface), then don't accept subpixel aa. */ - if (other->base.antialias != CAIRO_ANTIALIAS_DEFAULT) - options->base.antialias = other->base.antialias; - /* If the surface knows the subpixel order then use that. */ - if (options->base.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) - options->base.subpixel_order = other->base.subpixel_order; - } - - if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT) - options->base.hint_style = other->base.hint_style; - - if (other->base.hint_style == CAIRO_HINT_STYLE_NONE) - options->base.hint_style = CAIRO_HINT_STYLE_NONE; - - if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT) - options->base.lcd_filter = other->base.lcd_filter; - - if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE) - options->base.lcd_filter = CAIRO_LCD_FILTER_NONE; - - if (options->base.antialias == CAIRO_ANTIALIAS_NONE) { - if (options->base.hint_style == CAIRO_HINT_STYLE_NONE) - load_flags |= FT_LOAD_NO_HINTING; - else - load_target = FT_LOAD_TARGET_MONO; - load_flags |= FT_LOAD_MONOCHROME; - } else { - switch (options->base.hint_style) { - case CAIRO_HINT_STYLE_NONE: - load_flags |= FT_LOAD_NO_HINTING; - break; - case CAIRO_HINT_STYLE_SLIGHT: - load_target = FT_LOAD_TARGET_LIGHT; - break; - case CAIRO_HINT_STYLE_MEDIUM: - break; - case CAIRO_HINT_STYLE_FULL: - case CAIRO_HINT_STYLE_DEFAULT: - if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { - switch (options->base.subpixel_order) { - case CAIRO_SUBPIXEL_ORDER_DEFAULT: - case CAIRO_SUBPIXEL_ORDER_RGB: - case CAIRO_SUBPIXEL_ORDER_BGR: - load_target = FT_LOAD_TARGET_LCD; - break; - case CAIRO_SUBPIXEL_ORDER_VRGB: - case CAIRO_SUBPIXEL_ORDER_VBGR: - load_target = FT_LOAD_TARGET_LCD_V; - break; - } - } - break; - } - } - - options->load_flags = load_flags | load_target; - options->extra_flags = other->extra_flags; - if (options->base.hint_metrics != CAIRO_HINT_METRICS_OFF) - options->extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS; -} - -static cairo_status_t -_cairo_ft_font_face_scaled_font_create (void *abstract_font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font_out) -{ - cairo_ft_font_face_t *font_face = abstract_font_face; - cairo_ft_scaled_font_t *scaled_font; - FT_Face face; - FT_Size_Metrics *metrics; - cairo_font_extents_t fs_metrics; - cairo_status_t status; - cairo_ft_unscaled_font_t *unscaled; - - assert (font_face->unscaled); - - face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled); - if (unlikely (face == NULL)) /* backend error */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - scaled_font = malloc (sizeof (cairo_ft_scaled_font_t)); - if (unlikely (scaled_font == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - scaled_font->unscaled = unscaled = font_face->unscaled; - _cairo_unscaled_font_reference (&unscaled->base); - - _cairo_font_options_init_copy (&scaled_font->ft_options.base, options); - _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options); - - status = _cairo_scaled_font_init (&scaled_font->base, - &font_face->base, - font_matrix, ctm, options, - &_cairo_ft_scaled_font_backend); - if (unlikely (status)) - goto CLEANUP_SCALED_FONT; - - status = _cairo_ft_unscaled_font_set_scale (unscaled, - &scaled_font->base.scale); - if (unlikely (status)) { - /* This can only fail if we encounter an error with the underlying - * font, so propagate the error back to the font-face. */ - _cairo_ft_unscaled_font_unlock_face (unscaled); - _cairo_unscaled_font_destroy (&unscaled->base); - free (scaled_font); - return status; - } - - - metrics = &face->size->metrics; - - /* - * Get to unscaled metrics so that the upper level can get back to - * user space - * - * Also use this path for bitmap-only fonts. The other branch uses - * face members that are only relevant for scalable fonts. This is - * detected by simply checking for units_per_EM==0. - */ - if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF || - face->units_per_EM == 0) { - double x_factor, y_factor; - - if (unscaled->x_scale == 0) - x_factor = 0; - else - x_factor = 1 / unscaled->x_scale; - - if (unscaled->y_scale == 0) - y_factor = 0; - else - y_factor = 1 / unscaled->y_scale; - - fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; - fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; - fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; - if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { - fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; - fs_metrics.max_y_advance = 0; - } else { - fs_metrics.max_x_advance = 0; - fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor; - } - } else { - double scale = face->units_per_EM; - - fs_metrics.ascent = face->ascender / scale; - fs_metrics.descent = - face->descender / scale; - fs_metrics.height = face->height / scale; - if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { - fs_metrics.max_x_advance = face->max_advance_width / scale; - fs_metrics.max_y_advance = 0; - } else { - fs_metrics.max_x_advance = 0; - fs_metrics.max_y_advance = face->max_advance_height / scale; - } - } - - status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); - if (unlikely (status)) - goto CLEANUP_SCALED_FONT; - - _cairo_ft_unscaled_font_unlock_face (unscaled); - - *font_out = &scaled_font->base; - return CAIRO_STATUS_SUCCESS; - - CLEANUP_SCALED_FONT: - _cairo_unscaled_font_destroy (&unscaled->base); - free (scaled_font); - FAIL: - _cairo_ft_unscaled_font_unlock_face (font_face->unscaled); - *font_out = _cairo_scaled_font_create_in_error (status); - return CAIRO_STATUS_SUCCESS; /* non-backend error */ -} - -cairo_bool_t -_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) -{ - return scaled_font->backend == &_cairo_ft_scaled_font_backend; -} - -static void -_cairo_ft_scaled_font_fini (void *abstract_font) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - - if (scaled_font == NULL) - return; - - _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); -} - -static int -_move_to (FT_Vector *to, void *closure) -{ - cairo_path_fixed_t *path = closure; - cairo_fixed_t x, y; - - x = _cairo_fixed_from_26_6 (to->x); - y = _cairo_fixed_from_26_6 (to->y); - - if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS) - return 1; - if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS) - return 1; - - return 0; -} - -static int -_line_to (FT_Vector *to, void *closure) -{ - cairo_path_fixed_t *path = closure; - cairo_fixed_t x, y; - - x = _cairo_fixed_from_26_6 (to->x); - y = _cairo_fixed_from_26_6 (to->y); - - if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS) - return 1; - - return 0; -} - -static int -_conic_to (FT_Vector *control, FT_Vector *to, void *closure) -{ - cairo_path_fixed_t *path = closure; - - cairo_fixed_t x0, y0; - cairo_fixed_t x1, y1; - cairo_fixed_t x2, y2; - cairo_fixed_t x3, y3; - cairo_point_t conic; - - if (! _cairo_path_fixed_get_current_point (path, &x0, &y0)) - return 1; - - conic.x = _cairo_fixed_from_26_6 (control->x); - conic.y = _cairo_fixed_from_26_6 (control->y); - - x3 = _cairo_fixed_from_26_6 (to->x); - y3 = _cairo_fixed_from_26_6 (to->y); - - x1 = x0 + 2.0/3.0 * (conic.x - x0); - y1 = y0 + 2.0/3.0 * (conic.y - y0); - - x2 = x3 + 2.0/3.0 * (conic.x - x3); - y2 = y3 + 2.0/3.0 * (conic.y - y3); - - if (_cairo_path_fixed_curve_to (path, - x1, y1, - x2, y2, - x3, y3) != CAIRO_STATUS_SUCCESS) - return 1; - - return 0; -} - -static int -_cubic_to (FT_Vector *control1, FT_Vector *control2, - FT_Vector *to, void *closure) -{ - cairo_path_fixed_t *path = closure; - cairo_fixed_t x0, y0; - cairo_fixed_t x1, y1; - cairo_fixed_t x2, y2; - - x0 = _cairo_fixed_from_26_6 (control1->x); - y0 = _cairo_fixed_from_26_6 (control1->y); - - x1 = _cairo_fixed_from_26_6 (control2->x); - y1 = _cairo_fixed_from_26_6 (control2->y); - - x2 = _cairo_fixed_from_26_6 (to->x); - y2 = _cairo_fixed_from_26_6 (to->y); - - if (_cairo_path_fixed_curve_to (path, - x0, y0, - x1, y1, - x2, y2) != CAIRO_STATUS_SUCCESS) - return 1; - - return 0; -} - -static cairo_status_t -_decompose_glyph_outline (FT_Face face, - cairo_font_options_t *options, - cairo_path_fixed_t **pathp) -{ - static const FT_Outline_Funcs outline_funcs = { - (FT_Outline_MoveToFunc)_move_to, - (FT_Outline_LineToFunc)_line_to, - (FT_Outline_ConicToFunc)_conic_to, - (FT_Outline_CubicToFunc)_cubic_to, - 0, /* shift */ - 0, /* delta */ - }; - static const FT_Matrix invert_y = { - DOUBLE_TO_16_16 (1.0), 0, - 0, DOUBLE_TO_16_16 (-1.0), - }; - - FT_GlyphSlot glyph; - cairo_path_fixed_t *path; - cairo_status_t status; - - path = _cairo_path_fixed_create (); - if (!path) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - glyph = face->glyph; - - /* Font glyphs have an inverted Y axis compared to cairo. */ - FT_Outline_Transform (&glyph->outline, &invert_y); - if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) { - _cairo_path_fixed_destroy (path); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - status = _cairo_path_fixed_close_path (path); - if (unlikely (status)) { - _cairo_path_fixed_destroy (path); - return status; - } - - *pathp = path; - - return CAIRO_STATUS_SUCCESS; -} - -/* - * Translate glyph to match its metrics. - */ -static void -_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, - FT_GlyphSlot glyph) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - FT_Vector vector; - - vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX; - vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY; - - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { - FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape); - FT_Outline_Translate(&glyph->outline, vector.x, vector.y); - } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { - glyph->bitmap_left += vector.x / 64; - glyph->bitmap_top += vector.y / 64; - } -} - -static cairo_int_status_t -_cairo_ft_scaled_glyph_init (void *abstract_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) -{ - cairo_text_extents_t fs_metrics; - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; - FT_GlyphSlot glyph; - FT_Face face; - FT_Error error; - int load_flags = scaled_font->ft_options.load_flags; - FT_Glyph_Metrics *metrics; - double x_factor, y_factor; - cairo_bool_t vertical_layout = FALSE; - cairo_status_t status; - - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, - &scaled_font->base.scale); - if (unlikely (status)) - goto FAIL; - - /* Ignore global advance unconditionally */ - load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; - - if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && - (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) - load_flags |= FT_LOAD_NO_BITMAP; - - /* - * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as - * suggested by freetype people. - */ - if (load_flags & FT_LOAD_VERTICAL_LAYOUT) { - load_flags &= ~FT_LOAD_VERTICAL_LAYOUT; - vertical_layout = TRUE; - } - -#ifdef FT_LOAD_COLOR - /* Color-glyph support: - * - * This flags needs plumbing through fontconfig (does it?), and - * maybe we should cache color and grayscale bitmaps separately - * such that users of the font (ie. the surface) can choose which - * version to use based on target content type. - */ - - load_flags |= FT_LOAD_COLOR; -#endif - - error = FT_Load_Glyph (scaled_font->unscaled->face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - glyph = face->glyph; - -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - /* - * embolden glyphs if requested - */ - if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN) - FT_GlyphSlot_Embolden (glyph); -#endif - - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); - - if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { - - cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; - /* - * Compute font-space metrics - */ - metrics = &glyph->metrics; - - if (unscaled->x_scale == 0) - x_factor = 0; - else - x_factor = 1 / unscaled->x_scale; - - if (unscaled->y_scale == 0) - y_factor = 0; - else - y_factor = 1 / unscaled->y_scale; - - /* - * Note: Y coordinates of the horizontal bearing need to be negated. - * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType - * - * If we want hinted metrics but aren't asking for hinted glyphs from - * FreeType, then we need to do the metric hinting ourselves. - */ - - if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) - { - FT_Pos x1, x2; - FT_Pos y1, y2; - FT_Pos advance; - - if (!vertical_layout) { - x1 = (metrics->horiBearingX) & -64; - x2 = (metrics->horiBearingX + metrics->width + 63) & -64; - y1 = (-metrics->horiBearingY) & -64; - y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; - - advance = ((metrics->horiAdvance + 32) & -64); - - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - - fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; - - fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; - fs_metrics.y_advance = 0; - } else { - x1 = (metrics->vertBearingX) & -64; - x2 = (metrics->vertBearingX + metrics->width + 63) & -64; - y1 = (metrics->vertBearingY) & -64; - y2 = (metrics->vertBearingY + metrics->height + 63) & -64; - - advance = ((metrics->vertAdvance + 32) & -64); - - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - - fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; - - fs_metrics.x_advance = 0; - fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; - } - } else { - fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; - - if (!vertical_layout) { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; - - if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) - fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; - else - fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; - fs_metrics.y_advance = 0 * y_factor; - } else { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; - - fs_metrics.x_advance = 0 * x_factor; - if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) - fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; - else - fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; - } - } - - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &scaled_font->base, - &fs_metrics); - } - - if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { - cairo_image_surface_t *surface; - - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { - status = _render_glyph_outline (face, &scaled_font->ft_options.base, - &surface); - } else { - status = _render_glyph_bitmap (face, &scaled_font->ft_options.base, - &surface); - if (likely (status == CAIRO_STATUS_SUCCESS) && - unscaled->have_shape) - { - status = _transform_glyph_bitmap (&unscaled->current_shape, - &surface); - if (unlikely (status)) - cairo_surface_destroy (&surface->base); - } - } - if (unlikely (status)) - goto FAIL; - - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - surface); - } - - if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { - cairo_path_fixed_t *path = NULL; /* hide compiler warning */ - - /* - * A kludge -- the above code will trash the outline, - * so reload it. This will probably never occur though - */ - if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { - error = FT_Load_Glyph (face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags | FT_LOAD_NO_BITMAP); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - /* - * embolden glyphs if requested - */ - if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN) - FT_GlyphSlot_Embolden (glyph); -#endif - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); - - } - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) - status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, - &path); - else - status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (unlikely (status)) - goto FAIL; - - _cairo_scaled_glyph_set_path (scaled_glyph, - &scaled_font->base, - path); - } - FAIL: - _cairo_ft_unscaled_font_unlock_face (unscaled); - - return status; -} - -static unsigned long -_cairo_ft_ucs4_to_index (void *abstract_font, - uint32_t ucs4) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; - FT_Face face; - FT_UInt index; - - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return 0; - -#if CAIRO_HAS_FC_FONT - index = FcFreeTypeCharIndex (face, ucs4); -#else - index = FT_Get_Char_Index (face, ucs4); -#endif - - _cairo_ft_unscaled_font_unlock_face (unscaled); - return index; -} - -static cairo_int_status_t -_cairo_ft_load_truetype_table (void *abstract_font, - unsigned long tag, - long offset, - unsigned char *buffer, - unsigned long *length) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; - FT_Face face; - cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) - return CAIRO_INT_STATUS_UNSUPPORTED; - -#if HAVE_FT_LOAD_SFNT_TABLE - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (FT_IS_SFNT (face) && - FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) - status = CAIRO_STATUS_SUCCESS; - - _cairo_ft_unscaled_font_unlock_face (unscaled); -#endif - - return status; -} - -static cairo_int_status_t -_cairo_ft_index_to_ucs4(void *abstract_font, - unsigned long index, - uint32_t *ucs4) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; - FT_Face face; - FT_ULong charcode; - FT_UInt gindex; - - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *ucs4 = (uint32_t) -1; - charcode = FT_Get_First_Char(face, &gindex); - while (gindex != 0) { - if (gindex == index) { - *ucs4 = charcode; - break; - } - charcode = FT_Get_Next_Char (face, charcode, &gindex); - } - - _cairo_ft_unscaled_font_unlock_face (unscaled); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { - CAIRO_FONT_TYPE_FT, - _cairo_ft_scaled_font_fini, - _cairo_ft_scaled_glyph_init, - NULL, /* text_to_glyphs */ - _cairo_ft_ucs4_to_index, - NULL, /* show_glyphs */ - _cairo_ft_load_truetype_table, - _cairo_ft_index_to_ucs4 -}; - -/* #cairo_ft_font_face_t */ - -#if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, - cairo_font_face_t **out); - -static cairo_status_t -_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - FcPattern *pattern; - int fcslant; - int fcweight; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - pattern = FcPatternCreate (); - if (!pattern) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (!FcPatternAddString (pattern, - FC_FAMILY, (unsigned char *) toy_face->family)) - { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - switch (toy_face->slant) - { - case CAIRO_FONT_SLANT_ITALIC: - fcslant = FC_SLANT_ITALIC; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - fcslant = FC_SLANT_OBLIQUE; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - fcslant = FC_SLANT_ROMAN; - break; - } - - if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - switch (toy_face->weight) - { - case CAIRO_FONT_WEIGHT_BOLD: - fcweight = FC_WEIGHT_BOLD; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - fcweight = FC_WEIGHT_MEDIUM; - break; - } - - if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - status = _cairo_ft_font_face_create_for_pattern (pattern, font_face); - - FREE_PATTERN: - FcPatternDestroy (pattern); - - return status; -} -#endif - -static void -_cairo_ft_font_face_destroy (void *abstract_face) -{ - cairo_ft_font_face_t *font_face = abstract_face; - - /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, - * we have a special "zombie" state for the face when the unscaled font - * is still alive but there are no other references to a font face with - * the same FT_Face. - * - * We go from: - * - * font_face ------> unscaled - * <-....weak....../ - * - * To: - * - * font_face <------- unscaled - */ - - if (font_face->unscaled && - font_face->unscaled->from_face && - font_face->next == NULL && - font_face->unscaled->faces == font_face && - CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) - { - cairo_font_face_reference (&font_face->base); - - _cairo_unscaled_font_destroy (&font_face->unscaled->base); - font_face->unscaled = NULL; - - return; - } - - if (font_face->unscaled) { - cairo_ft_font_face_t *tmp_face = NULL; - cairo_ft_font_face_t *last_face = NULL; - - /* Remove face from linked list */ - for (tmp_face = font_face->unscaled->faces; - tmp_face; - tmp_face = tmp_face->next) - { - if (tmp_face == font_face) { - if (last_face) - last_face->next = tmp_face->next; - else - font_face->unscaled->faces = tmp_face->next; - } - - last_face = tmp_face; - } - - _cairo_unscaled_font_destroy (&font_face->unscaled->base); - font_face->unscaled = NULL; - } - -#if CAIRO_HAS_FC_FONT - if (font_face->pattern) { - FcPatternDestroy (font_face->pattern); - cairo_font_face_destroy (font_face->resolved_font_face); - } -#endif -} - -static cairo_font_face_t * -_cairo_ft_font_face_get_implementation (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - cairo_ft_font_face_t *font_face = abstract_face; - - /* The handling of font options is different depending on how the - * font face was created. When the user creates a font face with - * cairo_ft_font_face_create_for_ft_face(), then the load flags - * passed in augment the load flags for the options. But for - * cairo_ft_font_face_create_for_pattern(), the load flags are - * derived from a pattern where the user has called - * cairo_ft_font_options_substitute(), so *just* use those load - * flags and ignore the options. - */ - -#if CAIRO_HAS_FC_FONT - /* If we have an unresolved pattern, resolve it and create - * unscaled font. Otherwise, use the ones stored in font_face. - */ - if (font_face->pattern) { - cairo_font_face_t *resolved; - - /* Cache the resolved font whilst the FcConfig remains consistent. */ - resolved = font_face->resolved_font_face; - if (resolved != NULL) { - if (! FcInitBringUptoDate ()) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *) &_cairo_font_face_nil; - } - - if (font_face->resolved_config == FcConfigGetCurrent ()) - return cairo_font_face_reference (resolved); - - cairo_font_face_destroy (resolved); - font_face->resolved_font_face = NULL; - } - - resolved = _cairo_ft_resolve_pattern (font_face->pattern, - font_matrix, - ctm, - options); - if (unlikely (resolved->status)) - return resolved; - - font_face->resolved_font_face = cairo_font_face_reference (resolved); - font_face->resolved_config = FcConfigGetCurrent (); - - return resolved; - } -#endif - - return abstract_face; -} - -const cairo_font_face_backend_t _cairo_ft_font_face_backend = { - CAIRO_FONT_TYPE_FT, -#if CAIRO_HAS_FC_FONT - _cairo_ft_font_face_create_for_toy, -#else - NULL, -#endif - _cairo_ft_font_face_destroy, - _cairo_ft_font_face_scaled_font_create, - _cairo_ft_font_face_get_implementation -}; - -#if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, - cairo_font_face_t **out) -{ - cairo_ft_font_face_t *font_face; - - font_face = malloc (sizeof (cairo_ft_font_face_t)); - if (unlikely (font_face == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_face->unscaled = NULL; - font_face->next = NULL; - - font_face->pattern = FcPatternDuplicate (pattern); - if (unlikely (font_face->pattern == NULL)) { - free (font_face); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - font_face->resolved_font_face = NULL; - font_face->resolved_config = NULL; - - _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); - - *out = &font_face->base; - return CAIRO_STATUS_SUCCESS; -} -#endif - -static cairo_font_face_t * -_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, - cairo_ft_options_t *ft_options) -{ - cairo_ft_font_face_t *font_face, **prev_font_face; - - /* Looked for an existing matching font face */ - for (font_face = unscaled->faces, prev_font_face = &unscaled->faces; - font_face; - prev_font_face = &font_face->next, font_face = font_face->next) - { - if (font_face->ft_options.load_flags == ft_options->load_flags && - font_face->ft_options.extra_flags == ft_options->extra_flags && - cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base)) - { - if (font_face->base.status) { - /* The font_face has been left in an error state, abandon it. */ - *prev_font_face = font_face->next; - break; - } - - if (font_face->unscaled == NULL) { - /* Resurrect this "zombie" font_face (from - * _cairo_ft_font_face_destroy), switching its unscaled_font - * from owner to ownee. */ - font_face->unscaled = unscaled; - _cairo_unscaled_font_reference (&unscaled->base); - return &font_face->base; - } else - return cairo_font_face_reference (&font_face->base); - } - } - - /* No match found, create a new one */ - font_face = malloc (sizeof (cairo_ft_font_face_t)); - if (unlikely (!font_face)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } - - font_face->unscaled = unscaled; - _cairo_unscaled_font_reference (&unscaled->base); - - font_face->ft_options = *ft_options; - - if (unscaled->faces && unscaled->faces->unscaled == NULL) { - /* This "zombie" font_face (from _cairo_ft_font_face_destroy) - * is no longer needed. */ - assert (unscaled->from_face && unscaled->faces->next == NULL); - cairo_font_face_destroy (&unscaled->faces->base); - unscaled->faces = NULL; - } - - font_face->next = unscaled->faces; - unscaled->faces = font_face; - -#if CAIRO_HAS_FC_FONT - font_face->pattern = NULL; -#endif - - _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); - - return &font_face->base; -} - -/* implement the platform-specific interface */ - -#if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_options_substitute (const cairo_font_options_t *options, - FcPattern *pattern) -{ - FcValue v; - - if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) - { - if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) - { - if (! FcPatternAddBool (pattern, - FC_ANTIALIAS, - options->antialias != CAIRO_ANTIALIAS_NONE)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { - FcPatternDel (pattern, FC_RGBA); - if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - } - - if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) - { - if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch) - { - int rgba; - - if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { - switch (options->subpixel_order) { - case CAIRO_SUBPIXEL_ORDER_DEFAULT: - case CAIRO_SUBPIXEL_ORDER_RGB: - default: - rgba = FC_RGBA_RGB; - break; - case CAIRO_SUBPIXEL_ORDER_BGR: - rgba = FC_RGBA_BGR; - break; - case CAIRO_SUBPIXEL_ORDER_VRGB: - rgba = FC_RGBA_VRGB; - break; - case CAIRO_SUBPIXEL_ORDER_VBGR: - rgba = FC_RGBA_VBGR; - break; - } - } else { - rgba = FC_RGBA_NONE; - } - - if (! FcPatternAddInteger (pattern, FC_RGBA, rgba)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) - { - if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch) - { - int lcd_filter; - - switch (options->lcd_filter) { - case CAIRO_LCD_FILTER_NONE: - lcd_filter = FT_LCD_FILTER_NONE; - break; - case CAIRO_LCD_FILTER_DEFAULT: - case CAIRO_LCD_FILTER_INTRA_PIXEL: - lcd_filter = FT_LCD_FILTER_LEGACY; - break; - case CAIRO_LCD_FILTER_FIR3: - lcd_filter = FT_LCD_FILTER_LIGHT; - break; - default: - case CAIRO_LCD_FILTER_FIR5: - lcd_filter = FT_LCD_FILTER_DEFAULT; - break; - } - - if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT) - { - if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch) - { - if (! FcPatternAddBool (pattern, - FC_HINTING, - options->hint_style != CAIRO_HINT_STYLE_NONE)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - -#ifdef FC_HINT_STYLE - if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch) - { - int hint_style; - - switch (options->hint_style) { - case CAIRO_HINT_STYLE_NONE: - hint_style = FC_HINT_NONE; - break; - case CAIRO_HINT_STYLE_SLIGHT: - hint_style = FC_HINT_SLIGHT; - break; - case CAIRO_HINT_STYLE_MEDIUM: - hint_style = FC_HINT_MEDIUM; - break; - case CAIRO_HINT_STYLE_FULL: - case CAIRO_HINT_STYLE_DEFAULT: - default: - hint_style = FC_HINT_FULL; - break; - } - - if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } -#endif - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_ft_font_options_substitute: - * @options: a #cairo_font_options_t object - * @pattern: an existing #FcPattern - * - * Add options to a #FcPattern based on a #cairo_font_options_t font - * options object. Options that are already in the pattern, are not overridden, - * so you should call this function after calling FcConfigSubstitute() (the - * user's settings should override options based on the surface type), but - * before calling FcDefaultSubstitute(). - **/ -void -cairo_ft_font_options_substitute (const cairo_font_options_t *options, - FcPattern *pattern) -{ - if (cairo_font_options_status ((cairo_font_options_t *) options)) - return; - - _cairo_ft_font_options_substitute (options, pattern); -} - -static cairo_font_face_t * -_cairo_ft_resolve_pattern (FcPattern *pattern, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *font_options) -{ - cairo_status_t status; - - cairo_matrix_t scale; - FcPattern *resolved; - cairo_ft_font_transform_t sf; - FcResult result; - cairo_ft_unscaled_font_t *unscaled; - cairo_ft_options_t ft_options; - cairo_font_face_t *font_face; - - scale = *ctm; - scale.x0 = scale.y0 = 0; - cairo_matrix_multiply (&scale, - font_matrix, - &scale); - - status = _compute_transform (&sf, &scale, NULL); - if (unlikely (status)) - return (cairo_font_face_t *)&_cairo_font_face_nil; - - pattern = FcPatternDuplicate (pattern); - if (pattern == NULL) - return (cairo_font_face_t *)&_cairo_font_face_nil; - - if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { - font_face = (cairo_font_face_t *)&_cairo_font_face_nil; - goto FREE_PATTERN; - } - - if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { - font_face = (cairo_font_face_t *)&_cairo_font_face_nil; - goto FREE_PATTERN; - } - - status = _cairo_ft_font_options_substitute (font_options, pattern); - if (status) { - font_face = (cairo_font_face_t *)&_cairo_font_face_nil; - goto FREE_PATTERN; - } - - FcDefaultSubstitute (pattern); - - resolved = FcFontMatch (NULL, pattern, &result); - if (!resolved) { - /* We failed to find any font. Substitute twin so that the user can - * see something (and hopefully recognise that the font is missing) - * and not just receive a NO_MEMORY error during rendering. - */ - font_face = _cairo_font_face_twin_create_fallback (); - goto FREE_PATTERN; - } - - status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); - if (unlikely (status || unscaled == NULL)) { - font_face = (cairo_font_face_t *)&_cairo_font_face_nil; - goto FREE_RESOLVED; - } - - _get_pattern_ft_options (resolved, &ft_options); - font_face = _cairo_ft_font_face_create (unscaled, &ft_options); - _cairo_unscaled_font_destroy (&unscaled->base); - -FREE_RESOLVED: - FcPatternDestroy (resolved); - -FREE_PATTERN: - FcPatternDestroy (pattern); - - return font_face; -} - -/** - * cairo_ft_font_face_create_for_pattern: - * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern - * if it needs to. You are free to modify or free @pattern after this call. - * - * Creates a new font face for the FreeType font backend based on a - * fontconfig pattern. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). The - * #cairo_scaled_font_t returned from cairo_scaled_font_create() is - * also for the FreeType backend and can be used with functions such - * as cairo_ft_scaled_font_lock_face(). - * - * Font rendering options are represented both here and when you - * call cairo_scaled_font_create(). Font options that have a representation - * in a #FcPattern must be passed in here; to modify #FcPattern - * appropriately to reflect the options in a #cairo_font_options_t, call - * cairo_ft_font_options_substitute(). - * - * The pattern's FC_FT_FACE element is inspected first and if that is set, - * that will be the FreeType font face associated with the returned cairo - * font face. Otherwise the FC_FILE element is checked. If it's set, - * that and the value of the FC_INDEX element (defaults to zero) of @pattern - * are used to load a font face from file. - * - * If both steps from the previous paragraph fails, @pattern will be passed - * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch, - * and the resulting font pattern is used. - * - * If the FC_FT_FACE element of @pattern is set, the user is responsible - * for making sure that the referenced FT_Face remains valid for the life - * time of the returned #cairo_font_face_t. See - * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple - * the life time of the FT_Face to that of the cairo font-face. - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - **/ -cairo_font_face_t * -cairo_ft_font_face_create_for_pattern (FcPattern *pattern) -{ - cairo_ft_unscaled_font_t *unscaled; - cairo_font_face_t *font_face; - cairo_ft_options_t ft_options; - cairo_status_t status; - - status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); - if (unlikely (status)) - return (cairo_font_face_t *) &_cairo_font_face_nil; - if (unlikely (unscaled == NULL)) { - /* Store the pattern. We will resolve it and create unscaled - * font when creating scaled fonts */ - status = _cairo_ft_font_face_create_for_pattern (pattern, - &font_face); - if (unlikely (status)) - return (cairo_font_face_t *) &_cairo_font_face_nil; - - return font_face; - } - - _get_pattern_ft_options (pattern, &ft_options); - font_face = _cairo_ft_font_face_create (unscaled, &ft_options); - _cairo_unscaled_font_destroy (&unscaled->base); - - return font_face; -} -#endif - -/** - * cairo_ft_font_face_create_for_ft_face: - * @face: A FreeType face object, already opened. This must - * be kept around until the face's ref_count drops to - * zero and it is freed. Since the face may be referenced - * internally to Cairo, the best way to determine when it - * is safe to free the face is to pass a - * #cairo_destroy_func_t to cairo_font_face_set_user_data() - * @load_flags: flags to pass to FT_Load_Glyph when loading - * glyphs from the font. These flags are OR'ed together with - * the flags derived from the #cairo_font_options_t passed - * to cairo_scaled_font_create(), so only a few values such - * as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT - * are useful. You should not pass any of the flags affecting - * the load target, such as %FT_LOAD_TARGET_LIGHT. - * - * Creates a new font face for the FreeType font backend from a - * pre-opened FreeType face. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). The - * #cairo_scaled_font_t returned from cairo_scaled_font_create() is - * also for the FreeType backend and can be used with functions such - * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference - * to the FT_Face alive in a font-cache and the exact lifetime of the reference - * depends highly upon the exact usage pattern and is subject to external - * factors. You must not call FT_Done_Face() before the last reference to the - * #cairo_font_face_t has been dropped. - * - * As an example, below is how one might correctly couple the lifetime of - * the FreeType face object to the #cairo_font_face_t. - * - * - * static const cairo_user_data_key_t key; - * - * font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); - * status = cairo_font_face_set_user_data (font_face, &key, - * ft_face, (cairo_destroy_func_t) FT_Done_Face); - * if (status) { - * cairo_font_face_destroy (font_face); - * FT_Done_Face (ft_face); - * return ERROR; - * } - * - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - **/ -cairo_font_face_t * -cairo_ft_font_face_create_for_ft_face (FT_Face face, - int load_flags) -{ - cairo_ft_unscaled_font_t *unscaled; - cairo_font_face_t *font_face; - cairo_ft_options_t ft_options; - cairo_status_t status; - - status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); - if (unlikely (status)) - return (cairo_font_face_t *)&_cairo_font_face_nil; - - ft_options.load_flags = load_flags; - ft_options.extra_flags = 0; - _cairo_font_options_init_default (&ft_options.base); - - font_face = _cairo_ft_font_face_create (unscaled, &ft_options); - _cairo_unscaled_font_destroy (&unscaled->base); - - return font_face; -} - -/** - * cairo_ft_scaled_font_lock_face: - * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an - * object can be created by calling cairo_scaled_font_create() on a - * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), - * cairo_ft_font_face_create_for_ft_face()). - * - * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType - * backend font and scales it appropriately for the font. You must - * release the face with cairo_ft_scaled_font_unlock_face() - * when you are done using it. Since the #FT_Face object can be - * shared between multiple #cairo_scaled_font_t objects, you must not - * lock any other font objects until you unlock this one. A count is - * kept of the number of times cairo_ft_scaled_font_lock_face() is - * called. cairo_ft_scaled_font_unlock_face() must be called the same number - * of times. - * - * You must be careful when using this function in a library or in a - * threaded application, because freetype's design makes it unsafe to - * call freetype functions simultaneously from multiple threads, (even - * if using distinct FT_Face objects). Because of this, application - * code that acquires an FT_Face object with this call must add its - * own locking to protect any use of that object, (and which also must - * protect any other calls into cairo as almost any cairo function - * might result in a call into the freetype library). - * - * Return value: The #FT_Face object for @font, scaled appropriately, - * or %NULL if @scaled_font is in an error state (see - * cairo_scaled_font_status()) or there is insufficient memory. - **/ -FT_Face -cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) -{ - cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - FT_Face face; - cairo_status_t status; - - if (! _cairo_scaled_font_is_ft (abstract_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - return NULL; - } - - if (scaled_font->base.status) - return NULL; - - face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); - if (unlikely (face == NULL)) { - status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, - &scaled_font->base.scale); - if (unlikely (status)) { - _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); - status = _cairo_scaled_font_set_error (&scaled_font->base, status); - return NULL; - } - - /* Note: We deliberately release the unscaled font's mutex here, - * so that we are not holding a lock across two separate calls to - * cairo function, (which would give the application some - * opportunity for creating deadlock. This is obviously unsafe, - * but as documented, the user must add manual locking when using - * this function. */ - CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex); - - return face; -} - -/** - * cairo_ft_scaled_font_unlock_face: - * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an - * object can be created by calling cairo_scaled_font_create() on a - * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), - * cairo_ft_font_face_create_for_ft_face()). - * - * Releases a face obtained with cairo_ft_scaled_font_lock_face(). - **/ -void -cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) -{ - cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - - if (! _cairo_scaled_font_is_ft (abstract_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - return; - } - - if (scaled_font->base.status) - return; - - /* Note: We released the unscaled font's mutex at the end of - * cairo_ft_scaled_font_lock_face, so we have to acquire it again - * as _cairo_ft_unscaled_font_unlock_face expects it to be held - * when we call into it. */ - CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex); - - _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); -} - -/* We expose our unscaled font implementation internally for the the - * PDF backend, which needs to keep track of the the different - * fonts-on-disk used by a document, so it can embed them. - */ -cairo_unscaled_font_t * -_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *abstract_font) -{ - cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - - return &scaled_font->unscaled->base; -} - -cairo_bool_t -_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) -{ - cairo_ft_scaled_font_t *ft_scaled_font; - - if (!_cairo_scaled_font_is_ft (scaled_font)) - return FALSE; - - ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; - if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT) - return TRUE; - return FALSE; -} - -unsigned int -_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) -{ - cairo_ft_scaled_font_t *ft_scaled_font; - - if (! _cairo_scaled_font_is_ft (scaled_font)) - return 0; - - ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; - return ft_scaled_font->ft_options.load_flags; -} - -void -_cairo_ft_font_reset_static_data (void) -{ - _cairo_ft_unscaled_font_map_destroy (); -} diff --git a/libs/cairo/cairo/src/cairo-ft-private.h b/libs/cairo/cairo/src/cairo-ft-private.h deleted file mode 100644 index 42a1776ed..000000000 --- a/libs/cairo/cairo/src/cairo-ft-private.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FT_PRIVATE_H -#define CAIRO_FT_PRIVATE_H - -#include "cairo-ft.h" -#include "cairoint.h" - -#if CAIRO_HAS_FT_FONT - -CAIRO_BEGIN_DECLS - -typedef struct _cairo_ft_unscaled_font cairo_ft_unscaled_font_t; - -cairo_private cairo_bool_t -_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); - -/* These functions are needed by the PDF backend, which needs to keep track of the - * the different fonts-on-disk used by a document, so it can embed them - */ -cairo_private cairo_unscaled_font_t * -_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font); - -cairo_private FT_Face -_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled); - -cairo_private void -_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); - -cairo_private cairo_bool_t -_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); - -cairo_private unsigned int -_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); - -CAIRO_END_DECLS - -#endif /* CAIRO_HAS_FT_FONT */ -#endif /* CAIRO_FT_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-ft.h b/libs/cairo/cairo/src/cairo-ft.h deleted file mode 100644 index 2f584066f..000000000 --- a/libs/cairo/cairo/src/cairo-ft.h +++ /dev/null @@ -1,50 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FT_H -#define CAIRO_FT_H - -#include "cairo.h" - -#if CAIRO_HAS_FT_FONT - -/* Fontconfig/Freetype platform-specific font interface */ - -#include -#include FT_FREETYPE_H - -#if CAIRO_HAS_FC_FONT -#include -#endif - -CAIRO_BEGIN_DECLS - -cairo_public cairo_font_face_t * -cairo_ft_font_face_create_for_ft_face (FT_Face face, - int load_flags); - -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/libs/cairo/cairo/src/cairo-gl-glyphs.c b/libs/cairo/cairo/src/cairo-gl-glyphs.c deleted file mode 100644 index 883883fbf..000000000 --- a/libs/cairo/cairo/src/cairo-gl-glyphs.c +++ /dev/null @@ -1,571 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-error-private.h" -#include "cairo-rtree-private.h" - -#define GLYPH_CACHE_WIDTH 1024 -#define GLYPH_CACHE_HEIGHT 1024 -#define GLYPH_CACHE_MIN_SIZE 4 -#define GLYPH_CACHE_MAX_SIZE 128 - -typedef struct _cairo_gl_glyph_private { - cairo_rtree_node_t node; - cairo_gl_glyph_cache_t *cache; - struct { float x, y; } p1, p2; -} cairo_gl_glyph_private_t; - -static cairo_status_t -_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_gl_surface_t *cache_surface; - cairo_gl_glyph_private_t *glyph_private; - cairo_rtree_node_t *node = NULL; - cairo_status_t status; - int width, height; - - width = glyph_surface->width; - if (width < GLYPH_CACHE_MIN_SIZE) - width = GLYPH_CACHE_MIN_SIZE; - height = glyph_surface->height; - if (height < GLYPH_CACHE_MIN_SIZE) - height = GLYPH_CACHE_MIN_SIZE; - - /* search for an available slot */ - status = _cairo_rtree_insert (&cache->rtree, width, height, &node); - /* search for an unlocked slot */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_rtree_evict_random (&cache->rtree, - width, height, &node); - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_rtree_node_insert (&cache->rtree, - node, width, height, &node); - } - } - if (status) - return status; - - cache_surface = (cairo_gl_surface_t *) cache->pattern.surface; - - /* XXX: Make sure we use the mask texture. This should work automagically somehow */ - glActiveTexture (GL_TEXTURE1); - status = _cairo_gl_surface_draw_image (cache_surface, - glyph_surface, - 0, 0, - glyph_surface->width, glyph_surface->height, - node->x, node->y); - if (unlikely (status)) - return status; - - scaled_glyph->surface_private = node; - node->owner = &scaled_glyph->surface_private; - - glyph_private = (cairo_gl_glyph_private_t *) node; - glyph_private->cache = cache; - - /* compute tex coords */ - glyph_private->p1.x = node->x; - glyph_private->p1.y = node->y; - glyph_private->p2.x = node->x + glyph_surface->width; - glyph_private->p2.y = node->y + glyph_surface->height; - if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { - glyph_private->p1.x /= cache_surface->width; - glyph_private->p1.y /= cache_surface->height; - glyph_private->p2.x /= cache_surface->width; - glyph_private->p2.y /= cache_surface->height; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_gl_glyph_private_t * -_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, - cairo_scaled_glyph_t *scaled_glyph) -{ - return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private); -} - -static cairo_status_t -cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, - cairo_format_t format, - cairo_gl_glyph_cache_t **cache_out) -{ - cairo_gl_glyph_cache_t *cache; - cairo_content_t content; - - switch (format) { - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - cache = &ctx->glyph_cache[0]; - content = CAIRO_CONTENT_COLOR_ALPHA; - break; - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_A1: - cache = &ctx->glyph_cache[1]; - content = CAIRO_CONTENT_ALPHA; - break; - case CAIRO_FORMAT_INVALID: - ASSERT_NOT_REACHED; - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - } - - if (unlikely (cache->pattern.surface == NULL)) { - cairo_surface_t *surface; - surface = cairo_gl_surface_create (&ctx->base, - content, - GLYPH_CACHE_WIDTH, - GLYPH_CACHE_HEIGHT); - if (unlikely (surface->status)) { - cairo_status_t status = surface->status; - cairo_surface_destroy (surface); - return status; - } - _cairo_surface_release_device_reference (surface); - _cairo_pattern_init_for_surface (&cache->pattern, surface); - cairo_surface_destroy (surface); - cache->pattern.base.has_component_alpha = (content == CAIRO_CONTENT_COLOR_ALPHA); - } - - *cache_out = cache; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_unpin (&cache->rtree); -} - -static cairo_bool_t -_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_device_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &_cairo_gl_surface_backend) || - (font_private != NULL && font_private != surface->base.device)) - { - return FALSE; - } - - return TRUE; -} - -void -_cairo_gl_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_list_del (&scaled_font->link); -} - -void -_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_gl_glyph_private_t *glyph_private; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private != NULL) { - glyph_private->node.owner = NULL; - if (! glyph_private->node.pinned) { - /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ - _cairo_rtree_node_remove (&glyph_private->cache->rtree, - &glyph_private->node); - } - } -} - -static cairo_status_t -_render_glyphs (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_rectangle_int_t *glyph_extents, - cairo_scaled_font_t *scaled_font, - cairo_bool_t *has_component_alpha, - cairo_region_t *clip_region, - int *remaining_glyphs) -{ - cairo_format_t last_format = CAIRO_FORMAT_INVALID; - cairo_gl_glyph_cache_t *cache = NULL; - cairo_gl_context_t *ctx; - cairo_gl_composite_t setup; - cairo_status_t status; - int i = 0; - - *has_component_alpha = FALSE; - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_scaled_font_freeze_cache (scaled_font); - - status = _cairo_gl_composite_init (&setup, op, dst, - TRUE, glyph_extents); - - if (unlikely (status)) - goto FINISH; - - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FINISH; - } - - status = _cairo_gl_composite_set_source (&setup, source, - glyph_extents->x, glyph_extents->y, - dst_x, dst_y, - glyph_extents->width, - glyph_extents->height); - if (unlikely (status)) - goto FINISH; - - if (scaled_font->surface_private == NULL) { - scaled_font->surface_private = ctx; - scaled_font->surface_backend = &_cairo_gl_surface_backend; - cairo_list_add (&scaled_font->link, &ctx->fonts); - } - - _cairo_gl_composite_set_clip_region (&setup, clip_region); - - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - cairo_gl_glyph_private_t *glyph; - double x_offset, y_offset; - double x1, x2, y1, y2; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (unlikely (status)) - goto FINISH; - - if (scaled_glyph->surface->width == 0 || - scaled_glyph->surface->height == 0) - { - continue; - } - if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || - scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FINISH; - } - - if (scaled_glyph->surface->format != last_format) { - status = cairo_gl_context_get_glyph_cache (ctx, - scaled_glyph->surface->format, - &cache); - if (unlikely (status)) - goto FINISH; - - last_format = scaled_glyph->surface->format; - - status = _cairo_gl_composite_set_mask (&setup, - &cache->pattern.base, - 0, 0, 0, 0, 0, 0); - if (unlikely (status)) - goto FINISH; - - *has_component_alpha |= cache->pattern.base.has_component_alpha; - - /* XXX: _cairo_gl_composite_begin() acquires the context a - * second time. Need to refactor this loop so this doesn't happen. - */ - status = _cairo_gl_composite_begin (&setup, &ctx); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) - goto FINISH; - } - - if (scaled_glyph->surface_private == NULL) { - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - /* Cache is full, so flush existing prims and try again. */ - _cairo_gl_composite_flush (ctx); - _cairo_gl_glyph_cache_unlock (cache); - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); - } - - if (unlikely (_cairo_status_is_error (status))) - goto FINISH; - } - - x_offset = scaled_glyph->surface->base.device_transform.x0; - y_offset = scaled_glyph->surface->base.device_transform.y0; - - x1 = _cairo_lround (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); - x2 = x1 + scaled_glyph->surface->width; - y2 = y1 + scaled_glyph->surface->height; - - glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); - _cairo_gl_composite_emit_glyph (ctx, - x1, y1, x2, y2, - glyph->p1.x, glyph->p1.y, - glyph->p2.x, glyph->p2.y); - } - - status = CAIRO_STATUS_SUCCESS; - FINISH: - _cairo_scaled_font_thaw_cache (scaled_font); - - status = _cairo_gl_context_release (ctx, status); - - _cairo_gl_composite_fini (&setup); - - *remaining_glyphs = num_glyphs - i; - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_rectangle_int_t *glyph_extents, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_surface_t *mask; - cairo_status_t status; - cairo_bool_t has_component_alpha; - int i; - - /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ - mask = cairo_gl_surface_create (dst->base.device, - CAIRO_CONTENT_COLOR_ALPHA, - glyph_extents->width, - glyph_extents->height); - if (unlikely (mask->status)) - return mask->status; - - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x -= glyph_extents->x; - glyphs[i].y -= glyph_extents->y; - } - - status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - glyphs, num_glyphs, glyph_extents, - scaled_font, &has_component_alpha, - NULL, remaining_glyphs); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - cairo_surface_pattern_t mask_pattern; - - mask->is_clear = FALSE; - _cairo_pattern_init_for_surface (&mask_pattern, mask); - mask_pattern.base.has_component_alpha = has_component_alpha; - cairo_matrix_init_translate (&mask_pattern.base.matrix, - -glyph_extents->x, -glyph_extents->y); - status = _cairo_surface_mask (&dst->base, op, - source, &mask_pattern.base, clip); - _cairo_pattern_fini (&mask_pattern.base); - } else { - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x += glyph_extents->x; - glyphs[i].y += glyph_extents->y; - } - *remaining_glyphs = num_glyphs; - } - - cairo_surface_destroy (mask); - - return status; -} - -cairo_int_status_t -_cairo_gl_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_rectangle_int_t surface_extents; - cairo_rectangle_int_t extents; - cairo_region_t *clip_region = NULL; - cairo_bool_t overlap, use_mask = FALSE; - cairo_bool_t has_component_alpha; - cairo_status_t status; - int i; - - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - - if (! _cairo_operator_bounded_by_mask (op)) - use_mask |= TRUE; - - /* If any of the glyphs are component alpha, we have to go through a mask, - * since only _cairo_gl_surface_composite() currently supports component - * alpha. - */ - if (!use_mask && op != CAIRO_OPERATOR_OVER) { - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (!_cairo_status_is_error (status) && - scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32) - { - use_mask = TRUE; - break; - } - } - } - - /* For CLEAR, cairo's rendering equation (quoting Owen's description in: - * http://lists.cairographics.org/archives/cairo/2005-August/004992.html) - * is: - * mask IN clip ? src OP dest : dest - * or more simply: - * mask IN CLIP ? 0 : dest - * - * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). - * - * The model we use in _cairo_gl_set_operator() is Render's: - * src IN mask IN clip OP dest - * which would boil down to: - * 0 (bounded by the extents of the drawing). - * - * However, we can do a Render operation using an opaque source - * and DEST_OUT to produce: - * 1 IN mask IN clip DEST_OUT dest - * which is - * mask IN clip ? 0 : dest - */ - if (op == CAIRO_OPERATOR_CLEAR) { - source = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - /* For SOURCE, cairo's rendering equation is: - * (mask IN clip) ? src OP dest : dest - * or more simply: - * (mask IN clip) ? src : dest. - * - * If we just used the Render equation, we would get: - * (src IN mask IN clip) OP dest - * or: - * (src IN mask IN clip) bounded by extents of the drawing. - * - * The trick is that for GL blending, we only get our 4 source values - * into the blender, and since we need all 4 components of source, we - * can't also get the mask IN clip into the blender. But if we did - * two passes we could make it work: - * dest = (mask IN clip) DEST_OUT dest - * dest = src IN mask IN clip ADD dest - * - * But for now, composite via an intermediate mask. - */ - if (op == CAIRO_OPERATOR_SOURCE) - use_mask |= TRUE; - - /* XXX we don't need ownership of the font as we use a global - * glyph cache -- but we do need scaled_glyph eviction notification. :-( - */ - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("do not control font"); - - /* If the glyphs overlap, we need to build an intermediate mask rather - * then perform the compositing directly. - */ - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, num_glyphs, - &extents, - &overlap); - if (unlikely (status)) - return status; - - use_mask |= overlap; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - /* the empty clip should never be propagated this far */ - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (unlikely (_cairo_status_is_error (status))) - return status; - - use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_rectangle_intersect (&extents, - _cairo_clip_get_extents (clip))) - goto EMPTY; - } - - surface_extents.x = surface_extents.y = 0; - surface_extents.width = dst->width; - surface_extents.height = dst->height; - if (! _cairo_rectangle_intersect (&extents, &surface_extents)) - goto EMPTY; - - if (use_mask) { - return _cairo_gl_surface_show_glyphs_via_mask (dst, op, - source, - glyphs, num_glyphs, - &extents, - scaled_font, - clip, - remaining_glyphs); - } - - return _render_glyphs (dst, extents.x, extents.y, - op, source, - glyphs, num_glyphs, &extents, - scaled_font, &has_component_alpha, - clip_region, remaining_glyphs); - -EMPTY: - *remaining_glyphs = 0; - if (! _cairo_operator_bounded_by_mask (op)) - return _cairo_surface_paint (&dst->base, op, source, clip); - else - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_init (&cache->rtree, - GLYPH_CACHE_WIDTH, - GLYPH_CACHE_HEIGHT, - GLYPH_CACHE_MIN_SIZE, - sizeof (cairo_gl_glyph_private_t)); -} - -void -_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_fini (&cache->rtree); - - if (cache->pattern.surface) { - _cairo_pattern_fini (&cache->pattern.base); - cache->pattern.surface = NULL; - } -} - diff --git a/libs/cairo/cairo/src/cairo-gl-private.h b/libs/cairo/cairo/src/cairo-gl-private.h deleted file mode 100644 index 96a2a1b5b..000000000 --- a/libs/cairo/cairo/src/cairo-gl-private.h +++ /dev/null @@ -1,448 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_GL_PRIVATE_H -#define CAIRO_GL_PRIVATE_H - -#include "cairoint.h" - -#include "cairo-gl-gradient-private.h" - -#include "cairo-device-private.h" -#include "cairo-error-private.h" -#include "cairo-rtree-private.h" - -#include - -#include - -#include "cairo-gl.h" - -#include -#define GL_GLEXT_PROTOTYPES -#include - -#define DEBUG_GL 0 - -#if DEBUG_GL && __GNUC__ -#define UNSUPPORTED(reason) ({ \ - fprintf (stderr, \ - "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ - __FUNCTION__, __LINE__, reason); \ - CAIRO_INT_STATUS_UNSUPPORTED; \ -}) -#else -#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED -#endif - -/* maximal number of shaders we keep in the cache. - * Random number that is hopefully big enough to not cause many cache evictions. */ -#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 - -/* VBO size that we allocate, smaller size means we gotta flush more often */ -#define CAIRO_GL_VBO_SIZE 16384 - -typedef struct _cairo_gl_surface { - cairo_surface_t base; - - int width, height; - - GLuint tex; /* GL texture object containing our data. */ - GLuint fb; /* GL framebuffer object wrapping our data. */ - GLuint depth; /* GL framebuffer object holding depth */ - int owns_tex; -} cairo_gl_surface_t; - -typedef struct cairo_gl_glyph_cache { - cairo_rtree_t rtree; - cairo_surface_pattern_t pattern; -} cairo_gl_glyph_cache_t; - -typedef enum cairo_gl_tex { - CAIRO_GL_TEX_SOURCE = 0, - CAIRO_GL_TEX_MASK = 1, - CAIRO_GL_TEX_TEMP = 2 -} cairo_gl_tex_t; - -typedef enum cairo_gl_operand_type { - CAIRO_GL_OPERAND_NONE, - CAIRO_GL_OPERAND_CONSTANT, - CAIRO_GL_OPERAND_TEXTURE, - CAIRO_GL_OPERAND_LINEAR_GRADIENT, - CAIRO_GL_OPERAND_RADIAL_GRADIENT, - CAIRO_GL_OPERAND_SPANS, - - CAIRO_GL_OPERAND_COUNT -} cairo_gl_operand_type_t; - -typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t; - -typedef struct cairo_gl_shader { - GLuint fragment_shader; - GLuint program; -} cairo_gl_shader_t; - -typedef enum cairo_gl_shader_in { - CAIRO_GL_SHADER_IN_NORMAL, - CAIRO_GL_SHADER_IN_CA_SOURCE, - CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, - - CAIRO_GL_SHADER_IN_COUNT -} cairo_gl_shader_in_t; - -typedef enum cairo_gl_var_type { - CAIRO_GL_VAR_NONE, - CAIRO_GL_VAR_TEXCOORDS, - CAIRO_GL_VAR_COVERAGE -} cairo_gl_var_type_t; - -#define cairo_gl_var_type_hash(src,mask,dest) ((mask) << 2 | (src << 1) | (dest)) -#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_COVERAGE << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS) - -/* This union structure describes a potential source or mask operand to the - * compositing equation. - */ -typedef struct cairo_gl_operand { - cairo_gl_operand_type_t type; - union { - struct { - GLuint tex; - cairo_gl_surface_t *surface; - cairo_surface_attributes_t attributes; - } texture; - struct { - GLfloat color[4]; - } constant; - struct { - cairo_gl_gradient_t *gradient; - cairo_matrix_t m; - float segment_x; - float segment_y; - cairo_extend_t extend; - } linear; - struct { - cairo_gl_gradient_t *gradient; - cairo_matrix_t m; - float circle_1_x; - float circle_1_y; - float radius_0; - float radius_1; - cairo_extend_t extend; - } radial; - }; - unsigned int vertex_offset; -} cairo_gl_operand_t; - -struct _cairo_gl_context { - cairo_device_t base; - - GLuint dummy_tex; - GLuint texture_load_pbo; - GLuint vbo; - GLint max_framebuffer_size; - GLint max_texture_size; - GLint max_textures; - GLenum tex_target; - - const cairo_gl_shader_impl_t *shader_impl; - - GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1]; - cairo_gl_shader_t fill_rectangles_shader; - cairo_cache_t shaders; - - cairo_cache_t gradients; - - cairo_gl_glyph_cache_t glyph_cache[2]; - cairo_list_t fonts; - - cairo_gl_surface_t *current_target; - cairo_operator_t current_operator; - cairo_gl_shader_t *pre_shader; /* for component alpha */ - cairo_gl_shader_t *current_shader; - - cairo_gl_operand_t operands[2]; - - char *vb; - unsigned int vb_offset; - unsigned int vertex_size; - cairo_region_t *clip_region; - - void (*acquire) (void *ctx); - void (*release) (void *ctx); - - void (*make_current) (void *ctx, cairo_gl_surface_t *surface); - void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); - void (*destroy) (void *ctx); -}; - -typedef struct _cairo_gl_composite { - cairo_gl_surface_t *dst; - cairo_operator_t op; - cairo_region_t *clip_region; - - cairo_gl_operand_t src; - cairo_gl_operand_t mask; -} cairo_gl_composite_t; - -cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; - -static cairo_always_inline GLenum -_cairo_gl_get_error (void) -{ - GLenum err = glGetError(); - - if (unlikely (err)) - while (glGetError ()); - - return err; -} - -static inline cairo_device_t * -_cairo_gl_context_create_in_error (cairo_status_t status) -{ - return (cairo_device_t *) _cairo_device_create_in_error (status); -} - -cairo_private cairo_status_t -_cairo_gl_context_init (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_surface_init (cairo_device_t *device, - cairo_gl_surface_t *surface, - cairo_content_t content, - int width, int height); - -static cairo_always_inline cairo_bool_t cairo_warn -_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface) -{ - return surface->tex != 0; -} - -cairo_private cairo_status_t -_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, - cairo_image_surface_t *src, - int src_x, int src_y, - int width, int height, - int dst_x, int dst_y); - -static cairo_always_inline cairo_bool_t -_cairo_gl_device_has_glsl (cairo_device_t *device) -{ - return ((cairo_gl_context_t *) device)->shader_impl != NULL; -} - -static cairo_always_inline cairo_bool_t -_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device) -{ - return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE_EXT; -} - -static cairo_always_inline cairo_status_t cairo_warn -_cairo_gl_context_acquire (cairo_device_t *device, - cairo_gl_context_t **ctx) -{ - cairo_status_t status; - - status = cairo_device_acquire (device); - if (unlikely (status)) - return status; - - /* clear potential previous GL errors */ - _cairo_gl_get_error (); - - *ctx = (cairo_gl_context_t *) device; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_always_inline cairo_warn cairo_status_t -_cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) -{ - GLenum err; - - err = _cairo_gl_get_error (); - - if (unlikely (err)) { - cairo_status_t new_status; - new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - if (status == CAIRO_STATUS_SUCCESS) - status = new_status; - } - - cairo_device_release (&(ctx)->base); - - return status; -} - -cairo_private void -_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface); - -cairo_private void -_cairo_gl_context_activate (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit); - -cairo_private cairo_bool_t -_cairo_gl_operator_is_supported (cairo_operator_t op); - -cairo_private cairo_status_t -_cairo_gl_composite_init (cairo_gl_composite_t *setup, - cairo_operator_t op, - cairo_gl_surface_t *dst, - cairo_bool_t has_component_alpha, - const cairo_rectangle_int_t *rect); - -cairo_private void -_cairo_gl_composite_fini (cairo_gl_composite_t *setup); - -cairo_private void -_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, - cairo_region_t *clip_region); - -cairo_private cairo_int_status_t -_cairo_gl_composite_set_source (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height); - -cairo_private cairo_int_status_t -_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height); - -cairo_private void -_cairo_gl_composite_set_mask_spans (cairo_gl_composite_t *setup); - -cairo_private cairo_status_t -_cairo_gl_composite_begin (cairo_gl_composite_t *setup, - cairo_gl_context_t **ctx); - -cairo_private void -_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, - GLfloat x1, - GLfloat y1, - GLfloat x2, - GLfloat y2, - uint8_t alpha); - -cairo_private void -_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx, - GLfloat x1, - GLfloat y1, - GLfloat x2, - GLfloat y2, - GLfloat glyph_x1, - GLfloat glyph_y1, - GLfloat glyph_x2, - GLfloat glyph_y2); - -cairo_private void -_cairo_gl_composite_flush (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit); - -cairo_private cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha); - -cairo_private void -_cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); - -cairo_private void -_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache); - -cairo_private cairo_int_status_t -_cairo_gl_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -static inline int -_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y) -{ - if (surface->fb) - return y; - else - return (surface->height - 1) - y; -} - -cairo_private cairo_status_t -_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx); - -static cairo_always_inline cairo_bool_t -_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx) -{ - return ctx->vb == NULL; -} - -cairo_private cairo_status_t -_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, - cairo_gl_operand_type_t source, - cairo_gl_operand_type_t mask, - cairo_gl_shader_in_t in, - cairo_gl_shader_t **shader); - -cairo_private void -_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, - const char *name, - float value); - -cairo_private void -_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, - const char *name, - float value0, float value1); - -cairo_private void -_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, - const char *name, - float value0, - float value1, - float value2); - -cairo_private void -_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, - const char *name, - float value0, float value1, - float value2, float value3); - -cairo_private void -_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, - const char *name, - cairo_matrix_t* m); - -cairo_private void -_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx, - const char *name, - GLuint tex_unit); - -cairo_private void -_cairo_gl_set_shader (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader); - -cairo_private void -_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); - -slim_hidden_proto (cairo_gl_surface_create); -slim_hidden_proto (cairo_gl_surface_create_for_texture); - -#endif /* CAIRO_GL_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-gl-shaders.c b/libs/cairo/cairo/src/cairo-gl-shaders.c deleted file mode 100644 index d0edffa88..000000000 --- a/libs/cairo/cairo/src/cairo-gl-shaders.c +++ /dev/null @@ -1,960 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-gl-private.h" -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" - -typedef struct cairo_gl_shader_impl { - void - (*compile_shader) (GLuint *shader, GLenum type, const char *text); - - void - (*link_shader) (GLuint *program, GLuint vert, GLuint frag); - - void - (*destroy_shader) (GLuint shader); - - void - (*destroy_program) (GLuint program); - - void - (*bind_float) (cairo_gl_shader_t *shader, - const char *name, - float value); - - void - (*bind_vec2) (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1); - - void - (*bind_vec3) (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2); - - void - (*bind_vec4) (cairo_gl_shader_t *shader, - const char *name, - float value0, float value1, - float value2, float value3); - - void - (*bind_matrix) (cairo_gl_shader_t *shader, - const char *name, - cairo_matrix_t* m); - - void - (*bind_texture) (cairo_gl_shader_t *shader, - const char *name, - cairo_gl_tex_t tex_unit); - - void - (*use) (cairo_gl_shader_t *shader); -} shader_impl_t; - -static cairo_status_t -_cairo_gl_shader_compile (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - const char *fragment_text); - -/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions - API. */ -static void -compile_shader_arb (GLuint *shader, GLenum type, const char *text) -{ - const char* strings[1] = { text }; - GLint gl_status; - - *shader = glCreateShaderObjectARB (type); - glShaderSourceARB (*shader, 1, strings, 0); - glCompileShaderARB (*shader); - glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetObjectParameterivARB (*shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetInfoLogARB (*shader, log_size, &chars, log); - printf ("OpenGL shader compilation failed. Shader:\n" - "%s\n" - "OpenGL compilation log:\n" - "%s\n", - text, log); - - free (log); - } else { - printf ("OpenGL shader compilation failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -link_shader_arb (GLuint *program, GLuint vert, GLuint frag) -{ - GLint gl_status; - - *program = glCreateProgramObjectARB (); - glAttachObjectARB (*program, vert); - glAttachObjectARB (*program, frag); - glLinkProgramARB (*program); - glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetObjectParameterivARB (*program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetInfoLogARB (*program, log_size, &chars, log); - printf ("OpenGL shader link failed:\n%s\n", log); - - free (log); - } else { - printf ("OpenGL shader link failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -destroy_shader_arb (GLuint shader) -{ - glDeleteObjectARB (shader); -} - -static void -destroy_program_arb (GLuint shader) -{ - glDeleteObjectARB (shader); -} - -static void -bind_float_arb (cairo_gl_shader_t *shader, - const char *name, - float value) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform1fARB (location, value); -} - -static void -bind_vec2_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform2fARB (location, value0, value1); -} - -static void -bind_vec3_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform3fARB (location, value0, value1, value2); -} - -static void -bind_vec4_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2, - float value3) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform4fARB (location, value0, value1, value2, value3); -} - -static void -bind_matrix_arb (cairo_gl_shader_t *shader, - const char *name, - cairo_matrix_t* m) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - float gl_m[9] = { - m->xx, m->xy, m->x0, - m->yx, m->yy, m->y0, - 0, 0, 1 - }; - assert (location != -1); - glUniformMatrix3fvARB (location, 1, GL_TRUE, gl_m); -} - -static void -bind_texture_arb (cairo_gl_shader_t *shader, - const char *name, - cairo_gl_tex_t tex_unit) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform1iARB (location, tex_unit); -} - -static void -use_program_arb (cairo_gl_shader_t *shader) -{ - if (shader) - glUseProgramObjectARB (shader->program); - else - glUseProgramObjectARB (0); -} - -/* OpenGL Core 2.0 API. */ -static void -compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text) -{ - const char* strings[1] = { text }; - GLint gl_status; - - *shader = glCreateShader (type); - glShaderSource (*shader, 1, strings, 0); - glCompileShader (*shader); - glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetShaderInfoLog (*shader, log_size, &chars, log); - printf ("OpenGL shader compilation failed. Shader:\n" - "%s\n" - "OpenGL compilation log:\n" - "%s\n", - text, log); - - free (log); - } else { - printf ("OpenGL shader compilation failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag) -{ - GLint gl_status; - - *program = glCreateProgram (); - glAttachShader (*program, vert); - glAttachShader (*program, frag); - glLinkProgram (*program); - glGetProgramiv (*program, GL_LINK_STATUS, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetProgramInfoLog (*program, log_size, &chars, log); - printf ("OpenGL shader link failed:\n%s\n", log); - - free (log); - } else { - printf ("OpenGL shader link failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -destroy_shader_core_2_0 (GLuint shader) -{ - glDeleteShader (shader); -} - -static void -destroy_program_core_2_0 (GLuint shader) -{ - glDeleteProgram (shader); -} - -static void -bind_float_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform1f (location, value); -} - -static void -bind_vec2_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform2f (location, value0, value1); -} - -static void -bind_vec3_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform3f (location, value0, value1, value2); -} - -static void -bind_vec4_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2, - float value3) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform4f (location, value0, value1, value2, value3); -} - -static void -bind_matrix_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_matrix_t* m) -{ - GLint location = glGetUniformLocation (shader->program, name); - float gl_m[16] = { - m->xx, m->xy, m->x0, - m->yx, m->yy, m->y0, - 0, 0, 1 - }; - assert (location != -1); - glUniformMatrix3fv (location, 1, GL_TRUE, gl_m); -} - -static void -bind_texture_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_gl_tex_t tex_unit) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform1i (location, tex_unit); -} - -static void -use_program_core_2_0 (cairo_gl_shader_t *shader) -{ - if (shader) - glUseProgram (shader->program); - else - glUseProgram (0); -} - -static const cairo_gl_shader_impl_t shader_impl_core_2_0 = { - compile_shader_core_2_0, - link_shader_core_2_0, - destroy_shader_core_2_0, - destroy_program_core_2_0, - bind_float_core_2_0, - bind_vec2_core_2_0, - bind_vec3_core_2_0, - bind_vec4_core_2_0, - bind_matrix_core_2_0, - bind_texture_core_2_0, - use_program_core_2_0, -}; - -static const cairo_gl_shader_impl_t shader_impl_arb = { - compile_shader_arb, - link_shader_arb, - destroy_shader_arb, - destroy_program_arb, - bind_float_arb, - bind_vec2_arb, - bind_vec3_arb, - bind_vec4_arb, - bind_matrix_arb, - bind_texture_arb, - use_program_arb, -}; - -typedef struct _cairo_shader_cache_entry { - cairo_cache_entry_t base; - - cairo_gl_operand_type_t src; - cairo_gl_operand_type_t mask; - cairo_gl_operand_type_t dest; - cairo_gl_shader_in_t in; - - cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ - cairo_gl_shader_t shader; -} cairo_shader_cache_entry_t; - -static cairo_bool_t -_cairo_gl_shader_cache_equal (const void *key_a, const void *key_b) -{ - const cairo_shader_cache_entry_t *a = key_a; - const cairo_shader_cache_entry_t *b = key_b; - - return a->src == b->src && - a->mask == b->mask && - a->dest == b->dest && - a->in == b->in; -} - -static unsigned long -_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) -{ - return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in); -} - -static void -_cairo_gl_shader_cache_destroy (void *data) -{ - cairo_shader_cache_entry_t *entry = data; - - _cairo_gl_shader_fini (entry->ctx, &entry->shader); - if (entry->ctx->current_shader == &entry->shader) - entry->ctx->current_shader = NULL; - free (entry); -} - -static void -_cairo_gl_shader_init (cairo_gl_shader_t *shader) -{ - shader->fragment_shader = 0; - shader->program = 0; -} - -cairo_status_t -_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) -{ - static const char *fill_fs_source = - "uniform vec4 color;\n" - "void main()\n" - "{\n" - " gl_FragColor = color;\n" - "}\n"; - cairo_status_t status; - - /* XXX multiple device support? */ - if (GLEW_VERSION_2_0) { - ctx->shader_impl = &shader_impl_core_2_0; - } else if (GLEW_ARB_shader_objects && - GLEW_ARB_fragment_shader && - GLEW_ARB_vertex_program) { - ctx->shader_impl = &shader_impl_arb; - } else { - ctx->shader_impl = NULL; - } - - memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); - - status = _cairo_cache_init (&ctx->shaders, - _cairo_gl_shader_cache_equal, - NULL, - _cairo_gl_shader_cache_destroy, - CAIRO_GL_MAX_SHADERS_PER_CONTEXT); - if (unlikely (status)) - return status; - - if (ctx->shader_impl != NULL) { - _cairo_gl_shader_init (&ctx->fill_rectangles_shader); - status = _cairo_gl_shader_compile (ctx, - &ctx->fill_rectangles_shader, - CAIRO_GL_VAR_NONE, - CAIRO_GL_VAR_NONE, - fill_fs_source); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) -{ - int i; - - for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) { - if (ctx->vertex_shaders[i]) - ctx->shader_impl->destroy_shader (ctx->vertex_shaders[i]); - } - - _cairo_cache_fini (&ctx->shaders); -} - -void -_cairo_gl_shader_fini (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader) -{ - if (shader->fragment_shader) - ctx->shader_impl->destroy_shader (shader->fragment_shader); - - if (shader->program) - ctx->shader_impl->destroy_program (shader->program); -} - -static const char *operand_names[] = { "source", "mask", "dest" }; - -static cairo_gl_var_type_t -cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type) -{ - switch (type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT: - return CAIRO_GL_VAR_NONE; - case CAIRO_GL_OPERAND_TEXTURE: - return CAIRO_GL_VAR_TEXCOORDS; - case CAIRO_GL_OPERAND_SPANS: - return CAIRO_GL_VAR_COVERAGE; - } -} - -static void -cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, - cairo_gl_var_type_t type, - cairo_gl_tex_t name) -{ - switch (type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_VAR_NONE: - break; - case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n", - operand_names[name]); - break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n", - operand_names[name]); - break; - } -} - -static void -cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, - cairo_gl_var_type_t type, - cairo_gl_tex_t name) -{ - switch (type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_VAR_NONE: - break; - case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - " %s_texcoords = gl_MultiTexCoord%d.xy;\n", - operand_names[name], name); - break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - " %s_coverage = gl_Color.a;\n", - operand_names[name]); - break; - } -} - -static cairo_status_t -cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - cairo_gl_var_type_t dest, - char **out) -{ - cairo_output_stream_t *stream = _cairo_memory_stream_create (); - unsigned char *source; - unsigned int length; - cairo_status_t status; - - cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); - - _cairo_output_stream_printf (stream, - "void main()\n" - "{\n" - " gl_Position = ftransform();\n"); - - cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); - - _cairo_output_stream_write (stream, - "}\n\0", 3); - - status = _cairo_memory_stream_destroy (stream, &source, &length); - if (unlikely (status)) - return status; - - *out = (char *) source; - return CAIRO_STATUS_SUCCESS; -} - -static void -cairo_gl_shader_emit_color (cairo_output_stream_t *stream, - GLuint tex_target, - cairo_gl_operand_type_t type, - cairo_gl_tex_t name) -{ - const char *namestr = operand_names[name]; - const char *rectstr = (tex_target == GL_TEXTURE_RECTANGLE_EXT ? "Rect" : ""); - - switch (type) { - case CAIRO_GL_OPERAND_COUNT: - default: - ASSERT_NOT_REACHED; - break; - case CAIRO_GL_OPERAND_NONE: - _cairo_output_stream_printf (stream, - "vec4 get_%s()\n" - "{\n" - " return vec4 (0, 0, 0, 1);\n" - "}\n", - namestr); - break; - case CAIRO_GL_OPERAND_CONSTANT: - _cairo_output_stream_printf (stream, - "uniform vec4 %s_constant;\n" - "vec4 get_%s()\n" - "{\n" - " return %s_constant;\n" - "}\n", - namestr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_TEXTURE: - _cairo_output_stream_printf (stream, - "uniform sampler2D%s %s_sampler;\n" - "varying vec2 %s_texcoords;\n" - "vec4 get_%s()\n" - "{\n" - " return texture2D%s(%s_sampler, %s_texcoords);\n" - "}\n", - rectstr, namestr, namestr, namestr, rectstr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - _cairo_output_stream_printf (stream, - "uniform sampler1D %s_sampler;\n" - "uniform mat3 %s_matrix;\n" - "uniform vec2 %s_segment;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" - " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n" - " return texture1D (%s_sampler, t);\n" - "}\n", - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT: - _cairo_output_stream_printf (stream, - "uniform sampler1D %s_sampler;\n" - "uniform mat3 %s_matrix;\n" - "uniform vec2 %s_circle_1;\n" - "uniform float %s_radius_0;\n" - "uniform float %s_radius_1;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" - " \n" - " float dr = %s_radius_1 - %s_radius_0;\n" - " float dot_circle_1 = dot (%s_circle_1, %s_circle_1);\n" - " float dot_pos_circle_1 = dot (pos, %s_circle_1);\n" - " \n" - " float A = dot_circle_1 - dr * dr;\n" - " float B = -2.0 * (dot_pos_circle_1 + %s_radius_0 * dr);\n" - " float C = dot (pos, pos) - %s_radius_0 * %s_radius_0;\n" - " float det = B * B - 4.0 * A * C;\n" - " det = max (det, 0.0);\n" - " \n" - " float sqrt_det = sqrt (det);\n" - " sqrt_det *= sign(A);\n" - " \n" - " float t = (-B + sqrt_det) / (2.0 * A);\n" - " return texture1D (%s_sampler, t);\n" - "}\n", - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, - namestr); - break; - case CAIRO_GL_OPERAND_SPANS: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n" - "vec4 get_%s()\n" - "{\n" - " return vec4(0, 0, 0, %s_coverage);\n" - "}\n", - namestr, namestr, namestr); - break; - } -} - -static cairo_status_t -cairo_gl_shader_get_fragment_source (GLuint tex_target, - cairo_gl_shader_in_t in, - cairo_gl_operand_type_t src, - cairo_gl_operand_type_t mask, - cairo_gl_operand_type_t dest, - char **out) -{ - cairo_output_stream_t *stream = _cairo_memory_stream_create (); - unsigned char *source; - unsigned int length; - cairo_status_t status; - - cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK); - - _cairo_output_stream_printf (stream, - "void main()\n" - "{\n"); - switch (in) { - case CAIRO_GL_SHADER_IN_COUNT: - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_SHADER_IN_NORMAL: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask().a;\n"); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask();\n"); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source().a * get_mask();\n"); - break; - } - - _cairo_output_stream_write (stream, - "}\n\0", 3); - - status = _cairo_memory_stream_destroy (stream, &source, &length); - if (unlikely (status)) - return status; - - *out = (char *) source; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_shader_compile (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - const char *fragment_text) -{ - unsigned int vertex_shader; - cairo_status_t status; - - if (ctx->shader_impl == NULL) - return CAIRO_STATUS_SUCCESS; - - assert (shader->program == 0); - - vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE); - if (ctx->vertex_shaders[vertex_shader] == 0) { - char *source; - - status = cairo_gl_shader_get_vertex_source (src, - mask, - CAIRO_GL_VAR_NONE, - &source); - if (unlikely (status)) - goto FAILURE; - - ctx->shader_impl->compile_shader (&ctx->vertex_shaders[vertex_shader], - GL_VERTEX_SHADER, - source); - free (source); - } - - ctx->shader_impl->compile_shader (&shader->fragment_shader, - GL_FRAGMENT_SHADER, - fragment_text); - - ctx->shader_impl->link_shader (&shader->program, - ctx->vertex_shaders[vertex_shader], - shader->fragment_shader); - - return CAIRO_STATUS_SUCCESS; - - FAILURE: - _cairo_gl_shader_fini (ctx, shader); - shader->fragment_shader = 0; - shader->program = 0; - - return status; -} - -void -_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, - const char *name, - float value) -{ - ctx->shader_impl->bind_float (ctx->current_shader, name, value); -} - -void -_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, - const char *name, - float value0, - float value1) -{ - ctx->shader_impl->bind_vec2 (ctx->current_shader, name, value0, value1); -} - -void -_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, - const char *name, - float value0, - float value1, - float value2) -{ - ctx->shader_impl->bind_vec3 (ctx->current_shader, name, value0, value1, value2); -} - -void -_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, - const char *name, - float value0, float value1, - float value2, float value3) -{ - ctx->shader_impl->bind_vec4 (ctx->current_shader, name, value0, value1, value2, value3); -} - -void -_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, - const char *name, cairo_matrix_t* m) -{ - ctx->shader_impl->bind_matrix (ctx->current_shader, name, m); -} - -void -_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx, - const char *name, GLuint tex_unit) -{ - ctx->shader_impl->bind_texture (ctx->current_shader, name, tex_unit); -} - -void -_cairo_gl_set_shader (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader) -{ - if (ctx->shader_impl == NULL) - return; - - if (ctx->current_shader == shader) - return; - - ctx->shader_impl->use (shader); - - ctx->current_shader = shader; -} - -cairo_status_t -_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, - cairo_gl_operand_type_t source, - cairo_gl_operand_type_t mask, - cairo_gl_shader_in_t in, - cairo_gl_shader_t **shader) -{ - cairo_shader_cache_entry_t lookup, *entry; - char *fs_source; - cairo_status_t status; - - if (ctx->shader_impl == NULL) { - *shader = NULL; - return CAIRO_STATUS_SUCCESS; - } - - lookup.src = source; - lookup.mask = mask; - lookup.dest = CAIRO_GL_OPERAND_NONE; - lookup.in = in; - lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); - lookup.base.size = 1; - - entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); - if (entry) { - assert (entry->shader.program); - *shader = &entry->shader; - return CAIRO_STATUS_SUCCESS; - } - - status = cairo_gl_shader_get_fragment_source (ctx->tex_target, - in, - source, - mask, - CAIRO_GL_OPERAND_NONE, - &fs_source); - if (unlikely (status)) - return status; - - entry = malloc (sizeof (cairo_shader_cache_entry_t)); - if (unlikely (entry == NULL)) { - free (fs_source); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); - - entry->ctx = ctx; - _cairo_gl_shader_init (&entry->shader); - status = _cairo_gl_shader_compile (ctx, - &entry->shader, - cairo_gl_operand_get_var_type (source), - cairo_gl_operand_get_var_type (mask), - fs_source); - free (fs_source); - - if (unlikely (status)) { - free (entry); - return status; - } - - status = _cairo_cache_insert (&ctx->shaders, &entry->base); - if (unlikely (status)) { - _cairo_gl_shader_fini (ctx, &entry->shader); - free (entry); - return status; - } - - *shader = &entry->shader; - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-gl-surface.c b/libs/cairo/cairo/src/cairo-gl-surface.c deleted file mode 100644 index 458300050..000000000 --- a/libs/cairo/cairo/src/cairo-gl-surface.c +++ /dev/null @@ -1,1601 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-gl-private.h" - -static cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - -static cairo_int_status_t -_cairo_gl_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface); - -static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) -{ - return surface->backend == &_cairo_gl_surface_backend; -} - -cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha) -{ - *has_alpha = TRUE; - - switch (pixman_format) { - case PIXMAN_a8r8g8b8: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - return TRUE; - case PIXMAN_x8r8g8b8: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a8b8g8r8: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - return TRUE; - case PIXMAN_x8b8g8r8: - *internal_format = GL_RGB; - *format = GL_RGBA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_b8g8r8a8: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8; - return TRUE; - case PIXMAN_b8g8r8x8: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_r8g8b8: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_BYTE; - return TRUE; - case PIXMAN_b8g8r8: - *internal_format = GL_RGB; - *format = GL_BGR; - *type = GL_UNSIGNED_BYTE; - return TRUE; - case PIXMAN_r5g6b5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5; - return TRUE; - case PIXMAN_b5g6r5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5_REV; - return TRUE; - case PIXMAN_a1r5g5b5: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - return TRUE; - case PIXMAN_x1r5g5b5: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a1b5g5r5: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - return TRUE; - case PIXMAN_x1b5g5r5: - *internal_format = GL_RGB; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a8: - *internal_format = GL_ALPHA; - *format = GL_ALPHA; - *type = GL_UNSIGNED_BYTE; - return TRUE; - - case PIXMAN_a2b10g10r10: - case PIXMAN_x2b10g10r10: - case PIXMAN_a4r4g4b4: - case PIXMAN_x4r4g4b4: - case PIXMAN_a4b4g4r4: - case PIXMAN_x4b4g4r4: - case PIXMAN_r3g3b2: - case PIXMAN_b2g3r3: - case PIXMAN_a2r2g2b2: - case PIXMAN_a2b2g2r2: - case PIXMAN_c8: - case PIXMAN_x4a4: - /* case PIXMAN_x4c4: */ - case PIXMAN_x4g4: - case PIXMAN_a4: - case PIXMAN_r1g2b1: - case PIXMAN_b1g2r1: - case PIXMAN_a1r1g1b1: - case PIXMAN_a1b1g1r1: - case PIXMAN_c4: - case PIXMAN_g4: - case PIXMAN_a1: - case PIXMAN_g1: - case PIXMAN_yuy2: - case PIXMAN_yv12: - case PIXMAN_x2r10g10b10: - case PIXMAN_a2r10g10b10: - default: - return FALSE; - } -} - -cairo_bool_t -_cairo_gl_operator_is_supported (cairo_operator_t op) -{ - return op < CAIRO_OPERATOR_SATURATE; -} - -void -_cairo_gl_surface_init (cairo_device_t *device, - cairo_gl_surface_t *surface, - cairo_content_t content, - int width, int height) -{ - _cairo_surface_init (&surface->base, - &_cairo_gl_surface_backend, - device, - content); - - surface->width = width; - surface->height = height; -} - -static cairo_surface_t * -_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, - cairo_content_t content, - GLuint tex, - int width, - int height) -{ - cairo_gl_surface_t *surface; - - assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size); - - surface = calloc (1, sizeof (cairo_gl_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (&ctx->base, surface, content, width, height); - surface->tex = tex; - - /* Create the texture used to store the surface's data. */ - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (ctx->tex_target, surface->tex); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - return &surface->base; -} - -static cairo_surface_t * -_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height) -{ - cairo_gl_surface_t *surface; - GLenum format; - GLuint tex; - - glGenTextures (1, &tex); - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch_for_texture (ctx, content, - tex, width, height); - if (unlikely (surface->base.status)) - return &surface->base; - - surface->owns_tex = TRUE; - - /* adjust the texture size after setting our real extents */ - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - switch (content) { - default: - ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: - format = GL_RGBA; - break; - case CAIRO_CONTENT_ALPHA: - /* We want to be trying GL_ALPHA framebuffer objects here. */ - format = GL_RGBA; - break; - case CAIRO_CONTENT_COLOR: - /* GL_RGB is almost what we want here -- sampling 1 alpha when - * texturing, using 1 as destination alpha factor in blending, - * etc. However, when filtering with GL_CLAMP_TO_BORDER, the - * alpha channel of the border color will also be clamped to - * 1, when we actually want the border color we explicitly - * specified. So, we have to store RGBA, and fill the alpha - * channel with 1 when blending. - */ - format = GL_RGBA; - break; - } - - glTexImage2D (ctx->tex_target, 0, format, width, height, 0, - format, GL_UNSIGNED_BYTE, NULL); - - return &surface->base; -} - -static cairo_status_t -_cairo_gl_surface_clear (cairo_gl_surface_t *surface, - const cairo_color_t *color) -{ - cairo_gl_context_t *ctx; - cairo_status_t status; - double r, g, b, a; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_context_set_destination (ctx, surface); - if (surface->base.content & CAIRO_CONTENT_COLOR) { - r = color->red * color->alpha; - g = color->green * color->alpha; - b = color->blue * color->alpha; - } else { - r = g = b = 0; - } - if (surface->base.content & CAIRO_CONTENT_ALPHA) { - a = color->alpha; - } else { - a = 1.0; - } - - glDisable (GL_SCISSOR_TEST); - glClearColor (r, g, b, a); - glClear (GL_COLOR_BUFFER_BIT); - - return _cairo_gl_context_release (ctx, status); -} - -cairo_surface_t * -cairo_gl_surface_create (cairo_device_t *abstract_device, - cairo_content_t content, - int width, - int height) -{ - cairo_gl_context_t *ctx; - cairo_gl_surface_t *surface; - cairo_status_t status; - - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - if (abstract_device == NULL) { - return cairo_image_surface_create (_cairo_format_from_content (content), - width, height); - } - - if (abstract_device->status) - return _cairo_surface_create_in_error (abstract_device->status); - - if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - status = _cairo_gl_context_acquire (abstract_device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch (ctx, content, width, height); - if (unlikely (surface->base.status)) { - status = _cairo_gl_context_release (ctx, surface->base.status); - cairo_surface_destroy (&surface->base); - return _cairo_surface_create_in_error (status); - } - - /* Cairo surfaces start out initialized to transparent (black) */ - status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&surface->base); - return _cairo_surface_create_in_error (status); - } - - return &surface->base; -} -slim_hidden_def (cairo_gl_surface_create); - - -/** - * cairo_gl_surface_create_for_texture: - * @content: type of content in the surface - * @tex: name of texture to use for storage of surface pixels - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a GL surface for the specified texture with the specified - * content and dimensions. The texture must be kept around until the - * #cairo_surface_t is destroyed or cairo_surface_finish() is called - * on the surface. The initial contents of @tex will be used as the - * initial image contents; you must explicitly clear the buffer, - * using, for example, cairo_rectangle() and cairo_fill() if you want - * it cleared. The format of @tex should be compatible with @content, - * in the sense that it must have the color components required by - * @content. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - **/ -cairo_surface_t * -cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, - cairo_content_t content, - unsigned int tex, - int width, - int height) -{ - cairo_gl_context_t *ctx; - cairo_gl_surface_t *surface; - cairo_status_t status; - - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - if (abstract_device == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - - if (abstract_device->status) - return _cairo_surface_create_in_error (abstract_device->status); - - if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - status = _cairo_gl_context_acquire (abstract_device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch_for_texture (ctx, content, - tex, width, height); - status = _cairo_gl_context_release (ctx, status); - - return &surface->base; -} -slim_hidden_def (cairo_gl_surface_create_for_texture); - - -void -cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, - int width, - int height) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_gl (abstract_surface) || - ! _cairo_gl_surface_is_texture (surface)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - surface->width = width; - surface->height = height; -} - -int -cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (! _cairo_surface_is_gl (abstract_surface)) - return 0; - - return surface->width; -} - -int -cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (! _cairo_surface_is_gl (abstract_surface)) - return 0; - - return surface->height; -} - -void -cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_gl (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return; - } - - if (! _cairo_gl_surface_is_texture (surface)) { - cairo_gl_context_t *ctx; - cairo_status_t status; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return; - - cairo_surface_flush (abstract_surface); - - ctx->swap_buffers (ctx, surface); - - status = _cairo_gl_context_release (ctx, status); - if (status) - status = _cairo_surface_set_error (abstract_surface, status); - } -} - -static cairo_surface_t * -_cairo_gl_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *surface = abstract_surface; - cairo_gl_context_t *ctx; - cairo_status_t status; - - if (width < 1 || height < 1) - return cairo_image_surface_create (_cairo_format_from_content (content), - width, height); - - status = _cairo_gl_context_acquire (surface->device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - if (width > ctx->max_framebuffer_size || - height > ctx->max_framebuffer_size) - { - surface = NULL; - goto RELEASE; - } - - surface = _cairo_gl_surface_create_scratch (ctx, content, width, height); - -RELEASE: - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (status); - } - - return surface; -} - -cairo_status_t -_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, - cairo_image_surface_t *src, - int src_x, int src_y, - int width, int height, - int dst_x, int dst_y) -{ - GLenum internal_format, format, type; - cairo_bool_t has_alpha; - cairo_image_surface_t *clone = NULL; - cairo_gl_context_t *ctx; - int cpp; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (! _cairo_gl_get_image_format_and_type (src->pixman_format, - &internal_format, - &format, - &type, - &has_alpha)) - { - cairo_bool_t is_supported; - - clone = _cairo_image_surface_coerce (src); - if (unlikely (clone->base.status)) - return clone->base.status; - - is_supported = - _cairo_gl_get_image_format_and_type (clone->pixman_format, - &internal_format, - &format, - &type, - &has_alpha); - assert (is_supported); - src = clone; - } - - cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - status = _cairo_gl_surface_flush (&dst->base); - if (unlikely (status)) - goto FAIL; - - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); - if (_cairo_gl_surface_is_texture (dst)) { - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (ctx->tex_target, dst->tex); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexSubImage2D (ctx->tex_target, 0, - dst_x, dst_y, width, height, - format, type, - src->data + src_y * src->stride + src_x * cpp); - - /* If we just treated some rgb-only data as rgba, then we have to - * go back and fix up the alpha channel where we filled in this - * texture data. - */ - if (!has_alpha) { - cairo_rectangle_int_t rect; - - rect.x = dst_x; - rect.y = dst_y; - rect.width = width; - rect.height = height; - - _cairo_gl_composite_flush (ctx); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - _cairo_gl_surface_fill_rectangles (dst, - CAIRO_OPERATOR_SOURCE, - CAIRO_COLOR_BLACK, - &rect, 1); - _cairo_gl_composite_flush (ctx); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - } else { - cairo_surface_t *tmp; - - tmp = _cairo_gl_surface_create_scratch (ctx, - dst->base.content, - width, height); - if (unlikely (tmp->status)) { - cairo_surface_destroy (tmp); - goto FAIL; - } - status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, - src, - src_x, src_y, - width, height, - 0, 0); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_pattern_t tmp_pattern; - - _cairo_pattern_init_for_surface (&tmp_pattern, tmp); - _cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE, - &tmp_pattern.base, - NULL, - dst, - 0, 0, - 0, 0, - dst_x, dst_y, - width, height, - NULL); - _cairo_pattern_fini (&tmp_pattern.base); - } - - cairo_surface_destroy (tmp); - } - -FAIL: - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - - status = _cairo_gl_context_release (ctx, status); - - if (clone) - cairo_surface_destroy (&clone->base); - - return status; -} - -static cairo_status_t -_cairo_gl_surface_get_image (cairo_gl_surface_t *surface, - cairo_rectangle_int_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *rect_out) -{ - cairo_image_surface_t *image; - cairo_gl_context_t *ctx; - GLenum format, type; - cairo_format_t cairo_format; - unsigned int cpp; - cairo_status_t status; - - /* Want to use a switch statement here but the compiler gets whiny. */ - if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { - format = GL_BGRA; - cairo_format = CAIRO_FORMAT_ARGB32; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_COLOR) { - format = GL_BGRA; - cairo_format = CAIRO_FORMAT_RGB24; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { - format = GL_ALPHA; - cairo_format = CAIRO_FORMAT_A8; - type = GL_UNSIGNED_BYTE; - cpp = 1; - } else { - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - image = (cairo_image_surface_t*) - cairo_image_surface_create (cairo_format, - interest->width, interest->height); - if (unlikely (image->base.status)) - return image->base.status; - - /* This is inefficient, as we'd rather just read the thing without making - * it the destination. But then, this is the fallback path, so let's not - * fall back instead. - */ - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_composite_flush (ctx); - _cairo_gl_context_set_destination (ctx, surface); - - glPixelStorei (GL_PACK_ALIGNMENT, 1); - glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); - if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 1); - glReadPixels (interest->x, interest->y, - interest->width, interest->height, - format, type, image->data); - if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 0); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&image->base); - return status; - } - - *image_out = image; - if (rect_out != NULL) - *rect_out = *interest; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_surface_finish (void *abstract_surface) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_gl_context_t *ctx; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); - if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); - if (ctx->current_target == surface) - ctx->current_target = NULL; - - if (surface->depth) - glDeleteFramebuffersEXT (1, &surface->depth); - if (surface->fb) - glDeleteFramebuffersEXT (1, &surface->fb); - if (surface->owns_tex) - glDeleteTextures (1, &surface->tex); - - return _cairo_gl_context_release (ctx, status); -} - -static cairo_status_t -_cairo_gl_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_rectangle_int_t extents; - - *image_extra = NULL; - - extents.x = extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; - return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL); -} - -static void -_cairo_gl_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_gl_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_gl_surface_t *surface = abstract_surface; - - *image_extra = NULL; - return _cairo_gl_surface_get_image (surface, interest_rect, image_out, - image_rect_out); -} - -static void -_cairo_gl_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_status_t status; - - status = _cairo_gl_surface_draw_image (abstract_surface, image, - 0, 0, - image->width, image->height, - image_rect->x, image_rect->y); - /* as we created the image, its format should be directly applicable */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_gl_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_gl_surface_t *surface = abstract_surface; - - /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ - if (src->device == surface->base.device && - _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_gl_surface_t *clone; - cairo_status_t status; - - clone = (cairo_gl_surface_t *) - _cairo_gl_surface_create_similar (&surface->base, - src->content, - width, height); - if (clone == NULL) - return UNSUPPORTED ("create_similar failed"); - if (clone->base.status) - return clone->base.status; - - status = _cairo_gl_surface_draw_image (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (status) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_out = &clone->base; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - - return CAIRO_STATUS_SUCCESS; - } - - return UNSUPPORTED ("unknown src surface type in clone_similar"); -} - -/** Creates a cairo-gl pattern surface for the given trapezoids */ -static cairo_status_t -_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - int width, int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_antialias_t antialias, - cairo_surface_pattern_t *pattern) -{ - pixman_format_code_t pixman_format; - pixman_image_t *image; - cairo_surface_t *surface; - int i; - - pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, - image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); - if (unlikely (image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < num_traps; i++) { - pixman_trapezoid_t trap; - - trap.top = _cairo_fixed_to_16_16 (traps[i].top); - trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - - trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - - trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - } - - surface = _cairo_image_surface_create_for_pixman_image (image, - pixman_format); - if (unlikely (surface->status)) { - pixman_image_unref (image); - return surface->status; - } - - _cairo_pattern_init_for_surface (pattern, surface); - cairo_surface_destroy (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_gl_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_context_t *ctx; - cairo_status_t status; - cairo_gl_composite_t setup; - cairo_rectangle_int_t rect = { dst_x, dst_y, width, height }; - int dx, dy; - - if (op == CAIRO_OPERATOR_SOURCE && - mask == NULL && - src->type == CAIRO_PATTERN_TYPE_SURFACE && - _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) && - _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) { - cairo_image_surface_t *image = (cairo_image_surface_t *) - ((cairo_surface_pattern_t *) src)->surface; - dx += src_x; - dy += src_y; - if (dx >= 0 && - dy >= 0 && - dx + width <= (unsigned int) image->width && - dy + height <= (unsigned int) image->height) { - status = _cairo_gl_surface_draw_image (dst, image, - dx, dy, - width, height, - dst_x, dst_y); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - } - - status = _cairo_gl_composite_init (&setup, op, dst, - mask && mask->has_component_alpha, - &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_source (&setup, src, - src_x, src_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, mask, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - if (clip_region != NULL) { - int i, num_rectangles; - - num_rectangles = cairo_region_num_rectangles (clip_region); - - for (i = 0; i < num_rectangles; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, i, &rect); - _cairo_gl_composite_emit_rect (ctx, - rect.x, rect.y, - rect.x + rect.width, rect.y + rect.height, - 0); - } - } else { - _cairo_gl_composite_emit_rect (ctx, - dst_x, dst_y, - dst_x + width, dst_y + height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_surface_pattern_t traps_pattern; - cairo_int_status_t status; - - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - - status = _cairo_gl_get_traps_pattern (dst, - dst_x, dst_y, width, height, - traps, num_traps, antialias, - &traps_pattern); - if (unlikely (status)) - return status; - - status = _cairo_gl_surface_composite (op, - pattern, &traps_pattern.base, dst, - src_x, src_y, - 0, 0, - dst_x, dst_y, - width, height, - clip_region); - - _cairo_pattern_fini (&traps_pattern.base); - - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_solid_pattern_t solid; - cairo_gl_context_t *ctx; - cairo_status_t status; - cairo_gl_composite_t setup; - int i; - - status = _cairo_gl_composite_init (&setup, op, dst, - FALSE, - /* XXX */ NULL); - if (unlikely (status)) - goto CLEANUP; - - _cairo_pattern_init_solid (&solid, color); - status = _cairo_gl_composite_set_source (&setup, &solid.base, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, NULL, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - for (i = 0; i < num_rects; i++) { - _cairo_gl_composite_emit_rect (ctx, - rects[i].x, - rects[i].y, - rects[i].x + rects[i].width, - rects[i].y + rects[i].height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -typedef struct _cairo_gl_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_gl_composite_t setup; - - int xmin, xmax; - int ymin, ymax; - - cairo_gl_context_t *ctx; -} cairo_gl_surface_span_renderer_t; - -static cairo_status_t -_cairo_gl_render_bounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - } - - spans++; - } while (--num_spans > 1); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_render_unbounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (y > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, y, - 0); - } - - if (num_spans == 0) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - renderer->xmax, y + height, - 0); - } else { - if (spans[0].x != renderer->xmin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != renderer->xmax) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - renderer->xmax, y + height, - 0); - } - } - - renderer->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_unbounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (renderer->ymax > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, renderer->ymax, - 0); - } - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static cairo_status_t -_cairo_gl_finish_bounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static void -_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (!renderer) - return; - - _cairo_gl_composite_fini (&renderer->setup); - - free (renderer); -} - -static cairo_bool_t -_cairo_gl_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias) -{ - if (! _cairo_gl_operator_is_supported (op)) - return FALSE; - - return TRUE; - - (void) pattern; - (void) abstract_dst; - (void) antialias; -} - -static cairo_span_renderer_t * -_cairo_gl_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *src, - void *abstract_dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_surface_span_renderer_t *renderer; - cairo_status_t status; - const cairo_rectangle_int_t *extents; - - renderer = calloc (1, sizeof (*renderer)); - if (unlikely (renderer == NULL)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - - renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; - if (rects->is_bounded) { - renderer->base.render_rows = _cairo_gl_render_bounded_spans; - renderer->base.finish = _cairo_gl_finish_bounded_spans; - extents = &rects->bounded; - } else { - renderer->base.render_rows = _cairo_gl_render_unbounded_spans; - renderer->base.finish = _cairo_gl_finish_unbounded_spans; - extents = &rects->unbounded; - } - renderer->xmin = extents->x; - renderer->xmax = extents->x + extents->width; - renderer->ymin = extents->y; - renderer->ymax = extents->y + extents->height; - - status = _cairo_gl_composite_init (&renderer->setup, - op, dst, - FALSE, extents); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_set_source (&renderer->setup, src, - extents->x, extents->y, - extents->x, extents->y, - extents->width, extents->height); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_mask_spans (&renderer->setup); - _cairo_gl_composite_set_clip_region (&renderer->setup, clip_region); - - status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx); - if (unlikely (status)) - goto FAIL; - - return &renderer->base; - -FAIL: - _cairo_gl_composite_fini (&renderer->setup); - free (renderer); - return _cairo_span_renderer_create_in_error (status); -} - -static cairo_bool_t -_cairo_gl_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_gl_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static void -_cairo_gl_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); -} - -static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_gl_context_t *ctx; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) || - (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) || - (ctx->current_target == surface)) - _cairo_gl_composite_flush (ctx); - - return _cairo_gl_context_release (ctx, status); -} - -static cairo_int_status_t -_cairo_gl_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - /* simplify the common case of clearing the surface */ - if (clip == NULL) { - if (op == CAIRO_OPERATOR_CLEAR) - return _cairo_gl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT); - else if (source->type == CAIRO_PATTERN_TYPE_SOLID && - (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) { - return _cairo_gl_surface_clear (abstract_surface, - &((cairo_solid_pattern_t *) source)->color); - } - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return UNSUPPORTED ("a clip surface would be required"); - } - - if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) - return UNSUPPORTED ("no span renderer"); - - if (op == CAIRO_OPERATOR_SOURCE) - return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); - if (op == CAIRO_OPERATOR_CLEAR) { - op = CAIRO_OPERATOR_DEST_OUT; - src = &_cairo_pattern_white.base; - } - - status = _cairo_surface_composite_polygon (&dst->base, - op, - src, - fill_rule, - antialias, - extents, - polygon, - clip_region); - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_polygon_t polygon; - cairo_status_t status; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - surface->width, - surface->height, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_to_polygon (path, - style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_polygon_t polygon; - cairo_status_t status; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - surface->width, - surface->height, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - -#if 0 - if (extents.is_bounded && clip != NULL) { - cairo_clip_path_t *clip_path; - - if (((clip_path = _clip_get_single_path (clip)) != NULL) && - _cairo_path_fixed_equal (&clip_path->path, path)) - { - clip = NULL; - } - } -#endif - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -const cairo_surface_backend_t _cairo_gl_surface_backend = { - CAIRO_SURFACE_TYPE_GL, - _cairo_gl_surface_create_similar, - _cairo_gl_surface_finish, - - _cairo_gl_surface_acquire_source_image, - _cairo_gl_surface_release_source_image, - _cairo_gl_surface_acquire_dest_image, - _cairo_gl_surface_release_dest_image, - - _cairo_gl_surface_clone_similar, - _cairo_gl_surface_composite, - _cairo_gl_surface_fill_rectangles, - _cairo_gl_surface_composite_trapezoids, - _cairo_gl_surface_create_span_renderer, - _cairo_gl_surface_check_span_renderer, - - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_gl_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_gl_surface_get_font_options, - _cairo_gl_surface_flush, - NULL, /* mark_dirty_rectangle */ - _cairo_gl_surface_scaled_font_fini, - _cairo_gl_surface_scaled_glyph_fini, - _cairo_gl_surface_paint, - NULL, /* mask */ - _cairo_gl_surface_stroke, - _cairo_gl_surface_fill, - _cairo_gl_surface_show_glyphs, /* show_glyphs */ - NULL /* snapshot */ -}; diff --git a/libs/cairo/cairo/src/cairo-gl.h b/libs/cairo/cairo/src/cairo-gl.h deleted file mode 100644 index 17c5b03f1..000000000 --- a/libs/cairo/cairo/src/cairo-gl.h +++ /dev/null @@ -1,90 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_GL_H -#define CAIRO_GL_H - -#include "cairo.h" - -#if CAIRO_HAS_GL_SURFACE - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_gl_surface_create (cairo_device_t *device, - cairo_content_t content, - int width, int height); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, - cairo_content_t content, - unsigned int tex, - int width, int height); -cairo_public void -cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); - -cairo_public int -cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); - -cairo_public int -cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); - -cairo_public void -cairo_gl_surface_swapbuffers (cairo_surface_t *surface); - -#if CAIRO_HAS_GLX_FUNCTIONS -#include - -cairo_public cairo_device_t * -cairo_glx_device_create (Display *dpy, GLXContext gl_ctx); - -cairo_public Display * -cairo_glx_device_get_display (cairo_device_t *device); - -cairo_public GLXContext -cairo_glx_device_get_context (cairo_device_t *device); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_window (cairo_device_t *device, - Window win, - int width, int height); -#endif - -#if CAIRO_HAS_WGL_FUNCTIONS -#include - -cairo_public cairo_device_t * -cairo_wgl_device_create (HGLRC rc); - -cairo_public HGLRC -cairo_wgl_device_get_context (cairo_device_t *device); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_dc (cairo_device_t *device, - HDC dc, - int width, - int height); -#endif - -#if CAIRO_HAS_EGL_FUNCTIONS -#include - -cairo_public cairo_device_t * -cairo_egl_device_create (EGLDisplay dpy, EGLContext egl); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_egl (cairo_device_t *device, - EGLSurface egl, - int width, - int height); - -#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/libs/cairo/cairo/src/cairo-glitz-private.h b/libs/cairo/cairo/src/cairo-glitz-private.h deleted file mode 100644 index 144eff4a5..000000000 --- a/libs/cairo/cairo/src/cairo-glitz-private.h +++ /dev/null @@ -1,13 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_GLITZ_PRIVATE_H -#define CAIRO_GLITZ_PRIVATE_H - -#include "cairoint.h" -#include "cairo-glitz.h" - -slim_hidden_proto (cairo_glitz_surface_create); - -#endif /* CAIRO_GLITZ_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-glitz-surface.c b/libs/cairo/cairo/src/cairo-glitz-surface.c deleted file mode 100644 index bd176d056..000000000 --- a/libs/cairo/cairo/src/cairo-glitz-surface.c +++ /dev/null @@ -1,2424 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-glitz.h" -#include "cairo-glitz-private.h" -#include "cairo-region-private.h" - -typedef struct _cairo_glitz_surface { - cairo_surface_t base; - - glitz_surface_t *surface; - glitz_format_t *format; - - cairo_region_t *clip_region; - cairo_bool_t has_clip; - glitz_box_t *clip_boxes; - int num_clip_boxes; -} cairo_glitz_surface_t; - -static const cairo_surface_backend_t * -_cairo_glitz_surface_get_backend (void); - -static cairo_status_t -_cairo_glitz_surface_finish (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (surface->clip_boxes) - free (surface->clip_boxes); - - cairo_region_destroy (surface->clip_region); - glitz_surface_destroy (surface->surface); - - return CAIRO_STATUS_SUCCESS; -} - -static glitz_format_name_t -_glitz_format_from_content (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_COLOR: - return GLITZ_STANDARD_RGB24; - case CAIRO_CONTENT_ALPHA: - return GLITZ_STANDARD_A8; - case CAIRO_CONTENT_COLOR_ALPHA: - return GLITZ_STANDARD_ARGB32; - } - - ASSERT_NOT_REACHED; - return GLITZ_STANDARD_ARGB32; -} - -static cairo_surface_t * -_cairo_glitz_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_glitz_surface_t *src = abstract_src; - cairo_surface_t *crsurface; - glitz_drawable_t *drawable; - glitz_surface_t *surface; - glitz_format_t *gformat; - - drawable = glitz_surface_get_drawable (src->surface); - - gformat = - glitz_find_standard_format (drawable, - _glitz_format_from_content (content)); - if (!gformat) - return NULL; - - surface = glitz_surface_create (drawable, gformat, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height, - 0, NULL); - - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - crsurface = cairo_glitz_surface_create (surface); - - glitz_surface_destroy (surface); - - return crsurface; -} - -static cairo_status_t -_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, - cairo_rectangle_int_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *rect_out) -{ - cairo_image_surface_t *image; - cairo_rectangle_int_t extents; - cairo_format_t format; - cairo_format_masks_t masks; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - - extents.x = 0; - extents.y = 0; - extents.width = glitz_surface_get_width (surface->surface); - extents.height = glitz_surface_get_height (surface->surface); - - if (interest != NULL) { - if (! _cairo_rectangle_intersect (&extents, interest)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - if (rect_out != NULL) - *rect_out = extents; - - if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { - if (surface->format->color.red_size > 0) { - if (surface->format->color.alpha_size > 0) - format = CAIRO_FORMAT_ARGB32; - else - format = CAIRO_FORMAT_RGB24; - } else { - format = CAIRO_FORMAT_A8; - } - } else - format = CAIRO_FORMAT_ARGB32; - - image = (cairo_image_surface_t*) - cairo_image_surface_create (format, extents.width, extents.height); - if (image->base.status) - return image->base.status; - - _pixman_format_to_masks (image->pixman_format, &masks); - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - pf.xoffset = 0; - pf.skip_lines = 0; - - /* XXX: we should eventually return images with negative stride, - need to verify that libpixman have no problem with this first. */ - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - - buffer = glitz_buffer_create_for_data (image->data); - if (buffer == NULL) { - cairo_surface_destroy (&image->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* clear out the glitz clip; the clip affects glitz_get_pixels */ - if (surface->has_clip) - glitz_surface_set_clip_region (surface->surface, - 0, 0, NULL, 0); - - glitz_get_pixels (surface->surface, - extents.x, extents.y, - extents.width, extents.height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - /* restore the clip, if any */ - if (surface->has_clip) { - glitz_surface_set_clip_region (surface->surface, - 0, 0, - surface->clip_boxes, - surface->num_clip_boxes); - } - - *image_out = image; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int x_dst, - int y_dst) -{ - cairo_glitz_surface_t *surface = abstract_surface; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - cairo_format_masks_t masks; - char *data; - - _pixman_format_to_masks (image->pixman_format, &masks); - - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - pf.xoffset = src_x; - pf.skip_lines = src_y; - - /* check for negative stride */ - if (image->stride < 0) { - pf.bytes_per_line = -image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; - data = (char *) image->data + image->stride * (image->height - 1); - } else { - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - data = (char *) image->data; - } - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - glitz_set_pixels (surface->surface, - x_dst, y_dst, - width, height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - *image_extra = NULL; - - return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); -} - -static cairo_surface_t * -_cairo_glitz_surface_snapshot (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_image_surface_t *image; - - status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - return &image->base; -} - -static void -_cairo_glitz_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_glitz_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, - image_rect_out); - if (status) - return status; - - *image_out = image; - *image_extra = NULL; - - return status; -} - -static void -_cairo_glitz_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_glitz_surface_set_image (surface, image, 0, 0, - image->width, image->height, - image_rect->x, image_rect->y); - if (status) - status = _cairo_surface_set_error (&surface->base, status); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_glitz_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_glitz_surface_t *clone; - cairo_status_t status; - - if (surface->base.status) - return surface->base.status; - - if (src->backend == surface->base.backend) - { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - else if (_cairo_surface_is_image (src)) - { - cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - - clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (surface, src->content, - width, height); - if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (clone->base.status) - return clone->base.status; - - status = _cairo_glitz_surface_set_image (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (status) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_out = &clone->base; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, - cairo_matrix_t *matrix) -{ - glitz_transform_t transform; - - transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); - transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); - transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); - - transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); - transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); - transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); - - transform.matrix[2][0] = 0; - transform.matrix[2][1] = 0; - transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1); - - glitz_surface_set_transform (surface->surface, &transform); -} - -static cairo_bool_t -_is_supported_operator (cairo_operator_t op) -{ - /* This is really just a if (op < SATURATE), but we use a switch - * so the compiler will warn if we ever add more operators. - */ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - return TRUE; - - default: - ASSERT_NOT_REACHED; - case CAIRO_OPERATOR_SATURATE: - /* nobody likes saturate, expect that it's required to do - * seamless polygons! - */ - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return FALSE; - } -} - -static glitz_operator_t -_glitz_operator (cairo_operator_t op) -{ - switch ((int) op) { - case CAIRO_OPERATOR_CLEAR: - return GLITZ_OPERATOR_CLEAR; - - case CAIRO_OPERATOR_SOURCE: - return GLITZ_OPERATOR_SRC; - case CAIRO_OPERATOR_OVER: - return GLITZ_OPERATOR_OVER; - case CAIRO_OPERATOR_IN: - return GLITZ_OPERATOR_IN; - case CAIRO_OPERATOR_OUT: - return GLITZ_OPERATOR_OUT; - case CAIRO_OPERATOR_ATOP: - return GLITZ_OPERATOR_ATOP; - - case CAIRO_OPERATOR_DEST: - return GLITZ_OPERATOR_DST; - case CAIRO_OPERATOR_DEST_OVER: - return GLITZ_OPERATOR_OVER_REVERSE; - case CAIRO_OPERATOR_DEST_IN: - return GLITZ_OPERATOR_IN_REVERSE; - case CAIRO_OPERATOR_DEST_OUT: - return GLITZ_OPERATOR_OUT_REVERSE; - case CAIRO_OPERATOR_DEST_ATOP: - return GLITZ_OPERATOR_ATOP_REVERSE; - - case CAIRO_OPERATOR_XOR: - return GLITZ_OPERATOR_XOR; - case CAIRO_OPERATOR_ADD: - return GLITZ_OPERATOR_ADD; - - default: - ASSERT_NOT_REACHED; - - /* Something's very broken if this line of code can be reached, so - * we want to return something that would give a noticeably - * incorrect result. The XOR operator seems so rearely desired - * that it should fit the bill here. - */ - return CAIRO_OPERATOR_XOR; - } -} - -#define CAIRO_GLITZ_FEATURE_OK(surface, name) \ - (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \ - (GLITZ_FEATURE_ ## name ## _MASK)) - -static glitz_status_t -_glitz_ensure_target (glitz_surface_t *surface) -{ - if (!glitz_surface_get_attached_drawable (surface)) - { - glitz_drawable_format_t *target_format, templ; - glitz_format_t *format; - glitz_drawable_t *drawable, *target; - unsigned int width, height; - unsigned long mask; - - drawable = glitz_surface_get_drawable (surface); - format = glitz_surface_get_format (surface); - width = glitz_surface_get_width (surface); - height = glitz_surface_get_height (surface); - - if (format->color.fourcc != GLITZ_FOURCC_RGB) - return CAIRO_INT_STATUS_UNSUPPORTED; - - templ.color = format->color; - templ.depth_size = 0; - templ.stencil_size = 0; - templ.doublebuffer = 0; - templ.samples = 1; - - mask = - GLITZ_FORMAT_RED_SIZE_MASK | - GLITZ_FORMAT_GREEN_SIZE_MASK | - GLITZ_FORMAT_BLUE_SIZE_MASK | - GLITZ_FORMAT_ALPHA_SIZE_MASK | - GLITZ_FORMAT_DEPTH_SIZE_MASK | - GLITZ_FORMAT_STENCIL_SIZE_MASK | - GLITZ_FORMAT_DOUBLEBUFFER_MASK | - GLITZ_FORMAT_SAMPLES_MASK; - - target_format = glitz_find_drawable_format (drawable, mask, &templ, 0); - if (!target_format) - return CAIRO_INT_STATUS_UNSUPPORTED; - - target = glitz_create_drawable (drawable, target_format, - width, height); - if (!target) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_surface_attach (surface, target, - GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); - - glitz_drawable_destroy (target); - } - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_glitz_surface_attributes { - cairo_surface_attributes_t base; - - glitz_fill_t fill; - glitz_filter_t filter; - glitz_fixed16_16_t *params; - int n_params; - cairo_bool_t acquired; -} cairo_glitz_surface_attributes_t; - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_glitz_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **surface_out, - cairo_glitz_surface_attributes_t *attr) -{ - cairo_glitz_surface_t *src = NULL; - - attr->acquired = FALSE; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - char *data; - glitz_fixed16_16_t *params; - unsigned int n_params; - unsigned int *pixels; - unsigned int i, n_base_params; - glitz_buffer_t *buffer; - static const glitz_pixel_format_t format = { - GLITZ_FOURCC_RGB, - { - 32, - 0xff000000, - 0x00ff0000, - 0x0000ff00, - 0x000000ff - }, - 0, 0, 0, - GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP - }; - - /* XXX: the current color gradient acceleration provided by glitz is - * experimental, it's been proven inappropriate in a number of ways, - * most importantly, it's currently implemented as filters and - * gradients are not filters. eventually, it will be replaced with - * something more appropriate. - */ - - if (gradient->n_stops < 2) - break; - - if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) - break; - - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - n_base_params = 6; - else - n_base_params = 4; - - n_params = gradient->n_stops * 3 + n_base_params; - - /* check for int overflow */ - { - int size1, size2; - if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) || - gradient->n_stops >= INT32_MAX / sizeof (unsigned int)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - size1 = n_params * sizeof (glitz_fixed16_16_t); - size2 = gradient->n_stops * sizeof (unsigned int); - - if (size1 >= INT32_MAX - size2) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - data = malloc (size1 + size2); - } - - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - params = (glitz_fixed16_16_t *) data; - pixels = (unsigned int *) - (data + sizeof (glitz_fixed16_16_t) * n_params); - - buffer = glitz_buffer_create_for_data (pixels); - if (!buffer) { - free (data); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - src = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - gradient->n_stops, 1); - if (src->base.status) { - glitz_buffer_destroy (buffer); - free (data); - return src->base.status; - } - - for (i = 0; i < gradient->n_stops; i++) - { - pixels[i] = - (((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) | - (((int) (gradient->stops[i].color.red_short >> 8)) << 16) | - (((int) (gradient->stops[i].color.green_short >> 8)) << 8) | - (((int) (gradient->stops[i].color.blue_short >> 8))); - - params[n_base_params + 3 * i + 0] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); - params[n_base_params + 3 * i + 1] = i << 16; - params[n_base_params + 3 * i + 2] = 0; - } - - glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1, - (glitz_pixel_format_t *)&format, buffer); - - glitz_buffer_destroy (buffer); - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; - - params[0] = _cairo_fixed_to_16_16 (grad->p1.x); - params[1] = _cairo_fixed_to_16_16 (grad->p1.y); - params[2] = _cairo_fixed_to_16_16 (grad->p2.x); - params[3] = _cairo_fixed_to_16_16 (grad->p2.y); - attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; - } - else - { - cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; - - params[0] = _cairo_fixed_to_16_16 (grad->c1.x); - params[1] = _cairo_fixed_to_16_16 (grad->c1.y); - params[2] = _cairo_fixed_to_16_16 (grad->r1); - params[3] = _cairo_fixed_to_16_16 (grad->c2.x); - params[4] = _cairo_fixed_to_16_16 (grad->c2.y); - params[5] = _cairo_fixed_to_16_16 (grad->r2); - attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; - } - - switch (pattern->extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_TRANSPARENT; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - case CAIRO_EXTEND_PAD: - attr->fill = GLITZ_FILL_NEAREST; - break; - } - - attr->params = params; - attr->n_params = n_params; - attr->base.matrix = pattern->matrix; - attr->base.x_offset = 0; - attr->base.y_offset = 0; - } break; - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_SURFACE: - default: - break; - } - - if (!src) - { - cairo_int_status_t status; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attr->base); - if (status) - return status; - - if (src) - { - switch (attr->base.extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_TRANSPARENT; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - case CAIRO_EXTEND_PAD: - default: - attr->fill = GLITZ_FILL_NEAREST; - break; - } - - switch (attr->base.filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - attr->filter = GLITZ_FILTER_NEAREST; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - case CAIRO_FILTER_GAUSSIAN: - default: - attr->filter = GLITZ_FILTER_BILINEAR; - break; - } - - attr->params = NULL; - attr->n_params = 0; - attr->acquired = TRUE; - } - } - - *surface_out = src; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *attr) -{ - if (attr->acquired) - _cairo_pattern_release_surface (pattern, &surface->base, &attr->base); - else - cairo_surface_destroy (&surface->base); -} - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_glitz_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **src_out, - cairo_glitz_surface_t **mask_out, - cairo_glitz_surface_attributes_t *sattr, - cairo_glitz_surface_attributes_t *mattr) -{ - cairo_int_status_t status; - cairo_solid_pattern_t tmp; - - /* If src and mask are both solid, then the mask alpha can be - * combined into src and mask can be ignored. */ - - /* XXX: This optimization assumes that there is no color - * information in mask, so this will need to change when we - * support RENDER-style 4-channel masks. */ - - if (src->type == CAIRO_PATTERN_TYPE_SOLID && - mask->type == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_color_t combined; - cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; - cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; - - combined = src_solid->color; - _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - - _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA); - - mask = NULL; - src = &tmp.base; - } - - status = _cairo_glitz_pattern_acquire_surface (src, dst, - src_x, src_y, - width, height, - src_out, sattr); - - if (src == &tmp.base) - _cairo_pattern_fini (&tmp.base); - - if (status) - return status; - - if (mask) - { - status = _cairo_glitz_pattern_acquire_surface (mask, dst, - mask_x, mask_y, - width, height, - mask_out, mattr); - - if (status) { - /* XXX src == &tmp.base -> invalid (currently inconsequential) */ - _cairo_glitz_pattern_release_surface (src, *src_out, sattr); - } - - return status; - } - else - { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *a) -{ - _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); - glitz_surface_set_fill (surface->surface, a->fill); - glitz_surface_set_filter (surface->surface, a->filter, - a->params, a->n_params); -} - -static cairo_status_t -_cairo_glitz_get_boxes_from_region (cairo_region_t *region, - glitz_box_t **boxes, - int *nboxes) -{ - pixman_box32_t *pboxes; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - int n, i; - - n = 0; - pboxes = pixman_region32_rectangles (®ion->rgn, &n); - if (n == 0) { - *nboxes = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (n > *nboxes) { - *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); - if (*boxes == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto done; - } - } - - for (i = 0; i < n; i++) { - (*boxes)[i].x1 = pboxes[i].x1; - (*boxes)[i].y1 = pboxes[i].y1; - (*boxes)[i].x2 = pboxes[i].x2; - (*boxes)[i].y2 = pboxes[i].y2; - } - - *nboxes = n; -done: - return status; -} - -static cairo_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (region == surface->clip_region) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (region != NULL) { - cairo_status_t status; - - status = _cairo_glitz_get_boxes_from_region (region, - &surface->clip_boxes, - &surface->num_clip_boxes); - if (status) - return status; - - glitz_surface_set_clip_region (surface->surface, - 0, 0, - surface->clip_boxes, - surface->num_clip_boxes); - surface->has_clip = TRUE; - } else { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - surface->has_clip = FALSE; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t src_attr, mask_attr; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask; - cairo_int_status_t status; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, clip_region); - if (status) - return status; - - status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, - dst, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (status) - return status; - - _cairo_glitz_surface_set_attributes (src, &src_attr); - if (mask) - { - _cairo_glitz_surface_set_attributes (mask, &mask_attr); - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - mask_x + mask_attr.base.x_offset, - mask_y + mask_attr.base.y_offset, - dst_x, dst_y, - width, height); - } - else - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (status == CAIRO_STATUS_SUCCESS && - ! _cairo_operator_bounded_by_source (op)) - { - int src_width, src_height; - int mask_width, mask_height; - - src_width = glitz_surface_get_width (src->surface); - src_height = glitz_surface_get_height (src->surface); - if (mask) - { - mask_width = glitz_surface_get_width (mask->surface); - mask_height = glitz_surface_get_height (mask->surface); - } - else - { - mask_width = 0; - mask_height = 0; - } - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr.base, - src_width, src_height, - mask ? &mask_attr.base : NULL, - mask_width, mask_height, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - } - - if (mask) - { - if (mask_attr.n_params) - free (mask_attr.params); - - _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); - } - - if (src_attr.n_params) - free (src_attr.params); - - _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int n_rects) -{ - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; - glitz_rectangle_t *glitz_rects = stack_rects; - glitz_rectangle_t *current_rect; - cairo_status_t status; - int i; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - if (n_rects > ARRAY_LENGTH (stack_rects)) { - glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); - if (glitz_rects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_rects; i++) { - glitz_rects[i].x = rects[i].x; - glitz_rects[i].y = rects[i].y; - glitz_rects[i].width = rects[i].width; - glitz_rects[i].height = rects[i].height; - } - - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: { - glitz_color_t glitz_color; - glitz_format_t *format; - - glitz_color.red = color->red_short; - glitz_color.green = color->green_short; - glitz_color.blue = color->blue_short; - glitz_color.alpha = color->alpha_short; - - /* - * XXX even if the dst surface don't have an alpha channel, the - * above alpha still effect the dst surface because the - * underlying glitz drawable may have an alpha channel. So - * replacing the color with an opaque one is needed. - */ - format = glitz_surface_get_format (dst->surface); - if (format->color.alpha_size == 0) - glitz_color.alpha = 0xffff; - - glitz_set_rectangles (dst->surface, &glitz_color, - glitz_rects, n_rects); - } break; - case CAIRO_OPERATOR_SATURATE: - return CAIRO_INT_STATUS_UNSUPPORTED; - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - default: - if (_glitz_ensure_target (dst->surface)) - { - if (glitz_rects != stack_rects) - free (glitz_rects); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - src = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_solid (&dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - 1, 1, - (cairo_color_t *) color, - FALSE); - if (src == NULL || src->base.status) { - if (glitz_rects != stack_rects) - free (glitz_rects); - return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED; - } - - glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); - - current_rect = glitz_rects; - while (n_rects--) - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - 0, 0, - 0, 0, - current_rect->x, current_rect->y, - current_rect->width, current_rect->height); - current_rect++; - } - - cairo_surface_destroy (&src->base); - break; - } - - if (glitz_rects != stack_rects) - free (glitz_rects); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int n_traps, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t attributes; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask = NULL; - glitz_buffer_t *buffer = NULL; - void *data = NULL; - cairo_int_status_t status; - unsigned short alpha; - pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; - pixman_trapezoid_t *pixman_traps = stack_traps; - int i; - - if (antialias != CAIRO_ANTIALIAS_DEFAULT && - antialias != CAIRO_ANTIALIAS_GRAY) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - - /* Convert traps to pixman traps */ - if (n_traps > ARRAY_LENGTH (stack_traps)) { - pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); - if (pixman_traps == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_traps; i++) { - pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); - pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - } - - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - if (status) - goto FAIL; - - alpha = 0xffff; - - if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) { - static const glitz_color_t clear_black = { 0, 0, 0, 0 }; - glitz_color_t color; - glitz_geometry_format_t format; - int n_trap_added; - int offset = 0; - int data_size = 0; - int size = 30 * n_traps; /* just a guess */ - - format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; - format.vertex.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); - format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; - format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; - format.vertex.mask.offset = 2 * sizeof (glitz_float_t); - - mask = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_ALPHA, - 2, 1); - if (mask == NULL) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - if (mask->base.status) { - status = mask->base.status; - goto FAIL; - } - - color.red = color.green = color.blue = color.alpha = 0xffff; - - glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); - glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); - - glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); - glitz_surface_set_filter (mask->surface, - GLITZ_FILTER_BILINEAR, - NULL, 0); - - size *= format.vertex.bytes_per_vertex; - - while (n_traps) { - if (data_size < size) { - void *p; - - data_size = size; - p = realloc (data, data_size); - if (p == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - data = p; - - if (buffer) - glitz_buffer_destroy (buffer); - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - free (data); - goto FAIL; - } - } - - offset += - glitz_add_trapezoids (buffer, - offset, size - offset, - format.vertex.type, mask->surface, - (glitz_trapezoid_t *) pixman_traps, n_traps, - &n_trap_added); - - n_traps -= n_trap_added; - traps += n_trap_added; - size *= 2; - } - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_VERTEX, - &format, buffer); - glitz_set_array (dst->surface, 0, 3, - offset / format.vertex.bytes_per_vertex, - 0, 0); - } else { - cairo_image_surface_t *image; - unsigned char *ptr; - int stride; - - stride = (width + 3) & -4; - data = calloc (stride, height); - if (data == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - /* using negative stride */ - ptr = (unsigned char *) data + stride * (height - 1); - - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (ptr, - CAIRO_FORMAT_A8, - width, height, - -stride); - status = image->base.status; - if (status) { - free (data); - goto FAIL; - } - - pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, - n_traps, (pixman_trapezoid_t *) pixman_traps); - - mask = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_ALPHA, - width, height); - status = mask->base.status; - if (status) { - free (data); - cairo_surface_destroy (&image->base); - goto FAIL; - } - - status = _cairo_glitz_surface_set_image (mask, image, - 0, 0, width, height, 0, 0); - - cairo_surface_destroy (&image->base); - - if (status) - goto FAIL; - } - - _cairo_glitz_surface_set_attributes (src, &attributes); - - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + attributes.base.x_offset, - src_y + attributes.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - if (attributes.n_params) - free (attributes.params); - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_NONE, - NULL, NULL); - - if (buffer) - glitz_buffer_destroy (buffer); - - free (data); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - - if (! _cairo_operator_bounded_by_mask (op)) { - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes.base, - glitz_surface_get_width (src->surface), - glitz_surface_get_height (src->surface), - width, height, - src_x, src_y, - 0, 0, - dst_x, dst_y, - width, height, - clip_region); - } - -FAIL: - _cairo_glitz_pattern_release_surface (pattern, src, &attributes); - - if (mask != NULL) - cairo_surface_destroy (&mask->base); - - if (pixman_traps != stack_traps) - free (pixman_traps); - - return status; -} - -static cairo_bool_t -_cairo_glitz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = glitz_surface_get_width (surface->surface); - rectangle->height = glitz_surface_get_height (surface->surface); - - return TRUE; -} - -#define CAIRO_GLITZ_AREA_AVAILABLE 0 -#define CAIRO_GLITZ_AREA_DIVIDED 1 -#define CAIRO_GLITZ_AREA_OCCUPIED 2 - -typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t; - -typedef struct _cairo_glitz_area { - int state; - int level; - int x, y; - int width, height; - struct _cairo_glitz_area *area[4]; - cairo_glitz_root_area_t *root; - void *closure; -} cairo_glitz_area_t; - -static cairo_glitz_area_t _empty_area = { - 0, 0, 0, 0, 0, 0, - { NULL, NULL, NULL, NULL }, - NULL, - NULL -}; - -typedef struct _cairo_glitz_area_funcs { - cairo_status_t (*move_in) (cairo_glitz_area_t *area, - void *closure); - - void (*move_out) (cairo_glitz_area_t *area, - void *closure); - - int (*compare_score) (cairo_glitz_area_t *area, - void *closure1, - void *closure2); -} cairo_glitz_area_funcs_t; - -struct _cairo_glitz_root_area { - int max_level; - int width, height; - cairo_glitz_area_t *area; - const cairo_glitz_area_funcs_t *funcs; -}; - -static cairo_status_t -_cairo_glitz_area_move_in (cairo_glitz_area_t *area, - void *closure) -{ - area->closure = closure; - area->state = CAIRO_GLITZ_AREA_OCCUPIED; - - return (*area->root->funcs->move_in) (area, area->closure); -} - -static void -_cairo_glitz_area_move_out (cairo_glitz_area_t *area) -{ - if (area->root) - { - (*area->root->funcs->move_out) (area, area->closure); - - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - } -} - -static cairo_glitz_area_t * -_cairo_glitz_area_create (cairo_glitz_root_area_t *root, - int level, - int x, - int y, - int width, - int height) -{ - cairo_glitz_area_t *area; - int n = 4; - - area = malloc (sizeof (cairo_glitz_area_t)); - if (!area) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - area->level = level; - area->x = x; - area->y = y; - area->width = width; - area->height = height; - area->root = root; - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - - while (n--) - area->area[n] = NULL; - - return area; -} - -static void -_cairo_glitz_area_destroy (cairo_glitz_area_t *area) -{ - if (area == NULL) - return; - - if (area->state == CAIRO_GLITZ_AREA_OCCUPIED) - { - _cairo_glitz_area_move_out (area); - } - else - { - int n = 4; - - while (n--) - _cairo_glitz_area_destroy (area->area[n]); - } - - free (area); -} - -static cairo_glitz_area_t * -_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area) -{ - if (!area) - return NULL; - - switch (area->state) { - case CAIRO_GLITZ_AREA_OCCUPIED: - return area; - case CAIRO_GLITZ_AREA_AVAILABLE: - break; - case CAIRO_GLITZ_AREA_DIVIDED: { - cairo_glitz_area_t *tmp, *top = NULL; - int i; - - for (i = 0; i < 4; i++) - { - tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]); - if (tmp && top) - { - if ((*area->root->funcs->compare_score) (tmp, - tmp->closure, - top->closure) > 0) - top = tmp; - } - else if (tmp) - { - top = tmp; - } - } - return top; - } - } - - return NULL; -} - -static cairo_int_status_t -_cairo_glitz_area_find (cairo_glitz_area_t *area, - int width, - int height, - cairo_bool_t kick_out, - void *closure) -{ - cairo_status_t status; - - if (area->width < width || area->height < height) - return CAIRO_INT_STATUS_UNSUPPORTED; - - switch (area->state) { - case CAIRO_GLITZ_AREA_OCCUPIED: - if (kick_out) - { - if ((*area->root->funcs->compare_score) (area, - area->closure, - closure) >= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_glitz_area_move_out (area); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* fall-through */ - case CAIRO_GLITZ_AREA_AVAILABLE: { - if (area->level == area->root->max_level || - (area->width == width && area->height == height)) - { - return _cairo_glitz_area_move_in (area, closure); - } - else - { - int dx[4], dy[4], w[4], h[4], i; - - dx[0] = dx[2] = dy[0] = dy[1] = 0; - - w[0] = w[2] = dx[1] = dx[3] = width; - h[0] = h[1] = dy[2] = dy[3] = height; - - w[1] = w[3] = area->width - width; - h[2] = h[3] = area->height - height; - - for (i = 0; i < 2; i++) - { - if (w[i]) - area->area[i] = - _cairo_glitz_area_create (area->root, - area->level + 1, - area->x + dx[i], - area->y + dy[i], - w[i], h[i]); - } - - for (; i < 4; i++) - { - if (w[i] && h[i]) - area->area[i] = - _cairo_glitz_area_create (area->root, - area->level + 1, - area->x + dx[i], - area->y + dy[i], - w[i], h[i]); - } - - area->state = CAIRO_GLITZ_AREA_DIVIDED; - - status = _cairo_glitz_area_find (area->area[0], - width, height, - kick_out, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - } - } break; - case CAIRO_GLITZ_AREA_DIVIDED: { - cairo_glitz_area_t *to_area; - int i, rejected = FALSE; - - for (i = 0; i < 4; i++) - { - if (area->area[i]) - { - if (area->area[i]->width >= width && - area->area[i]->height >= height) - { - status = _cairo_glitz_area_find (area->area[i], - width, height, - kick_out, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - - rejected = TRUE; - } - } - } - - if (rejected) - return CAIRO_INT_STATUS_UNSUPPORTED; - - to_area = _cairo_glitz_area_get_top_scored_sub_area (area); - if (to_area) - { - if (kick_out) - { - if ((*area->root->funcs->compare_score) (to_area, - to_area->closure, - closure) >= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - for (i = 0; i < 4; i++) - { - _cairo_glitz_area_destroy (area->area[i]); - area->area[i] = NULL; - } - - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - - status = _cairo_glitz_area_find (area, width, height, - TRUE, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - - } break; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root, - int max_level, - int width, - int height, - const cairo_glitz_area_funcs_t *funcs) -{ - root->max_level = max_level; - root->funcs = funcs; - - root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height); - if (!root->area) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root) -{ - _cairo_glitz_area_destroy (root->area); -} - -typedef struct _cairo_glitz_surface_font_private { - cairo_glitz_root_area_t root; - glitz_surface_t *surface; -} cairo_glitz_surface_font_private_t; - -typedef struct _cairo_glitz_surface_glyph_private { - cairo_glitz_area_t *area; - cairo_bool_t locked; - cairo_point_double_t p1, p2; -} cairo_glitz_surface_glyph_private_t; - -static cairo_status_t -_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area, - void *closure) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure; - - glyph_private->area = area; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area, - void *closure) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure; - - glyph_private->area = NULL; -} - -static int -_cairo_glitz_glyph_compare (cairo_glitz_area_t *area, - void *closure1, - void *closure2) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure1; - - if (glyph_private->locked) - return 1; - - return -1; -} - -static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = { - _cairo_glitz_glyph_move_in, - _cairo_glitz_glyph_move_out, - _cairo_glitz_glyph_compare -}; - -#define GLYPH_CACHE_TEXTURE_SIZE 512 -#define GLYPH_CACHE_MAX_LEVEL 64 -#define GLYPH_CACHE_MAX_HEIGHT 96 -#define GLYPH_CACHE_MAX_WIDTH 96 - -#define WRITE_VEC2(ptr, _x, _y) \ - *(ptr)++ = (_x); \ - *(ptr)++ = (_y) - -#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \ - WRITE_VEC2 (ptr, _vx1, _vy1); \ - WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \ - WRITE_VEC2 (ptr, _vx2, _vy1); \ - WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \ - WRITE_VEC2 (ptr, _vx2, _vy2); \ - WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \ - WRITE_VEC2 (ptr, _vx1, _vy2); \ - WRITE_VEC2 (ptr, (p1)->x, (p1)->y) - -static cairo_status_t -_cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_glitz_surface_font_private_t *font_private; - glitz_drawable_t *drawable; - glitz_format_t *surface_format = NULL; - cairo_int_status_t status; - - drawable = glitz_surface_get_drawable (surface->surface); - - switch (format) { - case CAIRO_FORMAT_A1: - case CAIRO_FORMAT_A8: - surface_format = - glitz_find_standard_format (drawable, GLITZ_STANDARD_A8); - break; - case CAIRO_FORMAT_RGB24: - ASSERT_NOT_REACHED; - break; - case CAIRO_FORMAT_ARGB32: - surface_format = - glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32); - default: - break; - } - - if (!surface_format) - return CAIRO_INT_STATUS_UNSUPPORTED; - - font_private = malloc (sizeof (cairo_glitz_surface_font_private_t)); - if (!font_private) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->surface = glitz_surface_create (drawable, surface_format, - GLYPH_CACHE_TEXTURE_SIZE, - GLYPH_CACHE_TEXTURE_SIZE, - 0, NULL); - if (font_private->surface == NULL) - { - free (font_private); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (format == CAIRO_FORMAT_ARGB32) - glitz_surface_set_component_alpha (font_private->surface, 1); - - status = _cairo_glitz_root_area_init (&font_private->root, - GLYPH_CACHE_MAX_LEVEL, - GLYPH_CACHE_TEXTURE_SIZE, - GLYPH_CACHE_TEXTURE_SIZE, - &_cairo_glitz_area_funcs); - if (status != CAIRO_STATUS_SUCCESS) - { - glitz_surface_destroy (font_private->surface); - free (font_private); - - return status; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = _cairo_glitz_surface_get_backend (); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_glitz_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if (font_private) - { - _cairo_glitz_root_area_fini (&font_private->root); - glitz_surface_destroy (font_private->surface); - free (font_private); - } -} - -static void -_cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_glitz_surface_glyph_private_t *glyph_private; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private) - { - if (glyph_private->area) - _cairo_glitz_area_move_out (glyph_private->area); - - free (glyph_private); - } -} - -#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536) - -static cairo_status_t -_cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_glitz_surface_font_private_t *font_private; - cairo_glitz_surface_glyph_private_t *glyph_private; - glitz_point_fixed_t p1, p2; - glitz_pixel_format_t pf; - glitz_buffer_t *buffer; - cairo_format_masks_t masks; - cairo_int_status_t status; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private == NULL) - { - glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t)); - if (!glyph_private) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - glyph_private->area = NULL; - glyph_private->locked = FALSE; - - scaled_glyph->surface_private = (void *) glyph_private; - } - - if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH || - glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (scaled_font->surface_private == NULL) - { - status = _cairo_glitz_surface_font_init (surface, scaled_font, - glyph_surface->format); - if (status) - return status; - } - - font_private = scaled_font->surface_private; - - if (glyph_surface->width == 0 || glyph_surface->height == 0) - { - glyph_private->area = &_empty_area; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_glitz_area_find (font_private->root.area, - glyph_surface->width, - glyph_surface->height, - FALSE, glyph_private)) - { - if (_cairo_glitz_area_find (font_private->root.area, - glyph_surface->width, - glyph_surface->height, - TRUE, glyph_private)) - return CAIRO_STATUS_SUCCESS; - } - - buffer = glitz_buffer_create_for_data (glyph_surface->data); - if (!buffer) - { - _cairo_glitz_area_move_out (glyph_private->area); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _pixman_format_to_masks (glyph_surface->pixman_format, &masks); - - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - - pf.bytes_per_line = glyph_surface->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; - pf.xoffset = 0; - pf.skip_lines = 0; - - glitz_set_pixels (font_private->surface, - glyph_private->area->x, - glyph_private->area->y, - glyph_surface->width, - glyph_surface->height, - &pf, buffer); - - glitz_buffer_destroy (buffer); - - p1.x = glyph_private->area->x << 16; - p1.y = glyph_private->area->y << 16; - p2.x = (glyph_private->area->x + glyph_surface->width) << 16; - p2.y = (glyph_private->area->y + glyph_surface->height) << 16; - - glitz_surface_translate_point (font_private->surface, &p1, &p1); - glitz_surface_translate_point (font_private->surface, &p2, &p2); - - glyph_private->p1.x = FIXED_TO_FLOAT (p1.x); - glyph_private->p1.y = FIXED_TO_FLOAT (p1.y); - glyph_private->p2.x = FIXED_TO_FLOAT (p2.x); - glyph_private->p2.y = FIXED_TO_FLOAT (p2.y); - - return CAIRO_STATUS_SUCCESS; -} - -#define N_STACK_BUF 256 - -static cairo_int_status_t -_cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_surface, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t attributes; - cairo_glitz_surface_glyph_private_t *glyph_private; - cairo_glitz_surface_t *dst = abstract_surface; - cairo_glitz_surface_t *src; - cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF]; - cairo_scaled_glyph_t **scaled_glyphs; - glitz_float_t stack_vertices[N_STACK_BUF * 16]; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - cairo_int_status_t status; - int x_offset, y_offset; - int i, cached_glyphs = 0; - int remaining_glyps = num_glyphs; - glitz_float_t x1, y1, x2, y2; - static const glitz_vertex_format_t format = { - GLITZ_PRIMITIVE_QUADS, - GLITZ_DATA_TYPE_FLOAT, - sizeof (glitz_float_t) * 4, - GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK, - { 0 }, - { - GLITZ_DATA_TYPE_FLOAT, - GLITZ_COORDINATE_SIZE_XY, - sizeof (glitz_float_t) * 2, - } - }; - - if (scaled_font->surface_backend != NULL && - scaled_font->surface_backend != _cairo_glitz_surface_get_backend ()) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, NULL); - if (unlikely (status)) - return status; - - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - if (status) - return status; - - _cairo_glitz_surface_set_attributes (src, &attributes); - - if (num_glyphs > N_STACK_BUF) - { - char *data; - size_t size1, size2; - - if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) || - (size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) || - ((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16) - goto FAIL1; - - size1 = num_glyphs * sizeof(void *); - size2 = num_glyphs * sizeof(glitz_float_t) * 16; - if (size1 >= INT32_MAX - size2) - goto FAIL1; - - data = malloc (size1 + size2); - if (!data) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - goto FAIL1; - } - - scaled_glyphs = (cairo_scaled_glyph_t **) data; - vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *)); - } - else - { - scaled_glyphs = stack_scaled_glyphs; - vertices = stack_vertices; - } - - buffer = glitz_buffer_create_for_data (vertices); - if (!buffer) - goto FAIL2; - - _cairo_scaled_font_freeze_cache (scaled_font); - - for (i = 0; i < num_glyphs; i++) - { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyphs[i]); - if (status != CAIRO_STATUS_SUCCESS) - { - num_glyphs = i; - goto UNLOCK; - } - - glyph_private = scaled_glyphs[i]->surface_private; - if (!glyph_private || !glyph_private->area) - { - status = _cairo_glitz_surface_add_glyph (dst, - scaled_font, - scaled_glyphs[i]); - if (status != CAIRO_STATUS_SUCCESS) { - num_glyphs = i; - goto UNLOCK; - } - } - glyph_private = scaled_glyphs[i]->surface_private; - if (glyph_private && glyph_private->area) - { - remaining_glyps--; - - if (glyph_private->area->width) - { - x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; - y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; - - x1 = _cairo_lround (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); - x2 = x1 + glyph_private->area->width; - y2 = y1 + glyph_private->area->height; - - WRITE_BOX (vertices, x1, y1, x2, y2, - &glyph_private->p1, &glyph_private->p2); - - glyph_private->locked = TRUE; - - cached_glyphs++; - } - } - } - - if (remaining_glyps) - { - cairo_surface_t *image; - cairo_glitz_surface_t *clone; - - for (i = 0; i < num_glyphs; i++) - { - glyph_private = scaled_glyphs[i]->surface_private; - if (!glyph_private || !glyph_private->area) - { - int glyph_width, glyph_height; - int clone_offset_x, clone_offset_y; - - image = &scaled_glyphs[i]->surface->base; - glyph_width = scaled_glyphs[i]->surface->width; - glyph_height = scaled_glyphs[i]->surface->height; - status = - _cairo_glitz_surface_clone_similar (abstract_surface, - image, - 0, - 0, - glyph_width, - glyph_height, - &clone_offset_x, - &clone_offset_y, - (cairo_surface_t **) - &clone); - if (status) - goto UNLOCK; - - assert (clone_offset_x == 0); - assert (clone_offset_y == 0); - - x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; - y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; - x1 = _cairo_lround (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); - - glitz_composite (_glitz_operator (op), - src->surface, - clone->surface, - dst->surface, - src_x + attributes.base.x_offset + x1, - src_y + attributes.base.y_offset + y1, - 0, 0, - x1, y1, - glyph_width, - glyph_height); - - cairo_surface_destroy (&clone->base); - - if (glitz_surface_get_status (dst->surface) == - GLITZ_STATUS_NOT_SUPPORTED) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto UNLOCK; - } - } - } - } - - if (cached_glyphs) - { - cairo_glitz_surface_font_private_t *font_private; - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_VERTEX, - (glitz_geometry_format_t *) &format, - buffer); - - glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0); - - font_private = scaled_font->surface_private; - - glitz_composite (_glitz_operator (op), - src->surface, - font_private->surface, - dst->surface, - src_x + attributes.base.x_offset, - src_y + attributes.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_NONE, - NULL, NULL); - } - -UNLOCK: - if (cached_glyphs) - { - for (i = 0; i < num_glyphs; i++) - { - glyph_private = scaled_glyphs[i]->surface_private; - if (glyph_private) - glyph_private->locked = FALSE; - } - } - - _cairo_scaled_font_thaw_cache (scaled_font); - - glitz_buffer_destroy (buffer); - - FAIL2: - if (num_glyphs > N_STACK_BUF) - free (scaled_glyphs); - - FAIL1: - if (attributes.n_params) - free (attributes.params); - - _cairo_glitz_pattern_release_surface (pattern, src, &attributes); - - if (status) - return status; - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_flush (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - glitz_surface_flush (surface->surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_glitz_surface_is_similar (void *surface_a, - void *surface_b, - cairo_content_t content) -{ - cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a; - cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b; - - glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface); - glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface); - - /* XXX Disable caching of glitz surfaces by the solid pattern cache. - * Until glitz has a mechanism for releasing resources on connection - * closure, we will attempt to access invalid pointers when evicting - * old surfaces from the solid pattern cache. - */ - return FALSE; - - return drawable_a == drawable_b; -} - -static const cairo_surface_backend_t cairo_glitz_surface_backend = { - CAIRO_SURFACE_TYPE_GLITZ, - _cairo_glitz_surface_create_similar, - _cairo_glitz_surface_finish, - _cairo_glitz_surface_acquire_source_image, - _cairo_glitz_surface_release_source_image, - - _cairo_glitz_surface_acquire_dest_image, - _cairo_glitz_surface_release_dest_image, - _cairo_glitz_surface_clone_similar, - _cairo_glitz_surface_composite, - _cairo_glitz_surface_fill_rectangles, - _cairo_glitz_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_glitz_surface_get_extents, - _cairo_glitz_surface_old_show_glyphs, - NULL, /* get_font_options */ - _cairo_glitz_surface_flush, - NULL, /* mark_dirty_rectangle */ - _cairo_glitz_surface_scaled_font_fini, - _cairo_glitz_surface_scaled_glyph_fini, - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - - _cairo_glitz_surface_snapshot, - _cairo_glitz_surface_is_similar, -}; - -static const cairo_surface_backend_t * -_cairo_glitz_surface_get_backend (void) -{ - return &cairo_glitz_surface_backend; -} - -static cairo_content_t -_glitz_format_to_content (glitz_format_t * format) -{ - assert (format->color.fourcc == GLITZ_FOURCC_RGB); - - if (format->color.alpha_size != 0) { - if (format->color.red_size != 0 && - format->color.green_size != 0 && - format->color.blue_size != 0) - return CAIRO_CONTENT_COLOR_ALPHA; - else - return CAIRO_CONTENT_ALPHA; - } - return CAIRO_CONTENT_COLOR; -} - -cairo_surface_t * -cairo_glitz_surface_create (glitz_surface_t *surface) -{ - cairo_glitz_surface_t *crsurface; - glitz_format_t *format; - - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - - crsurface = malloc (sizeof (cairo_glitz_surface_t)); - if (crsurface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - format = glitz_surface_get_format (surface); - _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend, - _glitz_format_to_content (format)); - - glitz_surface_reference (surface); - - crsurface->surface = surface; - crsurface->format = format; - - crsurface->has_clip = FALSE; - crsurface->clip_boxes = NULL; - crsurface->num_clip_boxes = 0; - crsurface->clip_region = NULL; - - return &crsurface->base; -} -slim_hidden_def (cairo_glitz_surface_create); diff --git a/libs/cairo/cairo/src/cairo-glitz.h b/libs/cairo/cairo/src/cairo-glitz.h deleted file mode 100644 index b74e887bc..000000000 --- a/libs/cairo/cairo/src/cairo-glitz.h +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_GLITZ_H -#define CAIRO_GLITZ_H - -#include "cairo.h" - -#if CAIRO_HAS_GLITZ_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_glitz_surface_create (glitz_surface_t *surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_GLITZ_SURFACE */ -# error Cairo was not compiled with support for the glitz backend -#endif /* CAIRO_HAS_GLITZ_SURFACE */ - -#endif /* CAIRO_GLITZ_H */ diff --git a/libs/cairo/cairo/src/cairo-glx-context.c b/libs/cairo/cairo/src/cairo-glx-context.c deleted file mode 100644 index 1ceab6cc3..000000000 --- a/libs/cairo/cairo/src/cairo-glx-context.c +++ /dev/null @@ -1,226 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-error-private.h" - -#include - -/* XXX needs hooking into XCloseDisplay() */ - -typedef struct _cairo_glx_context { - cairo_gl_context_t base; - - Display *display; - Window dummy_window; - GLXContext context; -} cairo_glx_context_t; - -typedef struct _cairo_glx_surface { - cairo_gl_surface_t base; - - Window win; -} cairo_glx_surface_t; - -static void -_glx_acquire (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - GLXDrawable current_drawable; - - if (ctx->base.current_target == NULL || - _cairo_gl_surface_is_texture (ctx->base.current_target)) { - current_drawable = ctx->dummy_window; - } else { - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) ctx->base.current_target; - current_drawable = surface->win; - } - - glXMakeCurrent (ctx->display, current_drawable, ctx->context); -} - -static void -_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) -{ - cairo_glx_context_t *ctx = abstract_ctx; - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; - - /* Set the window as the target of our context. */ - glXMakeCurrent (ctx->display, surface->win, ctx->context); -} - -static void -_glx_release (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - - glXMakeCurrent (ctx->display, None, None); -} - -static void -_glx_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_glx_context_t *ctx = abstract_ctx; - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; - - glXSwapBuffers (ctx->display, surface->win); -} - -static void -_glx_destroy (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - - if (ctx->dummy_window != None) - XDestroyWindow (ctx->display, ctx->dummy_window); - - glXMakeCurrent (ctx->display, 0, 0); -} - -static cairo_status_t -_glx_dummy_ctx (Display *dpy, GLXContext gl_ctx, Window *dummy) -{ - int attr[3] = { GLX_FBCONFIG_ID, 0, None }; - GLXFBConfig *config; - XVisualInfo *vi; - Colormap cmap; - XSetWindowAttributes swa; - Window win = None; - int cnt; - - /* Create a dummy window created for the target GLX context that we can - * use to query the available GL/GLX extensions. - */ - glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); - - cnt = 0; - config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); - if (unlikely (cnt == 0)) - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - - vi = glXGetVisualFromFBConfig (dpy, config[0]); - XFree (config); - - if (unlikely (vi == NULL)) - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - - cmap = XCreateColormap (dpy, - RootWindow (dpy, vi->screen), - vi->visual, - AllocNone); - swa.colormap = cmap; - swa.border_pixel = 0; - win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), - -1, -1, 1, 1, 0, - vi->depth, - InputOutput, - vi->visual, - CWBorderPixel | CWColormap, &swa); - XFreeColormap (dpy, cmap); - XFree (vi); - - XFlush (dpy); - if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { - XDestroyWindow (dpy, win); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *dummy = win; - return CAIRO_STATUS_SUCCESS; -} - -cairo_device_t * -cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) -{ - cairo_glx_context_t *ctx; - cairo_status_t status; - Window dummy = None; - - status = _glx_dummy_ctx (dpy, gl_ctx, &dummy); - if (unlikely (status)) - return _cairo_gl_context_create_in_error (status); - - ctx = calloc (1, sizeof (cairo_glx_context_t)); - if (unlikely (ctx == NULL)) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - ctx->display = dpy; - ctx->dummy_window = dummy; - ctx->context = gl_ctx; - - ctx->base.acquire = _glx_acquire; - ctx->base.release = _glx_release; - ctx->base.make_current = _glx_make_current; - ctx->base.swap_buffers = _glx_swap_buffers; - ctx->base.destroy = _glx_destroy; - - status = _cairo_gl_context_init (&ctx->base); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - ctx->base.release (ctx); - - return &ctx->base.base; -} - -Display * -cairo_glx_device_get_display (cairo_device_t *device) -{ - cairo_glx_context_t *ctx; - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return NULL; - } - - ctx = (cairo_glx_context_t *) device; - - return ctx->display; -} - -GLXContext -cairo_glx_device_get_context (cairo_device_t *device) -{ - cairo_glx_context_t *ctx; - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return NULL; - } - - ctx = (cairo_glx_context_t *) device; - - return ctx->context; -} - -cairo_surface_t * -cairo_gl_surface_create_for_window (cairo_device_t *device, - Window win, - int width, - int height) -{ - cairo_glx_surface_t *surface; - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - surface = calloc (1, sizeof (cairo_glx_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (device, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->win = win; - - return &surface->base.base; -} diff --git a/libs/cairo/cairo/src/cairo-gstate-private.h b/libs/cairo/cairo/src/cairo-gstate-private.h deleted file mode 100644 index e8127d770..000000000 --- a/libs/cairo/cairo/src/cairo-gstate-private.h +++ /dev/null @@ -1,354 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_GSTATE_PRIVATE_H -#define CAIRO_GSTATE_PRIVATE_H - -#include "cairo-clip-private.h" - -struct _cairo_gstate { - cairo_operator_t op; - - double tolerance; - cairo_antialias_t antialias; - - cairo_stroke_style_t stroke_style; - - cairo_fill_rule_t fill_rule; - - cairo_font_face_t *font_face; - cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ - cairo_scaled_font_t *previous_scaled_font; /* holdover */ - cairo_matrix_t font_matrix; - cairo_font_options_t font_options; - - cairo_clip_t clip; - - cairo_surface_t *target; /* The target to which all rendering is directed */ - cairo_surface_t *parent_target; /* The previous target which was receiving rendering */ - cairo_surface_t *original_target; /* The original target the initial gstate was created with */ - - /* the user is allowed to update the device after we have cached the matrices... */ - cairo_observer_t device_transform_observer; - - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */ - cairo_bool_t is_identity; - - cairo_pattern_t *source; - - struct _cairo_gstate *next; -}; - -/* cairo-gstate.c */ -cairo_private cairo_status_t -_cairo_gstate_init (cairo_gstate_t *gstate, - cairo_surface_t *target); - -cairo_private void -_cairo_gstate_fini (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist); - -cairo_private cairo_status_t -_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist); - -cairo_private cairo_bool_t -_cairo_gstate_is_redirected (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child); - -cairo_private cairo_surface_t * -_cairo_gstate_get_target (cairo_gstate_t *gstate); - -cairo_private cairo_surface_t * -_cairo_gstate_get_parent_target (cairo_gstate_t *gstate); - -cairo_private cairo_surface_t * -_cairo_gstate_get_original_target (cairo_gstate_t *gstate); - -cairo_private cairo_clip_t * -_cairo_gstate_get_clip (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source); - -cairo_private cairo_pattern_t * -_cairo_gstate_get_source (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op); - -cairo_private cairo_operator_t -_cairo_gstate_get_operator (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance); - -cairo_private double -_cairo_gstate_get_tolerance (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule); - -cairo_private cairo_fill_rule_t -_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width); - -cairo_private double -_cairo_gstate_get_line_width (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); - -cairo_private cairo_line_cap_t -_cairo_gstate_get_line_cap (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join); - -cairo_private cairo_line_join_t -_cairo_gstate_get_line_join (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset); - -cairo_private void -_cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset); - -cairo_private cairo_status_t -_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit); - -cairo_private double -_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate); - -cairo_private void -_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); - -cairo_private cairo_status_t -_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty); - -cairo_private cairo_status_t -_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy); - -cairo_private cairo_status_t -_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle); - -cairo_private cairo_status_t -_cairo_gstate_transform (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix); - -cairo_private cairo_status_t -_cairo_gstate_set_matrix (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix); - -cairo_private void -_cairo_gstate_identity_matrix (cairo_gstate_t *gstate); - -cairo_private void -_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y); - -cairo_private void -_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy); - -cairo_private void -_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y); - -cairo_private void -_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy); - -cairo_private void -_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); - -static inline void -_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) -{ - if (! gstate->is_identity) - _do_cairo_gstate_user_to_backend (gstate, x, y); -} - -cairo_private void -_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); - -static inline void -_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) -{ - if (! gstate->is_identity) - _do_cairo_gstate_backend_to_user (gstate, x, y); -} - -cairo_private void -_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, - double *x1, double *y1, - double *x2, double *y2, - cairo_bool_t *is_tight); - -cairo_private void -_cairo_gstate_path_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2); - -cairo_private cairo_status_t -_cairo_gstate_paint (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_mask (cairo_gstate_t *gstate, - cairo_pattern_t *mask); - -cairo_private cairo_status_t -_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_gstate_copy_page (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_show_page (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2); - -cairo_private cairo_status_t -_cairo_gstate_fill_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2); - -cairo_private cairo_status_t -_cairo_gstate_in_stroke (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double x, - double y, - cairo_bool_t *inside_ret); - -cairo_private cairo_bool_t -_cairo_gstate_in_fill (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double x, - double y); - -cairo_private cairo_bool_t -_cairo_gstate_in_clip (cairo_gstate_t *gstate, - double x, - double y); - -cairo_private cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_gstate_reset_clip (cairo_gstate_t *gstate); - -cairo_private cairo_bool_t -_cairo_gstate_clip_extents (cairo_gstate_t *gstate, - double *x1, - double *y1, - double *x2, - double *y2); - -cairo_private cairo_rectangle_list_t* -_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_show_surface (cairo_gstate_t *gstate, - cairo_surface_t *surface, - double x, - double y, - double width, - double height); - -cairo_private cairo_status_t -_cairo_gstate_select_font_face (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); - -cairo_private cairo_status_t -_cairo_gstate_set_font_size (cairo_gstate_t *gstate, - double size); - -cairo_private void -_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); - -cairo_private cairo_status_t -_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix); - -cairo_private void -_cairo_gstate_get_font_options (cairo_gstate_t *gstate, - cairo_font_options_t *options); - -cairo_private void -_cairo_gstate_set_font_options (cairo_gstate_t *gstate, - const cairo_font_options_t *options); - -cairo_private cairo_status_t -_cairo_gstate_get_font_face (cairo_gstate_t *gstate, - cairo_font_face_t **font_face); - -cairo_private cairo_status_t -_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, - cairo_scaled_font_t **scaled_font); - -cairo_private cairo_status_t -_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents); - -cairo_private cairo_status_t -_cairo_gstate_set_font_face (cairo_gstate_t *gstate, - cairo_font_face_t *font_face); - -cairo_private cairo_status_t -_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags); - -cairo_private cairo_status_t -_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); - -cairo_private cairo_status_t -_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, - const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags); - -cairo_private cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_gstate_set_antialias (cairo_gstate_t *gstate, - cairo_antialias_t antialias); - -cairo_private cairo_antialias_t -_cairo_gstate_get_antialias (cairo_gstate_t *gstate); - -#endif /* CAIRO_GSTATE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-gstate.c b/libs/cairo/cairo/src/cairo-gstate.c deleted file mode 100644 index 6ba6f0b1d..000000000 --- a/libs/cairo/cairo/src/cairo-gstate.c +++ /dev/null @@ -1,2298 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-gstate-private.h" - -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif - -static cairo_status_t -_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other); - -static cairo_status_t -_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); - -static cairo_status_t -_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); - -static void -_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); - -static cairo_status_t -_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_glyph_t *transformed_glyphs, - int *num_transformed_glyphs, - cairo_text_cluster_t *transformed_clusters); - -static void -_cairo_gstate_update_device_transform (cairo_observer_t *observer, - void *arg) -{ - cairo_gstate_t *gstate = cairo_container_of (observer, - cairo_gstate_t, - device_transform_observer); - - gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) && - _cairo_matrix_is_identity (&gstate->target->device_transform)); -} - -cairo_status_t -_cairo_gstate_init (cairo_gstate_t *gstate, - cairo_surface_t *target) -{ - cairo_status_t status; - - VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); - - gstate->next = NULL; - - gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT; - - gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; - gstate->antialias = CAIRO_ANTIALIAS_DEFAULT; - - _cairo_stroke_style_init (&gstate->stroke_style); - - gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; - - gstate->font_face = NULL; - gstate->scaled_font = NULL; - gstate->previous_scaled_font = NULL; - - cairo_matrix_init_scale (&gstate->font_matrix, - CAIRO_GSTATE_DEFAULT_FONT_SIZE, - CAIRO_GSTATE_DEFAULT_FONT_SIZE); - - _cairo_font_options_init_default (&gstate->font_options); - - _cairo_clip_init (&gstate->clip); - - gstate->target = cairo_surface_reference (target); - gstate->parent_target = NULL; - gstate->original_target = cairo_surface_reference (target); - - gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; - cairo_list_add (&gstate->device_transform_observer.link, - &gstate->target->device_transform_observers); - - gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); - cairo_matrix_init_identity (&gstate->ctm); - gstate->ctm_inverse = gstate->ctm; - gstate->source_ctm_inverse = gstate->ctm; - - gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base; - - /* Now that the gstate is fully initialized and ready for the eventual - * _cairo_gstate_fini(), we can check for errors (and not worry about - * the resource deallocation). */ - status = target->status; - if (unlikely (status)) - return status; - - status = gstate->source->status; - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_gstate_init_copy: - * - * Initialize @gstate by performing a deep copy of state fields from - * @other. Note that gstate->next is not copied but is set to %NULL by - * this function. - **/ -static cairo_status_t -_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) -{ - cairo_status_t status; - - VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); - - gstate->op = other->op; - - gstate->tolerance = other->tolerance; - gstate->antialias = other->antialias; - - status = _cairo_stroke_style_init_copy (&gstate->stroke_style, - &other->stroke_style); - if (unlikely (status)) - return status; - - gstate->fill_rule = other->fill_rule; - - gstate->font_face = cairo_font_face_reference (other->font_face); - gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font); - gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font); - - gstate->font_matrix = other->font_matrix; - - _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); - - _cairo_clip_init_copy (&gstate->clip, &other->clip); - - gstate->target = cairo_surface_reference (other->target); - /* parent_target is always set to NULL; it's only ever set by redirect_target */ - gstate->parent_target = NULL; - gstate->original_target = cairo_surface_reference (other->original_target); - - gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; - cairo_list_add (&gstate->device_transform_observer.link, - &gstate->target->device_transform_observers); - - gstate->is_identity = other->is_identity; - gstate->ctm = other->ctm; - gstate->ctm_inverse = other->ctm_inverse; - gstate->source_ctm_inverse = other->source_ctm_inverse; - - gstate->source = cairo_pattern_reference (other->source); - - gstate->next = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gstate_fini (cairo_gstate_t *gstate) -{ - _cairo_stroke_style_fini (&gstate->stroke_style); - - cairo_font_face_destroy (gstate->font_face); - gstate->font_face = NULL; - - cairo_scaled_font_destroy (gstate->previous_scaled_font); - gstate->previous_scaled_font = NULL; - - cairo_scaled_font_destroy (gstate->scaled_font); - gstate->scaled_font = NULL; - - _cairo_clip_reset (&gstate->clip); - - cairo_list_del (&gstate->device_transform_observer.link); - - cairo_surface_destroy (gstate->target); - gstate->target = NULL; - - cairo_surface_destroy (gstate->parent_target); - gstate->parent_target = NULL; - - cairo_surface_destroy (gstate->original_target); - gstate->original_target = NULL; - - cairo_pattern_destroy (gstate->source); - gstate->source = NULL; - - VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t))); -} - -/** - * _cairo_gstate_save: - * @gstate: input/output gstate pointer - * - * Makes a copy of the current state of @gstate and saves it - * to @gstate->next, then put the address of the newly allcated - * copy into @gstate. _cairo_gstate_restore() reverses this. - **/ -cairo_status_t -_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) -{ - cairo_gstate_t *top; - cairo_status_t status; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - top = *freelist; - if (top == NULL) { - top = malloc (sizeof (cairo_gstate_t)); - if (unlikely (top == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else - *freelist = top->next; - - status = _cairo_gstate_init_copy (top, *gstate); - if (unlikely (status)) { - top->next = *freelist; - *freelist = top; - return status; - } - - top->next = *gstate; - *gstate = top; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_gstate_restore: - * @gstate: input/output gstate pointer - * - * Reverses the effects of one _cairo_gstate_save() call. - **/ -cairo_status_t -_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) -{ - cairo_gstate_t *top; - - top = *gstate; - if (top->next == NULL) - return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); - - *gstate = top->next; - - _cairo_gstate_fini (top); - VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *))); - top->next = *freelist; - *freelist = top; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_gstate_redirect_target: - * @gstate: a #cairo_gstate_t - * @child: the new child target - * - * Redirect @gstate rendering to a "child" target. The original - * "parent" target with which the gstate was created will not be - * affected. See _cairo_gstate_get_target(). - * - * Unless the redirected target has the same device offsets as the - * original #cairo_t target, the clip will be INVALID after this call, - * and the caller should either recreate or reset the clip. - **/ -cairo_status_t -_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) -{ - cairo_matrix_t matrix; - - /* If this gstate is already redirected, this is an error; we need a - * new gstate to be able to redirect */ - assert (gstate->parent_target == NULL); - - /* Set up our new parent_target based on our current target; - * gstate->parent_target will take the ref that is held by gstate->target - */ - cairo_surface_destroy (gstate->parent_target); - gstate->parent_target = gstate->target; - - /* Now set up our new target; we overwrite gstate->target directly, - * since its ref is now owned by gstate->parent_target */ - gstate->target = cairo_surface_reference (child); - gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform); - cairo_list_move (&gstate->device_transform_observer.link, - &gstate->target->device_transform_observers); - - /* The clip is in surface backend coordinates for the previous target; - * translate it into the child's backend coordinates. */ - cairo_matrix_init_translate (&matrix, - child->device_transform.x0 - gstate->parent_target->device_transform.x0, - child->device_transform.y0 - gstate->parent_target->device_transform.y0); - _cairo_clip_reset (&gstate->clip); - return _cairo_clip_init_copy_transformed (&gstate->clip, - &gstate->next->clip, - &matrix); -} - -/** - * _cairo_gstate_is_redirected - * @gstate: a #cairo_gstate_t - * - * This space left intentionally blank. - * - * Return value: %TRUE if the gstate is redirected to a target - * different than the original, %FALSE otherwise. - **/ -cairo_bool_t -_cairo_gstate_is_redirected (cairo_gstate_t *gstate) -{ - return (gstate->target != gstate->original_target); -} - -/** - * _cairo_gstate_get_target: - * @gstate: a #cairo_gstate_t - * - * Return the current drawing target; if drawing is not redirected, - * this will be the same as _cairo_gstate_get_original_target(). - * - * Return value: the current target surface - **/ -cairo_surface_t * -_cairo_gstate_get_target (cairo_gstate_t *gstate) -{ - return gstate->target; -} - -/** - * _cairo_gstate_get_parent_target: - * @gstate: a #cairo_gstate_t - * - * Return the parent surface of the current drawing target surface; - * if this particular gstate isn't a redirect gstate, this will return %NULL. - **/ -cairo_surface_t * -_cairo_gstate_get_parent_target (cairo_gstate_t *gstate) -{ - return gstate->parent_target; -} - -/** - * _cairo_gstate_get_original_target: - * @gstate: a #cairo_gstate_t - * - * Return the original target with which @gstate was created. This - * function always returns the original target independent of any - * child target that may have been set with - * _cairo_gstate_redirect_target. - * - * Return value: the original target surface - **/ -cairo_surface_t * -_cairo_gstate_get_original_target (cairo_gstate_t *gstate) -{ - return gstate->original_target; -} - -/** - * _cairo_gstate_get_clip: - * @gstate: a #cairo_gstate_t - * - * This space left intentionally blank. - * - * Return value: a pointer to the gstate's #cairo_clip_t structure. - */ -cairo_clip_t * -_cairo_gstate_get_clip (cairo_gstate_t *gstate) -{ - return &gstate->clip; -} - -cairo_status_t -_cairo_gstate_set_source (cairo_gstate_t *gstate, - cairo_pattern_t *source) -{ - if (source->status) - return source->status; - - source = cairo_pattern_reference (source); - cairo_pattern_destroy (gstate->source); - gstate->source = source; - gstate->source_ctm_inverse = gstate->ctm_inverse; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_pattern_t * -_cairo_gstate_get_source (cairo_gstate_t *gstate) -{ - if (gstate->source == &_cairo_pattern_black.base) { - /* do not expose the static object to the user */ - gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); - } - - return gstate->source; -} - -cairo_status_t -_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op) -{ - gstate->op = op; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_operator_t -_cairo_gstate_get_operator (cairo_gstate_t *gstate) -{ - return gstate->op; -} - -cairo_status_t -_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) -{ - gstate->tolerance = tolerance; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_get_tolerance (cairo_gstate_t *gstate) -{ - return gstate->tolerance; -} - -cairo_status_t -_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) -{ - gstate->fill_rule = fill_rule; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_fill_rule_t -_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) -{ - return gstate->fill_rule; -} - -cairo_status_t -_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) -{ - gstate->stroke_style.line_width = width; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_get_line_width (cairo_gstate_t *gstate) -{ - return gstate->stroke_style.line_width; -} - -cairo_status_t -_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) -{ - gstate->stroke_style.line_cap = line_cap; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_line_cap_t -_cairo_gstate_get_line_cap (cairo_gstate_t *gstate) -{ - return gstate->stroke_style.line_cap; -} - -cairo_status_t -_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) -{ - gstate->stroke_style.line_join = line_join; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_line_join_t -_cairo_gstate_get_line_join (cairo_gstate_t *gstate) -{ - return gstate->stroke_style.line_join; -} - -cairo_status_t -_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset) -{ - unsigned int i; - double dash_total; - - if (gstate->stroke_style.dash) - free (gstate->stroke_style.dash); - - gstate->stroke_style.num_dashes = num_dashes; - - if (gstate->stroke_style.num_dashes == 0) { - gstate->stroke_style.dash = NULL; - gstate->stroke_style.dash_offset = 0.0; - return CAIRO_STATUS_SUCCESS; - } - - gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double)); - if (unlikely (gstate->stroke_style.dash == NULL)) { - gstate->stroke_style.num_dashes = 0; - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double)); - - dash_total = 0.0; - for (i = 0; i < gstate->stroke_style.num_dashes; i++) { - if (gstate->stroke_style.dash[i] < 0) - return _cairo_error (CAIRO_STATUS_INVALID_DASH); - - dash_total += gstate->stroke_style.dash[i]; - } - - if (dash_total == 0.0) - return _cairo_error (CAIRO_STATUS_INVALID_DASH); - - /* An odd dash value indicate symmetric repeating, so the total - * is twice as long. */ - if (gstate->stroke_style.num_dashes & 1) - dash_total *= 2; - - /* The dashing code doesn't like a negative offset or a big positive - * offset, so we compute an equivalent offset which is guaranteed to be - * positive and less than twice the pattern length. */ - offset = fmod (offset, dash_total); - if (offset < 0.0) - offset += dash_total; - if (offset <= 0.0) /* Take care of -0 */ - offset = 0.0; - gstate->stroke_style.dash_offset = offset; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gstate_get_dash (cairo_gstate_t *gstate, - double *dashes, - int *num_dashes, - double *offset) -{ - if (dashes) { - memcpy (dashes, - gstate->stroke_style.dash, - sizeof (double) * gstate->stroke_style.num_dashes); - } - - if (num_dashes) - *num_dashes = gstate->stroke_style.num_dashes; - - if (offset) - *offset = gstate->stroke_style.dash_offset; -} - -cairo_status_t -_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) -{ - gstate->stroke_style.miter_limit = limit; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) -{ - return gstate->stroke_style.miter_limit; -} - -void -_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) -{ - *matrix = gstate->ctm; -} - -cairo_status_t -_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) -{ - cairo_matrix_t tmp; - - if (! ISFINITE (tx) || ! ISFINITE (ty)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_translate (&tmp, tx, ty); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - gstate->is_identity = FALSE; - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_translate (&tmp, -tx, -ty); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) -{ - cairo_matrix_t tmp; - - if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - if (! ISFINITE (sx) || ! ISFINITE (sy)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_scale (&tmp, sx, sy); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - gstate->is_identity = FALSE; - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) -{ - cairo_matrix_t tmp; - - if (angle == 0.) - return CAIRO_STATUS_SUCCESS; - - if (! ISFINITE (angle)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_rotate (&tmp, angle); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - gstate->is_identity = FALSE; - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_rotate (&tmp, -angle); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_transform (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix) -{ - cairo_matrix_t tmp; - cairo_status_t status; - - if (! _cairo_matrix_is_invertible (matrix)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - if (_cairo_matrix_is_identity (matrix)) - return CAIRO_STATUS_SUCCESS; - - tmp = *matrix; - status = cairo_matrix_invert (&tmp); - if (unlikely (status)) - return status; - - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - gstate->is_identity = FALSE; - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_set_matrix (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix) -{ - cairo_status_t status; - - if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0) - return CAIRO_STATUS_SUCCESS; - - if (! _cairo_matrix_is_invertible (matrix)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - if (_cairo_matrix_is_identity (matrix)) { - _cairo_gstate_identity_matrix (gstate); - return CAIRO_STATUS_SUCCESS; - } - - _cairo_gstate_unset_scaled_font (gstate); - - gstate->ctm = *matrix; - gstate->ctm_inverse = *matrix; - status = cairo_matrix_invert (&gstate->ctm_inverse); - assert (status == CAIRO_STATUS_SUCCESS); - gstate->is_identity = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gstate_identity_matrix (cairo_gstate_t *gstate) -{ - if (_cairo_matrix_is_identity (&gstate->ctm)) - return; - - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_identity (&gstate->ctm); - cairo_matrix_init_identity (&gstate->ctm_inverse); - gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); -} - -void -_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->ctm, x, y); -} - -void -_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, - double *dx, double *dy) -{ - cairo_matrix_transform_distance (&gstate->ctm, dx, dy); -} - -void -_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); -} - -void -_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, - double *dx, double *dy) -{ - cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); -} - -void -_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->ctm, x, y); - cairo_matrix_transform_point (&gstate->target->device_transform, x, y); -} - -void -_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y); - cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); -} - -void -_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, - double *x1, double *y1, - double *x2, double *y2, - cairo_bool_t *is_tight) -{ - cairo_matrix_t matrix_inverse; - - cairo_matrix_multiply (&matrix_inverse, - &gstate->target->device_transform_inverse, - &gstate->ctm_inverse); - _cairo_matrix_transform_bounding_box (&matrix_inverse, - x1, y1, x2, y2, is_tight); -} - -/* XXX: NYI -cairo_status_t -_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) -{ - cairo_status_t status; - - _cairo_pen_init (&gstate); - return CAIRO_STATUS_SUCCESS; -} -*/ - -void -_cairo_gstate_path_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_box_t box; - double px1, py1, px2, py2; - - if (_cairo_path_fixed_extents (path, &box)) { - px1 = _cairo_fixed_to_double (box.p1.x); - py1 = _cairo_fixed_to_double (box.p1.y); - px2 = _cairo_fixed_to_double (box.p2.x); - py2 = _cairo_fixed_to_double (box.p2.y); - - _cairo_gstate_backend_to_user_rectangle (gstate, - &px1, &py1, &px2, &py2, - NULL); - } else { - px1 = 0.0; - py1 = 0.0; - px2 = 0.0; - py2 = 0.0; - } - - if (x1) - *x1 = px1; - if (y1) - *y1 = py1; - if (x2) - *x2 = px2; - if (y2) - *y2 = py2; -} - -static void -_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, - const cairo_pattern_t *original) -{ - /* First check if the we can replace the original with a much simpler - * pattern. For example, gradients that are uniform or just have a single - * stop can sometimes be replaced with a solid. - */ - - if (_cairo_pattern_is_clear (original)) { - _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, - CAIRO_COLOR_TRANSPARENT); - return; - } - - if (original->type == CAIRO_PATTERN_TYPE_LINEAR || - original->type == CAIRO_PATTERN_TYPE_RADIAL) - { - cairo_color_t color; - if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original, - NULL, - &color)) - { - _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, - &color); - return; - } - } - - _cairo_pattern_init_static_copy (pattern, original); -} - -static void -_cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, - const cairo_pattern_t *original, - const cairo_matrix_t *ctm_inverse) -{ - _cairo_gstate_copy_pattern (pattern, original); - - /* apply device_transform first so that it is transformed by ctm_inverse */ - if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern; - cairo_surface_t *surface; - - surface_pattern = (cairo_surface_pattern_t *) original; - surface = surface_pattern->surface; - - if (_cairo_surface_has_device_transform (surface)) - _cairo_pattern_transform (pattern, &surface->device_transform); - } - - if (! _cairo_matrix_is_identity (ctm_inverse)) - _cairo_pattern_transform (pattern, ctm_inverse); - - if (_cairo_surface_has_device_transform (gstate->target)) { - _cairo_pattern_transform (pattern, - &gstate->target->device_transform_inverse); - } -} - -static void -_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, - cairo_pattern_t *pattern) -{ - _cairo_gstate_copy_transformed_pattern (gstate, pattern, - gstate->source, - &gstate->source_ctm_inverse); -} - -static void -_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, - cairo_pattern_t *mask) -{ - _cairo_gstate_copy_transformed_pattern (gstate, pattern, - mask, - &gstate->ctm_inverse); -} - -/* We need to take a copy of the clip so that the lower layers may modify it - * by, perhaps, intersecting it with the operation extents and other paths. - */ -#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip) - -static cairo_bool_t -_clipped (cairo_gstate_t *gstate) -{ - cairo_rectangle_int_t extents; - - if (gstate->clip.all_clipped) - return TRUE; - - /* XXX consider applying a surface clip? */ - - if (gstate->clip.path == NULL) - return FALSE; - - if (_cairo_surface_get_extents (gstate->target, &extents)) { - if (extents.width == 0 || extents.height == 0) - return TRUE; - - if (! _cairo_rectangle_intersect (&extents, - &gstate->clip.path->extents)) - { - return TRUE; - } - } - - /* perform a simple query to exclude trivial all-clipped cases */ - return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO; -} - -static cairo_operator_t -_reduce_op (cairo_gstate_t *gstate) -{ - cairo_operator_t op; - const cairo_pattern_t *pattern; - - op = gstate->op; - if (op != CAIRO_OPERATOR_SOURCE) - return op; - - pattern = gstate->source; - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - if (solid->color.alpha_short <= 0x00ff) { - op = CAIRO_OPERATOR_CLEAR; - } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) { - if ((solid->color.red_short | - solid->color.green_short | - solid->color.blue_short) <= 0x00ff) - { - op = CAIRO_OPERATOR_CLEAR; - } - } - } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; - if (surface->surface->is_clear && - surface->surface->content & CAIRO_CONTENT_ALPHA) - { - op = CAIRO_OPERATOR_CLEAR; - } - } else { - const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; - if (gradient->n_stops == 0) - op = CAIRO_OPERATOR_CLEAR; - } - - return op; -} - -cairo_status_t -_cairo_gstate_paint (cairo_gstate_t *gstate) -{ - cairo_pattern_union_t source_pattern; - const cairo_pattern_t *pattern; - cairo_clip_t clip; - cairo_status_t status; - cairo_operator_t op; - - if (unlikely (gstate->source->status)) - return gstate->source->status; - - if (gstate->op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (_clipped (gstate)) - return CAIRO_STATUS_SUCCESS; - - op = _reduce_op (gstate); - if (op == CAIRO_OPERATOR_CLEAR) { - pattern = &_cairo_pattern_clear.base; - } else { - _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - pattern = &source_pattern.base; - } - - status = _cairo_surface_paint (gstate->target, - op, pattern, - _gstate_get_clip (gstate, &clip)); - _cairo_clip_fini (&clip); - - return status; -} - -cairo_status_t -_cairo_gstate_mask (cairo_gstate_t *gstate, - cairo_pattern_t *mask) -{ - cairo_pattern_union_t source_pattern, mask_pattern; - const cairo_pattern_t *source; - cairo_operator_t op; - cairo_clip_t clip; - cairo_status_t status; - - if (unlikely (mask->status)) - return mask->status; - - if (unlikely (gstate->source->status)) - return gstate->source->status; - - if (gstate->op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (_clipped (gstate)) - return CAIRO_STATUS_SUCCESS; - - if (_cairo_pattern_is_opaque (mask, NULL)) - return _cairo_gstate_paint (gstate); - - if (_cairo_pattern_is_clear (mask) && - _cairo_operator_bounded_by_mask (gstate->op)) - { - return CAIRO_STATUS_SUCCESS; - } - - op = _reduce_op (gstate); - if (op == CAIRO_OPERATOR_CLEAR) { - source = &_cairo_pattern_clear.base; - } else { - _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - source = &source_pattern.base; - } - _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - - if (source->type == CAIRO_PATTERN_TYPE_SOLID && - mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID && - _cairo_operator_bounded_by_source (op)) - { - const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - cairo_color_t combined; - - if (mask_pattern.base.has_component_alpha) { -#define M(R, A, B, c) R.c = A.c * B.c - M(combined, solid->color, mask_pattern.solid.color, red); - M(combined, solid->color, mask_pattern.solid.color, green); - M(combined, solid->color, mask_pattern.solid.color, blue); - M(combined, solid->color, mask_pattern.solid.color, alpha); -#undef M - } else { - combined = solid->color; - _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); - } - - _cairo_pattern_init_solid (&source_pattern.solid, &combined); - - status = _cairo_surface_paint (gstate->target, op, - &source_pattern.base, - _gstate_get_clip (gstate, &clip)); - } - else - { - status = _cairo_surface_mask (gstate->target, op, - source, - &mask_pattern.base, - _gstate_get_clip (gstate, &clip)); - } - _cairo_clip_fini (&clip); - - return status; -} - -cairo_status_t -_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) -{ - cairo_pattern_union_t source_pattern; - cairo_stroke_style_t style; - double dash[2]; - cairo_clip_t clip; - cairo_status_t status; - - if (unlikely (gstate->source->status)) - return gstate->source->status; - - if (gstate->op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (gstate->stroke_style.line_width <= 0.0) - return CAIRO_STATUS_SUCCESS; - - if (_clipped (gstate)) - return CAIRO_STATUS_SUCCESS; - - memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); - if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) { - style.dash = dash; - _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, - &style.dash_offset, - style.dash, - &style.num_dashes); - } - - _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - - status = _cairo_surface_stroke (gstate->target, - gstate->op, - &source_pattern.base, - path, - &style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - gstate->antialias, - _gstate_get_clip (gstate, &clip)); - _cairo_clip_fini (&clip); - - return status; -} - -cairo_status_t -_cairo_gstate_in_stroke (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double x, - double y, - cairo_bool_t *inside_ret) -{ - cairo_status_t status; - cairo_rectangle_int_t extents; - cairo_box_t limit; - cairo_traps_t traps; - - if (gstate->stroke_style.line_width <= 0.0) { - *inside_ret = FALSE; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_gstate_user_to_backend (gstate, &x, &y); - - /* Before we perform the expensive stroke analysis, - * check whether the point is within the extents of the path. - */ - _cairo_path_fixed_approximate_stroke_extents (path, - &gstate->stroke_style, - &gstate->ctm, - &extents); - if (x < extents.x || x > extents.x + extents.width || - y < extents.y || y > extents.y + extents.height) - { - *inside_ret = FALSE; - return CAIRO_STATUS_SUCCESS; - } - - limit.p1.x = _cairo_fixed_from_double (x) - 5; - limit.p1.y = _cairo_fixed_from_double (y) - 5; - limit.p2.x = limit.p1.x + 10; - limit.p2.y = limit.p1.y + 10; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &limit, 1); - - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); - if (unlikely (status)) - goto BAIL; - - *inside_ret = _cairo_traps_contain (&traps, x, y); - -BAIL: - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) -{ - cairo_clip_t clip; - cairo_status_t status; - - if (unlikely (gstate->source->status)) - return gstate->source->status; - - if (gstate->op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (_clipped (gstate)) - return CAIRO_STATUS_SUCCESS; - - if (_cairo_path_fixed_fill_is_empty (path)) { - if (_cairo_operator_bounded_by_mask (gstate->op)) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_surface_paint (gstate->target, - CAIRO_OPERATOR_CLEAR, - &_cairo_pattern_clear.base, - _gstate_get_clip (gstate, &clip)); - } else { - cairo_pattern_union_t source_pattern; - const cairo_pattern_t *pattern; - cairo_operator_t op; - cairo_rectangle_int_t extents; - cairo_box_t box; - - op = _reduce_op (gstate); - if (op == CAIRO_OPERATOR_CLEAR) { - pattern = &_cairo_pattern_clear.base; - } else { - _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - pattern = &source_pattern.base; - } - - /* Toolkits often paint the entire background with a fill */ - if (_cairo_surface_get_extents (gstate->target, &extents) && - _cairo_path_fixed_is_box (path, &box) && - box.p1.x <= _cairo_fixed_from_int (extents.x) && - box.p1.y <= _cairo_fixed_from_int (extents.y) && - box.p2.x >= _cairo_fixed_from_int (extents.x + extents.width) && - box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height)) - { - status = _cairo_surface_paint (gstate->target, op, pattern, - _gstate_get_clip (gstate, &clip)); - } - else - { - status = _cairo_surface_fill (gstate->target, op, pattern, - path, - gstate->fill_rule, - gstate->tolerance, - gstate->antialias, - _gstate_get_clip (gstate, &clip)); - } - } - - _cairo_clip_fini (&clip); - - return status; -} - -cairo_bool_t -_cairo_gstate_in_fill (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double x, - double y) -{ - _cairo_gstate_user_to_backend (gstate, &x, &y); - - return _cairo_path_fixed_in_fill (path, - gstate->fill_rule, - gstate->tolerance, - x, y); -} - -cairo_bool_t -_cairo_gstate_in_clip (cairo_gstate_t *gstate, - double x, - double y) -{ - cairo_clip_path_t *clip_path; - - if (gstate->clip.all_clipped) - return FALSE; - - clip_path = gstate->clip.path; - if (clip_path == NULL) - return TRUE; - - _cairo_gstate_user_to_backend (gstate, &x, &y); - - if (x < clip_path->extents.x || - x >= clip_path->extents.x + clip_path->extents.width || - y < clip_path->extents.y || - y >= clip_path->extents.y + clip_path->extents.height) - { - return FALSE; - } - - do { - if (! _cairo_path_fixed_in_fill (&clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - x, y)) - return FALSE; - } while ((clip_path = clip_path->prev) != NULL); - - return TRUE; -} - -cairo_status_t -_cairo_gstate_copy_page (cairo_gstate_t *gstate) -{ - cairo_surface_copy_page (gstate->target); - return cairo_surface_status (gstate->target); -} - -cairo_status_t -_cairo_gstate_show_page (cairo_gstate_t *gstate) -{ - cairo_surface_show_page (gstate->target); - return cairo_surface_status (gstate->target); -} - -static void -_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate, - cairo_traps_t *traps, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_box_t extents; - - if (traps->num_traps == 0) { - /* no traps, so we actually won't draw anything */ - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - } else { - double px1, py1, px2, py2; - - _cairo_traps_extents (traps, &extents); - - px1 = _cairo_fixed_to_double (extents.p1.x); - py1 = _cairo_fixed_to_double (extents.p1.y); - px2 = _cairo_fixed_to_double (extents.p2.x); - py2 = _cairo_fixed_to_double (extents.p2.y); - - _cairo_gstate_backend_to_user_rectangle (gstate, - &px1, &py1, &px2, &py2, - NULL); - if (x1) - *x1 = px1; - if (y1) - *y1 = py1; - if (x2) - *x2 = px2; - if (y2) - *y2 = py2; - } -} - -cairo_status_t -_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_status_t status; - cairo_traps_t traps; - - if (gstate->stroke_style.line_width <= 0.0) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_traps_init (&traps); - - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, - x1, y1, x2, y2); - } - - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_fill_extents (cairo_gstate_t *gstate, - cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_status_t status; - cairo_traps_t traps; - - if (path->is_empty_fill) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_traps_init (&traps); - - status = _cairo_path_fixed_fill_to_traps (path, - gstate->fill_rule, - gstate->tolerance, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, - x1, y1, x2, y2); - } - - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_reset_clip (cairo_gstate_t *gstate) -{ - _cairo_clip_reset (&gstate->clip); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) -{ - return _cairo_clip_clip (&gstate->clip, - path, gstate->fill_rule, - gstate->tolerance, gstate->antialias); -} - -static cairo_bool_t -_cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, - cairo_rectangle_int_t *extents) -{ - const cairo_rectangle_int_t *clip_extents; - cairo_bool_t is_bounded; - - is_bounded = _cairo_surface_get_extents (gstate->target, extents); - - clip_extents = _cairo_clip_get_extents (&gstate->clip); - if (clip_extents != NULL) { - cairo_bool_t is_empty; - - is_empty = _cairo_rectangle_intersect (extents, clip_extents); - is_bounded = TRUE; - } - - return is_bounded; -} - -cairo_bool_t -_cairo_gstate_clip_extents (cairo_gstate_t *gstate, - double *x1, - double *y1, - double *x2, - double *y2) -{ - cairo_rectangle_int_t extents; - double px1, py1, px2, py2; - - if (! _cairo_gstate_int_clip_extents (gstate, &extents)) - return FALSE; - - px1 = extents.x; - py1 = extents.y; - px2 = extents.x + (int) extents.width; - py2 = extents.y + (int) extents.height; - - _cairo_gstate_backend_to_user_rectangle (gstate, - &px1, &py1, &px2, &py2, - NULL); - - if (x1) - *x1 = px1; - if (y1) - *y1 = py1; - if (x2) - *x2 = px2; - if (y2) - *y2 = py2; - - return TRUE; -} - -cairo_rectangle_list_t* -_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) -{ - cairo_clip_t clip; - cairo_rectangle_int_t extents; - cairo_rectangle_list_t *list; - - _cairo_clip_init_copy (&clip, &gstate->clip); - - if (_cairo_surface_get_extents (gstate->target, &extents)) - _cairo_clip_rectangle (&clip, &extents); - - list = _cairo_clip_copy_rectangle_list (&clip, gstate); - _cairo_clip_fini (&clip); - - return list; -} - -static void -_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) -{ - if (gstate->scaled_font == NULL) - return; - - if (gstate->previous_scaled_font != NULL) - cairo_scaled_font_destroy (gstate->previous_scaled_font); - - gstate->previous_scaled_font = gstate->scaled_font; - gstate->scaled_font = NULL; -} - -cairo_status_t -_cairo_gstate_select_font_face (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_font_face_t *font_face; - cairo_status_t status; - - font_face = cairo_toy_font_face_create (family, slant, weight); - if (font_face->status) - return font_face->status; - - status = _cairo_gstate_set_font_face (gstate, font_face); - cairo_font_face_destroy (font_face); - - return status; -} - -cairo_status_t -_cairo_gstate_set_font_size (cairo_gstate_t *gstate, - double size) -{ - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_scale (&gstate->font_matrix, size, size); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, - const cairo_matrix_t *matrix) -{ - if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0) - return CAIRO_STATUS_SUCCESS; - - if (! _cairo_matrix_is_invertible (matrix)) { - /* rank 0 matrices are ok even though they are not invertible */ - if (!(matrix->xx == 0. && matrix->xy == 0. && - matrix->yx == 0. && matrix->yy == 0.)) { - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - } - } - - _cairo_gstate_unset_scaled_font (gstate); - - gstate->font_matrix = *matrix; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - *matrix = gstate->font_matrix; -} - -void -_cairo_gstate_set_font_options (cairo_gstate_t *gstate, - const cairo_font_options_t *options) -{ - if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0) - return; - - _cairo_gstate_unset_scaled_font (gstate); - - _cairo_font_options_init_copy (&gstate->font_options, options); -} - -void -_cairo_gstate_get_font_options (cairo_gstate_t *gstate, - cairo_font_options_t *options) -{ - *options = gstate->font_options; -} - -cairo_status_t -_cairo_gstate_get_font_face (cairo_gstate_t *gstate, - cairo_font_face_t **font_face) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_font_face (gstate); - if (unlikely (status)) - return status; - - *font_face = gstate->font_face; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, - cairo_scaled_font_t **scaled_font) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - *scaled_font = gstate->scaled_font; - - return CAIRO_STATUS_SUCCESS; -} - -/* - * Like everything else in this file, fonts involve Too Many Coordinate Spaces; - * it is easy to get confused about what's going on. - * - * The user's view - * --------------- - * - * Users ask for things in user space. When cairo starts, a user space unit - * is about 1/96 inch, which is similar to (but importantly different from) - * the normal "point" units most users think in terms of. When a user - * selects a font, its scale is set to "one user unit". The user can then - * independently scale the user coordinate system *or* the font matrix, in - * order to adjust the rendered size of the font. - * - * Metrics are returned in user space, whether they are obtained from - * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t - * which is a font specialized to a particular scale matrix, CTM, and target - * surface. - * - * The font's view - * --------------- - * - * Fonts are designed and stored (in say .ttf files) in "font space", which - * describes an "EM Square" (a design tile) and has some abstract number - * such as 1000, 1024, or 2048 units per "EM". This is basically an - * uninteresting space for us, but we need to remember that it exists. - * - * Font resources (from libraries or operating systems) render themselves - * to a particular device. Since they do not want to make most programmers - * worry about the font design space, the scaling API is simplified to - * involve just telling the font the required pixel size of the EM square - * (that is, in device space). - * - * - * Cairo's gstate view - * ------------------- - * - * In addition to the CTM and CTM inverse, we keep a matrix in the gstate - * called the "font matrix" which describes the user's most recent - * font-scaling or font-transforming request. This is kept in terms of an - * abstract scale factor, composed with the CTM and used to set the font's - * pixel size. So if the user asks to "scale the font by 12", the matrix - * is: - * - * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] - * - * It is an affine matrix, like all cairo matrices, where its tx and ty - * components are used to "nudging" fonts around and are handled in gstate - * and then ignored by the "scaled-font" layer. - * - * In order to perform any action on a font, we must build an object - * called a #cairo_font_scale_t; this contains the central 2x2 matrix - * resulting from "font matrix * CTM" (sans the font matrix translation - * components as stated in the previous paragraph). - * - * We pass this to the font when making requests of it, which causes it to - * reply for a particular [user request, device] combination, under the CTM - * (to accommodate the "zoom in" == "bigger fonts" issue above). - * - * The other terms in our communication with the font are therefore in - * device space. When we ask it to perform text->glyph conversion, it will - * produce a glyph string in device space. Glyph vectors we pass to it for - * measuring or rendering should be in device space. The metrics which we - * get back from the font will be in device space. The contents of the - * global glyph image cache will be in device space. - * - * - * Cairo's public view - * ------------------- - * - * Since the values entering and leaving via public API calls are in user - * space, the gstate functions typically need to multiply arguments by the - * CTM (for user-input glyph vectors), and return values by the CTM inverse - * (for font responses such as metrics or glyph vectors). - * - */ - -static cairo_status_t -_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) -{ - cairo_font_face_t *font_face; - - if (gstate->font_face != NULL) - return gstate->font_face->status; - - - font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); - if (font_face->status) - return font_face->status; - - gstate->font_face = font_face; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) -{ - cairo_status_t status; - cairo_font_options_t options; - cairo_scaled_font_t *scaled_font; - - if (gstate->scaled_font != NULL) - return gstate->scaled_font->status; - - status = _cairo_gstate_ensure_font_face (gstate); - if (unlikely (status)) - return status; - - cairo_surface_get_font_options (gstate->target, &options); - cairo_font_options_merge (&options, &gstate->font_options); - - scaled_font = cairo_scaled_font_create (gstate->font_face, - &gstate->font_matrix, - &gstate->ctm, - &options); - - status = cairo_scaled_font_status (scaled_font); - if (unlikely (status)) - return status; - - gstate->scaled_font = scaled_font; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) -{ - cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - cairo_scaled_font_extents (gstate->scaled_font, extents); - - return cairo_scaled_font_status (gstate->scaled_font); -} - -cairo_status_t -_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags); -} - -cairo_status_t -_cairo_gstate_set_font_face (cairo_gstate_t *gstate, - cairo_font_face_t *font_face) -{ - if (font_face && font_face->status) - return _cairo_error (font_face->status); - - if (font_face == gstate->font_face) - return CAIRO_STATUS_SUCCESS; - - cairo_font_face_destroy (gstate->font_face); - gstate->font_face = cairo_font_face_reference (font_face); - - _cairo_gstate_unset_scaled_font (gstate); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - cairo_scaled_font_glyph_extents (gstate->scaled_font, - glyphs, num_glyphs, - extents); - - return cairo_scaled_font_status (gstate->scaled_font); -} - -cairo_status_t -_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, - const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags) -{ - cairo_pattern_union_t source_pattern; - const cairo_pattern_t *pattern; - cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; - cairo_glyph_t *transformed_glyphs; - cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; - cairo_text_cluster_t *transformed_clusters; - cairo_operator_t op; - cairo_status_t status; - cairo_clip_t clip; - - if (unlikely (gstate->source->status)) - return gstate->source->status; - - if (gstate->op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (_clipped (gstate)) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - transformed_glyphs = stack_transformed_glyphs; - transformed_clusters = stack_transformed_clusters; - - if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { - transformed_glyphs = cairo_glyph_allocate (num_glyphs); - if (unlikely (transformed_glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_GLYPHS; - } - } - - /* Just in case */ - if (!clusters) - num_clusters = 0; - - if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { - transformed_clusters = cairo_text_cluster_allocate (num_clusters); - if (unlikely (transformed_clusters == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_GLYPHS; - } - } - - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - clusters, - num_clusters, - cluster_flags, - transformed_glyphs, - &num_glyphs, - transformed_clusters); - - if (status || num_glyphs == 0) - goto CLEANUP_GLYPHS; - - op = _reduce_op (gstate); - if (op == CAIRO_OPERATOR_CLEAR) { - pattern = &_cairo_pattern_clear.base; - } else { - _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - pattern = &source_pattern.base; - } - _cairo_clip_init(&clip); - - /* For really huge font sizes, we can just do path;fill instead of - * show_glyphs, as show_glyphs would put excess pressure on the cache, - * not all components below us correctly handle huge font sizes, and - * path filling can be cheaper since parts of glyphs are likely to be - * clipped out. 256 seems like a good limit. But alas, seems like cairo's - * rasterizer is something like ten times slower than freetype's for huge - * sizes. So, no win just yet when we're using cairo's rasterizer. - * For now, if we're using cairo's rasterizer, use path filling only - * for insanely-huge sizes, just to make sure we don't make anyone - * unhappy. When we get a really fast rasterizer in cairo, we may - * want to readjust this. The threshold calculation is - * encapsulated in _cairo_surface_get_text_path_fill_threshold. - * - * Needless to say, do this only if show_text_glyphs is not available. */ - if (cairo_surface_has_show_text_glyphs (gstate->target) || - _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= - _cairo_surface_get_text_path_fill_threshold (gstate->target)) - { - status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, - utf8, utf8_len, - transformed_glyphs, num_glyphs, - transformed_clusters, num_clusters, - cluster_flags, - gstate->scaled_font, - _gstate_get_clip (gstate, &clip)); - } - else - { - cairo_path_fixed_t path; - - _cairo_path_fixed_init (&path); - - status = _cairo_scaled_font_glyph_path (gstate->scaled_font, - transformed_glyphs, num_glyphs, - &path); - - if (status == CAIRO_STATUS_SUCCESS && !_cairo_path_fixed_fill_is_empty (&path)) { - status = _cairo_surface_fill (gstate->target, op, pattern, - &path, - CAIRO_FILL_RULE_WINDING, - gstate->tolerance, - gstate->scaled_font->options.antialias, - _gstate_get_clip (gstate, &clip)); - } else { - /* if _cairo_scaled_font_glyph_path() failed, maybe the font doesn't support - * returning paths, so try the _cairo_surface_show_text_glyphs() option - */ - status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, - utf8, utf8_len, - transformed_glyphs, num_glyphs, - transformed_clusters, num_clusters, - cluster_flags, - gstate->scaled_font, - _gstate_get_clip (gstate, &clip)); - } - - _cairo_path_fixed_fini (&path); - } - - _cairo_clip_fini (&clip); - -CLEANUP_GLYPHS: - if (transformed_glyphs != stack_transformed_glyphs) - cairo_glyph_free (transformed_glyphs); - if (transformed_clusters != stack_transformed_clusters) - cairo_text_cluster_free (transformed_clusters); - - return status; -} - -cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path) -{ - cairo_status_t status; - cairo_glyph_t *transformed_glyphs; - cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { - transformed_glyphs = stack_transformed_glyphs; - } else { - transformed_glyphs = cairo_glyph_allocate (num_glyphs); - if (unlikely (transformed_glyphs == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - NULL, 0, 0, - transformed_glyphs, - NULL, NULL); - if (unlikely (status)) - goto CLEANUP_GLYPHS; - - status = _cairo_scaled_font_glyph_path (gstate->scaled_font, - transformed_glyphs, num_glyphs, - path); - - CLEANUP_GLYPHS: - if (transformed_glyphs != stack_transformed_glyphs) - cairo_glyph_free (transformed_glyphs); - - return status; -} - -cairo_status_t -_cairo_gstate_set_antialias (cairo_gstate_t *gstate, - cairo_antialias_t antialias) -{ - gstate->antialias = antialias; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_antialias_t -_cairo_gstate_get_antialias (cairo_gstate_t *gstate) -{ - return gstate->antialias; -} - -/** - * _cairo_gstate_transform_glyphs_to_backend: - * @gstate: a #cairo_gstate_t - * @glyphs: the array of #cairo_glyph_t objects to be transformed - * @num_glyphs: the number of elements in @glyphs - * @transformed_glyphs: a pre-allocated array of at least @num_glyphs - * #cairo_glyph_t objects - * @num_transformed_glyphs: the number of elements in @transformed_glyphs - * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be - * dropped - * - * Transform an array of glyphs to backend space by first adding the offset - * of the font matrix, then transforming from user space to backend space. - * The result of the transformation is placed in @transformed_glyphs. - * - * This also uses information from the scaled font and the surface to - * cull/drop glyphs that will not be visible. - **/ -static cairo_status_t -_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_glyph_t *transformed_glyphs, - int *num_transformed_glyphs, - cairo_text_cluster_t *transformed_clusters) -{ - int i, j, k; - cairo_matrix_t *ctm = &gstate->ctm; - cairo_matrix_t *font_matrix = &gstate->font_matrix; - cairo_matrix_t *device_transform = &gstate->target->device_transform; - cairo_bool_t drop = FALSE; - double x1 = 0, x2 = 0, y1 = 0, y2 = 0; - - if (num_transformed_glyphs != NULL) { - cairo_rectangle_int_t surface_extents; - - drop = TRUE; - if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { - drop = FALSE; /* unbounded surface */ - } else { - double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); - if (surface_extents.width == 0 || surface_extents.height == 0) { - /* No visible area. Don't draw anything */ - *num_transformed_glyphs = 0; - return CAIRO_STATUS_SUCCESS; - } - /* XXX We currently drop any glyphs that has its position outside - * of the surface boundaries by a safety margin depending on the - * font scale. This however can fail in extreme cases where the - * font has really long swashes for example... We can correctly - * handle that by looking the glyph up and using its device bbox - * to device if it's going to be visible, but I'm not inclined to - * do that now. - */ - x1 = surface_extents.x - scale10; - y1 = surface_extents.y - scale10; - x2 = surface_extents.x + (int) surface_extents.width + scale10; - y2 = surface_extents.y + (int) surface_extents.height + scale10; - } - - if (!drop) - *num_transformed_glyphs = num_glyphs; - } else - num_transformed_glyphs = &j; - -#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) - - j = 0; - if (_cairo_matrix_is_identity (ctm) && - _cairo_matrix_is_identity (device_transform) && - font_matrix->x0 == 0 && font_matrix->y0 == 0) - { - if (! drop) { - memcpy (transformed_glyphs, glyphs, - num_glyphs * sizeof (cairo_glyph_t)); - j = num_glyphs; - } else if (num_clusters == 0) { - for (i = 0; i < num_glyphs; i++) { - transformed_glyphs[j].index = glyphs[i].index; - transformed_glyphs[j].x = glyphs[i].x; - transformed_glyphs[j].y = glyphs[i].y; - if (KEEP_GLYPH (transformed_glyphs[j])) - j++; - } - } else { - const cairo_glyph_t *cur_glyph; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph = glyphs + num_glyphs - 1; - else - cur_glyph = glyphs; - - for (i = 0; i < num_clusters; i++) { - cairo_bool_t cluster_visible = FALSE; - - for (k = 0; k < clusters[i].num_glyphs; k++) { - transformed_glyphs[j+k].index = cur_glyph->index; - transformed_glyphs[j+k].x = cur_glyph->x; - transformed_glyphs[j+k].y = cur_glyph->y; - if (KEEP_GLYPH (transformed_glyphs[j+k])) - cluster_visible = TRUE; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph--; - else - cur_glyph++; - } - - transformed_clusters[i] = clusters[i]; - if (cluster_visible) - j += k; - else - transformed_clusters[i].num_glyphs = 0; - } - } - } - else if (_cairo_matrix_is_translation (ctm) && - _cairo_matrix_is_translation (device_transform)) - { - double tx = font_matrix->x0 + ctm->x0 + device_transform->x0; - double ty = font_matrix->y0 + ctm->y0 + device_transform->y0; - - if (! drop || num_clusters == 0) { - for (i = 0; i < num_glyphs; i++) { - transformed_glyphs[j].index = glyphs[i].index; - transformed_glyphs[j].x = glyphs[i].x + tx; - transformed_glyphs[j].y = glyphs[i].y + ty; - if (!drop || KEEP_GLYPH (transformed_glyphs[j])) - j++; - } - } else { - const cairo_glyph_t *cur_glyph; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph = glyphs + num_glyphs - 1; - else - cur_glyph = glyphs; - - for (i = 0; i < num_clusters; i++) { - cairo_bool_t cluster_visible = FALSE; - - for (k = 0; k < clusters[i].num_glyphs; k++) { - transformed_glyphs[j+k].index = cur_glyph->index; - transformed_glyphs[j+k].x = cur_glyph->x + tx; - transformed_glyphs[j+k].y = cur_glyph->y + ty; - if (KEEP_GLYPH (transformed_glyphs[j+k])) - cluster_visible = TRUE; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph--; - else - cur_glyph++; - } - - transformed_clusters[i] = clusters[i]; - if (cluster_visible) - j += k; - else - transformed_clusters[i].num_glyphs = 0; - } - } - } - else - { - cairo_matrix_t aggregate_transform; - - cairo_matrix_init_translate (&aggregate_transform, - gstate->font_matrix.x0, - gstate->font_matrix.y0); - cairo_matrix_multiply (&aggregate_transform, - &aggregate_transform, ctm); - cairo_matrix_multiply (&aggregate_transform, - &aggregate_transform, device_transform); - - if (! drop || num_clusters == 0) { - for (i = 0; i < num_glyphs; i++) { - transformed_glyphs[j] = glyphs[i]; - cairo_matrix_transform_point (&aggregate_transform, - &transformed_glyphs[j].x, - &transformed_glyphs[j].y); - if (! drop || KEEP_GLYPH (transformed_glyphs[j])) - j++; - } - } else { - const cairo_glyph_t *cur_glyph; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph = glyphs + num_glyphs - 1; - else - cur_glyph = glyphs; - - for (i = 0; i < num_clusters; i++) { - cairo_bool_t cluster_visible = FALSE; - for (k = 0; k < clusters[i].num_glyphs; k++) { - transformed_glyphs[j+k] = *cur_glyph; - cairo_matrix_transform_point (&aggregate_transform, - &transformed_glyphs[j+k].x, - &transformed_glyphs[j+k].y); - if (KEEP_GLYPH (transformed_glyphs[j+k])) - cluster_visible = TRUE; - - if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) - cur_glyph--; - else - cur_glyph++; - } - - transformed_clusters[i] = clusters[i]; - if (cluster_visible) - j += k; - else - transformed_clusters[i].num_glyphs = 0; - } - } - } - *num_transformed_glyphs = j; - - if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) { - for (i = 0; i < --j; i++) { - cairo_glyph_t tmp; - - tmp = transformed_glyphs[i]; - transformed_glyphs[i] = transformed_glyphs[j]; - transformed_glyphs[j] = tmp; - } - } - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-hash-private.h b/libs/cairo/cairo/src/cairo-hash-private.h deleted file mode 100644 index 0c2759dbf..000000000 --- a/libs/cairo/cairo/src/cairo-hash-private.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-hash.c b/libs/cairo/cairo/src/cairo-hash.c deleted file mode 100644 index 7e24d930d..000000000 --- a/libs/cairo/cairo/src/cairo-hash.c +++ /dev/null @@ -1,508 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -/* - * An entry can be in one of three states: - * - * FREE: Entry has never been used, terminates all searches. - * Appears in the table as a %NULL pointer. - * - * DEAD: Entry had been live in the past. A dead entry can be reused - * but does not terminate a search for an exact entry. - * Appears in the table as a pointer to DEAD_ENTRY. - * - * LIVE: Entry is currently being used. - * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer. - */ - -#define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1) - -#define ENTRY_IS_FREE(entry) ((entry) == NULL) -#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) -#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) - -/* We expect keys will not be destroyed frequently, so our table does not - * contain any explicit shrinking code nor any chain-coalescing code for - * entries randomly deleted by memory pressure (except during rehashing, of - * course). These assumptions are potentially bad, but they make the - * implementation straightforward. - * - * Revisit later if evidence appears that we're using excessive memory from - * a mostly-dead table. - * - * This table is open-addressed with double hashing. Each table size is a - * prime chosen to be a little more than double the high water mark for a - * given arrangement, so the tables should remain < 50% full. The table - * size makes for the "first" hash modulus; a second prime (2 less than the - * first prime) serves as the "second" hash modulus, which is co-prime and - * thus guarantees a complete permutation of table indices. - * - * This structure, and accompanying table, is borrowed/modified from the - * file xserver/render/glyph.c in the freedesktop.org x server, with - * permission (and suggested modification of doubling sizes) by Keith - * Packard. - */ - -typedef struct _cairo_hash_table_arrangement { - unsigned long high_water_mark; - unsigned long size; - unsigned long rehash; -} cairo_hash_table_arrangement_t; - -static const cairo_hash_table_arrangement_t hash_table_arrangements [] = { - { 16, 43, 41 }, - { 32, 73, 71 }, - { 64, 151, 149 }, - { 128, 283, 281 }, - { 256, 571, 569 }, - { 512, 1153, 1151 }, - { 1024, 2269, 2267 }, - { 2048, 4519, 4517 }, - { 4096, 9013, 9011 }, - { 8192, 18043, 18041 }, - { 16384, 36109, 36107 }, - { 32768, 72091, 72089 }, - { 65536, 144409, 144407 }, - { 131072, 288361, 288359 }, - { 262144, 576883, 576881 }, - { 524288, 1153459, 1153457 }, - { 1048576, 2307163, 2307161 }, - { 2097152, 4613893, 4613891 }, - { 4194304, 9227641, 9227639 }, - { 8388608, 18455029, 18455027 }, - { 16777216, 36911011, 36911009 }, - { 33554432, 73819861, 73819859 }, - { 67108864, 147639589, 147639587 }, - { 134217728, 295279081, 295279079 }, - { 268435456, 590559793, 590559791 } -}; - -#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements) - -struct _cairo_hash_table { - cairo_hash_keys_equal_func_t keys_equal; - - const cairo_hash_table_arrangement_t *arrangement; - cairo_hash_entry_t **entries; - - unsigned long live_entries; - unsigned long iterating; /* Iterating, no insert, no resize */ -}; - -/** - * _cairo_hash_table_create: - * @keys_equal: a function to return %TRUE if two keys are equal - * - * Creates a new hash table which will use the keys_equal() function - * to compare hash keys. Data is provided to the hash table in the - * form of user-derived versions of #cairo_hash_entry_t. A hash entry - * must be able to hold both a key (including a hash code) and a - * value. Sometimes only the key will be necessary, (as in - * _cairo_hash_table_remove), and other times both a key and a value - * will be necessary, (as in _cairo_hash_table_insert). - * - * See #cairo_hash_entry_t for more details. - * - * Return value: the new hash table or %NULL if out of memory. - **/ -cairo_hash_table_t * -_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) -{ - cairo_hash_table_t *hash_table; - - hash_table = malloc (sizeof (cairo_hash_table_t)); - if (unlikely (hash_table == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - hash_table->keys_equal = keys_equal; - - hash_table->arrangement = &hash_table_arrangements[0]; - - hash_table->entries = calloc (hash_table->arrangement->size, - sizeof(cairo_hash_entry_t *)); - if (unlikely (hash_table->entries == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - free (hash_table); - return NULL; - } - - hash_table->live_entries = 0; - hash_table->iterating = 0; - - return hash_table; -} - -/** - * _cairo_hash_table_destroy: - * @hash_table: an empty hash table to destroy - * - * Immediately destroys the given hash table, freeing all resources - * associated with it. - * - * WARNING: The hash_table must have no live entries in it before - * _cairo_hash_table_destroy is called. It is a fatal error otherwise, - * and this function will halt. The rationale for this behavior is to - * avoid memory leaks and to avoid needless complication of the API - * with destroy notifiy callbacks. - * - * WARNING: The hash_table must have no running iterators in it when - * _cairo_hash_table_destroy is called. It is a fatal error otherwise, - * and this function will halt. - **/ -void -_cairo_hash_table_destroy (cairo_hash_table_t *hash_table) -{ - /* The hash table must be empty. Otherwise, halt. */ - assert (hash_table->live_entries == 0); - /* No iterators can be running. Otherwise, halt. */ - assert (hash_table->iterating == 0); - - free (hash_table->entries); - hash_table->entries = NULL; - - free (hash_table); -} - -static cairo_hash_entry_t ** -_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key) -{ - unsigned long table_size, i, idx, step; - cairo_hash_entry_t **entry; - - table_size = hash_table->arrangement->size; - idx = key->hash % table_size; - - entry = &hash_table->entries[idx]; - if (! ENTRY_IS_LIVE (*entry)) - return entry; - - i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - do { - idx += step; - if (idx >= table_size) - idx -= table_size; - - entry = &hash_table->entries[idx]; - if (! ENTRY_IS_LIVE (*entry)) - return entry; - } while (++i < table_size); - - ASSERT_NOT_REACHED; - return NULL; -} - -/** - * _cairo_hash_table_resize: - * @hash_table: a hash table - * - * Resize the hash table if the number of entries has gotten much - * bigger or smaller than the ideal number of entries for the current - * size. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if out of memory. - **/ -static cairo_status_t -_cairo_hash_table_resize (cairo_hash_table_t *hash_table) -{ - cairo_hash_table_t tmp; - unsigned long new_size, i; - - /* This keeps the hash table between 25% and 50% full. */ - unsigned long high = hash_table->arrangement->high_water_mark; - unsigned long low = high >> 2; - - if (hash_table->live_entries >= low && hash_table->live_entries <= high) - return CAIRO_STATUS_SUCCESS; - - tmp = *hash_table; - - if (hash_table->live_entries > high) - { - tmp.arrangement = hash_table->arrangement + 1; - /* This code is being abused if we can't make a table big enough. */ - assert (tmp.arrangement - hash_table_arrangements < - NUM_HASH_TABLE_ARRANGEMENTS); - } - else /* hash_table->live_entries < low */ - { - /* Can't shrink if we're at the smallest size */ - if (hash_table->arrangement == &hash_table_arrangements[0]) - return CAIRO_STATUS_SUCCESS; - tmp.arrangement = hash_table->arrangement - 1; - } - - new_size = tmp.arrangement->size; - tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); - if (unlikely (tmp.entries == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < hash_table->arrangement->size; ++i) { - if (ENTRY_IS_LIVE (hash_table->entries[i])) { - *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i]) - = hash_table->entries[i]; - } - } - - free (hash_table->entries); - hash_table->entries = tmp.entries; - hash_table->arrangement = tmp.arrangement; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_hash_table_lookup: - * @hash_table: a hash table - * @key: the key of interest - * - * Performs a lookup in @hash_table looking for an entry which has a - * key that matches @key, (as determined by the keys_equal() function - * passed to _cairo_hash_table_create). - * - * Return value: the matching entry, of %NULL if no match was found. - **/ -void * -_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key) -{ - cairo_hash_entry_t *entry; - unsigned long table_size, i, idx, step; - - table_size = hash_table->arrangement->size; - idx = key->hash % table_size; - - entry = hash_table->entries[idx]; - if (ENTRY_IS_LIVE (entry)) { - if (hash_table->keys_equal (key, entry)) - return entry; - } else if (ENTRY_IS_FREE (entry)) - return NULL; - - i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - do { - idx += step; - if (idx >= table_size) - idx -= table_size; - - entry = hash_table->entries[idx]; - if (ENTRY_IS_LIVE (entry)) { - if (hash_table->keys_equal (key, entry)) - return entry; - } else if (ENTRY_IS_FREE (entry)) - return NULL; - } while (++i < table_size); - - return NULL; -} - -/** - * _cairo_hash_table_random_entry: - * @hash_table: a hash table - * @predicate: a predicate function. - * - * Find a random entry in the hash table satisfying the given - * @predicate. - * - * We use the same algorithm as the lookup algorithm to walk over the - * entries in the hash table in a pseudo-random order. Walking - * linearly would favor entries following gaps in the hash table. We - * could also call rand() repeatedly, which works well for almost-full - * tables, but degrades when the table is almost empty, or predicate - * returns %TRUE for most entries. - * - * Return value: a random live entry or %NULL if there are no entries - * that match the given predicate. In particular, if predicate is - * %NULL, a %NULL return value indicates that the table is empty. - **/ -void * -_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, - cairo_hash_predicate_func_t predicate) -{ - cairo_hash_entry_t *entry; - unsigned long hash; - unsigned long table_size, i, idx, step; - - assert (predicate != NULL); - - table_size = hash_table->arrangement->size; - hash = rand (); - idx = hash % table_size; - - entry = hash_table->entries[idx]; - if (ENTRY_IS_LIVE (entry) && predicate (entry)) - return entry; - - i = 1; - step = hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - do { - idx += step; - if (idx >= table_size) - idx -= table_size; - - entry = hash_table->entries[idx]; - if (ENTRY_IS_LIVE (entry) && predicate (entry)) - return entry; - } while (++i < table_size); - - return NULL; -} - -/** - * _cairo_hash_table_insert: - * @hash_table: a hash table - * @key_and_value: an entry to be inserted - * - * Insert the entry #key_and_value into the hash table. - * - * WARNING: There must not be an existing entry in the hash table - * with a matching key. - * - * WARNING: It is a fatal error to insert an element while - * an iterator is running - * - * Instead of using insert to replace an entry, consider just editing - * the entry obtained with _cairo_hash_table_lookup. Or if absolutely - * necessary, use _cairo_hash_table_remove first. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. - **/ -cairo_status_t -_cairo_hash_table_insert (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key_and_value) -{ - cairo_status_t status; - - /* Insert is illegal while an iterator is running. */ - assert (hash_table->iterating == 0); - - hash_table->live_entries++; - status = _cairo_hash_table_resize (hash_table); - if (unlikely (status)) { - /* abort the insert... */ - hash_table->live_entries--; - return status; - } - - *_cairo_hash_table_lookup_unique_key (hash_table, - key_and_value) = key_and_value; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_hash_entry_t ** -_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key) -{ - unsigned long table_size, i, idx, step; - cairo_hash_entry_t **entry; - - table_size = hash_table->arrangement->size; - idx = key->hash % table_size; - - entry = &hash_table->entries[idx]; - if (*entry == key) - return entry; - - i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - do { - idx += step; - if (idx >= table_size) - idx -= table_size; - - entry = &hash_table->entries[idx]; - if (*entry == key) - return entry; - } while (++i < table_size); - - ASSERT_NOT_REACHED; - return NULL; -} -/** - * _cairo_hash_table_remove: - * @hash_table: a hash table - * @key: key of entry to be removed - * - * Remove an entry from the hash table which points to @key. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful or - * %CAIRO_STATUS_NO_MEMORY if out of memory. - **/ -void -_cairo_hash_table_remove (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key) -{ - *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY; - hash_table->live_entries--; - - /* Check for table resize. Don't do this when iterating as this will - * reorder elements of the table and cause the iteration to potentially - * skip some elements. */ - if (hash_table->iterating == 0) { - /* This call _can_ fail, but only in failing to allocate new - * memory to shrink the hash table. It does leave the table in a - * consistent state, and we've already succeeded in removing the - * entry, so we don't examine the failure status of this call. */ - _cairo_hash_table_resize (hash_table); - } -} - -/** - * _cairo_hash_table_foreach: - * @hash_table: a hash table - * @hash_callback: function to be called for each live entry - * @closure: additional argument to be passed to @hash_callback - * - * Call @hash_callback for each live entry in the hash table, in a - * non-specified order. - * - * Entries in @hash_table may be removed by code executed from @hash_callback. - * - * Entries may not be inserted to @hash_table, nor may @hash_table - * be destroyed by code executed from @hash_callback. The relevant - * functions will halt in these cases. - **/ -void -_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, - cairo_hash_callback_func_t hash_callback, - void *closure) -{ - unsigned long i; - cairo_hash_entry_t *entry; - - /* Mark the table for iteration */ - ++hash_table->iterating; - for (i = 0; i < hash_table->arrangement->size; i++) { - entry = hash_table->entries[i]; - if (ENTRY_IS_LIVE(entry)) - hash_callback (entry, closure); - } - /* If some elements were deleted during the iteration, - * the table may need resizing. Just do this every time - * as the check is inexpensive. - */ - if (--hash_table->iterating == 0) { - /* Should we fail to shrink the hash table, it is left unaltered, - * and we don't need to propagate the error status. */ - _cairo_hash_table_resize (hash_table); - } -} diff --git a/libs/cairo/cairo/src/cairo-hull.c b/libs/cairo/cairo/src/cairo-hull.c deleted file mode 100644 index a9cb27982..000000000 --- a/libs/cairo/cairo/src/cairo-hull.c +++ /dev/null @@ -1,203 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-image-info-private.h b/libs/cairo/cairo/src/cairo-image-info-private.h deleted file mode 100644 index 1107fa4f9..000000000 --- a/libs/cairo/cairo/src/cairo-image-info-private.h +++ /dev/null @@ -1,32 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-image-info.c b/libs/cairo/cairo/src/cairo-image-info.c deleted file mode 100644 index 5269ce25f..000000000 --- a/libs/cairo/cairo/src/cairo-image-info.c +++ /dev/null @@ -1,259 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-image-info-private.h" - -static uint32_t -_get_be32 (const unsigned char *p) -{ - return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -} - -/* JPEG (image/jpeg) - * - * http://www.w3.org/Graphics/JPEG/itu-t81.pdf - */ - -/* Markers with no parameters. All other markers are followed by a two - * byte length of the parameters. */ -#define TEM 0x01 -#define RST_begin 0xd0 -#define RST_end 0xd7 -#define SOI 0xd8 -#define EOI 0xd9 - -/* Start of frame markers. */ -#define SOF0 0xc0 -#define SOF1 0xc1 -#define SOF2 0xc2 -#define SOF3 0xc3 -#define SOF5 0xc5 -#define SOF6 0xc6 -#define SOF7 0xc7 -#define SOF9 0xc9 -#define SOF10 0xca -#define SOF11 0xcb -#define SOF13 0xcd -#define SOF14 0xce -#define SOF15 0xcf - -static const unsigned char * -_jpeg_skip_segment (const unsigned char *p) -{ - int len; - - p++; - len = (p[0] << 8) | p[1]; - - return p + len; -} - -static void -_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p) -{ - info->width = (p[6] << 8) + p[7]; - info->height = (p[4] << 8) + p[5]; - info->num_components = p[8]; - info->bits_per_component = p[3]; -} - -cairo_int_status_t -_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, - const unsigned char *data, - long length) -{ - const unsigned char *p = data; - - while (p + 1 < data + length) { - if (*p != 0xff) - return CAIRO_INT_STATUS_UNSUPPORTED; - p++; - - switch (*p) { - /* skip fill bytes */ - case 0xff: - p++; - break; - - case TEM: - case SOI: - case EOI: - p++; - break; - - case SOF0: - case SOF1: - case SOF2: - case SOF3: - case SOF5: - case SOF6: - case SOF7: - case SOF9: - case SOF10: - case SOF11: - case SOF13: - case SOF14: - case SOF15: - /* Start of frame found. Extract the image parameters. */ - if (p + 8 > data + length) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _jpeg_extract_info (info, p); - return CAIRO_STATUS_SUCCESS; - - default: - if (*p >= RST_begin && *p <= RST_end) { - p++; - break; - } - - if (p + 2 > data + length) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p = _jpeg_skip_segment (p); - break; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -/* JPEG 2000 (image/jp2) - * - * http://www.jpeg.org/public/15444-1annexi.pdf - */ - -#define JPX_FILETYPE 0x66747970 -#define JPX_JP2_HEADER 0x6A703268 -#define JPX_IMAGE_HEADER 0x69686472 - -static const unsigned char _jpx_signature[] = { - 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a -}; - -static const unsigned char * -_jpx_next_box (const unsigned char *p) -{ - return p + _get_be32 (p); -} - -static const unsigned char * -_jpx_get_box_contents (const unsigned char *p) -{ - return p + 8; -} - -static cairo_bool_t -_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) -{ - uint32_t length; - - if (p + 8 < end) { - length = _get_be32 (p); - if (_get_be32 (p + 4) == type && p + length < end) - return TRUE; - } - - return FALSE; -} - -static const unsigned char * -_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) -{ - while (p < end) { - if (_jpx_match_box (p, end, type)) - return p; - p = _jpx_next_box (p); - } - - return NULL; -} - -static void -_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) -{ - info->height = _get_be32 (p); - info->width = _get_be32 (p + 4); - info->num_components = (p[8] << 8) + p[9]; - info->bits_per_component = p[10]; -} - -cairo_int_status_t -_cairo_image_info_get_jpx_info (cairo_image_info_t *info, - const unsigned char *data, - unsigned long length) -{ - const unsigned char *p = data; - const unsigned char *end = data + length; - - /* First 12 bytes must be the JPEG 2000 signature box. */ - if (length < ARRAY_LENGTH(_jpx_signature) || - memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p += ARRAY_LENGTH(_jpx_signature); - - /* Next box must be a File Type Box */ - if (! _jpx_match_box (p, end, JPX_FILETYPE)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p = _jpx_next_box (p); - - /* Locate the JP2 header box. */ - p = _jpx_find_box (p, end, JPX_JP2_HEADER); - if (!p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Step into the JP2 header box. First box must be the Image - * Header */ - p = _jpx_get_box_contents (p); - if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Get the image info */ - p = _jpx_get_box_contents (p); - _jpx_extract_info (p, info); - - return CAIRO_STATUS_SUCCESS; -} - -/* PNG (image/png) - * - * http://www.w3.org/TR/2003/REC-PNG-20031110/ - */ - -#define PNG_IHDR 0x49484452 - -static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - -cairo_int_status_t -_cairo_image_info_get_png_info (cairo_image_info_t *info, - const unsigned char *data, - unsigned long length) -{ - const unsigned char *p = data; - const unsigned char *end = data + length; - - if (length < 8 || memcmp (data, _png_magic, 8) != 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p += 8; - - /* The first chunk must be IDHR. IDHR has 13 bytes of data plus - * the 12 bytes of overhead for the chunk. */ - if (p + 13 + 12 > end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p += 4; - if (_get_be32 (p) != PNG_IHDR) - return CAIRO_INT_STATUS_UNSUPPORTED; - - p += 4; - info->width = _get_be32 (p); - p += 4; - info->height = _get_be32 (p); - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-image-surface.c b/libs/cairo/cairo/src/cairo-image-surface.c deleted file mode 100644 index cc496ece5..000000000 --- a/libs/cairo/cairo/src/cairo-image-surface.c +++ /dev/null @@ -1,4743 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-scaled-font-private.h" -#include "cairo-surface-snapshot-private.h" -#include "cairo-surface-subsurface-private.h" - -/* Limit on the width / height of an image surface in pixels. This is - * mainly determined by coordinates of things sent to pixman at the - * moment being in 16.16 format. */ -#define MAX_IMAGE_SIZE 32767 -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ - -/** - * SECTION:cairo-image - * @Title: Image Surfaces - * @Short_Description: Rendering to memory buffers - * @See_Also: #cairo_surface_t - * - * Image surfaces provide the ability to render to memory buffers - * either allocated by cairo or by the calling code. The supported - * image formats are those defined in #cairo_format_t. - */ - -/** - * CAIRO_HAS_IMAGE_SURFACE: - * - * Defined if the image surface backend is available. - * The image surface backend is always built in. - * This macro was added for completeness in cairo 1.8. - * - * @Since: 1.8 - */ - -static cairo_int_status_t -_cairo_image_surface_fill (void *dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -static pixman_image_t * -_pixman_image_for_solid (const cairo_solid_pattern_t *pattern); - -static cairo_bool_t -_cairo_image_surface_is_size_valid (int width, int height) -{ - return 0 <= width && width <= MAX_IMAGE_SIZE && - 0 <= height && height <= MAX_IMAGE_SIZE; -} - -cairo_format_t -_cairo_format_from_pixman_format (pixman_format_code_t pixman_format) -{ - switch (pixman_format) { - case PIXMAN_a8r8g8b8: - return CAIRO_FORMAT_ARGB32; - case PIXMAN_x8r8g8b8: - return CAIRO_FORMAT_RGB24; - case PIXMAN_a8: - return CAIRO_FORMAT_A8; - case PIXMAN_a1: - return CAIRO_FORMAT_A1; - case PIXMAN_r5g6b5: - return CAIRO_FORMAT_RGB16_565; - case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8: - case PIXMAN_b8g8r8: case PIXMAN_b5g6r5: - case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: - case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: - case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: - case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: - case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4: - case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: - case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: - case PIXMAN_g4: case PIXMAN_g1: - case PIXMAN_yuy2: case PIXMAN_yv12: - case PIXMAN_b8g8r8x8: - case PIXMAN_b8g8r8a8: - case PIXMAN_x2b10g10r10: - case PIXMAN_a2b10g10r10: - case PIXMAN_x2r10g10b10: - case PIXMAN_a2r10g10b10: - default: - return CAIRO_FORMAT_INVALID; - } - - return CAIRO_FORMAT_INVALID; -} - -cairo_content_t -_cairo_content_from_pixman_format (pixman_format_code_t pixman_format) -{ - cairo_content_t content; - - content = 0; - if (PIXMAN_FORMAT_RGB (pixman_format)) - content |= CAIRO_CONTENT_COLOR; - if (PIXMAN_FORMAT_A (pixman_format)) - content |= CAIRO_CONTENT_ALPHA; - - return content; -} - -cairo_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - pixman_format_code_t pixman_format) -{ - cairo_image_surface_t *surface; - int width = pixman_image_get_width (pixman_image); - int height = pixman_image_get_height (pixman_image); - - surface = malloc (sizeof (cairo_image_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_image_surface_backend, - NULL, /* device */ - _cairo_content_from_pixman_format (pixman_format)); - - surface->pixman_image = pixman_image; - - surface->pixman_format = pixman_format; - surface->format = _cairo_format_from_pixman_format (pixman_format); - surface->data = (uint8_t *) pixman_image_get_data (pixman_image); - surface->owns_data = FALSE; - surface->transparency = CAIRO_IMAGE_UNKNOWN; - - surface->width = width; - surface->height = height; - surface->stride = pixman_image_get_stride (pixman_image); - surface->depth = pixman_image_get_depth (pixman_image); - - return &surface->base; -} - -cairo_bool_t -_pixman_format_from_masks (cairo_format_masks_t *masks, - pixman_format_code_t *format_ret) -{ - pixman_format_code_t format; - int format_type; - int a, r, g, b; - cairo_format_masks_t format_masks; - - a = _cairo_popcount (masks->alpha_mask); - r = _cairo_popcount (masks->red_mask); - g = _cairo_popcount (masks->green_mask); - b = _cairo_popcount (masks->blue_mask); - - if (masks->red_mask) { - if (masks->red_mask > masks->blue_mask) - format_type = PIXMAN_TYPE_ARGB; - else - format_type = PIXMAN_TYPE_ABGR; - } else if (masks->alpha_mask) { - format_type = PIXMAN_TYPE_A; - } else { - return FALSE; - } - - format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b); - - if (! pixman_format_supported_destination (format)) - return FALSE; - - /* Sanity check that we got out of PIXMAN_FORMAT exactly what we - * expected. This avoid any problems from something bizarre like - * alpha in the least-significant bits, or insane channel order, - * or whatever. */ - if (!_pixman_format_to_masks (format, &format_masks) || - masks->bpp != format_masks.bpp || - masks->red_mask != format_masks.red_mask || - masks->green_mask != format_masks.green_mask || - masks->blue_mask != format_masks.blue_mask) - { - return FALSE; - } - - *format_ret = format; - return TRUE; -} - -/* A mask consisting of N bits set to 1. */ -#define MASK(N) ((1UL << (N))-1) - -cairo_bool_t -_pixman_format_to_masks (pixman_format_code_t format, - cairo_format_masks_t *masks) -{ - int a, r, g, b; - - masks->bpp = PIXMAN_FORMAT_BPP (format); - - /* Number of bits in each channel */ - a = PIXMAN_FORMAT_A (format); - r = PIXMAN_FORMAT_R (format); - g = PIXMAN_FORMAT_G (format); - b = PIXMAN_FORMAT_B (format); - - switch (PIXMAN_FORMAT_TYPE (format)) { - case PIXMAN_TYPE_ARGB: - masks->alpha_mask = MASK (a) << (r + g + b); - masks->red_mask = MASK (r) << (g + b); - masks->green_mask = MASK (g) << (b); - masks->blue_mask = MASK (b); - return TRUE; - case PIXMAN_TYPE_ABGR: - masks->alpha_mask = MASK (a) << (b + g + r); - masks->blue_mask = MASK (b) << (g + r); - masks->green_mask = MASK (g) << (r); - masks->red_mask = MASK (r); - return TRUE; -#ifdef PIXMAN_TYPE_BGRA - case PIXMAN_TYPE_BGRA: - masks->blue_mask = MASK (b) << (masks->bpp - b); - masks->green_mask = MASK (g) << (masks->bpp - b - g); - masks->red_mask = MASK (r) << (masks->bpp - b - g - r); - masks->alpha_mask = MASK (a); - return TRUE; -#endif - case PIXMAN_TYPE_A: - masks->alpha_mask = MASK (a); - masks->red_mask = 0; - masks->green_mask = 0; - masks->blue_mask = 0; - return TRUE; - case PIXMAN_TYPE_OTHER: - case PIXMAN_TYPE_COLOR: - case PIXMAN_TYPE_GRAY: - case PIXMAN_TYPE_YUY2: - case PIXMAN_TYPE_YV12: - default: - masks->alpha_mask = 0; - masks->red_mask = 0; - masks->green_mask = 0; - masks->blue_mask = 0; - return FALSE; - } -} - -pixman_format_code_t -_cairo_format_to_pixman_format_code (cairo_format_t format) -{ - pixman_format_code_t ret; - switch (format) { - case CAIRO_FORMAT_A1: - ret = PIXMAN_a1; - break; - case CAIRO_FORMAT_A8: - ret = PIXMAN_a8; - break; - case CAIRO_FORMAT_RGB24: - ret = PIXMAN_x8r8g8b8; - break; - case CAIRO_FORMAT_RGB16_565: - ret = PIXMAN_r5g6b5; - break; - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_INVALID: - default: - ret = PIXMAN_a8r8g8b8; - break; - } - return ret; -} - -cairo_surface_t * -_cairo_image_surface_create_with_pixman_format (unsigned char *data, - pixman_format_code_t pixman_format, - int width, - int height, - int stride) -{ - cairo_surface_t *surface; - pixman_image_t *pixman_image; - - if (! _cairo_image_surface_is_size_valid (width, height)) - { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - } - - pixman_image = pixman_image_create_bits (pixman_format, width, height, - (uint32_t *) data, stride ? stride : 4); - - if (unlikely (pixman_image == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - if (unlikely (surface->status)) { - pixman_image_unref (pixman_image); - return surface; - } - - /* we can not make any assumptions about the initial state of user data */ - surface->is_clear = data == NULL; - return surface; -} - -/** - * cairo_image_surface_create: - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates an image surface of the specified format and - * dimensions. Initially the surface contents are all - * 0. (Specifically, within each pixel, each color or alpha channel - * belonging to format will be 0. The contents of bits within a pixel, - * but not belonging to the given format are undefined). - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - **/ -cairo_surface_t * -cairo_image_surface_create (cairo_format_t format, - int width, - int height) -{ - pixman_format_code_t pixman_format; - - if (! CAIRO_FORMAT_VALID (format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - pixman_format = _cairo_format_to_pixman_format_code (format); - - return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, - width, height, -1); -} -slim_hidden_def (cairo_image_surface_create); - -cairo_surface_t * -_cairo_image_surface_create_with_content (cairo_content_t content, - int width, - int height) -{ - return cairo_image_surface_create (_cairo_format_from_content (content), - width, height); -} - -/** - * cairo_format_stride_for_width: - * @format: A #cairo_format_t value - * @width: The desired width of an image surface to be created. - * - * This function provides a stride value that will respect all - * alignment requirements of the accelerated image-rendering code - * within cairo. Typical usage will be of the form: - * - * - * int stride; - * unsigned char *data; - * #cairo_surface_t *surface; - * - * stride = cairo_format_stride_for_width (format, width); - * data = malloc (stride * height); - * surface = cairo_image_surface_create_for_data (data, format, - * width, height, - * stride); - * - * - * Return value: the appropriate stride to use given the desired - * format and width, or -1 if either the format is invalid or the width - * too large. - * - * Since: 1.6 - **/ -int -cairo_format_stride_for_width (cairo_format_t format, - int width) -{ - int bpp; - - if (! CAIRO_FORMAT_VALID (format)) { - _cairo_error_throw (CAIRO_STATUS_INVALID_FORMAT); - return -1; - } - - bpp = _cairo_format_bits_per_pixel (format); - if ((unsigned) (width) >= (INT32_MAX - 7) / (unsigned) (bpp)) - return -1; - - return CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); -} -slim_hidden_def (cairo_format_stride_for_width); - -/** - * cairo_image_surface_create_for_data: - * @data: a pointer to a buffer supplied by the application in which - * to write contents. This pointer must be suitably aligned for any - * kind of variable, (for example, a pointer returned by malloc). - * @format: the format of pixels in the buffer - * @width: the width of the image to be stored in the buffer - * @height: the height of the image to be stored in the buffer - * @stride: the number of bytes between the start of rows in the - * buffer as allocated. This value should always be computed by - * cairo_format_stride_for_width() before allocating the data - * buffer. - * - * Creates an image surface for the provided pixel data. The output - * buffer must be kept around until the #cairo_surface_t is destroyed - * or cairo_surface_finish() is called on the surface. The initial - * contents of @data will be used as the initial image contents; you - * must explicitly clear the buffer, using, for example, - * cairo_rectangle() and cairo_fill() if you want it cleared. - * - * Note that the stride may be larger than - * width*bytes_per_pixel to provide proper alignment for each pixel - * and row. This alignment is required to allow high-performance rendering - * within cairo. The correct way to obtain a legal stride value is to - * call cairo_format_stride_for_width() with the desired format and - * maximum image width value, and then use the resulting stride value - * to allocate the data and to create the image surface. See - * cairo_format_stride_for_width() for example code. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface in the case of an error such as out of - * memory or an invalid stride value. In case of invalid stride value - * the error status of the returned surface will be - * %CAIRO_STATUS_INVALID_STRIDE. You can use - * cairo_surface_status() to check for this. - * - * See cairo_surface_set_user_data() for a means of attaching a - * destroy-notification fallback to the surface if necessary. - **/ -cairo_surface_t * -cairo_image_surface_create_for_data (unsigned char *data, - cairo_format_t format, - int width, - int height, - int stride) -{ - pixman_format_code_t pixman_format; - int minstride; - - if (! CAIRO_FORMAT_VALID (format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); - - if (! _cairo_image_surface_is_size_valid (width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - minstride = cairo_format_stride_for_width (format, width); - if (stride < 0) { - if (stride > -minstride) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); - } - } else { - if (stride < minstride) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); - } - } - - pixman_format = _cairo_format_to_pixman_format_code (format); - return _cairo_image_surface_create_with_pixman_format (data, - pixman_format, - width, height, - stride); -} -slim_hidden_def (cairo_image_surface_create_for_data); - -/** - * cairo_image_surface_get_data: - * @surface: a #cairo_image_surface_t - * - * Get a pointer to the data of the image surface, for direct - * inspection or modification. - * - * Return value: a pointer to the image data of this surface or %NULL - * if @surface is not an image surface, or if cairo_surface_finish() - * has been called. - * - * Since: 1.2 - **/ -unsigned char * -cairo_image_surface_get_data (cairo_surface_t *surface) -{ - cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - if (! _cairo_surface_is_image (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return image_surface->data; -} -slim_hidden_def (cairo_image_surface_get_data); - -/** - * cairo_image_surface_get_format: - * @surface: a #cairo_image_surface_t - * - * Get the format of the surface. - * - * Return value: the format of the surface - * - * Since: 1.2 - **/ -cairo_format_t -cairo_image_surface_get_format (cairo_surface_t *surface) -{ - cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - if (! _cairo_surface_is_image (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return CAIRO_FORMAT_INVALID; - } - - return image_surface->format; -} -slim_hidden_def (cairo_image_surface_get_format); - -/** - * cairo_image_surface_get_width: - * @surface: a #cairo_image_surface_t - * - * Get the width of the image surface in pixels. - * - * Return value: the width of the surface in pixels. - **/ -int -cairo_image_surface_get_width (cairo_surface_t *surface) -{ - cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - if (! _cairo_surface_is_image (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return image_surface->width; -} -slim_hidden_def (cairo_image_surface_get_width); - -/** - * cairo_image_surface_get_height: - * @surface: a #cairo_image_surface_t - * - * Get the height of the image surface in pixels. - * - * Return value: the height of the surface in pixels. - **/ -int -cairo_image_surface_get_height (cairo_surface_t *surface) -{ - cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - if (! _cairo_surface_is_image (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return image_surface->height; -} -slim_hidden_def (cairo_image_surface_get_height); - -/** - * cairo_image_surface_get_stride: - * @surface: a #cairo_image_surface_t - * - * Get the stride of the image surface in bytes - * - * Return value: the stride of the image surface in bytes (or 0 if - * @surface is not an image surface). The stride is the distance in - * bytes from the beginning of one row of the image data to the - * beginning of the next row. - * - * Since: 1.2 - **/ -int -cairo_image_surface_get_stride (cairo_surface_t *surface) -{ - - cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - if (! _cairo_surface_is_image (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return image_surface->stride; -} -slim_hidden_def (cairo_image_surface_get_stride); - -cairo_format_t -_cairo_format_from_content (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_COLOR: - return CAIRO_FORMAT_RGB24; - case CAIRO_CONTENT_ALPHA: - return CAIRO_FORMAT_A8; - case CAIRO_CONTENT_COLOR_ALPHA: - return CAIRO_FORMAT_ARGB32; - } - - ASSERT_NOT_REACHED; - return CAIRO_FORMAT_INVALID; -} - -cairo_content_t -_cairo_content_from_format (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: - return CAIRO_CONTENT_COLOR_ALPHA; - case CAIRO_FORMAT_RGB24: - return CAIRO_CONTENT_COLOR; - case CAIRO_FORMAT_RGB16_565: - return CAIRO_CONTENT_COLOR; - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_A1: - return CAIRO_CONTENT_ALPHA; - case CAIRO_FORMAT_INVALID: - break; - } - - ASSERT_NOT_REACHED; - return CAIRO_CONTENT_COLOR_ALPHA; -} - -int -_cairo_format_bits_per_pixel (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: - return 32; - case CAIRO_FORMAT_RGB24: - return 32; - case CAIRO_FORMAT_RGB16_565: - return 16; - case CAIRO_FORMAT_A8: - return 8; - case CAIRO_FORMAT_A1: - return 1; - case CAIRO_FORMAT_INVALID: - default: - ASSERT_NOT_REACHED; - return 0; - } -} - -static cairo_surface_t * -_cairo_image_surface_create_similar (void *abstract_other, - cairo_content_t content, - int width, - int height) -{ - cairo_image_surface_t *other = abstract_other; - - if (! _cairo_image_surface_is_size_valid (width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - if (content == other->base.content) { - return _cairo_image_surface_create_with_pixman_format (NULL, - other->pixman_format, - width, height, - 0); - } - - return _cairo_image_surface_create_with_content (content, - width, height); -} - -static cairo_status_t -_cairo_image_surface_finish (void *abstract_surface) -{ - cairo_image_surface_t *surface = abstract_surface; - - if (surface->pixman_image) { - pixman_image_unref (surface->pixman_image); - surface->pixman_image = NULL; - } - - if (surface->owns_data) { - free (surface->data); - surface->data = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) -{ - surface->owns_data = TRUE; -} - -static cairo_status_t -_cairo_image_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - *image_out = abstract_surface; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -/* XXX: I think we should fix pixman to match the names/order of the - * cairo operators, but that will likely be better done at the same - * time the X server is ported to pixman, (which will change a lot of - * things in pixman I think). - */ -static pixman_op_t -_pixman_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return PIXMAN_OP_CLEAR; - - case CAIRO_OPERATOR_SOURCE: - return PIXMAN_OP_SRC; - case CAIRO_OPERATOR_OVER: - return PIXMAN_OP_OVER; - case CAIRO_OPERATOR_IN: - return PIXMAN_OP_IN; - case CAIRO_OPERATOR_OUT: - return PIXMAN_OP_OUT; - case CAIRO_OPERATOR_ATOP: - return PIXMAN_OP_ATOP; - - case CAIRO_OPERATOR_DEST: - return PIXMAN_OP_DST; - case CAIRO_OPERATOR_DEST_OVER: - return PIXMAN_OP_OVER_REVERSE; - case CAIRO_OPERATOR_DEST_IN: - return PIXMAN_OP_IN_REVERSE; - case CAIRO_OPERATOR_DEST_OUT: - return PIXMAN_OP_OUT_REVERSE; - case CAIRO_OPERATOR_DEST_ATOP: - return PIXMAN_OP_ATOP_REVERSE; - - case CAIRO_OPERATOR_XOR: - return PIXMAN_OP_XOR; - case CAIRO_OPERATOR_ADD: - return PIXMAN_OP_ADD; - case CAIRO_OPERATOR_SATURATE: - return PIXMAN_OP_SATURATE; - - case CAIRO_OPERATOR_MULTIPLY: - return PIXMAN_OP_MULTIPLY; - case CAIRO_OPERATOR_SCREEN: - return PIXMAN_OP_SCREEN; - case CAIRO_OPERATOR_OVERLAY: - return PIXMAN_OP_OVERLAY; - case CAIRO_OPERATOR_DARKEN: - return PIXMAN_OP_DARKEN; - case CAIRO_OPERATOR_LIGHTEN: - return PIXMAN_OP_LIGHTEN; - case CAIRO_OPERATOR_COLOR_DODGE: - return PIXMAN_OP_COLOR_DODGE; - case CAIRO_OPERATOR_COLOR_BURN: - return PIXMAN_OP_COLOR_BURN; - case CAIRO_OPERATOR_HARD_LIGHT: - return PIXMAN_OP_HARD_LIGHT; - case CAIRO_OPERATOR_SOFT_LIGHT: - return PIXMAN_OP_SOFT_LIGHT; - case CAIRO_OPERATOR_DIFFERENCE: - return PIXMAN_OP_DIFFERENCE; - case CAIRO_OPERATOR_EXCLUSION: - return PIXMAN_OP_EXCLUSION; - case CAIRO_OPERATOR_HSL_HUE: - return PIXMAN_OP_HSL_HUE; - case CAIRO_OPERATOR_HSL_SATURATION: - return PIXMAN_OP_HSL_SATURATION; - case CAIRO_OPERATOR_HSL_COLOR: - return PIXMAN_OP_HSL_COLOR; - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return PIXMAN_OP_HSL_LUMINOSITY; - - default: - ASSERT_NOT_REACHED; - return PIXMAN_OP_OVER; - } -} - -static cairo_status_t -_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, - cairo_region_t *region) -{ - if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface) -{ - pixman_image_set_clip_region32 (surface->pixman_image, NULL); -} - -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_bool_t -_nearest_sample (cairo_filter_t filter, double *tx, double *ty) -{ - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { - *tx = _pixman_nearest_sample (*tx); - *ty = _pixman_nearest_sample (*ty); - } else { - if (*tx != floor (*tx) || *ty != floor (*ty)) - return FALSE; - } - return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; -} - -#if PIXMAN_HAS_ATOMIC_OPS -static pixman_image_t *__pixman_transparent_image; -static pixman_image_t *__pixman_black_image; -static pixman_image_t *__pixman_white_image; - -static pixman_image_t * -_pixman_transparent_image (void) -{ - pixman_image_t *image; - - image = __pixman_transparent_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0x00; - color.green = 0x00; - color.blue = 0x00; - color.alpha = 0x00; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static pixman_image_t * -_pixman_black_image (void) -{ - pixman_image_t *image; - - image = __pixman_black_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0x00; - color.green = 0x00; - color.blue = 0x00; - color.alpha = 0xffff; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static pixman_image_t * -_pixman_white_image (void) -{ - pixman_image_t *image; - - image = __pixman_white_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0xffff; - color.green = 0xffff; - color.blue = 0xffff; - color.alpha = 0xffff; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static uint32_t -hars_petruska_f54_1_random (void) -{ -#define rol(x,k) ((x << k) | (x >> (32-k))) - static uint32_t x; - return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; -#undef rol -} - -static struct { - cairo_color_t color; - pixman_image_t *image; -} cache[16]; -static int n_cached; - -#else /* !PIXMAN_HAS_ATOMIC_OPS */ -static pixman_image_t * -_pixman_transparent_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_clear); -} - -static pixman_image_t * -_pixman_black_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_black); -} - -static pixman_image_t * -_pixman_white_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_white); -} -#endif /* !PIXMAN_HAS_ATOMIC_OPS */ - -void -_cairo_image_reset_static_data (void) -{ -#if PIXMAN_HAS_ATOMIC_OPS - while (n_cached) - pixman_image_unref (cache[--n_cached].image); - - if (__pixman_transparent_image) { - pixman_image_unref (__pixman_transparent_image); - __pixman_transparent_image = NULL; - } - - if (__pixman_black_image) { - pixman_image_unref (__pixman_black_image); - __pixman_black_image = NULL; - } - - if (__pixman_white_image) { - pixman_image_unref (__pixman_white_image); - __pixman_white_image = NULL; - } -#endif -} - -static pixman_image_t * -_pixman_image_for_solid (const cairo_solid_pattern_t *pattern) -{ - pixman_color_t color; - pixman_image_t *image; - -#if PIXMAN_HAS_ATOMIC_OPS - int i; - - if (pattern->color.alpha_short <= 0x00ff) - return _pixman_transparent_image (); - - if (pattern->color.alpha_short >= 0xff00) { - if (pattern->color.red_short <= 0x00ff && - pattern->color.green_short <= 0x00ff && - pattern->color.blue_short <= 0x00ff) - { - return _pixman_black_image (); - } - - if (pattern->color.red_short >= 0xff00 && - pattern->color.green_short >= 0xff00 && - pattern->color.blue_short >= 0xff00) - { - return _pixman_white_image (); - } - } - - CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); - for (i = 0; i < n_cached; i++) { - if (_cairo_color_equal (&cache[i].color, &pattern->color)) { - image = pixman_image_ref (cache[i].image); - goto UNLOCK; - } - } -#endif - - color.red = pattern->color.red_short; - color.green = pattern->color.green_short; - color.blue = pattern->color.blue_short; - color.alpha = pattern->color.alpha_short; - - image = pixman_image_create_solid_fill (&color); -#if PIXMAN_HAS_ATOMIC_OPS - if (image == NULL) - goto UNLOCK; - - if (n_cached < ARRAY_LENGTH (cache)) { - i = n_cached++; - } else { - i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); - pixman_image_unref (cache[i].image); - } - cache[i].image = pixman_image_ref (image); - cache[i].color = pattern->color; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); -#endif - return image; -} - -static double -clamp (double val, double min, double max) -{ - return val < min ? min : (val > max ? max : val); -} - -static pixman_image_t * -_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - int *ix, int *iy) -{ - pixman_image_t *pixman_image; - pixman_gradient_stop_t pixman_stops_static[2]; - pixman_gradient_stop_t *pixman_stops = pixman_stops_static; - cairo_matrix_t matrix = pattern->base.matrix; - double tx, ty; - unsigned int i; - - if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, - sizeof(pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return NULL; - } - - for (i = 0; i < pattern->n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); - pixman_stops[i].color.red = pattern->stops[i].color.red_short; - pixman_stops[i].color.green = pattern->stops[i].color.green_short; - pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; - pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - pixman_point_fixed_t p1, p2; - double x0, y0, x1, y1, maxabs; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ - x0 = _cairo_fixed_to_double (linear->p1.x); - y0 = _cairo_fixed_to_double (linear->p1.y); - x1 = _cairo_fixed_to_double (linear->p2.x); - y1 = _cairo_fixed_to_double (linear->p2.y); - cairo_matrix_transform_point (&matrix, &x0, &y0); - cairo_matrix_transform_point (&matrix, &x1, &y1); - maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); - - if (maxabs > PIXMAN_MAX_INT) - { - double sf; - cairo_matrix_t scale; - - sf = PIXMAN_MAX_INT / maxabs; - - p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - /* cairo_matrix_scale does a pre-scale, we want a post-scale */ - cairo_matrix_init_scale (&scale, sf, sf); - cairo_matrix_multiply (&matrix, &matrix, &scale); - } - else - { - p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - pixman_image = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - pattern->n_stops); - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - pixman_point_fixed_t c1, c2; - pixman_fixed_t r1, r2; - - c1.x = _cairo_fixed_to_16_16 (radial->c1.x); - c1.y = _cairo_fixed_to_16_16 (radial->c1.y); - r1 = _cairo_fixed_to_16_16 (radial->r1); - - c2.x = _cairo_fixed_to_16_16 (radial->c2.x); - c2.y = _cairo_fixed_to_16_16 (radial->c2.y); - r2 = _cairo_fixed_to_16_16 (radial->r2); - - pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2, - pixman_stops, - pattern->n_stops); - } - - if (pixman_stops != pixman_stops_static) - free (pixman_stops); - - if (unlikely (pixman_image == NULL)) - return NULL; - - tx = matrix.x0; - ty = matrix.y0; - if (! _cairo_matrix_is_translation (&matrix) || - ! _nearest_sample (pattern->base.filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - - if (tx != 0. || ty != 0.) { - cairo_matrix_t m, inv; - cairo_status_t status; - double x, y, max_x, max_y; - - /* Pixman also limits the [xy]_offset to 16 bits. We try to evenly - * spread the bits between the two, but we need to ensure that - * fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT && - * fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT, - * otherwise the gradient won't render. - */ - inv = matrix; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = _cairo_lround (inv.x0 / 2); - y = _cairo_lround (inv.y0 / 2); - - max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width); - x = clamp(x, -max_x, max_x); - max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height); - y = clamp(y, -max_y, max_y); - - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &matrix); - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } else { - tx = ty = 0; - _cairo_matrix_to_pixman_matrix (&pattern->base.matrix, - &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } - - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { - pixman_image_unref (pixman_image); - return NULL; - } - } - *ix = tx; - *iy = ty; - - { - pixman_repeat_t pixman_repeat; - - switch (pattern->base.extend) { - default: - case CAIRO_EXTEND_NONE: - pixman_repeat = PIXMAN_REPEAT_NONE; - break; - case CAIRO_EXTEND_REPEAT: - pixman_repeat = PIXMAN_REPEAT_NORMAL; - break; - case CAIRO_EXTEND_REFLECT: - pixman_repeat = PIXMAN_REPEAT_REFLECT; - break; - case CAIRO_EXTEND_PAD: - pixman_repeat = PIXMAN_REPEAT_PAD; - break; - } - - pixman_image_set_repeat (pixman_image, pixman_repeat); - } - - return pixman_image; -} - -struct acquire_source_cleanup { - cairo_surface_t *surface; - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -_acquire_source_cleanup (pixman_image_t *pixman_image, - void *closure) -{ - struct acquire_source_cleanup *data = closure; - - _cairo_surface_release_source_image (data->surface, - data->image, - data->image_extra); - free (data); -} - -static cairo_filter_t -sampled_area (const cairo_surface_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - cairo_rectangle_int_t *sample) -{ - cairo_filter_t filter; - double x1, x2, y1, y2; - double pad; - - x1 = extents->x; - y1 = extents->y; - x2 = extents->x + (int) extents->width; - y2 = extents->y + (int) extents->height; - - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, - &x1, &y1, &x2, &y2, - NULL); - - filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); - sample->x = floor (x1 - pad); - sample->y = floor (y1 - pad); - sample->width = ceil (x2 + pad) - sample->x; - sample->height = ceil (y2 + pad) - sample->y; - - return filter; -} - -static uint16_t -expand_channel (uint16_t v, uint32_t bits) -{ - int offset = 16 - bits; - while (offset > 0) { - v |= v >> bits; - offset -= bits; - bits += bits; - } - return v; -} - -static pixman_image_t * -_pixel_to_solid (cairo_image_surface_t *image, int x, int y) -{ - uint32_t pixel; - pixman_color_t color; - - switch (image->format) { - default: - case CAIRO_FORMAT_INVALID: - ASSERT_NOT_REACHED; - return NULL; - - case CAIRO_FORMAT_A1: - pixel = *(uint8_t *) (image->data + y * image->stride + x/8); - return pixel & (1 << (x&7)) ? _pixman_white_image () : _pixman_transparent_image (); - - case CAIRO_FORMAT_A8: - color.alpha = *(uint8_t *) (image->data + y * image->stride + x); - color.alpha |= color.alpha << 8; - if (color.alpha == 0) - return _pixman_transparent_image (); - - color.red = color.green = color.blue = 0; - return pixman_image_create_solid_fill (&color); - - case CAIRO_FORMAT_RGB16_565: - pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); - if (pixel == 0) - return _pixman_black_image (); - if (pixel == 0xffff) - return _pixman_white_image (); - - color.alpha = 0xffff; - color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); - color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); - color.blue = expand_channel ((pixel & 0x1f) << 11, 5); - return pixman_image_create_solid_fill (&color); - - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); - color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; - if (color.alpha == 0) - return _pixman_transparent_image (); - if (pixel == 0xffffffff) - return _pixman_white_image (); - if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) - return _pixman_black_image (); - - color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); - color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); - color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); - return pixman_image_create_solid_fill (&color); - } -} - -static pixman_image_t * -_pixman_image_for_surface (const cairo_surface_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - int *ix, int *iy) -{ - pixman_image_t *pixman_image; - cairo_rectangle_int_t sample; - cairo_extend_t extend; - cairo_filter_t filter; - double tx, ty; - - tx = pattern->base.matrix.x0; - ty = pattern->base.matrix.y0; - - extend = pattern->base.extend; - filter = sampled_area (pattern, extents, &sample); - - pixman_image = NULL; - if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && - (! is_mask || ! pattern->base.has_component_alpha || - (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) - { - cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; - cairo_surface_type_t type; - - if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) - source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target; - - type = source->base.backend->type; - if (type == CAIRO_SURFACE_TYPE_IMAGE) { - if (sample.width == 1 && sample.height == 1) { - if (sample.x < 0 || - sample.y < 0 || - sample.x >= source->width || - sample.y >= source->height) - { - if (extend == CAIRO_EXTEND_NONE) - return _pixman_transparent_image (); - } - else - { - return _pixel_to_solid (source, sample.x, sample.y); - } - } - -#if PIXMAN_HAS_ATOMIC_OPS - /* avoid allocating a 'pattern' image if we can reuse the original */ - if (extend == CAIRO_EXTEND_NONE && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) - { - *ix = tx; - *iy = ty; - return pixman_image_ref (source->pixman_image); - } -#endif - - pixman_image = pixman_image_create_bits (source->pixman_format, - source->width, - source->height, - (uint32_t *) source->data, - source->stride); - if (unlikely (pixman_image == NULL)) - return NULL; - } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub; - cairo_bool_t is_contained = FALSE; - - sub = (cairo_surface_subsurface_t *) source; - source = (cairo_image_surface_t *) sub->target; - - if (sample.x >= 0 && - sample.y >= 0 && - sample.x + sample.width <= sub->extents.width && - sample.y + sample.height <= sub->extents.height) - { - is_contained = TRUE; - } - - if (sample.width == 1 && sample.height == 1) { - if (is_contained) { - return _pixel_to_solid (source, - sub->extents.x + sample.x, - sub->extents.y + sample.y); - } else { - if (extend == CAIRO_EXTEND_NONE) - return _pixman_transparent_image (); - } - } - -#if PIXMAN_HAS_ATOMIC_OPS - if (is_contained && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) - { - *ix = tx + sub->extents.x; - *iy = ty + sub->extents.y; - return pixman_image_ref (source->pixman_image); - } -#endif - - /* Avoid sub-byte offsets, force a copy in that case. */ - if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { - void *data = source->data - + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 - + sub->extents.y * source->stride; - pixman_image = pixman_image_create_bits (source->pixman_format, - sub->extents.width, - sub->extents.height, - data, - source->stride); - if (unlikely (pixman_image == NULL)) - return NULL; - } - } - } - - if (pixman_image == NULL) { - struct acquire_source_cleanup *cleanup; - cairo_image_surface_t *image; - void *extra; - cairo_status_t status; - - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); - if (unlikely (status)) - return NULL; - - if (sample.width == 1 && sample.height == 1) { - if (sample.x < 0 || - sample.y < 0 || - sample.x >= image->width || - sample.y >= image->height) - { - if (extend == CAIRO_EXTEND_NONE) { - pixman_image = _pixman_transparent_image (); - _cairo_surface_release_source_image (pattern->surface, image, extra); - return pixman_image; - } - } - else - { - pixman_image = _pixel_to_solid (image, sample.x, sample.y); - _cairo_surface_release_source_image (pattern->surface, image, extra); - return pixman_image; - } - } - - pixman_image = pixman_image_create_bits (image->pixman_format, - image->width, - image->height, - (uint32_t *) image->data, - image->stride); - if (unlikely (pixman_image == NULL)) { - _cairo_surface_release_source_image (pattern->surface, image, extra); - return NULL; - } - - cleanup = malloc (sizeof (*cleanup)); - if (unlikely (cleanup == NULL)) { - _cairo_surface_release_source_image (pattern->surface, image, extra); - pixman_image_unref (pixman_image); - return NULL; - } - - cleanup->surface = pattern->surface; - cleanup->image = image; - cleanup->image_extra = extra; - pixman_image_set_destroy_function (pixman_image, - _acquire_source_cleanup, cleanup); - } - - if (! _cairo_matrix_is_translation (&pattern->base.matrix) || - ! _nearest_sample (filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - cairo_matrix_t m; - - m = pattern->base.matrix; - if (m.x0 != 0. || m.y0 != 0.) { - cairo_matrix_t inv; - cairo_status_t status; - double x, y; - - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - inv = m; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = floor (inv.x0 / 2); - y = floor (inv.y0 / 2); - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &m); - } else { - tx = ty = 0; - } - - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { - pixman_image_unref (pixman_image); - return NULL; - } - } - *ix = tx; - *iy = ty; - - if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) && - tx == pattern->base.matrix.x0 && - ty == pattern->base.matrix.y0) - { - pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); - } - else - { - pixman_filter_t pixman_filter; - - switch (filter) { - case CAIRO_FILTER_FAST: - pixman_filter = PIXMAN_FILTER_FAST; - break; - case CAIRO_FILTER_GOOD: - pixman_filter = PIXMAN_FILTER_GOOD; - break; - case CAIRO_FILTER_BEST: - pixman_filter = PIXMAN_FILTER_BEST; - break; - case CAIRO_FILTER_NEAREST: - pixman_filter = PIXMAN_FILTER_NEAREST; - break; - case CAIRO_FILTER_BILINEAR: - pixman_filter = PIXMAN_FILTER_BILINEAR; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - pixman_filter = PIXMAN_FILTER_BEST; - } - - pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); - } - - { - pixman_repeat_t pixman_repeat; - - switch (extend) { - default: - case CAIRO_EXTEND_NONE: - pixman_repeat = PIXMAN_REPEAT_NONE; - break; - case CAIRO_EXTEND_REPEAT: - pixman_repeat = PIXMAN_REPEAT_NORMAL; - break; - case CAIRO_EXTEND_REFLECT: - pixman_repeat = PIXMAN_REPEAT_REFLECT; - break; - case CAIRO_EXTEND_PAD: - pixman_repeat = PIXMAN_REPEAT_PAD; - break; - } - - pixman_image_set_repeat (pixman_image, pixman_repeat); - } - - if (pattern->base.has_component_alpha) - pixman_image_set_component_alpha (pixman_image, TRUE); - - return pixman_image; -} - -static pixman_image_t * -_pixman_image_for_pattern (const cairo_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - int *tx, int *ty) -{ - *tx = *ty = 0; - - if (pattern == NULL) - return _pixman_white_image (); - - switch (pattern->type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); - - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_LINEAR: - return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, - extents, tx, ty); - - case CAIRO_PATTERN_TYPE_SURFACE: - return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern, - is_mask, extents, tx, ty); - } -} - -static cairo_status_t -_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst, - const cairo_composite_rectangles_t *rects, - cairo_clip_t *clip) -{ - pixman_image_t *mask = NULL; - pixman_box32_t boxes[4]; - int i, mask_x = 0, mask_y = 0, n_boxes = 0; - - if (clip != NULL) { - cairo_surface_t *clip_surface; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; - mask_x = -clip_x; - mask_y = -clip_y; - } else { - if (rects->bounded.width == rects->unbounded.width && - rects->bounded.height == rects->unbounded.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - /* wholly unbounded? */ - if (rects->bounded.width == 0 || rects->bounded.height == 0) { - int x = rects->unbounded.x; - int y = rects->unbounded.y; - int width = rects->unbounded.width; - int height = rects->unbounded.height; - - if (mask != NULL) { - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - x + mask_x, y + mask_y, - 0, 0, - x, y, - width, height); - } else { - pixman_color_t color = { 0, }; - pixman_box32_t box = { x, y, x + width, y + height }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - 1, &box)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; - } - - /* top */ - if (rects->bounded.y != rects->unbounded.y) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->unbounded.y; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->bounded.y; - n_boxes++; - } - - /* left */ - if (rects->bounded.x != rects->unbounded.x) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->bounded.y; - boxes[n_boxes].x2 = rects->bounded.x; - boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; - n_boxes++; - } - - /* right */ - if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { - boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width; - boxes[n_boxes].y1 = rects->bounded.y; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; - n_boxes++; - } - - /* bottom */ - if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height; - n_boxes++; - } - - if (mask != NULL) { - for (i = 0; i < n_boxes; i++) { - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - boxes[i].x1 + mask_x, boxes[i].y1 + mask_y, - 0, 0, - boxes[i].x1, boxes[i].y1, - boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1); - } - } else { - pixman_color_t color = { 0, }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - n_boxes, - boxes)) - { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst, - const cairo_composite_rectangles_t *extents, - cairo_region_t *clip_region, - cairo_boxes_t *boxes) -{ - cairo_boxes_t clear; - cairo_box_t box; - cairo_status_t status; - struct _cairo_boxes_chunk *chunk; - int i; - - // If we have no boxes then we need to clear the entire extents - // because we have nothing to draw. - if (boxes->num_boxes < 1 && clip_region == NULL) { - int x = extents->unbounded.x; - int y = extents->unbounded.y; - int width = extents->unbounded.width; - int height = extents->unbounded.height; - - pixman_color_t color = { 0 }; - pixman_box32_t box = { x, y, x + width, y + height }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - 1, &box)) { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - return CAIRO_STATUS_SUCCESS; - } - - _cairo_boxes_init (&clear); - - box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); - box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); - box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); - box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); - - if (clip_region == NULL) { - cairo_boxes_t tmp; - - _cairo_boxes_init (&tmp); - - status = _cairo_boxes_add (&tmp, &box); - assert (status == CAIRO_STATUS_SUCCESS); - - tmp.chunks.next = &boxes->chunks; - tmp.num_boxes += boxes->num_boxes; - - status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, - CAIRO_FILL_RULE_WINDING, - &clear); - - tmp.chunks.next = NULL; - } else { - pixman_box32_t *pbox; - - pbox = pixman_region32_rectangles (&clip_region->rgn, &i); - _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); - - status = _cairo_boxes_add (&clear, &box); - assert (status == CAIRO_STATUS_SUCCESS); - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - status = _cairo_boxes_add (&clear, &chunk->base[i]); - if (unlikely (status)) { - _cairo_boxes_fini (&clear); - return status; - } - } - } - - status = _cairo_bentley_ottmann_tessellate_boxes (&clear, - CAIRO_FILL_RULE_WINDING, - &clear); - } - - if (likely (status == CAIRO_STATUS_SUCCESS)) { - for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); - int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); - int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); - int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 <= x1 || y2 <= y1) - continue; - pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - 0); - } - } - } - - _cairo_boxes_fini (&clear); - - return status; -} - -static cairo_bool_t -can_reduce_alpha_op (cairo_operator_t op) -{ - int iop = op; - switch (iop) { - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_ADD: - return TRUE; - default: - return FALSE; - } -} - -static cairo_bool_t -reduce_alpha_op (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - return dst->base.is_clear && - dst->base.content == CAIRO_CONTENT_ALPHA && - _cairo_pattern_is_opaque_solid (pattern) && - can_reduce_alpha_op (op); -} - -/* low level compositor */ -typedef cairo_status_t -(*image_draw_func_t) (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *src, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static pixman_image_t * -_create_composite_mask_pattern (cairo_clip_t *clip, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_region_t *clip_region = NULL; - pixman_image_t *mask; - cairo_status_t status; - cairo_bool_t need_clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (! _cairo_status_is_error (status)); - - /* The all-clipped state should never propagate this far. */ - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - - mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) - return NULL; - - /* Is it worth setting the clip region here? */ - if (clip_region != NULL) { - pixman_bool_t ret; - - pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y); - ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn); - pixman_region32_translate (&clip_region->rgn, extents->x, extents->y); - - if (! ret) { - pixman_image_unref (mask); - return NULL; - } - } - - status = draw_func (draw_closure, - mask, PIXMAN_a8, - CAIRO_OPERATOR_ADD, NULL, - extents->x, extents->y, - extents, NULL); - if (unlikely (status)) { - pixman_image_unref (mask); - return NULL; - } - - if (need_clip_surface) { - cairo_surface_t *tmp; - - tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8); - if (unlikely (tmp->status)) { - pixman_image_unref (mask); - return NULL; - } - - pixman_image_ref (mask); - - status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y); - cairo_surface_destroy (tmp); - if (unlikely (status)) { - pixman_image_unref (mask); - return NULL; - } - } - - if (clip_region != NULL) - pixman_image_set_clip_region (mask, NULL); - - return mask; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *mask; - - mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (pattern == NULL) { - if (dst->pixman_format == PIXMAN_a8) { - pixman_image_composite32 (_pixman_operator (op), - mask, NULL, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - } else { - pixman_image_t *src; - - src = _pixman_white_image (); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - pixman_image_unref (src); - } - } else { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - pixman_image_unref (src); - } - - pixman_image_unref (mask); - - return CAIRO_STATUS_SUCCESS; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *tmp; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - tmp = pixman_image_create_bits (dst->pixman_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (tmp == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (src == NULL) { - status = (*draw_func) (draw_closure, - tmp, dst->pixman_format, - CAIRO_OPERATOR_ADD, NULL, - extents->x, extents->y, - extents, NULL); - } else { - /* Initialize the temporary surface from the destination surface */ - if (! dst->base.is_clear) { - pixman_image_composite32 (PIXMAN_OP_SRC, - dst->pixman_image, NULL, tmp, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height); - } - - status = (*draw_func) (draw_closure, - tmp, dst->pixman_format, - op, src, - extents->x, extents->y, - extents, NULL); - } - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - if (! dst->base.is_clear) { -#if PIXMAN_HAS_OP_LERP - pixman_image_composite32 (PIXMAN_OP_LERP, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); -#else - /* Punch the clip out of the destination */ - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - NULL, dst->pixman_image, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - /* Now add the two results together */ - pixman_image_composite32 (PIXMAN_OP_ADD, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); - } - - CLEANUP_SURFACE: - pixman_image_unref (tmp); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *mask, *src; - int src_x, src_y; - - if (pattern == NULL) { - cairo_region_t *clip_region; - cairo_status_t status; - - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - CAIRO_OPERATOR_SOURCE, NULL, - extents->x, extents->y, - extents, NULL); - if (unlikely (status)) - return status; - - if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0); - - return status; - } - - /* Create a surface that is mask IN clip */ - mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (! dst->base.is_clear) { -#if PIXMAN_HAS_OP_LERP - pixman_image_composite32 (PIXMAN_OP_LERP, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); -#else - /* Compute dest' = dest OUT (mask IN clip) */ - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - pixman_image_composite32 (PIXMAN_OP_ADD, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - } - - pixman_image_unref (src); - pixman_image_unref (mask); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_clip_and_composite (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - image_draw_func_t draw_func, - void *draw_closure, - cairo_composite_rectangles_t*extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_region_t *clip_region = NULL; - cairo_bool_t need_clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (_cairo_status_is_error (status))) - return status; - - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - cairo_bool_t is_empty; - - cairo_region_get_extents (clip_region, &rect); - is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect); - if (unlikely (is_empty)) - return CAIRO_STATUS_SUCCESS; - - is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect); - if (unlikely (is_empty && extents->is_bounded)) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - } - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - if (reduce_alpha_op (dst, op, src)) { - op = CAIRO_OPERATOR_ADD; - src = NULL; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, src, - draw_func, draw_closure, - dst, &extents->bounded); - } else { - if (op == CAIRO_OPERATOR_CLEAR) { - src = NULL; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (need_clip_surface) { - if (extents->is_bounded) { - status = _clip_and_composite_with_mask (clip, op, src, - draw_func, draw_closure, - dst, &extents->bounded); - } else { - status = _clip_and_composite_combine (clip, op, src, - draw_func, draw_closure, - dst, &extents->bounded); - } - } else { - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - op, src, - 0, 0, - &extents->bounded, - clip_region); - } - } - - if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { - status = _cairo_image_surface_fixup_unbounded (dst, extents, - need_clip_surface ? clip : NULL); - } - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) -#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) - -static cairo_bool_t -_line_exceeds_16_16 (const cairo_line_t *line) -{ - return - line->p1.x <= CAIRO_FIXED_16_16_MIN || - line->p1.x >= CAIRO_FIXED_16_16_MAX || - - line->p2.x <= CAIRO_FIXED_16_16_MIN || - line->p2.x >= CAIRO_FIXED_16_16_MAX || - - line->p1.y <= CAIRO_FIXED_16_16_MIN || - line->p1.y >= CAIRO_FIXED_16_16_MAX || - - line->p2.y <= CAIRO_FIXED_16_16_MIN || - line->p2.y >= CAIRO_FIXED_16_16_MAX; -} - -static void -_project_line_x_onto_16_16 (const cairo_line_t *line, - cairo_fixed_t top, - cairo_fixed_t bottom, - pixman_line_fixed_t *out) -{ - cairo_point_double_t p1, p2; - double m; - - p1.x = _cairo_fixed_to_double (line->p1.x); - p1.y = _cairo_fixed_to_double (line->p1.y); - - p2.x = _cairo_fixed_to_double (line->p2.x); - p2.y = _cairo_fixed_to_double (line->p2.y); - - m = (p2.x - p1.x) / (p2.y - p1.y); - out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); - out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); -} - - -typedef struct { - cairo_trapezoid_t *traps; - int num_traps; - cairo_antialias_t antialias; -} composite_traps_info_t; - -static void -_pixman_image_add_traps (pixman_image_t *image, - int dst_x, int dst_y, - composite_traps_info_t *info) -{ - cairo_trapezoid_t *t = info->traps; - int num_traps = info->num_traps; - while (num_traps--) { - pixman_trapezoid_t trap; - - /* top/bottom will be clamped to surface bounds */ - trap.top = _cairo_fixed_to_16_16 (t->top); - trap.bottom = _cairo_fixed_to_16_16 (t->bottom); - - /* However, all the other coordinates will have been left untouched so - * as not to introduce numerical error. Recompute them if they - * exceed the 16.16 limits. - */ - if (unlikely (_line_exceeds_16_16 (&t->left))) { - _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); - trap.left.p1.y = trap.top; - trap.left.p2.y = trap.bottom; - } else { - trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); - } - - if (unlikely (_line_exceeds_16_16 (&t->right))) { - _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); - trap.right.p1.y = trap.top; - trap.right.p2.y = trap.bottom; - } else { - trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); - } - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - - t++; - } -} - -static cairo_status_t -_composite_traps (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_traps_info_t *info = closure; - pixman_image_t *src, *mask; - pixman_format_code_t format; - int src_x = 0, src_y = 0; - cairo_status_t status; - - /* Special case adding trapezoids onto a mask surface; we want to avoid - * creating an intermediate temporary mask unnecessarily. - * - * We make the assumption here that the portion of the trapezoids - * contained within the surface is bounded by [dst_x,dst_y,width,height]; - * the Cairo core code passes bounds based on the trapezoid extents. - */ - format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; - if (dst_format == format && - (pattern == NULL || - (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern)))) - { - _pixman_image_add_traps (dst, dst_x, dst_y, info); - return CAIRO_STATUS_SUCCESS; - } - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = pixman_image_create_bits (format, extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_SOURCE; - } - - _pixman_image_add_traps (mask, extents->x, extents->y, info); - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - - pixman_image_unref (mask); - - status = CAIRO_STATUS_SUCCESS; - CLEANUP_SOURCE: - pixman_image_unref (src); - - return status; -} - -static inline uint32_t -color_to_uint32 (const cairo_color_t *color) -{ - return - (color->alpha_short >> 8 << 24) | - (color->red_short >> 8 << 16) | - (color->green_short & 0xff00) | - (color->blue_short >> 8); -} - -static inline cairo_bool_t -color_to_pixel (const cairo_color_t *color, - pixman_format_code_t format, - uint32_t *pixel) -{ - uint32_t c; - - if (!(format == PIXMAN_a8r8g8b8 || - format == PIXMAN_x8r8g8b8 || - format == PIXMAN_a8b8g8r8 || - format == PIXMAN_x8b8g8r8 || - format == PIXMAN_b8g8r8a8 || - format == PIXMAN_b8g8r8x8 || - format == PIXMAN_r5g6b5 || - format == PIXMAN_b5g6r5 || - format == PIXMAN_a8)) - { - return FALSE; - } - - c = color_to_uint32 (color); - - if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { - c = ((c & 0xff000000) >> 0) | - ((c & 0x00ff0000) >> 16) | - ((c & 0x0000ff00) >> 0) | - ((c & 0x000000ff) << 16); - } - - if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { - c = ((c & 0xff000000) >> 24) | - ((c & 0x00ff0000) >> 8) | - ((c & 0x0000ff00) << 8) | - ((c & 0x000000ff) << 24); - } - - if (format == PIXMAN_a8) { - c = c >> 24; - } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { - c = ((((c) >> 3) & 0x001f) | - (((c) >> 5) & 0x07e0) | - (((c) >> 8) & 0xf800)); - } - - *pixel = c; - return TRUE; -} - -static inline cairo_bool_t -pattern_to_pixel (const cairo_solid_pattern_t *solid, - cairo_operator_t op, - pixman_format_code_t format, - uint32_t *pixel) -{ - if (op == CAIRO_OPERATOR_CLEAR) { - *pixel = 0; - return TRUE; - } - - if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID) - return FALSE; - - if (op == CAIRO_OPERATOR_OVER) { - if (solid->color.alpha_short >= 0xff00) - op = CAIRO_OPERATOR_SOURCE; - } - - if (op != CAIRO_OPERATOR_SOURCE) - return FALSE; - - return color_to_pixel (&solid->color, format, pixel); -} - -typedef struct _fill_span { - cairo_span_renderer_t base; - - uint8_t *mask_data; - pixman_image_t *src, *dst, *mask; -} fill_span_renderer_t; - -static cairo_status_t -_fill_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - fill_span_renderer_t *renderer = abstract_renderer; - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - row = renderer->mask_data - spans[0].x; - for (i = 0; i < num_spans - 1; i++) { - /* We implement setting the most common single pixel wide - * span case to avoid the overhead of a memset call. - * Open coding setting longer spans didn't show a - * noticeable improvement over memset. - */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } - - do { - pixman_image_composite32 (PIXMAN_OP_OVER, - renderer->src, renderer->mask, renderer->dst, - 0, 0, 0, 0, - spans[0].x, y++, - spans[i].x - spans[0].x, 1); - } while (--height); - - return CAIRO_STATUS_SUCCESS; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_unaligned_boxes (cairo_image_surface_t *dst, - const cairo_pattern_t *pattern, - uint32_t pixel, - const cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; - fill_span_renderer_t renderer; - cairo_rectangular_scan_converter_t converter; - const struct _cairo_boxes_chunk *chunk; - cairo_status_t status; - int i; - - /* XXX - * using composite for fill: - * spiral-box-nonalign-evenodd-fill.512 2201957 2.202 - * spiral-box-nonalign-nonzero-fill.512 336726 0.337 - * spiral-box-pixalign-evenodd-fill.512 352256 0.352 - * spiral-box-pixalign-nonzero-fill.512 147056 0.147 - * using fill: - * spiral-box-nonalign-evenodd-fill.512 3174565 3.175 - * spiral-box-nonalign-nonzero-fill.512 182710 0.183 - * spiral-box-pixalign-evenodd-fill.512 353863 0.354 - * spiral-box-pixalign-nonzero-fill.512 147402 0.147 - * - * cairo-perf-trace seems to favour using fill. - */ - - renderer.base.render_rows = _fill_span; - renderer.dst = dst->pixman_image; - - if ((unsigned) extents->bounded.width <= sizeof (buf)) { - renderer.mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, 1, - (uint32_t *) buf, - sizeof (buf)); - } else { - renderer.mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, 1, - NULL, 0); - } - if (unlikely (renderer.mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask); - - renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); - if (unlikely (renderer.src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_MASK; - } - - _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); - - /* first blit any aligned part of the boxes */ - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_ceil (box[i].p1.x); - int y1 = _cairo_fixed_integer_ceil (box[i].p1.y); - int x2 = _cairo_fixed_integer_floor (box[i].p2.x); - int y2 = _cairo_fixed_integer_floor (box[i].p2.y); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 > x1 && y2 > y1) { - cairo_box_t b; - - pixman_fill ((uint32_t *) dst->data, - dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - pixel); - - /* top */ - if (! _cairo_fixed_is_integer (box[i].p1.y)) { - b.p1.x = box[i].p1.x; - b.p1.y = box[i].p1.y; - b.p2.x = box[i].p2.x; - b.p2.y = _cairo_fixed_from_int (y1); - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* left */ - if (! _cairo_fixed_is_integer (box[i].p1.x)) { - b.p1.x = box[i].p1.x; - b.p1.y = box[i].p1.y; - b.p2.x = _cairo_fixed_from_int (x1); - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* right */ - if (! _cairo_fixed_is_integer (box[i].p2.x)) { - b.p1.x = _cairo_fixed_from_int (x2); - b.p1.y = box[i].p1.y; - b.p2.x = box[i].p2.x; - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* bottom */ - if (! _cairo_fixed_is_integer (box[i].p2.y)) { - b.p1.x = box[i].p1.x; - b.p1.y = _cairo_fixed_from_int (y2); - b.p2.x = box[i].p2.x; - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - } else { - status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - } - } - - status = converter.base.generate (&converter.base, &renderer.base); - - CLEANUP_CONVERTER: - converter.base.destroy (&converter.base); - pixman_image_unref (renderer.src); - CLEANUP_MASK: - pixman_image_unref (renderer.mask); - - return status; -} - -typedef struct _cairo_image_surface_span_renderer { - cairo_span_renderer_t base; - - uint8_t *mask_data; - uint32_t mask_stride; -} cairo_image_surface_span_renderer_t; - -cairo_status_t -_cairo_image_surface_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - /* XXX will it be quicker to repeat the sparse memset, - * or perform a simpler memcpy? - * The fairly dense spiral benchmarks suggests that the sparse - * memset is a win there as well. - */ - row = renderer->mask_data + y * renderer->mask_stride; - do { - for (i = 0; i < num_spans - 1; i++) { - if (! spans[i].coverage) - continue; - - /* We implement setting rendering the most common single - * pixel wide span case to avoid the overhead of a memset - * call. Open coding setting longer spans didn't show a - * noticeable improvement over memset. */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } - row += renderer->mask_stride; - } while (--height); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_composite_unaligned_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; - cairo_image_surface_span_renderer_t renderer; - cairo_rectangular_scan_converter_t converter; - pixman_image_t *mask, *src; - cairo_status_t status; - const struct _cairo_boxes_chunk *chunk; - int i, src_x, src_y; - - i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height; - if ((unsigned) i <= sizeof (buf)) { - mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, - extents->bounded.height, - (uint32_t *) buf, - CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8)); - memset (buf, 0, i); - } else { - mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, - extents->bounded.height, - NULL, 0); - } - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = pixman_image_get_stride (mask); - renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); - renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x; - - _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); - if (unlikely (status)) - goto CLEANUP; - } - } - - status = converter.base.generate (&converter.base, &renderer.base); - if (unlikely (status)) - goto CLEANUP; - - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - extents->bounded.x + src_x, extents->bounded.y + src_y, - 0, 0, - extents->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.height); - pixman_image_unref (src); - - CLEANUP: - converter.base.destroy (&converter.base); - pixman_image_unref (mask); - - return status; -} - -static cairo_status_t -_composite_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_boxes_t *boxes, - cairo_antialias_t antialias, - cairo_clip_t *clip, - const cairo_composite_rectangles_t *extents) -{ - cairo_region_t *clip_region = NULL; - cairo_bool_t need_clip_mask = FALSE; - cairo_status_t status; - struct _cairo_boxes_chunk *chunk; - uint32_t pixel; - int i; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED; - if (need_clip_mask && - (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - if (! boxes->is_pixel_aligned) { - if (need_clip_mask) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, - dst->pixman_format, &pixel)) - { - return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents); - } - else - { - return _composite_unaligned_boxes (dst, op, pattern, boxes, extents); - } - } - } - - status = CAIRO_STATUS_SUCCESS; - if (! need_clip_mask && - pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format, - &pixel)) - { - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); - int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); - int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); - int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 <= x1 || y2 <= y1) - continue; - - pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - pixel); - } - } - } - else - { - pixman_image_t *src = NULL, *mask = NULL; - int src_x, src_y, mask_x = 0, mask_y = 0; - pixman_op_t pixman_op = _pixman_operator (op); - - if (need_clip_mask) { - cairo_surface_t *clip_surface; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - mask_x = -clip_x; - mask_y = -clip_y; - - if (op == CAIRO_OPERATOR_CLEAR) { - pattern = NULL; - pixman_op = PIXMAN_OP_OUT_REVERSE; - } - - mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; - } - - if (pattern != NULL) { - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - src = mask; - src_x = mask_x; - src_y = mask_y; - mask = NULL; - } - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); - int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); - int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); - int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); - - if (x2 == x1 || y2 == y1) - continue; - - pixman_image_composite32 (pixman_op, - src, mask, dst->pixman_image, - x1 + src_x, y1 + src_y, - x1 + mask_x, y1 + mask_y, - x1, y1, - x2 - x1, y2 - y1); - } - } - - if (pattern != NULL) - pixman_image_unref (src); - - if (! extents->is_bounded) { - status = - _cairo_image_surface_fixup_unbounded_boxes (dst, extents, - clip_region, boxes); - } - } - - return status; -} - -static cairo_status_t -_clip_and_composite_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_boxes_t *boxes, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_traps_t traps; - cairo_status_t status; - composite_traps_info_t info; - - if (boxes->num_boxes == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - /* Use a fast path if the boxes are pixel aligned */ - status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Otherwise render via a mask and composite in the usual fashion. */ - status = _cairo_traps_init_boxes (&traps, boxes); - if (unlikely (status)) - return status; - - info.num_traps = traps.num_traps; - info.traps = traps.traps; - info.antialias = antialias; - status = _clip_and_composite (dst, op, src, - _composite_traps, &info, - extents, clip); - - _cairo_traps_fini (&traps); - return status; -} - -static cairo_bool_t -_mono_edge_is_vertical (const cairo_line_t *line) -{ - return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); -} - -static cairo_bool_t -_traps_are_pixel_aligned (cairo_traps_t *traps, - cairo_antialias_t antialias) -{ - int i; - - if (antialias == CAIRO_ANTIALIAS_NONE) { - for (i = 0; i < traps->num_traps; i++) { - if (! _mono_edge_is_vertical (&traps->traps[i].left) || - ! _mono_edge_is_vertical (&traps->traps[i].right)) - { - traps->maybe_region = FALSE; - return FALSE; - } - } - } else { - for (i = 0; i < traps->num_traps; i++) { - if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || - traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || - ! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return FALSE; - } - } - } - - return TRUE; -} - -static void -_boxes_for_traps (cairo_boxes_t *boxes, - cairo_traps_t *traps, - cairo_antialias_t antialias) -{ - int i; - - _cairo_boxes_init (boxes); - - boxes->num_boxes = traps->num_traps; - boxes->chunks.base = (cairo_box_t *) traps->traps; - boxes->chunks.count = traps->num_traps; - boxes->chunks.size = traps->num_traps; - - if (antialias != CAIRO_ANTIALIAS_NONE) { - for (i = 0; i < traps->num_traps; i++) { - /* Note the traps and boxes alias so we need to take the local copies first. */ - cairo_fixed_t x1 = traps->traps[i].left.p1.x; - cairo_fixed_t x2 = traps->traps[i].right.p1.x; - cairo_fixed_t y1 = traps->traps[i].top; - cairo_fixed_t y2 = traps->traps[i].bottom; - - boxes->chunks.base[i].p1.x = x1; - boxes->chunks.base[i].p1.y = y1; - boxes->chunks.base[i].p2.x = x2; - boxes->chunks.base[i].p2.y = y2; - - if (boxes->is_pixel_aligned) { - boxes->is_pixel_aligned = - _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && - _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); - } - } - } else { - boxes->is_pixel_aligned = TRUE; - - for (i = 0; i < traps->num_traps; i++) { - /* Note the traps and boxes alias so we need to take the local copies first. */ - cairo_fixed_t x1 = traps->traps[i].left.p1.x; - cairo_fixed_t x2 = traps->traps[i].right.p1.x; - cairo_fixed_t y1 = traps->traps[i].top; - cairo_fixed_t y2 = traps->traps[i].bottom; - - /* round down here to match Pixman's behavior when using traps. */ - boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); - boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); - boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); - boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); - } - } -} - -static cairo_status_t -_clip_and_composite_trapezoids (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - composite_traps_info_t info; - cairo_bool_t need_clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - cairo_region_t *clip_region; - - status = _cairo_clip_get_region (clip, &clip_region); - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) && - (! need_clip_surface || - (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) - { - cairo_boxes_t boxes; - - _boxes_for_traps (&boxes, traps, antialias); - return _clip_and_composite_boxes (dst, op, src, - &boxes, antialias, - extents, clip); - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - info.traps = traps->traps; - info.num_traps = traps->num_traps; - info.antialias = antialias; - return _clip_and_composite (dst, op, src, - _composite_traps, &info, - extents, clip); -} - -static cairo_clip_path_t * -_clip_get_single_path (cairo_clip_t *clip) -{ - if (clip->path->prev == NULL) - return clip->path; - - return NULL; -} - -/* high level image interface */ - -static cairo_int_status_t -_cairo_image_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_clip_path_t *clip_path; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && - extents.is_bounded && - (clip_path = _clip_get_single_path (clip)) != NULL) - { - status = _cairo_image_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - else - { - cairo_boxes_t boxes; - - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _clip_and_composite_boxes (surface, op, source, - &boxes, CAIRO_ANTIALIAS_DEFAULT, - &extents, clip); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_status_t -_composite_mask (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - const cairo_pattern_t *mask_pattern = closure; - pixman_image_t *src, *mask = NULL; - int src_x = 0, src_y = 0; - int mask_x = 0, mask_y = 0; - - if (src_pattern != NULL) { - src = _pixman_image_for_pattern (src_pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = _pixman_image_for_pattern (mask_pattern, TRUE, extents, &mask_x, &mask_y); - if (unlikely (mask == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (mask_pattern->has_component_alpha) - pixman_image_set_component_alpha (mask, TRUE); - } else { - src = _pixman_image_for_pattern (mask_pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - extents->x + mask_x, extents->y + mask_y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - - if (mask != NULL) - pixman_image_unref (mask); - pixman_image_unref (src); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_image_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - clip = _cairo_clip_init_copy (&local_clip, clip); - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) { - _cairo_clip_fini (&local_clip); - return status; - } - - have_clip = TRUE; - } - - status = _clip_and_composite (surface, op, source, - _composite_mask, (void *) mask, - &extents, clip); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -typedef struct { - cairo_polygon_t *polygon; - cairo_fill_rule_t fill_rule; - cairo_antialias_t antialias; -} composite_spans_info_t; - -//#define USE_BOTOR_SCAN_CONVERTER -static cairo_status_t -_composite_spans (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE]; - composite_spans_info_t *info = closure; - cairo_image_surface_span_renderer_t renderer; -#if USE_BOTOR_SCAN_CONVERTER - cairo_box_t box; - cairo_botor_scan_converter_t converter; -#else - cairo_scan_converter_t *converter; -#endif - pixman_image_t *mask; - cairo_status_t status; - -#if USE_BOTOR_SCAN_CONVERTER - box.p1.x = _cairo_fixed_from_int (extents->x); - box.p1.y = _cairo_fixed_from_int (extents->y); - box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); - box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); - _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); - status = converter.base.add_polygon (&converter.base, info->polygon); -#else - converter = _cairo_tor_scan_converter_create (extents->x, extents->y, - extents->x + extents->width, - extents->y + extents->height, - info->fill_rule); - status = converter->add_polygon (converter, info->polygon); -#endif - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - - if (pattern == NULL && - dst_format == PIXMAN_a8 && - op == CAIRO_OPERATOR_SOURCE) - { - mask = dst; - dst = NULL; - } - else - { - int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8); - uint8_t *data = mask_buf; - - if (extents->height * stride <= (int) sizeof (mask_buf)) - memset (data, 0, extents->height * stride); - else - data = NULL, stride = 0; - - mask = pixman_image_create_bits (PIXMAN_a8, - extents->width, - extents->height, - (uint32_t *) data, - stride); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_CONVERTER; - } - } - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = pixman_image_get_stride (mask); - renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); - if (dst != NULL) - renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; - else - renderer.mask_data -= dst_y * renderer.mask_stride + dst_x; - -#if USE_BOTOR_SCAN_CONVERTER - status = converter.base.generate (&converter.base, &renderer.base); -#else - status = converter->generate (converter, &renderer.base); -#endif - if (unlikely (status)) - goto CLEANUP_RENDERER; - - if (dst != NULL) { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_RENDERER; - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - pixman_image_unref (src); - } - - CLEANUP_RENDERER: - if (dst != NULL) - pixman_image_unref (mask); - CLEANUP_CONVERTER: -#if USE_BOTOR_SCAN_CONVERTER - converter.base.destroy (&converter.base); -#else - converter->destroy (converter); -#endif - return status; -} - -static cairo_status_t -_clip_and_composite_polygon (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (polygon->num_edges == 0) { - cairo_traps_t traps; - - if (extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - _cairo_traps_init (&traps); - status = _clip_and_composite_trapezoids (dst, op, src, - &traps, antialias, - extents, clip); - _cairo_traps_fini (&traps); - - return status; - } - - if (_cairo_operator_bounded_by_mask(op)) { - _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); - if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) - return CAIRO_STATUS_SUCCESS; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - composite_spans_info_t info; - - info.polygon = polygon; - info.fill_rule = fill_rule; - info.antialias = antialias; - - status = _clip_and_composite (dst, op, src, - _composite_spans, &info, - extents, clip); - } else { - cairo_traps_t traps; - - _cairo_traps_init (&traps); - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_trapezoids (dst, op, src, - &traps, antialias, - extents, clip); - } - - _cairo_traps_fini (&traps); - } - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - status = CAIRO_INT_STATUS_UNSUPPORTED; - if (path->is_rectilinear) { - cairo_boxes_t boxes; - - _cairo_boxes_init (&boxes); - _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, - style, - ctm, - &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, antialias, - &extents, clip); - } - - _cairo_boxes_fini (&boxes); - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - cairo_polygon_t polygon; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_to_polygon (path, - style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (extents.is_bounded && clip != NULL) { - cairo_clip_path_t *clip_path; - - if (((clip_path = _clip_get_single_path (clip)) != NULL) && - _cairo_path_fixed_equal (&clip_path->path, path)) - { - clip = NULL; - } - } - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - if (_cairo_path_fixed_is_rectilinear_fill (path)) { - cairo_boxes_t boxes; - - _cairo_boxes_init (&boxes); - _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, - fill_rule, - &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, antialias, - &extents, clip); - } - - _cairo_boxes_fini (&boxes); - } else { - cairo_polygon_t polygon; - - assert (! path->is_empty_fill); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -typedef struct { - cairo_scaled_font_t *font; - cairo_glyph_t *glyphs; - int num_glyphs; -} composite_glyphs_info_t; - -static cairo_status_t -_composite_glyphs_via_mask (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_glyphs_info_t *info = closure; - cairo_scaled_font_t *font = info->font; - cairo_glyph_t *glyphs = info->glyphs; - int num_glyphs = info->num_glyphs; - pixman_image_t *mask = NULL; - pixman_image_t *src; - pixman_image_t *white; - pixman_format_code_t mask_format = 0; /* silence gcc */ - cairo_status_t status; - int src_x, src_y; - int i; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - white = _pixman_white_image (); - if (unlikely (white == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_scaled_font_freeze_cache (font); - - for (i = 0; i < num_glyphs; i++) { - int x, y; - cairo_image_surface_t *glyph_surface; - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (font, glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - - if (unlikely (status)) - goto CLEANUP; - - glyph_surface = scaled_glyph->surface; - - if (glyph_surface->width == 0 || glyph_surface->height == 0) - continue; - - /* To start, create the mask using the format from the first - * glyph. Later we'll deal with different formats. */ - if (mask == NULL) { - mask_format = glyph_surface->pixman_format; - mask = pixman_image_create_bits (mask_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (PIXMAN_FORMAT_RGB (mask_format)) - pixman_image_set_component_alpha (mask, TRUE); - } - - /* If we have glyphs of different formats, we "upgrade" the mask - * to the wider of the formats. */ - if (glyph_surface->pixman_format != mask_format && - PIXMAN_FORMAT_BPP (mask_format) < - PIXMAN_FORMAT_BPP (glyph_surface->pixman_format)) - { - pixman_image_t *new_mask; - - mask_format = glyph_surface->pixman_format; - new_mask = pixman_image_create_bits (mask_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (new_mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - white, mask, new_mask, - 0, 0, 0, 0, 0, 0, - extents->width, extents->height); - - pixman_image_unref (mask); - mask = new_mask; - if (PIXMAN_FORMAT_RGB (mask_format)) - pixman_image_set_component_alpha (mask, TRUE); - } - - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (glyphs[i].x - - glyph_surface->base.device_transform.x0); - y = _cairo_lround (glyphs[i].y - - glyph_surface->base.device_transform.y0); - if (glyph_surface->pixman_format == mask_format) { - pixman_image_composite32 (PIXMAN_OP_ADD, - glyph_surface->pixman_image, NULL, mask, - 0, 0, 0, 0, - x - extents->x, y - extents->y, - glyph_surface->width, - glyph_surface->height); - } else { - pixman_image_composite32 (PIXMAN_OP_ADD, - white, glyph_surface->pixman_image, mask, - 0, 0, 0, 0, - x - extents->x, y - extents->y, - glyph_surface->width, - glyph_surface->height); - } - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - -CLEANUP: - _cairo_scaled_font_thaw_cache (font); - if (mask != NULL) - pixman_image_unref (mask); - pixman_image_unref (src); - pixman_image_unref (white); - - return status; -} - -static cairo_status_t -_composite_glyphs (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_glyphs_info_t *info = closure; - cairo_scaled_glyph_t *glyph_cache[64]; - pixman_op_t pixman_op = _pixman_operator (op); - pixman_image_t *src = NULL; - int src_x = 0, src_y = 0; - cairo_status_t status; - int i; - - memset (glyph_cache, 0, sizeof (glyph_cache)); - status = CAIRO_STATUS_SUCCESS; - - _cairo_scaled_font_freeze_cache (info->font); - for (i = 0; i < info->num_glyphs; i++) { - int x, y; - cairo_image_surface_t *glyph_surface; - cairo_scaled_glyph_t *scaled_glyph; - unsigned long glyph_index = info->glyphs[i].index; - int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); - - scaled_glyph = glyph_cache[cache_index]; - if (scaled_glyph == NULL || - _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) - { - status = _cairo_scaled_glyph_lookup (info->font, glyph_index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - - if (unlikely (status)) - break; - - glyph_cache[cache_index] = scaled_glyph; - } - - glyph_surface = scaled_glyph->surface; - if (glyph_surface->width && glyph_surface->height) { - int x1, y1, x2, y2; - - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (info->glyphs[i].x - - glyph_surface->base.device_transform.x0); - y = _cairo_lround (info->glyphs[i].y - - glyph_surface->base.device_transform.y0); - - x1 = x; - if (x1 < extents->x) - x1 = extents->x; - x2 = x + glyph_surface->width; - if (x2 > extents->x + extents->width) - x2 = extents->x + extents->width; - - y1 = y; - if (y1 < extents->y) - y1 = extents->y; - y2 = y + glyph_surface->height; - if (y2 > extents->y + extents->height) - y2 = extents->y + extents->height; - - if (glyph_surface->format == CAIRO_FORMAT_A8 || - glyph_surface->format == CAIRO_FORMAT_A1 || - (glyph_surface->format == CAIRO_FORMAT_ARGB32 && - pixman_image_get_component_alpha (glyph_surface->pixman_image))) - { - if (unlikely (src == NULL)) { - if (pattern != NULL) { - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - src_x -= dst_x; - src_y -= dst_y; - } else { - src = _pixman_white_image (); - } - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - } - } - - pixman_image_composite32 (pixman_op, - src, glyph_surface->pixman_image, dst, - x1 + src_x, y1 + src_y, - x1 - x, y1 - y, - x1 - dst_x, y1 - dst_y, - x2 - x1, y2 - y1); - } else { - pixman_image_composite32 (pixman_op, - glyph_surface->pixman_image, NULL, dst, - x1 - x, y1 - y, - 0, 0, - x1 - dst_x, y1 - dst_y, - x2 - x1, y2 - y1); - } - } - } - _cairo_scaled_font_thaw_cache (info->font); - - if (src != NULL) - pixman_image_unref (src); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *num_remaining) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - composite_glyphs_info_t glyph_info; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_bool_t overlap; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_rectangle (clip, &extents.mask)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - clip = _cairo_clip_init_copy (&local_clip, clip); - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - - have_clip = TRUE; - } - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - status = _clip_and_composite (surface, op, source, - overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs, - &glyph_info, - &extents, clip); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - *num_remaining = 0; - return status; -} - -static cairo_bool_t -_cairo_image_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_image_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static void -_cairo_image_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); -} - -/* legacy interface kept for compatibility until surface-fallback is removed */ -static cairo_status_t -_cairo_image_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_image_surface_t *surface = abstract_surface; - - image_rect_out->x = 0; - image_rect_out->y = 0; - image_rect_out->width = surface->width; - image_rect_out->height = surface->height; - - *image_out = surface; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_image_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_image_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_image_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *dst = abstract_dst; - cairo_composite_rectangles_t extents; - pixman_image_t *src; - int src_offset_x, src_offset_y; - cairo_status_t status; - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - extents.source.x = src_x; - extents.source.y = src_y; - extents.source.width = width; - extents.source.height = height; - - extents.mask.x = mask_x; - extents.mask.y = mask_y; - extents.mask.width = width; - extents.mask.height = height; - - extents.bounded.x = dst_x; - extents.bounded.y = dst_y; - extents.bounded.width = width; - extents.bounded.height = height; - - extents.unbounded.x = 0; - extents.unbounded.y = 0; - extents.unbounded.width = dst->width; - extents.unbounded.height = dst->height; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip_region, &rect); - if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) - return CAIRO_STATUS_SUCCESS; - } - - extents.is_bounded = _cairo_operator_bounded_by_either (op); - - src = _pixman_image_for_pattern (src_pattern, FALSE, &extents.source, &src_offset_x, &src_offset_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = CAIRO_STATUS_SUCCESS; - if (mask_pattern != NULL) { - pixman_image_t *mask; - int mask_offset_x, mask_offset_y; - - mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents.mask, &mask_offset_x, &mask_offset_y); - if (unlikely (mask == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - src_x + src_offset_x, - src_y + src_offset_y, - mask_x + mask_offset_x, - mask_y + mask_offset_y, - dst_x, dst_y, width, height); - - pixman_image_unref (mask); - } else { - pixman_image_composite32 (_pixman_operator (op), - src, NULL, dst->pixman_image, - src_x + src_offset_x, - src_y + src_offset_y, - 0, 0, - dst_x, dst_y, width, height); - } - - pixman_image_unref (src); - - if (! extents.is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_image_surface_t *surface = abstract_surface; - - pixman_color_t pixman_color; - pixman_box32_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; - pixman_box32_t *pixman_boxes = stack_boxes; - int i; - - cairo_int_status_t status; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pixman_color.red = color->red_short; - pixman_color.green = color->green_short; - pixman_color.blue = color->blue_short; - pixman_color.alpha = color->alpha_short; - - if (num_rects > ARRAY_LENGTH (stack_boxes)) { - pixman_boxes = _cairo_malloc_ab (num_rects, sizeof (pixman_box32_t)); - if (unlikely (pixman_boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < num_rects; i++) { - pixman_boxes[i].x1 = rects[i].x; - pixman_boxes[i].y1 = rects[i].y; - pixman_boxes[i].x2 = rects[i].x + rects[i].width; - pixman_boxes[i].y2 = rects[i].y + rects[i].height; - } - - status = CAIRO_STATUS_SUCCESS; - if (! pixman_image_fill_boxes (_pixman_operator (op), - surface->pixman_image, - &pixman_color, - num_rects, - pixman_boxes)) - { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (pixman_boxes != stack_boxes) - free (pixman_boxes); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *dst = abstract_dst; - cairo_composite_rectangles_t extents; - cairo_pattern_union_t source_pattern; - composite_traps_info_t info; - cairo_status_t status; - - if (height == 0 || width == 0) - return CAIRO_STATUS_SUCCESS; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - extents.source.x = src_x; - extents.source.y = src_y; - extents.source.width = width; - extents.source.height = height; - - extents.mask.x = dst_x; - extents.mask.y = dst_y; - extents.mask.width = width; - extents.mask.height = height; - - extents.bounded.x = dst_x; - extents.bounded.y = dst_y; - extents.bounded.width = width; - extents.bounded.height = height; - - extents.unbounded.x = 0; - extents.unbounded.y = 0; - extents.unbounded.width = dst->width; - extents.unbounded.height = dst->height; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip_region, &rect); - if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) - return CAIRO_STATUS_SUCCESS; - } - - extents.is_bounded = _cairo_operator_bounded_by_either (op); - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - _cairo_pattern_init_static_copy (&source_pattern.base, pattern); - cairo_matrix_translate (&source_pattern.base.matrix, - src_x - extents.bounded.x, - src_y - extents.bounded.y); - - info.traps = traps; - info.num_traps = num_traps; - info.antialias = antialias; - status = _composite_traps (&info, - dst->pixman_image, - dst->pixman_format, - op, - &source_pattern.base, - 0, 0, - &extents.bounded, - clip_region); - - if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -typedef struct _legacy_image_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_operator_t op; - const cairo_pattern_t *pattern; - cairo_antialias_t antialias; - cairo_region_t *clip_region; - - pixman_image_t *mask; - uint8_t *mask_data; - uint32_t mask_stride; - - cairo_image_surface_t *dst; - cairo_composite_rectangles_t composite_rectangles; -} legacy_image_surface_span_renderer_t; - -void -_cairo_image_surface_span_render_row ( - int y, - const cairo_half_open_span_t *spans, - unsigned num_spans, - uint8_t *data, - uint32_t stride) -{ - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return; - - row = data + y * stride; - for (i = 0; i < num_spans - 1; i++) { - if (! spans[i].coverage) - continue; - - /* We implement setting the most common single pixel wide - * span case to avoid the overhead of a memset call. - * Open coding setting longer spans didn't show a - * noticeable improvement over memset. - */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } -} - -static cairo_status_t -_cairo_image_surface_span_renderer_render_rows ( - void *abstract_renderer, - int y, - int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - while (height--) - _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_span_renderer_destroy (void *abstract_renderer) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - if (renderer == NULL) - return; - - pixman_image_unref (renderer->mask); - - free (renderer); -} - -static cairo_status_t -_cairo_image_surface_span_renderer_finish (void *abstract_renderer) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; - cairo_image_surface_t *dst = renderer->dst; - pixman_image_t *src; - int src_x, src_y; - cairo_status_t status; - - if (renderer->clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region); - if (unlikely (status)) - return status; - } - - src = _pixman_image_for_pattern (renderer->pattern, FALSE, &rects->bounded, &src_x, &src_y); - if (src == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = CAIRO_STATUS_SUCCESS; - pixman_image_composite32 (_pixman_operator (renderer->op), - src, - renderer->mask, - dst->pixman_image, - rects->bounded.x + src_x, - rects->bounded.y + src_y, - 0, 0, - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height); - - if (! rects->is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, rects, NULL); - - if (renderer->clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -static cairo_bool_t -_cairo_image_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias) -{ - return TRUE; - (void) op; - (void) pattern; - (void) abstract_dst; - (void) antialias; -} - -static cairo_span_renderer_t * -_cairo_image_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *dst = abstract_dst; - legacy_image_surface_span_renderer_t *renderer; - - renderer = calloc(1, sizeof(*renderer)); - if (unlikely (renderer == NULL)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - - renderer->base.destroy = _cairo_image_surface_span_renderer_destroy; - renderer->base.finish = _cairo_image_surface_span_renderer_finish; - renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows; - renderer->op = op; - renderer->pattern = pattern; - renderer->antialias = antialias; - renderer->dst = dst; - renderer->clip_region = clip_region; - - renderer->composite_rectangles = *rects; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - renderer->mask = pixman_image_create_bits (PIXMAN_a8, - rects->bounded.width, - rects->bounded.height, - NULL, 0); - if (renderer->mask == NULL) { - free (renderer); - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - - renderer->mask_stride = pixman_image_get_stride (renderer->mask); - renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride; - - return &renderer->base; -} - -/** - * _cairo_surface_is_image: - * @surface: a #cairo_surface_t - * - * Checks if a surface is an #cairo_image_surface_t - * - * Return value: %TRUE if the surface is an image surface - **/ -cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface) -{ - return surface->backend == &_cairo_image_surface_backend; -} - -const cairo_surface_backend_t _cairo_image_surface_backend = { - CAIRO_SURFACE_TYPE_IMAGE, - _cairo_image_surface_create_similar, - _cairo_image_surface_finish, - _cairo_image_surface_acquire_source_image, - _cairo_image_surface_release_source_image, - _cairo_image_surface_acquire_dest_image, - _cairo_image_surface_release_dest_image, - _cairo_image_surface_clone_similar, - _cairo_image_surface_composite, - _cairo_image_surface_fill_rectangles, - _cairo_image_surface_composite_trapezoids, - _cairo_image_surface_create_span_renderer, - _cairo_image_surface_check_span_renderer, - - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_image_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_image_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark dirty */ - NULL, /* font_fini */ - NULL, /* glyph_fini */ - - _cairo_image_surface_paint, - _cairo_image_surface_mask, - _cairo_image_surface_stroke, - _cairo_image_surface_fill, - _cairo_image_surface_glyphs, - NULL, /* show_text_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ -}; - -/* A convenience function for when one needs to coerce an image - * surface to an alternate format. */ -cairo_image_surface_t * -_cairo_image_surface_coerce (cairo_image_surface_t *surface) -{ - return _cairo_image_surface_coerce_to_format (surface, - _cairo_format_from_content (surface->base.content)); - -} - -/* A convenience function for when one needs to coerce an image - * surface to an alternate format. */ -cairo_image_surface_t * -_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, - cairo_format_t format) -{ - cairo_image_surface_t *clone; - cairo_status_t status; - - status = surface->base.status; - if (unlikely (status)) - return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); - - if (surface->format == format) - return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); - - clone = (cairo_image_surface_t *) - cairo_image_surface_create (format, surface->width, surface->height); - if (unlikely (clone->base.status)) - return clone; - - pixman_image_composite32 (PIXMAN_OP_SRC, - surface->pixman_image, NULL, clone->pixman_image, - 0, 0, - 0, 0, - 0, 0, - surface->width, surface->height); - clone->base.is_clear = FALSE; - - clone->base.device_transform = - surface->base.device_transform; - clone->base.device_transform_inverse = - surface->base.device_transform_inverse; - - return clone; -} - -cairo_image_transparency_t -_cairo_image_analyze_transparency (cairo_image_surface_t *image) -{ - int x, y; - - if (image->transparency != CAIRO_IMAGE_UNKNOWN) - return image->transparency; - - if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) - return image->transparency = CAIRO_IMAGE_IS_OPAQUE; - - if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { - if (image->format == CAIRO_FORMAT_A1) - return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; - else - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; - } - - if (image->format == CAIRO_FORMAT_RGB16_565) { - image->transparency = CAIRO_IMAGE_IS_OPAQUE; - return CAIRO_IMAGE_IS_OPAQUE; - } - - if (image->format != CAIRO_FORMAT_ARGB32) - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; - - image->transparency = CAIRO_IMAGE_IS_OPAQUE; - for (y = 0; y < image->height; y++) { - uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel++) { - int a = (*pixel & 0xff000000) >> 24; - if (a > 0 && a < 255) { - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; - } else if (a == 0) { - image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; - } - } - } - - return image->transparency; -} diff --git a/libs/cairo/cairo/src/cairo-list-private.h b/libs/cairo/cairo/src/cairo-list-private.h deleted file mode 100644 index ca4e368fc..000000000 --- a/libs/cairo/cairo/src/cairo-list-private.h +++ /dev/null @@ -1,183 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_LIST_PRIVATE_H -#define CAIRO_LIST_PRIVATE_H - -#include "cairo-compiler-private.h" - -/* Basic circular, doubly linked list implementation */ - -typedef struct _cairo_list { - struct _cairo_list *next, *prev; -} cairo_list_t; - -#define cairo_list_entry(ptr, type, member) \ - cairo_container_of(ptr, type, member) - -#define cairo_list_first_entry(ptr, type, member) \ - cairo_list_entry((ptr)->next, type, member) - -#define cairo_list_last_entry(ptr, type, member) \ - cairo_list_entry((ptr)->prev, type, member) - -#define cairo_list_foreach(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -#define cairo_list_foreach_entry(pos, type, head, member) \ - for (pos = cairo_list_entry((head)->next, type, member);\ - &pos->member != (head); \ - pos = cairo_list_entry(pos->member.next, type, member)) - -#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \ - for (pos = cairo_list_entry ((head)->next, type, member),\ - n = cairo_list_entry (pos->member.next, type, member);\ - &pos->member != (head); \ - pos = n, n = cairo_list_entry (n->member.next, type, member)) - -#define cairo_list_foreach_entry_reverse(pos, type, head, member) \ - for (pos = cairo_list_entry((head)->prev, type, member);\ - &pos->member != (head); \ - pos = cairo_list_entry(pos->member.prev, type, member)) - -#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \ - for (pos = cairo_list_entry((head)->prev, type, member),\ - n = cairo_list_entry (pos->member.prev, type, member);\ - &pos->member != (head); \ - pos = n, n = cairo_list_entry (n->member.prev, type, member)) - -#ifdef CAIRO_LIST_DEBUG -static inline void -_cairo_list_validate (const cairo_list_t *link) -{ - assert (link->next->prev == link); - assert (link->prev->next == link); -} -static inline void -cairo_list_validate (const cairo_list_t *head) -{ - cairo_list_t *link; - - cairo_list_foreach (link, head) - _cairo_list_validate (link); -} -static inline cairo_bool_t -cairo_list_is_empty (const cairo_list_t *head); -static inline void -cairo_list_validate_is_empty (const cairo_list_t *head) -{ - assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev)); -} -#else -#define _cairo_list_validate(link) -#define cairo_list_validate(head) -#define cairo_list_validate_is_empty(head) -#endif - -static inline void -cairo_list_init (cairo_list_t *entry) -{ - entry->next = entry; - entry->prev = entry; -} - -static inline void -__cairo_list_add (cairo_list_t *entry, - cairo_list_t *prev, - cairo_list_t *next) -{ - next->prev = entry; - entry->next = next; - entry->prev = prev; - prev->next = entry; -} - -static inline void -cairo_list_add (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - cairo_list_validate_is_empty (entry); - __cairo_list_add (entry, head, head->next); - cairo_list_validate (head); -} - -static inline void -cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - cairo_list_validate_is_empty (entry); - __cairo_list_add (entry, head->prev, head); - cairo_list_validate (head); -} - -static inline void -__cairo_list_del (cairo_list_t *prev, cairo_list_t *next) -{ - next->prev = prev; - prev->next = next; -} - -static inline void -cairo_list_del (cairo_list_t *entry) -{ - __cairo_list_del (entry->prev, entry->next); - cairo_list_init (entry); -} - -static inline void -cairo_list_move (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - __cairo_list_del (entry->prev, entry->next); - __cairo_list_add (entry, head, head->next); - cairo_list_validate (head); -} - -static inline void -cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - __cairo_list_del (entry->prev, entry->next); - __cairo_list_add (entry, head->prev, head); - cairo_list_validate (head); -} - -static inline void -cairo_list_swap (cairo_list_t *entry, cairo_list_t *other) -{ - __cairo_list_add (entry, other->prev, other->next); - cairo_list_init (other); -} - -static inline cairo_bool_t -cairo_list_is_first (const cairo_list_t *entry, - const cairo_list_t *head) -{ - cairo_list_validate (head); - return entry->prev == head; -} - -static inline cairo_bool_t -cairo_list_is_last (const cairo_list_t *entry, - const cairo_list_t *head) -{ - cairo_list_validate (head); - return entry->next == head; -} - -static inline cairo_bool_t -cairo_list_is_empty (const cairo_list_t *head) -{ - cairo_list_validate (head); - return head->next == head; -} - -static inline cairo_bool_t -cairo_list_is_singular (const cairo_list_t *head) -{ - cairo_list_validate (head); - return head->next == head || head->next == head->prev; -} - -#endif /* CAIRO_LIST_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-lzw.c b/libs/cairo/cairo/src/cairo-lzw.c deleted file mode 100644 index e98e613bc..000000000 --- a/libs/cairo/cairo/src/cairo-lzw.c +++ /dev/null @@ -1,372 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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<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/libs/cairo/cairo/src/cairo-malloc-private.h b/libs/cairo/cairo/src/cairo-malloc-private.h deleted file mode 100644 index 765fb65b4..000000000 --- a/libs/cairo/cairo/src/cairo-malloc-private.h +++ /dev/null @@ -1,116 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_MALLOC_PRIVATE_H -#define CAIRO_MALLOC_PRIVATE_H - -#include "cairo-wideint-private.h" - -#if HAVE_MEMFAULT -#include -#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT() -#else -#define CAIRO_INJECT_FAULT() 0 -#endif - -/** - * _cairo_malloc: - * @size: size in bytes - * - * Allocate @size memory using malloc(). - * The memory should be freed using free(). - * malloc is skipped, if 0 bytes are requested, and %NULL will be returned. - * - * Return value: A pointer to the newly allocated memory, or %NULL in - * case of malloc() failure or size is 0. - */ - -#define _cairo_malloc(size) \ - ((size) ? malloc((unsigned) (size)) : NULL) - -/** - * _cairo_malloc_ab: - * @n: number of elements to allocate - * @size: size of each element - * - * Allocates @n*@size memory using _cairo_malloc(), taking care to not - * overflow when doing the multiplication. Behaves much like - * calloc(), except that the returned memory is not set to zero. - * The memory should be freed using free(). - * - * @size should be a constant so that the compiler can optimize - * out a constant division. - * - * Return value: A pointer to the newly allocated memory, or %NULL in - * case of malloc() failure or overflow. - */ - -#define _cairo_malloc_ab(a, size) \ - ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ - _cairo_malloc((unsigned) (a) * (unsigned) (size))) - -/** - * _cairo_realloc_ab: - * @ptr: original pointer to block of memory to be resized - * @n: number of elements to allocate - * @size: size of each element - * - * Reallocates @ptr a block of @n*@size memory using realloc(), taking - * care to not overflow when doing the multiplication. The memory - * should be freed using free(). - * - * @size should be a constant so that the compiler can optimize - * out a constant division. - * - * Return value: A pointer to the newly allocated memory, or %NULL in - * case of realloc() failure or overflow (whereupon the original block - * of memory * is left untouched). - */ - -#define _cairo_realloc_ab(ptr, a, size) \ - ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ - realloc(ptr, (unsigned) (a) * (unsigned) (size))) - -/** - * _cairo_malloc_abc: - * @n: first factor of number of elements to allocate - * @b: second factor of number of elements to allocate - * @size: size of each element - * - * Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not - * overflow when doing the multiplication. Behaves like - * _cairo_malloc_ab(). The memory should be freed using free(). - * - * @size should be a constant so that the compiler can optimize - * out a constant division. - * - * Return value: A pointer to the newly allocated memory, or %NULL in - * case of malloc() failure or overflow. - */ - -#define _cairo_malloc_abc(a, b, size) \ - ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \ - (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \ - _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size))) - -/** - * _cairo_malloc_ab_plus_c: - * @n: number of elements to allocate - * @size: size of each element - * @k: additional size to allocate - * - * Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not - * overflow when doing the arithmetic. Behaves like - * _cairo_malloc_ab(). The memory should be freed using free(). - * - * Return value: A pointer to the newly allocated memory, or %NULL in - * case of malloc() failure or overflow. - */ - -#define _cairo_malloc_ab_plus_c(n, size, k) \ - ((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \ - (unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \ - _cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k))) - -#endif /* CAIRO_MALLOC_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-matrix.c b/libs/cairo/cairo/src/cairo-matrix.c deleted file mode 100644 index b9691b8ee..000000000 --- a/libs/cairo/cairo/src/cairo-matrix.c +++ /dev/null @@ -1,974 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif - -/** - * SECTION:cairo-matrix - * @Title: cairo_matrix_t - * @Short_Description: Generic matrix operations - * @See_Also: #cairo_t - * - * #cairo_matrix_t is used throughout cairo to convert between different - * coordinate spaces. A #cairo_matrix_t holds an affine transformation, - * such as a scale, rotation, shear, or a combination of these. - * The transformation of a point (x,y) - * is given by: - * - * - * x_new = xx * x + xy * y + x0; - * y_new = yx * x + yy * y + y0; - * - * - * The current transformation matrix of a #cairo_t, represented as a - * #cairo_matrix_t, defines the transformation from user-space - * coordinates to device-space coordinates. See cairo_get_matrix() and - * cairo_set_matrix(). - */ - -static void -_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); - -static void -_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); - -/** - * cairo_matrix_init_identity: - * @matrix: a #cairo_matrix_t - * - * Modifies @matrix to be an identity transformation. - **/ -void -cairo_matrix_init_identity (cairo_matrix_t *matrix) -{ - cairo_matrix_init (matrix, - 1, 0, - 0, 1, - 0, 0); -} -slim_hidden_def(cairo_matrix_init_identity); - -/** - * cairo_matrix_init: - * @matrix: a #cairo_matrix_t - * @xx: xx component of the affine transformation - * @yx: yx component of the affine transformation - * @xy: xy component of the affine transformation - * @yy: yy component of the affine transformation - * @x0: X translation component of the affine transformation - * @y0: Y translation component of the affine transformation - * - * Sets @matrix to be the affine transformation given by - * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given - * by: - * - * x_new = xx * x + xy * y + x0; - * y_new = yx * x + yy * y + y0; - * - **/ -void -cairo_matrix_init (cairo_matrix_t *matrix, - double xx, double yx, - - double xy, double yy, - double x0, double y0) -{ - matrix->xx = xx; matrix->yx = yx; - matrix->xy = xy; matrix->yy = yy; - matrix->x0 = x0; matrix->y0 = y0; -} -slim_hidden_def(cairo_matrix_init); - -/** - * _cairo_matrix_get_affine: - * @matrix: a #cairo_matrix_t - * @xx: location to store xx component of matrix - * @yx: location to store yx component of matrix - * @xy: location to store xy component of matrix - * @yy: location to store yy component of matrix - * @x0: location to store x0 (X-translation component) of matrix, or %NULL - * @y0: location to store y0 (Y-translation component) of matrix, or %NULL - * - * Gets the matrix values for the affine transformation that @matrix represents. - * See cairo_matrix_init(). - * - * - * This function is a leftover from the old public API, but is still - * mildly useful as an internal means for getting at the matrix - * members in a positional way. For example, when reassigning to some - * external matrix type, or when renaming members to more meaningful - * names (such as a,b,c,d,e,f) for particular manipulations. - **/ -void -_cairo_matrix_get_affine (const cairo_matrix_t *matrix, - double *xx, double *yx, - double *xy, double *yy, - double *x0, double *y0) -{ - *xx = matrix->xx; - *yx = matrix->yx; - - *xy = matrix->xy; - *yy = matrix->yy; - - if (x0) - *x0 = matrix->x0; - if (y0) - *y0 = matrix->y0; -} - -/** - * cairo_matrix_init_translate: - * @matrix: a #cairo_matrix_t - * @tx: amount to translate in the X direction - * @ty: amount to translate in the Y direction - * - * Initializes @matrix to a transformation that translates by @tx and - * @ty in the X and Y dimensions, respectively. - **/ -void -cairo_matrix_init_translate (cairo_matrix_t *matrix, - double tx, double ty) -{ - cairo_matrix_init (matrix, - 1, 0, - 0, 1, - tx, ty); -} -slim_hidden_def(cairo_matrix_init_translate); - -/** - * cairo_matrix_translate: - * @matrix: a #cairo_matrix_t - * @tx: amount to translate in the X direction - * @ty: amount to translate in the Y direction - * - * Applies a translation by @tx, @ty to the transformation in - * @matrix. The effect of the new transformation is to first translate - * the coordinates by @tx and @ty, then apply the original transformation - * to the coordinates. - **/ -void -cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) -{ - cairo_matrix_t tmp; - - cairo_matrix_init_translate (&tmp, tx, ty); - - cairo_matrix_multiply (matrix, &tmp, matrix); -} -slim_hidden_def (cairo_matrix_translate); - -/** - * cairo_matrix_init_scale: - * @matrix: a #cairo_matrix_t - * @sx: scale factor in the X direction - * @sy: scale factor in the Y direction - * - * Initializes @matrix to a transformation that scales by @sx and @sy - * in the X and Y dimensions, respectively. - **/ -void -cairo_matrix_init_scale (cairo_matrix_t *matrix, - double sx, double sy) -{ - cairo_matrix_init (matrix, - sx, 0, - 0, sy, - 0, 0); -} -slim_hidden_def(cairo_matrix_init_scale); - -/** - * cairo_matrix_scale: - * @matrix: a #cairo_matrix_t - * @sx: scale factor in the X direction - * @sy: scale factor in the Y direction - * - * Applies scaling by @sx, @sy to the transformation in @matrix. The - * effect of the new transformation is to first scale the coordinates - * by @sx and @sy, then apply the original transformation to the coordinates. - **/ -void -cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) -{ - cairo_matrix_t tmp; - - cairo_matrix_init_scale (&tmp, sx, sy); - - cairo_matrix_multiply (matrix, &tmp, matrix); -} -slim_hidden_def(cairo_matrix_scale); - -/** - * cairo_matrix_init_rotate: - * @matrix: a #cairo_matrix_t - * @radians: angle of rotation, in radians. The direction of rotation - * is defined such that positive angles rotate in the direction from - * the positive X axis toward the positive Y axis. With the default - * axis orientation of cairo, positive angles rotate in a clockwise - * direction. - * - * Initialized @matrix to a transformation that rotates by @radians. - **/ -void -cairo_matrix_init_rotate (cairo_matrix_t *matrix, - double radians) -{ - double s; - double c; - - s = sin (radians); - c = cos (radians); - - cairo_matrix_init (matrix, - c, s, - -s, c, - 0, 0); -} -slim_hidden_def(cairo_matrix_init_rotate); - -/** - * cairo_matrix_rotate: - * @matrix: a #cairo_matrix_t - * @radians: angle of rotation, in radians. The direction of rotation - * is defined such that positive angles rotate in the direction from - * the positive X axis toward the positive Y axis. With the default - * axis orientation of cairo, positive angles rotate in a clockwise - * direction. - * - * Applies rotation by @radians to the transformation in - * @matrix. The effect of the new transformation is to first rotate the - * coordinates by @radians, then apply the original transformation - * to the coordinates. - **/ -void -cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) -{ - cairo_matrix_t tmp; - - cairo_matrix_init_rotate (&tmp, radians); - - cairo_matrix_multiply (matrix, &tmp, matrix); -} - -/** - * cairo_matrix_multiply: - * @result: a #cairo_matrix_t in which to store the result - * @a: a #cairo_matrix_t - * @b: a #cairo_matrix_t - * - * Multiplies the affine transformations in @a and @b together - * and stores the result in @result. The effect of the resulting - * transformation is to first apply the transformation in @a to the - * coordinates and then apply the transformation in @b to the - * coordinates. - * - * It is allowable for @result to be identical to either @a or @b. - **/ -/* - * XXX: The ordering of the arguments to this function corresponds - * to [row_vector]*A*B. If we want to use column vectors instead, - * then we need to switch the two arguments and fix up all - * uses. - */ -void -cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) -{ - cairo_matrix_t r; - - r.xx = a->xx * b->xx + a->yx * b->xy; - r.yx = a->xx * b->yx + a->yx * b->yy; - - r.xy = a->xy * b->xx + a->yy * b->xy; - r.yy = a->xy * b->yx + a->yy * b->yy; - - r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; - r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; - - *result = r; -} -slim_hidden_def(cairo_matrix_multiply); - -/** - * cairo_matrix_transform_distance: - * @matrix: a #cairo_matrix_t - * @dx: X component of a distance vector. An in/out parameter - * @dy: Y component of a distance vector. An in/out parameter - * - * Transforms the distance vector (@dx,@dy) by @matrix. This is - * similar to cairo_matrix_transform_point() except that the translation - * components of the transformation are ignored. The calculation of - * the returned vector is as follows: - * - * - * dx2 = dx1 * a + dy1 * c; - * dy2 = dx1 * b + dy1 * d; - * - * - * Affine transformations are position invariant, so the same vector - * always transforms to the same vector. If (@x1,@y1) transforms - * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to - * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. - **/ -void -cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) -{ - double new_x, new_y; - - new_x = (matrix->xx * *dx + matrix->xy * *dy); - new_y = (matrix->yx * *dx + matrix->yy * *dy); - - *dx = new_x; - *dy = new_y; -} -slim_hidden_def(cairo_matrix_transform_distance); - -/** - * cairo_matrix_transform_point: - * @matrix: a #cairo_matrix_t - * @x: X position. An in/out parameter - * @y: Y position. An in/out parameter - * - * Transforms the point (@x, @y) by @matrix. - **/ -void -cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y) -{ - cairo_matrix_transform_distance (matrix, x, y); - - *x += matrix->x0; - *y += matrix->y0; -} -slim_hidden_def(cairo_matrix_transform_point); - -void -_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, - double *x1, double *y1, - double *x2, double *y2, - cairo_bool_t *is_tight) -{ - int i; - double quad_x[4], quad_y[4]; - double min_x, max_x; - double min_y, max_y; - - if (matrix->xy == 0. && matrix->yx == 0.) { - /* non-rotation/skew matrix, just map the two extreme points */ - - if (matrix->xx != 1.) { - quad_x[0] = *x1 * matrix->xx; - quad_x[1] = *x2 * matrix->xx; - if (quad_x[0] < quad_x[1]) { - *x1 = quad_x[0]; - *x2 = quad_x[1]; - } else { - *x1 = quad_x[1]; - *x2 = quad_x[0]; - } - } - if (matrix->x0 != 0.) { - *x1 += matrix->x0; - *x2 += matrix->x0; - } - - if (matrix->yy != 1.) { - quad_y[0] = *y1 * matrix->yy; - quad_y[1] = *y2 * matrix->yy; - if (quad_y[0] < quad_y[1]) { - *y1 = quad_y[0]; - *y2 = quad_y[1]; - } else { - *y1 = quad_y[1]; - *y2 = quad_y[0]; - } - } - if (matrix->y0 != 0.) { - *y1 += matrix->y0; - *y2 += matrix->y0; - } - - if (is_tight) - *is_tight = TRUE; - - return; - } - - /* general matrix */ - quad_x[0] = *x1; - quad_y[0] = *y1; - cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); - - quad_x[1] = *x2; - quad_y[1] = *y1; - cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]); - - quad_x[2] = *x1; - quad_y[2] = *y2; - cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]); - - quad_x[3] = *x2; - quad_y[3] = *y2; - cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]); - - min_x = max_x = quad_x[0]; - min_y = max_y = quad_y[0]; - - for (i=1; i < 4; i++) { - if (quad_x[i] < min_x) - min_x = quad_x[i]; - if (quad_x[i] > max_x) - max_x = quad_x[i]; - - if (quad_y[i] < min_y) - min_y = quad_y[i]; - if (quad_y[i] > max_y) - max_y = quad_y[i]; - } - - *x1 = min_x; - *y1 = min_y; - *x2 = max_x; - *y2 = max_y; - - if (is_tight) { - /* it's tight if and only if the four corner points form an axis-aligned - rectangle. - And that's true if and only if we can derive corners 0 and 3 from - corners 1 and 2 in one of two straightforward ways... - We could use a tolerance here but for now we'll fall back to FALSE in the case - of floating point error. - */ - *is_tight = - (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] && - quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) || - (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] && - quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]); - } -} - -cairo_private void -_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, - cairo_box_t *bbox, - cairo_bool_t *is_tight) -{ - double x1, y1, x2, y2; - - _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2); - _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight); - _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2); -} - -static void -_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) -{ - matrix->xx *= scalar; - matrix->yx *= scalar; - - matrix->xy *= scalar; - matrix->yy *= scalar; - - matrix->x0 *= scalar; - matrix->y0 *= scalar; -} - -/* This function isn't a correct adjoint in that the implicit 1 in the - homogeneous result should actually be ad-bc instead. But, since this - adjoint is only used in the computation of the inverse, which - divides by det (A)=ad-bc anyway, everything works out in the end. */ -static void -_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) -{ - /* adj (A) = transpose (C:cofactor (A,i,j)) */ - double a, b, c, d, tx, ty; - - _cairo_matrix_get_affine (matrix, - &a, &b, - &c, &d, - &tx, &ty); - - cairo_matrix_init (matrix, - d, -b, - -c, a, - c*ty - d*tx, b*tx - a*ty); -} - -/** - * cairo_matrix_invert: - * @matrix: a #cairo_matrix_t - * - * Changes @matrix to be the inverse of its original value. Not - * all transformation matrices have inverses; if the matrix - * collapses points together (it is degenerate), - * then it has no inverse and this function will fail. - * - * Returns: If @matrix has an inverse, modifies @matrix to - * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, - * returns %CAIRO_STATUS_INVALID_MATRIX. - **/ -cairo_status_t -cairo_matrix_invert (cairo_matrix_t *matrix) -{ - double det; - - /* Simple scaling|translation matrices are quite common... */ - if (matrix->xy == 0. && matrix->yx == 0.) { - matrix->x0 = -matrix->x0; - matrix->y0 = -matrix->y0; - - if (matrix->xx != 1.) { - if (matrix->xx == 0.) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - matrix->xx = 1. / matrix->xx; - matrix->x0 *= matrix->xx; - } - - if (matrix->yy != 1.) { - if (matrix->yy == 0.) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - matrix->yy = 1. / matrix->yy; - matrix->y0 *= matrix->yy; - } - - return CAIRO_STATUS_SUCCESS; - } - - /* inv (A) = 1/det (A) * adj (A) */ - det = _cairo_matrix_compute_determinant (matrix); - - if (! ISFINITE (det)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - if (det == 0) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - _cairo_matrix_compute_adjoint (matrix); - _cairo_matrix_scalar_multiply (matrix, 1 / det); - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_invert); - -cairo_bool_t -_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) -{ - double det; - - det = _cairo_matrix_compute_determinant (matrix); - - return ISFINITE (det) && det != 0.; -} - -cairo_bool_t -_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) -{ - return matrix->xx == 0. && - matrix->xy == 0. && - matrix->yx == 0. && - matrix->yy == 0.; -} - -double -_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) -{ - double a, b, c, d; - - a = matrix->xx; b = matrix->yx; - c = matrix->xy; d = matrix->yy; - - return a*d - b*c; -} - -/** - * _cairo_matrix_compute_basis_scale_factors: - * @matrix: a matrix - * @basis_scale: the scale factor in the direction of basis - * @normal_scale: the scale factor in the direction normal to the basis - * @x_basis: basis to use. X basis if true, Y basis otherwise. - * - * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1] - * otherwise, and M is @matrix. - * - * Return value: the scale factor of @matrix on the height of the font, - * or 1.0 if @matrix is %NULL. - **/ -cairo_status_t -_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, - double *basis_scale, double *normal_scale, - cairo_bool_t x_basis) -{ - double det; - - det = _cairo_matrix_compute_determinant (matrix); - - if (! ISFINITE (det)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - if (det == 0) - { - *basis_scale = *normal_scale = 0; - } - else - { - double x = x_basis != 0; - double y = x == 0; - double major, minor; - - cairo_matrix_transform_distance (matrix, &x, &y); - major = hypot (x, y); - /* - * ignore mirroring - */ - if (det < 0) - det = -det; - if (major) - minor = det / major; - else - minor = 0.0; - if (x_basis) - { - *basis_scale = major; - *normal_scale = minor; - } - else - { - *basis_scale = minor; - *normal_scale = major; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_matrix_is_identity (const cairo_matrix_t *matrix) -{ - return (matrix->xx == 1.0 && matrix->yx == 0.0 && - matrix->xy == 0.0 && matrix->yy == 1.0 && - matrix->x0 == 0.0 && matrix->y0 == 0.0); -} - -cairo_bool_t -_cairo_matrix_is_translation (const cairo_matrix_t *matrix) -{ - return (matrix->xx == 1.0 && matrix->yx == 0.0 && - matrix->xy == 0.0 && matrix->yy == 1.0); -} - -cairo_bool_t -_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, - int *itx, int *ity) -{ - if (_cairo_matrix_is_translation (matrix)) - { - cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0); - cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0); - - if (_cairo_fixed_is_integer (x0_fixed) && - _cairo_fixed_is_integer (y0_fixed)) - { - if (itx) - *itx = _cairo_fixed_integer_part (x0_fixed); - if (ity) - *ity = _cairo_fixed_integer_part (y0_fixed); - - return TRUE; - } - } - - return FALSE; -} - -cairo_bool_t -_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix) -{ - if (matrix->xy == 0.0 && matrix->yx == 0.0) { - if (! (matrix->xx == 1.0 || matrix->xx == -1.0)) - return FALSE; - if (! (matrix->yy == 1.0 || matrix->yy == -1.0)) - return FALSE; - } else if (matrix->xx == 0.0 && matrix->yy == 0.0) { - if (! (matrix->xy == 1.0 || matrix->xy == -1.0)) - return FALSE; - if (! (matrix->yx == 1.0 || matrix->yx == -1.0)) - return FALSE; - } else - return FALSE; - - return TRUE; -} - -/* By pixel exact here, we mean a matrix that is composed only of - * 90 degree rotations, flips, and integer translations and produces a 1:1 - * mapping between source and destination pixels. If we transform an image - * with a pixel-exact matrix, filtering is not useful. - */ -cairo_bool_t -_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) -{ - cairo_fixed_t x0_fixed, y0_fixed; - - if (! _cairo_matrix_has_unity_scale (matrix)) - return FALSE; - - x0_fixed = _cairo_fixed_from_double (matrix->x0); - y0_fixed = _cairo_fixed_from_double (matrix->y0); - - return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed); -} - -/* - A circle in user space is transformed into an ellipse in device space. - - The following is a derivation of a formula to calculate the length of the - major axis for this ellipse; this is useful for error bounds calculations. - - Thanks to Walter Brisken for this derivation: - - 1. First some notation: - - All capital letters represent vectors in two dimensions. A prime ' - represents a transformed coordinate. Matrices are written in underlined - form, ie _R_. Lowercase letters represent scalar real values. - - 2. The question has been posed: What is the maximum expansion factor - achieved by the linear transformation - - X' = X _R_ - - where _R_ is a real-valued 2x2 matrix with entries: - - _R_ = [a b] - [c d] . - - In other words, what is the maximum radius, MAX[ |X'| ], reached for any - X on the unit circle ( |X| = 1 ) ? - - 3. Some useful formulae - - (A) through (C) below are standard double-angle formulae. (D) is a lesser - known result and is derived below: - - (A) sin²(θ) = (1 - cos(2*θ))/2 - (B) cos²(θ) = (1 + cos(2*θ))/2 - (C) sin(θ)*cos(θ) = sin(2*θ)/2 - (D) MAX[a*cos(θ) + b*sin(θ)] = sqrt(a² + b²) - - Proof of (D): - - find the maximum of the function by setting the derivative to zero: - - -a*sin(θ)+b*cos(θ) = 0 - - From this it follows that - - tan(θ) = b/a - - and hence - - sin(θ) = b/sqrt(a² + b²) - - and - - cos(θ) = a/sqrt(a² + b²) - - Thus the maximum value is - - MAX[a*cos(θ) + b*sin(θ)] = (a² + b²)/sqrt(a² + b²) - = sqrt(a² + b²) - - 4. Derivation of maximum expansion - - To find MAX[ |X'| ] we search brute force method using calculus. The unit - circle on which X is constrained is to be parameterized by t: - - X(θ) = (cos(θ), sin(θ)) - - Thus - - X'(θ) = X(θ) * _R_ = (cos(θ), sin(θ)) * [a b] - [c d] - = (a*cos(θ) + c*sin(θ), b*cos(θ) + d*sin(θ)). - - Define - - r(θ) = |X'(θ)| - - Thus - - r²(θ) = (a*cos(θ) + c*sin(θ))² + (b*cos(θ) + d*sin(θ))² - = (a² + b²)*cos²(θ) + (c² + d²)*sin²(θ) - + 2*(a*c + b*d)*cos(θ)*sin(θ) - - Now apply the double angle formulae (A) to (C) from above: - - r²(θ) = (a² + b² + c² + d²)/2 - + (a² + b² - c² - d²)*cos(2*θ)/2 - + (a*c + b*d)*sin(2*θ) - = f + g*cos(φ) + h*sin(φ) - - Where - - f = (a² + b² + c² + d²)/2 - g = (a² + b² - c² - d²)/2 - h = (a*c + d*d) - φ = 2*θ - - It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]). Here we determine MAX[ r² ] - using (D) from above: - - MAX[ r² ] = f + sqrt(g² + h²) - - And finally - - MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) ) - - Which is the solution to this problem. - - Walter Brisken - 2004/10/08 - - (Note that the minor axis length is at the minimum of the above solution, - which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)). - - - For another derivation of the same result, using Singular Value Decomposition, - see doc/tutorial/src/singular.c. -*/ - -/* determine the length of the major and minor axes of a circle of the given - radius after applying the transformation matrix. */ -void -_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, - double radius, - double *major, - double *minor) -{ - double a, b, c, d, f, g, h, i, j, k; - - _cairo_matrix_get_affine (matrix, - &a, &b, - &c, &d, - NULL, NULL); - - i = a*a + b*b; - j = c*c + d*d; - k = a*c + b*d; - - f = 0.5 * (i + j); - g = 0.5 * (i - j); - h = hypot (g, k); - - if (major) - *major = radius * sqrt (f + h); - if (minor) - *minor = radius * sqrt (f - h); -} - -/* determine the length of the major axis of a circle of the given radius - after applying the transformation matrix. */ -double -_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, - double radius) -{ - double major; - - _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL); - - return major; -} - -void -_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform, - double xc, - double yc) -{ - static const pixman_transform_t pixman_identity_transform = {{ - {1 << 16, 0, 0}, - { 0, 1 << 16, 0}, - { 0, 0, 1 << 16} - }}; - - if (_cairo_matrix_is_identity (matrix)) { - *pixman_transform = pixman_identity_transform; - } else { - cairo_matrix_t inv; - unsigned max_iterations; - - pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); - pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); - pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); - - pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); - pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); - pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); - - pixman_transform->matrix[2][0] = 0; - pixman_transform->matrix[2][1] = 0; - pixman_transform->matrix[2][2] = 1 << 16; - - /* The conversion above breaks cairo's translation invariance: - * a translation of (a, b) in device space translates to - * a translation of (xx * a + xy * b, yx * a + yy * b) - * for cairo, while pixman uses rounded versions of xx ... yy. - * This error increases as a and b get larger. - * - * To compensate for this, we fix the point (xc, yc) in pattern - * space and adjust pixman's transform to agree with cairo's at - * that point. - */ - - if (_cairo_matrix_has_unity_scale (matrix)) - return; - - /* Note: If we can't invert the transformation, skip the adjustment. */ - inv = *matrix; - if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) - return; - - /* find the pattern space coordinate that maps to (xc, yc) */ - xc += .5; yc += .5; /* offset for the pixel centre */ - max_iterations = 5; - do { - double x,y; - pixman_vector_t vector; - cairo_fixed_16_16_t dx, dy; - - vector.vector[0] = _cairo_fixed_16_16_from_double (xc); - vector.vector[1] = _cairo_fixed_16_16_from_double (yc); - vector.vector[2] = 1 << 16; - - if (! pixman_transform_point_3d (pixman_transform, &vector)) - return; - - x = pixman_fixed_to_double (vector.vector[0]); - y = pixman_fixed_to_double (vector.vector[1]); - cairo_matrix_transform_point (&inv, &x, &y); - - /* Ideally, the vector should now be (xc, yc). - * We can now compensate for the resulting error. - */ - x -= xc; - y -= yc; - cairo_matrix_transform_distance (matrix, &x, &y); - dx = _cairo_fixed_16_16_from_double (x); - dy = _cairo_fixed_16_16_from_double (y); - pixman_transform->matrix[0][2] -= dx; - pixman_transform->matrix[1][2] -= dy; - - if (dx == 0 && dy == 0) - break; - } while (--max_iterations); - } -} diff --git a/libs/cairo/cairo/src/cairo-meta-surface-private.h b/libs/cairo/cairo/src/cairo-meta-surface-private.h deleted file mode 100644 index c1f6c40f2..000000000 --- a/libs/cairo/cairo/src/cairo-meta-surface-private.h +++ /dev/null @@ -1,155 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_META_SURFACE_H -#define CAIRO_META_SURFACE_H - -#include "cairoint.h" -#include "cairo-path-fixed-private.h" - -typedef enum { - /* The 5 basic drawing operations. */ - CAIRO_COMMAND_PAINT, - CAIRO_COMMAND_MASK, - CAIRO_COMMAND_STROKE, - CAIRO_COMMAND_FILL, - CAIRO_COMMAND_SHOW_TEXT_GLYPHS, - - /* Other junk. For most of these, we should be able to assert that - * they never get called except as part of fallbacks for the 5 - * basic drawing operations (which we implement already so the - * fallbacks should never get triggered). So the plan is to - * eliminate as many of these as possible. */ - - CAIRO_COMMAND_INTERSECT_CLIP_PATH - -} cairo_command_type_t; - -typedef enum { - CAIRO_META_REGION_ALL, - CAIRO_META_REGION_NATIVE, - CAIRO_META_REGION_IMAGE_FALLBACK -} cairo_meta_region_type_t; - -typedef struct _cairo_command_header { - cairo_command_type_t type; - cairo_meta_region_type_t region; - cairo_rectangle_int_t extents; -} cairo_command_header_t; - -typedef struct _cairo_command_paint { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; -} cairo_command_paint_t; - -typedef struct _cairo_command_mask { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_pattern_union_t mask; -} cairo_command_mask_t; - -typedef struct _cairo_command_stroke { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_stroke_style_t style; - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_stroke_t; - -typedef struct _cairo_command_fill { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_fill_t; - -typedef struct _cairo_command_show_text_glyphs { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - char *utf8; - int utf8_len; - cairo_glyph_t *glyphs; - unsigned int num_glyphs; - cairo_text_cluster_t *clusters; - int num_clusters; - cairo_text_cluster_flags_t cluster_flags; - cairo_scaled_font_t *scaled_font; -} cairo_command_show_text_glyphs_t; - -typedef struct _cairo_command_intersect_clip_path { - cairo_command_header_t header; - cairo_path_fixed_t *path_pointer; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_intersect_clip_path_t; - -typedef union _cairo_command { - cairo_command_header_t header; - - /* The 5 basic drawing operations. */ - cairo_command_paint_t paint; - cairo_command_mask_t mask; - cairo_command_stroke_t stroke; - cairo_command_fill_t fill; - cairo_command_show_text_glyphs_t show_text_glyphs; - - /* The other junk. */ - cairo_command_intersect_clip_path_t intersect_clip_path; -} cairo_command_t; - -typedef struct _cairo_meta_surface { - cairo_surface_t base; - - cairo_content_t content; - - /* A meta-surface is logically unbounded, but when used as a - * source we need to render it to an image, so we need a size at - * which to create that image. */ - double width_pixels; - double height_pixels; - cairo_rectangle_int_t extents; - - cairo_array_t commands; - cairo_surface_t *commands_owner; - - cairo_bool_t is_clipped; - int replay_start_idx; -} cairo_meta_surface_t; - -slim_hidden_proto (cairo_meta_surface_create); -slim_hidden_proto (cairo_meta_surface_replay); - -cairo_private cairo_int_status_t -_cairo_meta_surface_get_path (cairo_surface_t *surface, - cairo_path_fixed_t *path); - - -cairo_private cairo_status_t -_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface, - cairo_surface_t *target); - -cairo_private cairo_status_t -_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target); -cairo_private cairo_status_t -_cairo_meta_surface_replay_region (cairo_surface_t *surface, - cairo_surface_t *target, - cairo_meta_region_type_t region); - -cairo_private cairo_bool_t -_cairo_surface_is_meta (const cairo_surface_t *surface); - -#endif /* CAIRO_META_SURFACE_H */ diff --git a/libs/cairo/cairo/src/cairo-misc.c b/libs/cairo/cairo/src/cairo-misc.c deleted file mode 100644 index 6aa793f61..000000000 --- a/libs/cairo/cairo/src/cairo-misc.c +++ /dev/null @@ -1,895 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED); -COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); - -/** - * SECTION:cairo-status - * @Title: Error handling - * @Short_Description: Decoding cairo's status - * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(), - * cairo_font_face_status(), cairo_scaled_font_status(), - * cairo_region_status() - * - * Cairo uses a single status type to represent all kinds of errors. A status - * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value - * of zero. All other status values represent an error. - * - * Cairo's error handling is designed to be easy to use and safe. All major - * cairo objects retain an error status internally which - * can be queried anytime by the users using cairo*_status() calls. In - * the mean time, it is safe to call all cairo functions normally even if the - * underlying object is in an error status. This means that no error handling - * code is required before or after each individual cairo function call. - */ - -/* Public stuff */ - -/** - * cairo_status_to_string: - * @status: a cairo status - * - * Provides a human-readable description of a #cairo_status_t. - * - * Returns: a string representation of the status - */ -const char * -cairo_status_to_string (cairo_status_t status) -{ - switch (status) { - case CAIRO_STATUS_SUCCESS: - return "no error has occurred"; - case CAIRO_STATUS_NO_MEMORY: - return "out of memory"; - case CAIRO_STATUS_INVALID_RESTORE: - return "cairo_restore() without matching cairo_save()"; - case CAIRO_STATUS_INVALID_POP_GROUP: - return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()"; - case CAIRO_STATUS_NO_CURRENT_POINT: - return "no current point defined"; - case CAIRO_STATUS_INVALID_MATRIX: - return "invalid matrix (not invertible)"; - case CAIRO_STATUS_INVALID_STATUS: - return "invalid value for an input cairo_status_t"; - case CAIRO_STATUS_NULL_POINTER: - return "NULL pointer"; - case CAIRO_STATUS_INVALID_STRING: - return "input string not valid UTF-8"; - case CAIRO_STATUS_INVALID_PATH_DATA: - return "input path data not valid"; - case CAIRO_STATUS_READ_ERROR: - return "error while reading from input stream"; - case CAIRO_STATUS_WRITE_ERROR: - return "error while writing to output stream"; - case CAIRO_STATUS_SURFACE_FINISHED: - return "the target surface has been finished"; - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: - return "the surface type is not appropriate for the operation"; - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: - return "the pattern type is not appropriate for the operation"; - case CAIRO_STATUS_INVALID_CONTENT: - return "invalid value for an input cairo_content_t"; - case CAIRO_STATUS_INVALID_FORMAT: - return "invalid value for an input cairo_format_t"; - case CAIRO_STATUS_INVALID_VISUAL: - return "invalid value for an input Visual*"; - case CAIRO_STATUS_FILE_NOT_FOUND: - return "file not found"; - case CAIRO_STATUS_INVALID_DASH: - return "invalid value for a dash setting"; - case CAIRO_STATUS_INVALID_DSC_COMMENT: - return "invalid value for a DSC comment"; - case CAIRO_STATUS_INVALID_INDEX: - return "invalid index passed to getter"; - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: - return "clip region not representable in desired format"; - case CAIRO_STATUS_TEMP_FILE_ERROR: - return "error creating or writing to a temporary file"; - case CAIRO_STATUS_INVALID_STRIDE: - return "invalid value for stride"; - case CAIRO_STATUS_FONT_TYPE_MISMATCH: - return "the font type is not appropriate for the operation"; - case CAIRO_STATUS_USER_FONT_IMMUTABLE: - return "the user-font is immutable"; - case CAIRO_STATUS_USER_FONT_ERROR: - return "error occurred in a user-font callback function"; - case CAIRO_STATUS_NEGATIVE_COUNT: - return "negative number used where it is not allowed"; - case CAIRO_STATUS_INVALID_CLUSTERS: - return "input clusters do not represent the accompanying text and glyph arrays"; - case CAIRO_STATUS_INVALID_SLANT: - return "invalid value for an input cairo_font_slant_t"; - case CAIRO_STATUS_INVALID_WEIGHT: - return "invalid value for an input cairo_font_weight_t"; - case CAIRO_STATUS_INVALID_SIZE: - return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)"; - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: - return "user-font method not implemented"; - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: - return "the device type is not appropriate for the operation"; - case CAIRO_STATUS_DEVICE_ERROR: - return "an operation to the device caused an unspecified error"; - default: - case CAIRO_STATUS_LAST_STATUS: - return ""; - } -} - - -/** - * cairo_glyph_allocate: - * @num_glyphs: number of glyphs to allocate - * - * Allocates an array of #cairo_glyph_t's. - * This function is only useful in implementations of - * #cairo_user_scaled_font_text_to_glyphs_func_t where the user - * needs to allocate an array of glyphs that cairo will free. - * For all other uses, user can use their own allocation method - * for glyphs. - * - * This function returns %NULL if @num_glyphs is not positive, - * or if out of memory. That means, the %NULL return value - * signals out-of-memory only if @num_glyphs was positive. - * - * Returns: the newly allocated array of glyphs that should be - * freed using cairo_glyph_free() - * - * Since: 1.8 - */ -cairo_glyph_t * -cairo_glyph_allocate (int num_glyphs) -{ - if (num_glyphs <= 0) - return NULL; - - return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); -} -slim_hidden_def (cairo_glyph_allocate); - -/** - * cairo_glyph_free: - * @glyphs: array of glyphs to free, or %NULL - * - * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate(). - * This function is only useful to free glyph array returned - * by cairo_scaled_font_text_to_glyphs() where cairo returns - * an array of glyphs that the user will free. - * For all other uses, user can use their own allocation method - * for glyphs. - * - * Since: 1.8 - */ -void -cairo_glyph_free (cairo_glyph_t *glyphs) -{ - if (glyphs) - free (glyphs); -} -slim_hidden_def (cairo_glyph_free); - -/** - * cairo_text_cluster_allocate: - * @num_clusters: number of text_clusters to allocate - * - * Allocates an array of #cairo_text_cluster_t's. - * This function is only useful in implementations of - * #cairo_user_scaled_font_text_to_glyphs_func_t where the user - * needs to allocate an array of text clusters that cairo will free. - * For all other uses, user can use their own allocation method - * for text clusters. - * - * This function returns %NULL if @num_clusters is not positive, - * or if out of memory. That means, the %NULL return value - * signals out-of-memory only if @num_clusters was positive. - * - * Returns: the newly allocated array of text clusters that should be - * freed using cairo_text_cluster_free() - * - * Since: 1.8 - */ -cairo_text_cluster_t * -cairo_text_cluster_allocate (int num_clusters) -{ - if (num_clusters <= 0) - return NULL; - - return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); -} -slim_hidden_def (cairo_text_cluster_allocate); - -/** - * cairo_text_cluster_free: - * @clusters: array of text clusters to free, or %NULL - * - * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate(). - * This function is only useful to free text cluster array returned - * by cairo_scaled_font_text_to_glyphs() where cairo returns - * an array of text clusters that the user will free. - * For all other uses, user can use their own allocation method - * for text clusters. - * - * Since: 1.8 - */ -void -cairo_text_cluster_free (cairo_text_cluster_t *clusters) -{ - if (clusters) - free (clusters); -} -slim_hidden_def (cairo_text_cluster_free); - - -/* Private stuff */ - -/** - * _cairo_validate_text_clusters: - * @utf8: UTF-8 text - * @utf8_len: length of @utf8 in bytes - * @glyphs: array of glyphs - * @num_glyphs: number of glyphs - * @clusters: array of cluster mapping information - * @num_clusters: number of clusters in the mapping - * @cluster_flags: cluster flags - * - * Check that clusters cover the entire glyphs and utf8 arrays, - * and that cluster boundaries are UTF-8 boundaries. - * - * Return value: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_INVALID_CLUSTERS on error. - * The error is either invalid UTF-8 input, - * or bad cluster mapping. - */ -cairo_status_t -_cairo_validate_text_clusters (const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags) -{ - cairo_status_t status; - unsigned int n_bytes = 0; - unsigned int n_glyphs = 0; - int i; - - for (i = 0; i < num_clusters; i++) { - int cluster_bytes = clusters[i].num_bytes; - int cluster_glyphs = clusters[i].num_glyphs; - - if (cluster_bytes < 0 || cluster_glyphs < 0) - goto BAD; - - /* A cluster should cover at least one character or glyph. - * I can't see any use for a 0,0 cluster. - * I can't see an immediate use for a zero-text cluster - * right now either, but they don't harm. - * Zero-glyph clusters on the other hand are useful for - * things like U+200C ZERO WIDTH NON-JOINER */ - if (cluster_bytes == 0 && cluster_glyphs == 0) - goto BAD; - - /* Since n_bytes and n_glyphs are unsigned, but the rest of - * values involved are signed, we can detect overflow easily */ - if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs) - goto BAD; - - /* Make sure we've got valid UTF-8 for the cluster */ - status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL); - if (unlikely (status)) - return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); - - n_bytes += cluster_bytes ; - n_glyphs += cluster_glyphs; - } - - if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) { - BAD: - return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_operator_bounded_by_mask: - * @op: a #cairo_operator_t - * - * A bounded operator is one where mask pixel - * of zero results in no effect on the destination image. - * - * Unbounded operators often require special handling; if you, for - * example, draw trapezoids with an unbounded operator, the effect - * extends past the bounding box of the trapezoids. - * - * Return value: %TRUE if the operator is bounded by the mask operand - **/ -cairo_bool_t -_cairo_operator_bounded_by_mask (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return TRUE; - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_ATOP: - return FALSE; - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -/** - * _cairo_operator_bounded_by_source: - * @op: a #cairo_operator_t - * - * A bounded operator is one where source pixels of zero - * (in all four components, r, g, b and a) effect no change - * in the resulting destination image. - * - * Unbounded operators often require special handling; if you, for - * example, copy a surface with the SOURCE operator, the effect - * extends past the bounding box of the source surface. - * - * Return value: %TRUE if the operator is bounded by the source operand - **/ -cairo_bool_t -_cairo_operator_bounded_by_source (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return TRUE; - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_ATOP: - return FALSE; - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -uint32_t -_cairo_operator_bounded_by_either (cairo_operator_t op) -{ - switch (op) { - default: - ASSERT_NOT_REACHED; - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE; - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - return CAIRO_OPERATOR_BOUND_BY_MASK; - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_ATOP: - return 0; - } - -} - -#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L -/* This function is identical to the C99 function lround(), except that it - * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and - * has a valid input range of (INT_MIN, INT_MAX] instead of - * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems - * than other commonly used methods for rounding (lround, round, rint, lrint - * or float (d + 0.5)). - * - * The reason why this function is much faster on x86 than other - * methods is due to the fact that it avoids the fldcw instruction. - * This instruction incurs a large performance penalty on modern Intel - * processors due to how it prevents efficient instruction pipelining. - * - * The reason why this function is much faster on FPU-less systems is for - * an entirely different reason. All common rounding methods involve multiple - * floating-point operations. Each one of these operations has to be - * emulated in software, which adds up to be a large performance penalty. - * This function doesn't perform any floating-point calculations, and thus - * avoids this penalty. - */ -int -_cairo_lround (double d) -{ - uint32_t top, shift_amount, output; - union { - double d; - uint64_t ui64; - uint32_t ui32[2]; - } u; - - u.d = d; - - /* If the integer word order doesn't match the float word order, we swap - * the words of the input double. This is needed because we will be - * treating the whole double as a 64-bit unsigned integer. Notice that we - * use WORDS_BIGENDIAN to detect the integer word order, which isn't - * exactly correct because WORDS_BIGENDIAN refers to byte order, not word - * order. Thus, we are making the assumption that the byte order is the - * same as the integer word order which, on the modern machines that we - * care about, is OK. - */ -#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \ - (!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN)) - { - uint32_t temp = u.ui32[0]; - u.ui32[0] = u.ui32[1]; - u.ui32[1] = temp; - } -#endif - -#ifdef WORDS_BIGENDIAN - #define MSW (0) /* Most Significant Word */ - #define LSW (1) /* Least Significant Word */ -#else - #define MSW (1) - #define LSW (0) -#endif - - /* By shifting the most significant word of the input double to the - * right 20 places, we get the very "top" of the double where the exponent - * and sign bit lie. - */ - top = u.ui32[MSW] >> 20; - - /* Here, we calculate how much we have to shift the mantissa to normalize - * it to an integer value. We extract the exponent "top" by masking out the - * sign bit, then we calculate the shift amount by subtracting the exponent - * from the bias. Notice that the correct bias for 64-bit doubles is - * actually 1075, but we use 1053 instead for two reasons: - * - * 1) To perform rounding later on, we will first need the target - * value in a 31.1 fixed-point format. Thus, the bias needs to be one - * less: (1075 - 1: 1074). - * - * 2) To avoid shifting the mantissa as a full 64-bit integer (which is - * costly on certain architectures), we break the shift into two parts. - * First, the upper and lower parts of the mantissa are shifted - * individually by a constant amount that all valid inputs will require - * at the very least. This amount is chosen to be 21, because this will - * allow the two parts of the mantissa to later be combined into a - * single 32-bit representation, on which the remainder of the shift - * will be performed. Thus, we decrease the bias by an additional 21: - * (1074 - 21: 1053). - */ - shift_amount = 1053 - (top & 0x7FF); - - /* We are done with the exponent portion in "top", so here we shift it off - * the end. - */ - top >>= 11; - - /* Before we perform any operations on the mantissa, we need to OR in - * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask - * off the sign bit nor the exponent bits because these higher bits won't - * make a bit of difference in the rest of our calculations. - */ - u.ui32[MSW] |= 0x100000; - - /* If the input double is negative, we have to decrease the mantissa - * by a hair. This is an important part of performing arithmetic rounding, - * as negative numbers must round towards positive infinity in the - * halfwase case of -x.5. Since "top" contains only the sign bit at this - * point, we can just decrease the mantissa by the value of "top". - */ - u.ui64 -= top; - - /* By decrementing "top", we create a bitmask with a value of either - * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive - * and thus the unsigned subtraction underflowed) that we'll use later. - */ - top--; - - /* Here, we shift the mantissa by the constant value as described above. - * We can emulate a 64-bit shift right by 21 through shifting the top 32 - * bits left 11 places and ORing in the bottom 32 bits shifted 21 places - * to the right. Both parts of the mantissa are now packed into a single - * 32-bit integer. Although we severely truncate the lower part in the - * process, we still have enough significant bits to perform the conversion - * without error (for all valid inputs). - */ - output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21); - - /* Next, we perform the shift that converts the X.Y fixed-point number - * currently found in "output" to the desired 31.1 fixed-point format - * needed for the following rounding step. It is important to consider - * all possible values for "shift_amount" at this point: - * - * - {shift_amount < 0} Since shift_amount is an unsigned integer, it - * really can't have a value less than zero. But, if the shift_amount - * calculation above caused underflow (which would happen with - * input > INT_MAX or input <= INT_MIN) then shift_amount will now be - * a very large number, and so this shift will result in complete - * garbage. But that's OK, as the input was out of our range, so our - * output is undefined. - * - * - {shift_amount > 31} If the magnitude of the input was very small - * (i.e. |input| << 1.0), shift_amount will have a value greater than - * 31. Thus, this shift will also result in garbage. After performing - * the shift, we will zero-out "output" if this is the case. - * - * - {0 <= shift_amount < 32} In this case, the shift will properly convert - * the mantissa into a 31.1 fixed-point number. - */ - output >>= shift_amount; - - /* This is where we perform rounding with the 31.1 fixed-point number. - * Since what we're after is arithmetic rounding, we simply add the single - * fractional bit into the integer part of "output", and just keep the - * integer part. - */ - output = (output >> 1) + (output & 1); - - /* Here, we zero-out the result if the magnitude if the input was very small - * (as explained in the section above). Notice that all input out of the - * valid range is also caught by this condition, which means we produce 0 - * for all invalid input, which is a nice side effect. - * - * The most straightforward way to do this would be: - * - * if (shift_amount > 31) - * output = 0; - * - * But we can use a little trick to avoid the potential branch. The - * expression (shift_amount > 31) will be either 1 or 0, which when - * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow), - * which can be used to conditionally mask away all the bits in "output" - * (in the 0x0 case), effectively zeroing it out. Certain, compilers would - * have done this for us automatically. - */ - output &= ((shift_amount > 31) - 1); - - /* If the input double was a negative number, then we have to negate our - * output. The most straightforward way to do this would be: - * - * if (!top) - * output = -output; - * - * as "top" at this point is either 0x0 (if the input was negative) or - * 0xFFFFFFFF (if the input was positive). But, we can use a trick to - * avoid the branch. Observe that the following snippet of code has the - * same effect as the reference snippet above: - * - * if (!top) - * output = 0 - output; - * else - * output = output - 0; - * - * Armed with the bitmask found in "top", we can condense the two statements - * into the following: - * - * output = (output & top) - (output & ~top); - * - * where, in the case that the input double was negative, "top" will be 0, - * and the statement will be equivalent to: - * - * output = (0) - (output); - * - * and if the input double was positive, "top" will be 0xFFFFFFFF, and the - * statement will be equivalent to: - * - * output = (output) - (0); - * - * Which, as pointed out earlier, is equivalent to the original reference - * snippet. - */ - output = (output & top) - (output & ~top); - - return output; -#undef MSW -#undef LSW -} -#endif - -/* Convert a 32-bit IEEE single precision floating point number to a - * 'half' representation (s10.5) - */ -uint16_t -_cairo_half_from_float (float f) -{ - union { - uint32_t ui; - float f; - } u; - int s, e, m; - - u.f = f; - s = (u.ui >> 16) & 0x00008000; - e = ((u.ui >> 23) & 0x000000ff) - (127 - 15); - m = u.ui & 0x007fffff; - if (e <= 0) { - if (e < -10) { - /* underflow */ - return 0; - } - - m = (m | 0x00800000) >> (1 - e); - - /* round to nearest, round 0.5 up. */ - if (m & 0x00001000) - m += 0x00002000; - return s | (m >> 13); - } else if (e == 0xff - (127 - 15)) { - if (m == 0) { - /* infinity */ - return s | 0x7c00; - } else { - /* nan */ - m >>= 13; - return s | 0x7c00 | m | (m == 0); - } - } else { - /* round to nearest, round 0.5 up. */ - if (m & 0x00001000) { - m += 0x00002000; - - if (m & 0x00800000) { - m = 0; - e += 1; - } - } - - if (e > 30) { - /* overflow -> infinity */ - return s | 0x7c00; - } - - return s | (e << 10) | (m >> 13); - } -} - - -#ifdef _WIN32 - -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - -#include -#include - -#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/libs/cairo/cairo/src/cairo-mutex-impl-private.h b/libs/cairo/cairo/src/cairo-mutex-impl-private.h deleted file mode 100644 index 72086036c..000000000 --- a/libs/cairo/cairo/src/cairo-mutex-impl-private.h +++ /dev/null @@ -1,244 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_MUTEX_IMPL_PRIVATE_H -#define CAIRO_MUTEX_IMPL_PRIVATE_H - -#include "cairo.h" - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_LOCKDEP -#include -#endif - -/* A fully qualified no-operation statement */ -#define CAIRO_MUTEX_IMPL_NOOP do {/*no-op*/} while (0) -/* And one that evaluates its argument once */ -#define CAIRO_MUTEX_IMPL_NOOP1(expr) do { (void)(expr); } while (0) -/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the - * result of __attribute__((warn_used_result)) functions. */ - -/* Cairo mutex implementation: - * - * Any new mutex implementation needs to do the following: - * - * - Condition on the right header or feature. Headers are - * preferred as eg. you still can use win32 mutex implementation - * on a win32 system even if you do not compile the win32 - * surface/backend. - * - * - typedef #cairo_mutex_impl_t to the proper mutex type on your target - * system. Note that you may or may not need to use a pointer, - * depending on what kinds of initialization your mutex - * implementation supports. No trailing semicolon needed. - * You should be able to compile the following snippet (don't try - * running it): - * - * - * cairo_mutex_impl_t _cairo_some_mutex; - * - * - * - #define %CAIRO_MUTEX_IMPL_ 1 with suitable name for your platform. You - * can later use this symbol in cairo-system.c. - * - * - #define CAIRO_MUTEX_IMPL_LOCK(mutex) and CAIRO_MUTEX_IMPL_UNLOCK(mutex) to - * proper statement to lock/unlock the mutex object passed in. - * You can (and should) assume that the mutex is already - * initialized, and is-not-already-locked/is-locked, - * respectively. Use the "do { ... } while (0)" idiom if necessary. - * No trailing semicolons are needed (in any macro you define here). - * You should be able to compile the following snippet: - * - * - * cairo_mutex_impl_t _cairo_some_mutex; - * - * if (1) - * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); - * else - * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); - * - * - * - #define %CAIRO_MUTEX_IMPL_NIL_INITIALIZER to something that can - * initialize the #cairo_mutex_impl_t type you defined. Most of the - * time one of 0, %NULL, or {} works. At this point - * you should be able to compile the following snippet: - * - * - * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; - * - * if (1) - * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); - * else - * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); - * - * - * - If the above code is not enough to initialize a mutex on - * your platform, #define CAIRO_MUTEX_IMPL_INIT(mutex) to statement - * to initialize the mutex (allocate resources, etc). Such that - * you should be able to compile AND RUN the following snippet: - * - * - * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; - * - * CAIRO_MUTEX_IMPL_INIT (_cairo_some_mutex); - * - * if (1) - * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); - * else - * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); - * - * - * - If you define CAIRO_MUTEX_IMPL_INIT(mutex), cairo will use it to - * initialize all static mutex'es. If for any reason that should - * not happen (eg. %CAIRO_MUTEX_IMPL_INIT is just a faster way than - * what cairo does using %CAIRO_MUTEX_IMPL_NIL_INITIALIZER), then - * - * #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP - * - * - * - If your system supports freeing a mutex object (deallocating - * resources, etc), then #define CAIRO_MUTEX_IMPL_FINI(mutex) to do - * that. - * - * - If you define CAIRO_MUTEX_IMPL_FINI(mutex), cairo will use it to - * define a finalizer function to finalize all static mutex'es. - * However, it's up to you to call CAIRO_MUTEX_IMPL_FINALIZE() at - * proper places, eg. when the system is unloading the cairo library. - * So, if for any reason finalizing static mutex'es is not needed - * (eg. you never call CAIRO_MUTEX_IMPL_FINALIZE()), then - * - * #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP - * - * - * - That is all. If for any reason you think the above API is - * not enough to implement #cairo_mutex_impl_t on your system, please - * stop and write to the cairo mailing list about it. DO NOT - * poke around cairo-mutex-private.h for possible solutions. - */ - -#if CAIRO_NO_MUTEX - -/* No mutexes */ - - typedef int cairo_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_NO 1 -# define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP -# define CAIRO_MUTEX_IMPL_LOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 - -# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 - - typedef int cairo_recursive_mutex_impl_t; - -# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) -# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER 0 - -#elif defined(_WIN32) /******************************************************/ - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -/* We require Windows 7 features */ -#if !defined(WINVER) || (WINVER < 0x0601) -# define WINVER 0x0601 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) -# define _WIN32_WINNT 0x0601 -#endif - -# include - - typedef SRWLOCK cairo_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_WIN32 1 -# define CAIRO_MUTEX_IMPL_LOCK(mutex) AcquireSRWLockExclusive (&(mutex)) -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) ReleaseSRWLockExclusive (&(mutex)) -# define CAIRO_MUTEX_IMPL_INIT(mutex) InitializeSRWLock (&(mutex)) -# define CAIRO_MUTEX_IMPL_FINI(mutex) CAIRO_MUTEX_IMPL_NOOP -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER SRWLOCK_INIT - -#elif defined __OS2__ /******************************************************/ - -# define INCL_BASE -# define INCL_PM -# include - - typedef HMTX cairo_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_OS2 1 -# define CAIRO_MUTEX_IMPL_LOCK(mutex) DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT) -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) DosReleaseMutexSem(mutex) -# define CAIRO_MUTEX_IMPL_INIT(mutex) DosCreateMutexSem (NULL, &(mutex), 0L, FALSE) -# define CAIRO_MUTEX_IMPL_FINI(mutex) DosCloseMutexSem (mutex) -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 - -#elif CAIRO_HAS_BEOS_SURFACE /***********************************************/ - - typedef BLocker* cairo_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_BEOS 1 -# define CAIRO_MUTEX_IMPL_LOCK(mutex) (mutex)->Lock() -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) (mutex)->Unlock() -# define CAIRO_MUTEX_IMPL_INIT(mutex) (mutex) = new BLocker() -# define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex) -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL - -#elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/ - -# include - - typedef pthread_mutex_t cairo_mutex_impl_t; - typedef pthread_mutex_t cairo_recursive_mutex_impl_t; - -# define CAIRO_MUTEX_IMPL_PTHREAD 1 -#if HAVE_LOCKDEP -/* expose all mutexes to the validator */ -# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) -#endif -# define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) -# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) -#if HAVE_LOCKDEP -# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) -# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) -#endif -# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) -#if ! HAVE_LOCKDEP -# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP -#endif -# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER - -# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 -# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) do { \ - pthread_mutexattr_t attr; \ - pthread_mutexattr_init (&attr); \ - pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); \ - pthread_mutex_init (&(mutex), &attr); \ - pthread_mutexattr_destroy (&attr); \ -} while (0) -# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - -#else /**********************************************************************/ - -# error "XXX: No mutex implementation found. Cairo will not work with multiple threads. Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support." - -#endif - -/* By default mutex implementations are assumed to be recursive */ -#if ! CAIRO_MUTEX_HAS_RECURSIVE_IMPL - -# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 - - typedef cairo_mutex_impl_t cairo_recursive_mutex_impl_t; - -# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) CAIRO_MUTEX_IMPL_INIT(mutex) -# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER - -#endif - -#endif diff --git a/libs/cairo/cairo/src/cairo-mutex-list-private.h b/libs/cairo/cairo/src/cairo-mutex-list-private.h deleted file mode 100644 index 3f2e44119..000000000 --- a/libs/cairo/cairo/src/cairo-mutex-list-private.h +++ /dev/null @@ -1,49 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_FEATURES_H -/* This block is to just make this header file standalone */ -#define CAIRO_MUTEX_DECLARE(mutex) -#endif - -CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) - -CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex) - -CAIRO_MUTEX_DECLARE (_cairo_error_mutex) -CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) -CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) -CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) -CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex) -CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex) - -#if CAIRO_HAS_FT_FONT -CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) -#endif - -#if CAIRO_HAS_WIN32_FONT -CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex) -#endif - -#if CAIRO_HAS_XLIB_SURFACE -CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) -#endif - -#if CAIRO_HAS_XCB_SURFACE -CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) -#endif - -#if CAIRO_HAS_GL_SURFACE -CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) -#endif - -#if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) -CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) -#endif - -#if CAIRO_HAS_DRM_SURFACE -CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex) -#endif -/* Undefine, to err on unintended inclusion */ -#undef CAIRO_MUTEX_DECLARE diff --git a/libs/cairo/cairo/src/cairo-mutex-private.h b/libs/cairo/cairo/src/cairo-mutex-private.h deleted file mode 100644 index e9359ef55..000000000 --- a/libs/cairo/cairo/src/cairo-mutex-private.h +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_MUTEX_PRIVATE_H -#define CAIRO_MUTEX_PRIVATE_H - -#include "cairo-mutex-type-private.h" - -CAIRO_BEGIN_DECLS - -#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER -cairo_private void _cairo_mutex_initialize (void); -#endif -#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER -cairo_private void _cairo_mutex_finalize (void); -#endif -/* only if using static initializer and/or finalizer define the boolean */ -#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER - cairo_private extern cairo_bool_t _cairo_mutex_initialized; -#endif - -/* Finally, extern the static mutexes and undef */ - -#define CAIRO_MUTEX_DECLARE(mutex) cairo_private extern cairo_mutex_t mutex; -#include "cairo-mutex-list-private.h" -#undef CAIRO_MUTEX_DECLARE - -CAIRO_END_DECLS - -#endif diff --git a/libs/cairo/cairo/src/cairo-mutex-type-private.h b/libs/cairo/cairo/src/cairo-mutex-type-private.h deleted file mode 100644 index eac1d48e6..000000000 --- a/libs/cairo/cairo/src/cairo-mutex-type-private.h +++ /dev/null @@ -1,158 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-mutex.c b/libs/cairo/cairo/src/cairo-mutex.c deleted file mode 100644 index d859e28d9..000000000 --- a/libs/cairo/cairo/src/cairo-mutex.c +++ /dev/null @@ -1,53 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-mutex-private.h" - -#define CAIRO_MUTEX_DECLARE(mutex) cairo_mutex_t mutex = CAIRO_MUTEX_NIL_INITIALIZER; -#include "cairo-mutex-list-private.h" -#undef CAIRO_MUTEX_DECLARE - -#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER - -# if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER -# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE FALSE -# else -# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE TRUE -# endif - -cairo_bool_t _cairo_mutex_initialized = _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE; - -# undef _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE - -#endif - -#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER -void _cairo_mutex_initialize (void) -{ - if (_cairo_mutex_initialized) - return; - - _cairo_mutex_initialized = TRUE; - -#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_INIT (mutex); -#include "cairo-mutex-list-private.h" -#undef CAIRO_MUTEX_DECLARE -} -#endif - -#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER -void _cairo_mutex_finalize (void) -{ - if (!_cairo_mutex_initialized) - return; - - _cairo_mutex_initialized = FALSE; - -#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex); -#include "cairo-mutex-list-private.h" -#undef CAIRO_MUTEX_DECLARE -} -#endif diff --git a/libs/cairo/cairo/src/cairo-no-features.h b/libs/cairo/cairo/src/cairo-no-features.h deleted file mode 100644 index 9b3d86be2..000000000 --- a/libs/cairo/cairo/src/cairo-no-features.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Generated by configure. Do not edit */ -#ifndef CAIRO_NO_FEATURES_H -#define CAIRO_NO_FEATURES_H - -#include - -/* This is a dummy header, to trick gtk-doc only */ - -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_WIN32_SURFACE 1 - -#endif diff --git a/libs/cairo/cairo/src/cairo-observer.c b/libs/cairo/cairo/src/cairo-observer.c deleted file mode 100644 index c8ce5eea2..000000000 --- a/libs/cairo/cairo/src/cairo-observer.c +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -void -_cairo_observers_notify (cairo_list_t *observers, void *arg) -{ - cairo_observer_t *obs, *next; - - cairo_list_foreach_entry_safe (obs, next, - cairo_observer_t, - observers, link) - { - obs->callback (obs, arg); - } -} diff --git a/libs/cairo/cairo/src/cairo-os2-private.h b/libs/cairo/cairo/src/cairo-os2-private.h deleted file mode 100644 index e47efd316..000000000 --- a/libs/cairo/cairo/src/cairo-os2-private.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-os2-surface.c b/libs/cairo/cairo/src/cairo-os2-surface.c deleted file mode 100644 index c0464f62b..000000000 --- a/libs/cairo/cairo/src/cairo-os2-surface.c +++ /dev/null @@ -1,1440 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-os2-private.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FC_FONT -#include -#endif - -#include -#ifdef BUILD_CAIRO_DLL -# include "cairo-os2.h" -# ifndef __WATCOMC__ -# include -# endif -#endif - -/* - * Here comes the extra API for the OS/2 platform. Currently it consists - * of two extra functions, the cairo_os2_init() and the - * cairo_os2_fini(). Both of them are called automatically if - * Cairo is compiled to be a DLL file, but you have to call them before - * using the Cairo API if you link to Cairo statically! - * - * You'll also find the code in here which deals with DLL initialization - * and termination, if the code is built to be a DLL. - * (if BUILD_CAIRO_DLL is defined) - */ - -/* Initialization counter: */ -static int cairo_os2_initialization_count = 0; - -static inline void -DisableFPUException (void) -{ - unsigned short usCW; - - /* Some OS/2 PM API calls modify the FPU Control Word, - * but forget to restore it. - * - * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions, - * so to be sure, we disable Invalid Opcode FPU exception - * before using FPU stuffs. - */ - usCW = _control87 (0, 0); - usCW = usCW | EM_INVALID | 0x80; - _control87 (usCW, MCW_EM | 0x80); -} - -/** - * cairo_os2_init: - * - * Initializes the Cairo library. This function is automatically called if - * Cairo was compiled to be a DLL (however it's not a problem if it's called - * multiple times). But if you link to Cairo statically, you have to call it - * once to set up Cairo's internal structures and mutexes. - * - * Since: 1.4 - **/ -cairo_public void -cairo_os2_init (void) -{ - /* This may initialize some stuffs, like create mutex semaphores etc.. */ - - cairo_os2_initialization_count++; - if (cairo_os2_initialization_count > 1) return; - - DisableFPUException (); - -#if CAIRO_HAS_FC_FONT - /* Initialize FontConfig */ - FcInit (); -#endif - - CAIRO_MUTEX_INITIALIZE (); -} - -/** - * cairo_os2_fini: - * - * Uninitializes the Cairo library. This function is automatically called if - * Cairo was compiled to be a DLL (however it's not a problem if it's called - * multiple times). But if you link to Cairo statically, you have to call it - * once to shut down Cairo, to let it free all the resources it has allocated. - * - * Since: 1.4 - **/ -cairo_public void -cairo_os2_fini (void) -{ - /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */ - - if (cairo_os2_initialization_count <= 0) return; - cairo_os2_initialization_count--; - if (cairo_os2_initialization_count > 0) return; - - DisableFPUException (); - - cairo_debug_reset_static_data (); - -#if CAIRO_HAS_FC_FONT -# if HAVE_FCFINI - /* Uninitialize FontConfig */ - FcFini (); -# endif -#endif - -#ifdef __WATCOMC__ - /* It can happen that the libraries we use have memory leaks, - * so there are still memory chunks allocated at this point. - * In these cases, Watcom might still have a bigger memory chunk, - * called "the heap" allocated from the OS. - * As we want to minimize the memory we lose from the point of - * view of the OS, we call this function to shrink that heap - * as much as possible. - */ - _heapshrink (); -#else - /* GCC has a heapmin function that approximately corresponds to - * what the Watcom function does - */ - _heapmin (); -#endif -} - -/* - * This function calls the allocation function depending on which - * method was compiled into the library: it can be native allocation - * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free). - * Actually, for pixel buffers that we use this function for, cairo - * uses _cairo_malloc_abc, so we use that here, too. And use the - * change to check the size argument - */ -void *_buffer_alloc (size_t a, size_t b, const unsigned int size) -{ - size_t nbytes; - void *buffer = NULL; - - if (!a || !b || !size || - a >= INT32_MAX / b || a*b >= INT32_MAX / size) { - return NULL; - } - nbytes = a * b * size; - -#ifdef OS2_USE_PLATFORM_ALLOC - /* Using OBJ_ANY on a machine that isn't configured for hi-mem - * will cause ERROR_INVALID_PARAMETER. If this occurs, or this - * build doesn't have hi-mem enabled, fall back to using lo-mem. - */ -#ifdef OS2_HIGH_MEMORY - if (!DosAllocMem (&buffer, nbytes, - OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) - return buffer; -#endif - if (DosAllocMem (&buffer, nbytes, - PAG_READ | PAG_WRITE | PAG_COMMIT)) - return NULL; -#else - /* Clear the malloc'd buffer the way DosAllocMem() does. */ - buffer = malloc (nbytes); - if (buffer) { - memset (buffer, 0, nbytes); - } -#endif - return buffer; -} - -/* - * This function selects the free function depending on which - * allocation method was compiled into the library - */ -void _buffer_free (void *buffer) -{ -#ifdef OS2_USE_PLATFORM_ALLOC - DosFreeMem (buffer); -#else - free (buffer); -#endif -} - -/* XXX - * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and - * the LibMain code moved to cairo-system.c. It should also call - * cairo_debug_reset_static_data() instead of duplicating its logic... - */ - -#ifdef BUILD_CAIRO_DLL -/* The main DLL entry for DLL initialization and uninitialization */ -/* Only include this code if we're about to build a DLL. */ - -#ifdef __WATCOMC__ -unsigned _System -LibMain (unsigned hmod, - unsigned termination) -#else -unsigned long _System -_DLL_InitTerm (unsigned long hModule, - unsigned long termination) -#endif -{ - if (termination) { - /* Unloading the DLL */ - cairo_os2_fini (); - -#ifndef __WATCOMC__ - /* Uninitialize RTL of GCC */ - __ctordtorTerm (); - _CRT_term (); -#endif - return 1; - } else { - /* Loading the DLL */ -#ifndef __WATCOMC__ - /* Initialize RTL of GCC */ - if (_CRT_init () != 0) - return 0; - __ctordtorInit (); -#endif - - cairo_os2_init (); - return 1; - } -} - -#endif /* BUILD_CAIRO_DLL */ - -/* - * The following part of the source file contains the code which might - * be called the "core" of the OS/2 backend support. This contains the - * OS/2 surface support functions and structures. - */ - -/* Forward declaration */ -static const cairo_surface_backend_t cairo_os2_surface_backend; - -/* Unpublished API: - * GpiEnableYInversion = PMGPI.723 - * GpiQueryYInversion = PMGPI.726 - * BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); - * LONG APIENTRY GpiQueryYInversion (HPS hps); - */ -BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); -LONG APIENTRY GpiQueryYInversion (HPS hps); - -#ifdef __WATCOMC__ -/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */ -LONG APIENTRY GpiDrawBits (HPS hps, - PVOID pBits, - PBITMAPINFO2 pbmiInfoTable, - LONG lCount, - PPOINTL aptlPoints, - LONG lRop, - ULONG flOptions); -#endif - -static void -_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, - HPS hps_begin_paint, - PRECTL prcl_begin_paint_rect) -{ - POINTL aptlPoints[4]; - LONG lOldYInversion; - LONG rc = GPI_OK; - - /* Check the limits (may not be necessary) */ - if (prcl_begin_paint_rect->xLeft < 0) - prcl_begin_paint_rect->xLeft = 0; - if (prcl_begin_paint_rect->yBottom < 0) - prcl_begin_paint_rect->yBottom = 0; - if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) - prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; - if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) - prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; - - /* Exit if the rectangle is empty */ - if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || - prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) - return; - - /* Set the Target & Source coordinates */ - *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; - *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; - - /* Make the Target coordinates non-inclusive */ - aptlPoints[1].x -= 1; - aptlPoints[1].y -= 1; - - /* Enable Y Inversion for the HPS, so GpiDrawBits will - * work with upside-top image, not with upside-down image! - */ - lOldYInversion = GpiQueryYInversion (hps_begin_paint); - GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); - - /* Debug code to draw rectangle limits */ -#if 0 - { - int x, y; - unsigned char *pixels; - - pixels = surface->pixels; - for (x = 0; x < surface->bitmap_info.cx; x++) { - for (y = 0; y < surface->bitmap_info.cy; y++) { - if ((x == 0) || - (y == 0) || - (x == y) || - (x >= surface->bitmap_info.cx-1) || - (y >= surface->bitmap_info.cy-1)) - { - pixels[y*surface->bitmap_info.cx*4+x*4] = 255; - } - } - } - } -#endif - if (!surface->use_24bpp) { - rc = GpiDrawBits (hps_begin_paint, - surface->pixels, - &(surface->bitmap_info), - 4, - aptlPoints, - ROP_SRCCOPY, - BBO_IGNORE); - if (rc != GPI_OK) - surface->use_24bpp = TRUE; - } - - if (surface->use_24bpp) { - /* If GpiDrawBits () failed then this is most likely because the - * display driver could not handle a 32bit bitmap. So we need to - * - create a buffer that only contains 3 bytes per pixel - * - change the bitmap info header to contain 24bit - * - pass the new buffer to GpiDrawBits () again - * - clean up the new buffer - */ - BITMAPINFO2 bmpinfo; - unsigned char *pchPixBuf; - unsigned char *pchTarget; - ULONG *pulSource; - ULONG ulX; - ULONG ulY; - ULONG ulPad; - - /* Set up the bitmap header, but this time for 24bit depth. */ - bmpinfo = surface->bitmap_info; - bmpinfo.cBitCount = 24; - - /* The start of each row has to be DWORD aligned. Calculate the - * of number aligned bytes per row, the total size of the bitmap, - * and the number of padding bytes at the end of each row. - */ - ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; - bmpinfo.cbImage = ulX * bmpinfo.cy; - ulPad = ulX - bmpinfo.cx * 3; - - /* Allocate temporary pixel buffer. If the rows don't need - * padding, it has to be 1 byte larger than the size of the - * bitmap or else the high-order byte from the last source - * row will end up in unallocated memory. - */ - pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, - bmpinfo.cbImage + (ulPad ? 0 : 1)); - - if (pchPixBuf) { - /* Copy 4 bytes from the source but advance the target ptr only - * 3 bytes, so the high-order alpha byte will be overwritten by - * the next copy. At the end of each row, skip over the padding. - */ - pchTarget = pchPixBuf; - pulSource = (ULONG*)surface->pixels; - for (ulY = bmpinfo.cy; ulY; ulY--) { - for (ulX = bmpinfo.cx; ulX; ulX--) { - *((ULONG*)pchTarget) = *pulSource++; - pchTarget += 3; - } - pchTarget += ulPad; - } - - rc = GpiDrawBits (hps_begin_paint, - pchPixBuf, - &bmpinfo, - 4, - aptlPoints, - ROP_SRCCOPY, - BBO_IGNORE); - if (rc != GPI_OK) - surface->use_24bpp = FALSE; - - _buffer_free (pchPixBuf); - } - } - - /* Restore Y inversion */ - GpiEnableYInversion (hps_begin_paint, lOldYInversion); -} - -static void -_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, - HPS hps_begin_paint, - PRECTL prcl_begin_paint_rect) -{ - HPS hps; - HDC hdc; - SIZEL sizlTemp; - HBITMAP hbmpTemp; - BITMAPINFO2 bmi2Temp; - POINTL aptlPoints[4]; - int y; - unsigned char *pchTemp; - - /* To copy pixels from screen to our buffer, we do the following steps: - * - * - Blit pixels from screen to a HBITMAP: - * -- Create Memory Device Context - * -- Create a PS into it - * -- Create a HBITMAP - * -- Select HBITMAP into memory PS - * -- Blit dirty pixels from screen to HBITMAP - * - Copy HBITMAP lines (pixels) into our buffer - * - Free resources - */ - - /* Create a memory device context */ - hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE); - if (!hdc) { - return; - } - - /* Create a memory PS */ - sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft; - sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom; - hps = GpiCreatePS (0, - hdc, - &sizlTemp, - PU_PELS | GPIT_NORMAL | GPIA_ASSOC); - if (!hps) { - DevCloseDC (hdc); - return; - } - - /* Create an uninitialized bitmap. */ - /* Prepare BITMAPINFO2 structure for our buffer */ - memset (&bmi2Temp, 0, sizeof (bmi2Temp)); - bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2); - bmi2Temp.cx = sizlTemp.cx; - bmi2Temp.cy = sizlTemp.cy; - bmi2Temp.cPlanes = 1; - bmi2Temp.cBitCount = 32; - - hbmpTemp = GpiCreateBitmap (hps, - (PBITMAPINFOHEADER2) &bmi2Temp, - 0, - NULL, - NULL); - - if (!hbmpTemp) { - GpiDestroyPS (hps); - DevCloseDC (hdc); - return; - } - - /* Select the bitmap into the memory device context. */ - GpiSetBitmap (hps, hbmpTemp); - - /* Target coordinates (Noninclusive) */ - aptlPoints[0].x = 0; - aptlPoints[0].y = 0; - - aptlPoints[1].x = sizlTemp.cx; - aptlPoints[1].y = sizlTemp.cy; - - /* Source coordinates (Inclusive) */ - aptlPoints[2].x = prcl_begin_paint_rect->xLeft; - aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; - - aptlPoints[3].x = prcl_begin_paint_rect->xRight; - aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop; - - /* Blit pixels from screen to bitmap */ - GpiBitBlt (hps, - hps_begin_paint, - 4, - aptlPoints, - ROP_SRCCOPY, - BBO_IGNORE); - - /* Now we have to extract the pixels from the bitmap. */ - pchTemp = - surface->pixels + - (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 + - prcl_begin_paint_rect->xLeft*4; - for (y = 0; y < sizlTemp.cy; y++) { - /* Get one line of pixels */ - GpiQueryBitmapBits (hps, - sizlTemp.cy - y - 1, /* lScanStart */ - 1, /* lScans */ - (PBYTE)pchTemp, - &bmi2Temp); - - /* Go for next line */ - pchTemp += surface->bitmap_info.cx*4; - } - - /* Clean up resources */ - GpiSetBitmap (hps, (HBITMAP) NULL); - GpiDeleteBitmap (hbmpTemp); - GpiDestroyPS (hps); - DevCloseDC (hdc); -} - -static cairo_status_t -_cairo_os2_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - - /* Increase lend counter */ - local_os2_surface->pixel_array_lend_count++; - - *image_out = local_os2_surface->image_surface; - *image_extra = NULL; - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_os2_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } - - /* Decrease Lend counter! */ - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - - if (local_os2_surface->pixel_array_lend_count > 0) - local_os2_surface->pixel_array_lend_count--; - DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - return; -} - -static cairo_status_t -_cairo_os2_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - - /* Increase lend counter */ - local_os2_surface->pixel_array_lend_count++; - - *image_out = local_os2_surface->image_surface; - *image_extra = NULL; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = local_os2_surface->bitmap_info.cx; - image_rect->height = local_os2_surface->bitmap_info.cy; - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_os2_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_os2_surface_t *local_os2_surface; - RECTL rclToBlit; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } - - /* So, we got back the image, and if all goes well, then - * something has been changed inside the interest_rect. - * So, we blit it to the screen! - */ - - if (local_os2_surface->blit_as_changes) { - /* Get mutex, we'll work with the pixel array! */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { - /* Could not get mutex! */ - return; - } - - if (local_os2_surface->hwnd_client_window) { - /* We know the HWND, so let's invalidate the window region, - * so the application will redraw itself, using the - * cairo_os2_surface_refresh_window () API from its own PM thread. - * - * This is the safe method, which should be preferred every time. - */ - rclToBlit.xLeft = interest_rect->x; - rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ - rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (interest_rect->y); - rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (interest_rect->y+interest_rect->height); /* Noninclusive */ - - WinInvalidateRect (local_os2_surface->hwnd_client_window, - &rclToBlit, - FALSE); - } else { - /* We don't know the HWND, so try to blit the pixels from here! - * Please note that it can be problematic if this is not the PM thread! - * - * It can cause internal PM stuffs to be scewed up, for some reason. - * Please always tell the HWND to the surface using the - * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () - * from your WM_PAINT, if it's possible! - */ - rclToBlit.xLeft = interest_rect->x; - rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ - rclToBlit.yBottom = interest_rect->y; - rclToBlit.yTop = interest_rect->y+interest_rect->height; /* Noninclusive */ - /* Now blit there the stuffs! */ - _cairo_os2_surface_blit_pixels (local_os2_surface, - local_os2_surface->hps_client_window, - &rclToBlit); - } - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - } - /* Also decrease Lend counter! */ - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - - if (local_os2_surface->pixel_array_lend_count > 0) - local_os2_surface->pixel_array_lend_count--; - DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); -} - -static cairo_bool_t -_cairo_os2_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = local_os2_surface->bitmap_info.cx; - rectangle->height = local_os2_surface->bitmap_info.cy; - - return TRUE; -} - -/** - * cairo_os2_surface_create: - * @hps_client_window: the presentation handle to bind the surface to - * @width: the width of the surface - * @height: the height of the surface - * - * Create a Cairo surface which is bound to a given presentation space (HPS). - * The caller retains ownership of the HPS and must dispose of it after the - * the surface has been destroyed. The surface will be created to have the - * given size. By default every change to the surface will be made visible - * immediately by blitting it into the window. This can be changed with - * cairo_os2_surface_set_manual_window_refresh(). - * Note that the surface will contain garbage when created, so the pixels - * have to be initialized by hand first. You can use the Cairo functions to - * fill it with black, or use cairo_surface_mark_dirty() to fill the surface - * with pixels from the window/HPS. - * - * Return value: the newly created surface - * - * Since: 1.4 - **/ -cairo_surface_t * -cairo_os2_surface_create (HPS hps_client_window, - int width, - int height) -{ - cairo_os2_surface_t *local_os2_surface = 0; - cairo_status_t status; - int rc; - - /* Check the size of the window */ - if ((width <= 0) || (height <= 0)) { - status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); - goto error_exit; - } - - /* Allocate an OS/2 surface structure. */ - local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); - if (!local_os2_surface) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto error_exit; - } - - memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); - - /* Allocate resources: mutex & event semaphores and the pixel buffer */ - if (DosCreateMutexSem (NULL, - &(local_os2_surface->hmtx_use_private_fields), - 0, - FALSE)) - { - status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - goto error_exit; - } - - if (DosCreateEventSem (NULL, - &(local_os2_surface->hev_pixel_array_came_back), - 0, - FALSE)) - { - status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - goto error_exit; - } - - local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); - if (!local_os2_surface->pixels) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto error_exit; - } - - /* Create image surface from pixel array */ - local_os2_surface->image_surface = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (local_os2_surface->pixels, - CAIRO_FORMAT_ARGB32, - width, /* Width */ - height, /* Height */ - width * 4); /* Rowstride */ - status = local_os2_surface->image_surface->base.status; - if (status) - goto error_exit; - - /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. - * Note: hps_client_window may be null if this was called by - * cairo_os2_surface_create_for_window(). - */ - local_os2_surface->hps_client_window = hps_client_window; - local_os2_surface->blit_as_changes = TRUE; - - /* Prepare BITMAPINFO2 structure for our buffer */ - local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); - local_os2_surface->bitmap_info.cx = width; - local_os2_surface->bitmap_info.cy = height; - local_os2_surface->bitmap_info.cPlanes = 1; - local_os2_surface->bitmap_info.cBitCount = 32; - - /* Initialize base surface */ - _cairo_surface_init (&local_os2_surface->base, - &cairo_os2_surface_backend, - NULL, /* device */ - _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); - - /* Successful exit */ - return (cairo_surface_t *)local_os2_surface; - - error_exit: - - /* This point will only be reached if an error occured */ - - if (local_os2_surface) { - if (local_os2_surface->pixels) - _buffer_free (local_os2_surface->pixels); - if (local_os2_surface->hev_pixel_array_came_back) - DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); - if (local_os2_surface->hmtx_use_private_fields) - DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); - free (local_os2_surface); - } - - return _cairo_surface_create_in_error (status); -} - -/** - * cairo_os2_surface_create_for_window: - * @hwnd_client_window: the window handle to bind the surface to - * @width: the width of the surface - * @height: the height of the surface - * - * Create a Cairo surface which is bound to a given window; the caller retains - * ownership of the window. This is a convenience function for use with - * windows that will only be updated when cairo_os2_surface_refresh_window() - * is called (usually in response to a WM_PAINT message). It avoids the need - * to create a persistent HPS for every window and assumes that one will be - * supplied by the caller when a cairo function needs one. If it isn't, an - * HPS will be created on-the-fly and released before the function which needs - * it returns. - * - * Return value: the newly created surface - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_os2_surface_create_for_window (HWND hwnd_client_window, - int width, - int height) -{ - cairo_os2_surface_t *local_os2_surface; - - /* A window handle must be provided. */ - if (!hwnd_client_window) { - return _cairo_surface_create_in_error ( - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* Create the surface. */ - local_os2_surface = (cairo_os2_surface_t *) - cairo_os2_surface_create (0, width, height); - - /* If successful, save the hwnd & turn off automatic repainting. */ - if (!local_os2_surface->image_surface->base.status) { - local_os2_surface->hwnd_client_window = hwnd_client_window; - local_os2_surface->blit_as_changes = FALSE; - } - - return (cairo_surface_t *)local_os2_surface; -} - -/** - * cairo_os2_surface_set_size: - * @surface: the cairo surface to resize - * @new_width: the new width of the surface - * @new_height: the new height of the surface - * @timeout: timeout value in milliseconds - * - * When the client window is resized, call this API to set the new size in the - * underlying surface accordingly. This function will reallocate everything, - * so you'll have to redraw everything in the surface after this call. - * The surface will contain garbage after the resizing. So the notes of - * cairo_os2_surface_create() apply here, too. - * - * The timeout value specifies how long the function should wait on other parts - * of the program to release the buffers. It is necessary, because it can happen - * that Cairo is just drawing something into the surface while we want to - * destroy and recreate it. - * - * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, - * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, - * %CAIRO_STATUS_INVALID_SIZE for invalid sizes - * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the - * timeout happened before all the buffers were released - * - * Since: 1.4 - **/ -int -cairo_os2_surface_set_size (cairo_surface_t *surface, - int new_width, - int new_height, - int timeout) -{ - cairo_os2_surface_t *local_os2_surface; - unsigned char *pchNewPixels; - int rc; - cairo_image_surface_t *pNewImageSurface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - if ((new_width <= 0) || - (new_height <= 0)) - { - /* Invalid size! */ - return _cairo_error (CAIRO_STATUS_INVALID_SIZE); - } - - /* Allocate memory for new stuffs */ - pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4); - if (!pchNewPixels) { - /* Not enough memory for the pixels! - * Everything remains the same! - */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* Create image surface from new pixel array */ - pNewImageSurface = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (pchNewPixels, - CAIRO_FORMAT_ARGB32, - new_width, /* Width */ - new_height, /* Height */ - new_width * 4); /* Rowstride */ - - if (pNewImageSurface->base.status) { - /* Could not create image surface! - * Everything remains the same! - */ - _buffer_free (pchNewPixels); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* Okay, new memory allocated, so it's time to swap old buffers - * to new ones! - */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { - /* Could not get mutex! - * Everything remains the same! - */ - cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); - _buffer_free (pchNewPixels); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* We have to make sure that we won't destroy a surface which - * is lent to some other code (Cairo is drawing into it)! - */ - while (local_os2_surface->pixel_array_lend_count > 0) { - ULONG ulPostCount; - DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount); - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - /* Wait for somebody to return the pixels! */ - rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout); - if (rc != NO_ERROR) { - /* Either timeout or something wrong... Exit. */ - cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); - _buffer_free (pchNewPixels); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - /* Okay, grab mutex and check counter again! */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) - != NO_ERROR) - { - /* Could not get mutex! - * Everything remains the same! - */ - cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); - _buffer_free (pchNewPixels); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - /* Destroy old image surface */ - cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); - /* Destroy old pixel buffer */ - _buffer_free (local_os2_surface->pixels); - /* Set new image surface */ - local_os2_surface->image_surface = pNewImageSurface; - /* Set new pixel buffer */ - local_os2_surface->pixels = pchNewPixels; - /* Change bitmap2 structure */ - local_os2_surface->bitmap_info.cx = new_width; - local_os2_surface->bitmap_info.cy = new_height; - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_os2_surface_refresh_window: - * @surface: the cairo surface to refresh - * @hps_begin_paint: the presentation handle of the window to refresh - * @prcl_begin_paint_rect: the rectangle to redraw - * - * This function can be used to force a repaint of a given area of the client - * window. It should usually be called from the WM_PAINT processing of the - * window procedure. However, it can be called any time a given part of the - * window has to be updated. - * - * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call - * of the window procedure, but you can also get the HPS using WinGetPS, and you - * can assemble your own update rectangle by hand. - * If hps_begin_paint is %NULL, the function will use the HPS passed into - * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function - * will query the current window size and repaint the whole window. - * - * Cairo assumes that if you set the HWND to the surface using - * cairo_os2_surface_set_hwnd(), this function will be called by the application - * every time it gets a WM_PAINT for that HWND. If the HWND is set in the - * surface, Cairo uses this function to handle dirty areas too. - * - * Since: 1.4 - **/ -void -cairo_os2_surface_refresh_window (cairo_surface_t *surface, - HPS hps_begin_paint, - PRECTL prcl_begin_paint_rect) -{ - cairo_os2_surface_t *local_os2_surface; - RECTL rclTemp; - HPS hpsTemp = 0; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } - - /* If an HPS wasn't provided, see if we can get one. */ - if (!hps_begin_paint) { - hps_begin_paint = local_os2_surface->hps_client_window; - if (!hps_begin_paint) { - if (local_os2_surface->hwnd_client_window) { - hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window); - hps_begin_paint = hpsTemp; - } - /* No HPS & no way to get one, so exit */ - if (!hps_begin_paint) - return; - } - } - - if (prcl_begin_paint_rect == NULL) { - /* Update the whole window! */ - rclTemp.xLeft = 0; - rclTemp.xRight = local_os2_surface->bitmap_info.cx; - rclTemp.yTop = local_os2_surface->bitmap_info.cy; - rclTemp.yBottom = 0; - } else { - /* Use the rectangle we got passed as parameter! */ - rclTemp.xLeft = prcl_begin_paint_rect->xLeft; - rclTemp.xRight = prcl_begin_paint_rect->xRight; - rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; - rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ; - } - - /* Get mutex, we'll work with the pixel array! */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) - != NO_ERROR) - { - /* Could not get mutex! */ - if (hpsTemp) - WinReleasePS(hpsTemp); - return; - } - - if ((local_os2_surface->dirty_area_present) && - (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) && - (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) && - (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) && - (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom)) - { - /* Aha, this call was because of a dirty area, so in this case we - * have to blit the pixels from the screen to the surface! - */ - local_os2_surface->dirty_area_present = FALSE; - _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, - hps_begin_paint, - &rclTemp); - } else { - /* Okay, we have the surface, have the HPS - * (might be from WinBeginPaint () or from WinGetPS () ) - * Now blit there the stuffs! - */ - _cairo_os2_surface_blit_pixels (local_os2_surface, - hps_begin_paint, - &rclTemp); - } - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - - if (hpsTemp) - WinReleasePS(hpsTemp); -} - -static cairo_status_t -_cairo_os2_surface_finish (void *abstract_surface) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - - /* Destroy old image surface */ - cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); - /* Destroy old pixel buffer */ - _buffer_free (local_os2_surface->pixels); - DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); - DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); - - /* The memory itself will be free'd by the cairo_surface_destroy () - * who called us. - */ - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_os2_surface_set_hwnd: - * @surface: the cairo surface to associate with the window handle - * @hwnd_client_window: the window handle of the client window - * - * Sets window handle for surface; the caller retains ownership of the window. - * If Cairo wants to blit into the window because it is set to blit as the - * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then - * there are two ways it can choose: - * If it knows the HWND of the surface, then it invalidates that area, so the - * application will get a WM_PAINT message and it can call - * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself - * will use the HPS it got at surface creation time, and blit the pixels itself. - * It's also a solution, but experience shows that if this happens from a non-PM - * thread, then it can screw up PM internals. - * - * So, best solution is to set the HWND for the surface after the surface - * creation, so every blit will be done from application's message processing - * loop, which is the safest way to do. - * - * Since: 1.4 - **/ -void -cairo_os2_surface_set_hwnd (cairo_surface_t *surface, - HWND hwnd_client_window) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } - - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) - != NO_ERROR) - { - /* Could not get mutex! */ - return; - } - - local_os2_surface->hwnd_client_window = hwnd_client_window; - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); -} - -/** - * cairo_os2_surface_set_manual_window_refresh: - * @surface: the cairo surface to set the refresh mode for - * @manual_refresh: the switch for manual surface refresh - * - * This API can tell Cairo if it should show every change to this surface - * immediately in the window or if it should be cached and will only be visible - * once the user calls cairo_os2_surface_refresh_window() explicitly. If the - * HWND was not set in the cairo surface, then the HPS will be used to blit the - * graphics. Otherwise it will invalidate the given window region so the user - * will get the WM_PAINT message to redraw that area of the window. - * - * So, if you're only interested in displaying the final result after several - * drawing operations, you might get better performance if you put the surface - * into manual refresh mode by passing a true value to this function. Then call - * cairo_os2_surface_refresh() whenever desired. - * - * Since: 1.4 - **/ -void -cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, - cairo_bool_t manual_refresh) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } - - local_os2_surface->blit_as_changes = !manual_refresh; -} - -/** - * cairo_os2_surface_get_manual_window_refresh: - * @surface: the cairo surface to query the refresh mode from - * - * This space left intentionally blank. - * - * Return value: current refresh mode of the surface (true by default) - * - * Since: 1.4 - **/ -cairo_bool_t -cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return FALSE; - } - - return !(local_os2_surface->blit_as_changes); -} - -/** - * cairo_os2_surface_get_hps: - * @surface: the cairo surface to be querued - * @hps: HPS currently associated with the surface (if any) - * - * This API retrieves the HPS associated with the surface. - * - * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved, - * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, - * %CAIRO_STATUS_NULL_POINTER if the hps argument is null - * - * Since: 1.10 - **/ -cairo_status_t -cairo_os2_surface_get_hps (cairo_surface_t *surface, - HPS *hps) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - if (!hps) - { - return _cairo_error (CAIRO_STATUS_NULL_POINTER); - } - *hps = local_os2_surface->hps_client_window; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_os2_surface_set_hps: - * @surface: the cairo surface to associate with the HPS - * @hps: new HPS to be associated with the surface (the HPS may be null) - * - * This API replaces the HPS associated with the surface with a new one. - * The caller retains ownership of the HPS and must dispose of it after - * the surface has been destroyed or it has been replaced by another - * call to this function. - * - * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced, - * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, - * - * Since: 1.10 - **/ -cairo_status_t -cairo_os2_surface_set_hps (cairo_surface_t *surface, - HPS hps) -{ - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - local_os2_surface->hps_client_window = hps; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_os2_surface_mark_dirty_rectangle (void *surface, - int x, - int y, - int width, - int height) -{ - cairo_os2_surface_t *local_os2_surface; - RECTL rclToBlit; - - local_os2_surface = (cairo_os2_surface_t *) surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - /* Get mutex, we'll work with the pixel array! */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) - != NO_ERROR) - { - /* Could not get mutex! */ - return CAIRO_STATUS_NO_MEMORY; - } - - /* Check for defaults */ - if (width < 0) - width = local_os2_surface->bitmap_info.cx; - if (height < 0) - height = local_os2_surface->bitmap_info.cy; - - if (local_os2_surface->hwnd_client_window) { - /* We know the HWND, so let's invalidate the window region, - * so the application will redraw itself, using the - * cairo_os2_surface_refresh_window () API from its own PM thread. - * From that function we'll note that it's not a redraw but a - * dirty-rectangle deal stuff, so we'll handle the things from - * there. - * - * This is the safe method, which should be preferred every time. - */ - rclToBlit.xLeft = x; - rclToBlit.xRight = x + width; /* Noninclusive */ - rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y); - rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */ - -#if 0 - if (local_os2_surface->dirty_area_present) { - /* Yikes, there is already a dirty area which should be - * cleaned up, but we'll overwrite it. Sorry. - * TODO: Something clever should be done here. - */ - } -#endif - - /* Set up dirty area reminder stuff */ - memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL)); - local_os2_surface->dirty_area_present = TRUE; - - /* Invalidate window area */ - WinInvalidateRect (local_os2_surface->hwnd_client_window, - &rclToBlit, - FALSE); - } else { - /* We don't know the HWND, so try to blit the pixels from here! - * Please note that it can be problematic if this is not the PM thread! - * - * It can cause internal PM stuffs to be scewed up, for some reason. - * Please always tell the HWND to the surface using the - * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () - * from your WM_PAINT, if it's possible! - */ - - rclToBlit.xLeft = x; - rclToBlit.xRight = x + width; /* Noninclusive */ - rclToBlit.yBottom = y; - rclToBlit.yTop = y + height; /* Noninclusive */ - /* Now get the pixels from the screen! */ - _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, - local_os2_surface->hps_client_window, - &rclToBlit); - } - - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_os2_surface_backend = { - CAIRO_SURFACE_TYPE_OS2, - NULL, /* create_similar */ - _cairo_os2_surface_finish, - _cairo_os2_surface_acquire_source_image, - _cairo_os2_surface_release_source_image, - _cairo_os2_surface_acquire_dest_image, - _cairo_os2_surface_release_dest_image, - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_os2_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - _cairo_os2_surface_mark_dirty_rectangle, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - NULL, /* has_show_text_glyphs */ - NULL /* show_text_glyphs */ -}; diff --git a/libs/cairo/cairo/src/cairo-os2.h b/libs/cairo/cairo/src/cairo-os2.h deleted file mode 100644 index 16a4fc564..000000000 --- a/libs/cairo/cairo/src/cairo-os2.h +++ /dev/null @@ -1,76 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _CAIRO_OS2_H_ -#define _CAIRO_OS2_H_ - -#define INCL_DOS -#define INCL_DOSSEMAPHORES -#define INCL_DOSERRORS -#define INCL_WIN -#define INCL_GPI - -#include "cairo.h" - -#include - -CAIRO_BEGIN_DECLS - -/* The OS/2 Specific Cairo API */ - -cairo_public void -cairo_os2_init (void); - -cairo_public void -cairo_os2_fini (void); - -#if CAIRO_HAS_OS2_SURFACE - -cairo_public cairo_surface_t * -cairo_os2_surface_create (HPS hps_client_window, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_os2_surface_create_for_window (HWND hwnd_client_window, - int width, - int height); - -cairo_public void -cairo_os2_surface_set_hwnd (cairo_surface_t *surface, - HWND hwnd_client_window); - -cairo_public int -cairo_os2_surface_set_size (cairo_surface_t *surface, - int new_width, - int new_height, - int timeout); - -cairo_public void -cairo_os2_surface_refresh_window (cairo_surface_t *surface, - HPS hps_begin_paint, - PRECTL prcl_begin_paint_rect); - -cairo_public void -cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, - cairo_bool_t manual_refresh); - -cairo_public cairo_bool_t -cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface); - -cairo_public cairo_status_t -cairo_os2_surface_get_hps (cairo_surface_t *surface, - HPS *hps); - -cairo_public cairo_status_t -cairo_os2_surface_set_hps (cairo_surface_t *surface, - HPS hps); - -#else /* CAIRO_HAS_OS2_SURFACE */ -# error Cairo was not compiled with support for the OS/2 backend -#endif /* CAIRO_HAS_OS2_SURFACE */ - -CAIRO_END_DECLS - -#endif /* _CAIRO_OS2_H_ */ diff --git a/libs/cairo/cairo/src/cairo-output-stream-private.h b/libs/cairo/cairo/src/cairo-output-stream-private.h deleted file mode 100644 index 8f3cb3c6b..000000000 --- a/libs/cairo/cairo/src/cairo-output-stream-private.h +++ /dev/null @@ -1,165 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H -#define CAIRO_OUTPUT_STREAM_PRIVATE_H - -#include "cairo-compiler-private.h" -#include "cairo-types-private.h" - -#include -#include -#include - -typedef cairo_status_t -(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, - const unsigned char *data, - unsigned int length); - -typedef cairo_status_t -(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); - -typedef cairo_status_t -(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); - -struct _cairo_output_stream { - cairo_output_stream_write_func_t write_func; - cairo_output_stream_flush_func_t flush_func; - cairo_output_stream_close_func_t close_func; - unsigned long position; - cairo_status_t status; - cairo_bool_t closed; -}; - -extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; - -cairo_private void -_cairo_output_stream_init (cairo_output_stream_t *stream, - cairo_output_stream_write_func_t write_func, - cairo_output_stream_flush_func_t flush_func, - cairo_output_stream_close_func_t close_func); - -cairo_private cairo_status_t -_cairo_output_stream_fini (cairo_output_stream_t *stream); - - -/* We already have the following declared in cairo.h: - -typedef cairo_status_t (*cairo_write_func_t) (void *closure, - const unsigned char *data, - unsigned int length); -*/ -typedef cairo_status_t (*cairo_close_func_t) (void *closure); - - -/* This function never returns %NULL. If an error occurs (NO_MEMORY) - * while trying to create the output stream this function returns a - * valid pointer to a nil output stream. - * - * Note that even with a nil surface, the close_func callback will be - * called by a call to _cairo_output_stream_close or - * _cairo_output_stream_destroy. - */ -cairo_private cairo_output_stream_t * -_cairo_output_stream_create (cairo_write_func_t write_func, - cairo_close_func_t close_func, - void *closure); - -cairo_private cairo_output_stream_t * -_cairo_output_stream_create_in_error (cairo_status_t status); - -/* Tries to flush any buffer maintained by the stream or its delegates. */ -cairo_private cairo_status_t -_cairo_output_stream_flush (cairo_output_stream_t *stream); - -/* Returns the final status value associated with this object, just - * before its last gasp. This final status value will capture any - * status failure returned by the stream's close_func as well. */ -cairo_private cairo_status_t -_cairo_output_stream_close (cairo_output_stream_t *stream); - -/* Returns the final status value associated with this object, just - * before its last gasp. This final status value will capture any - * status failure returned by the stream's close_func as well. */ -cairo_private cairo_status_t -_cairo_output_stream_destroy (cairo_output_stream_t *stream); - -cairo_private void -_cairo_output_stream_write (cairo_output_stream_t *stream, - const void *data, size_t length); - -cairo_private void -_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, - const unsigned char *data, - size_t length); - -cairo_private void -_cairo_output_stream_vprintf (cairo_output_stream_t *stream, - const char *fmt, - va_list ap) CAIRO_PRINTF_FORMAT ( 2, 0); - -cairo_private void -_cairo_output_stream_printf (cairo_output_stream_t *stream, - const char *fmt, - ...) CAIRO_PRINTF_FORMAT (2, 3); - -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/libs/cairo/cairo/src/cairo-output-stream.c b/libs/cairo/cairo/src/cairo-output-stream.c deleted file mode 100644 index 5f8d774cd..000000000 --- a/libs/cairo/cairo/src/cairo-output-stream.c +++ /dev/null @@ -1,738 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for snprintf() */ -#include "cairoint.h" - -#include "cairo-output-stream-private.h" -#include "cairo-error-private.h" -#include "cairo-compiler-private.h" - -#include -#include -#include - -/* Numbers printed with %f are printed with this number of significant - * digits after the decimal. - */ -#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6 - -/* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS - * bits of precision available after the decimal point. - * - * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal - * digits after the decimal point required to preserve the available - * precision. - * - * The conversion is: - * - * - * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) ) - * - * - * We can replace ceil(x) with (int)(x+1) since x will never be an - * integer for any likely value of %CAIRO_FIXED_FRAC_BITS. - */ -#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1)) - -void -_cairo_output_stream_init (cairo_output_stream_t *stream, - cairo_output_stream_write_func_t write_func, - cairo_output_stream_flush_func_t flush_func, - cairo_output_stream_close_func_t close_func) -{ - stream->write_func = write_func; - stream->flush_func = flush_func; - stream->close_func = close_func; - stream->position = 0; - stream->status = CAIRO_STATUS_SUCCESS; - stream->closed = FALSE; -} - -cairo_status_t -_cairo_output_stream_fini (cairo_output_stream_t *stream) -{ - return _cairo_output_stream_close (stream); -} - -const cairo_output_stream_t _cairo_output_stream_nil = { - NULL, /* write_func */ - NULL, /* flush_func */ - NULL, /* close_func */ - 0, /* position */ - CAIRO_STATUS_NO_MEMORY, - FALSE /* closed */ -}; - -static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { - NULL, /* write_func */ - NULL, /* flush_func */ - NULL, /* close_func */ - 0, /* position */ - CAIRO_STATUS_WRITE_ERROR, - FALSE /* closed */ -}; - -typedef struct _cairo_output_stream_with_closure { - cairo_output_stream_t base; - cairo_write_func_t write_func; - cairo_close_func_t close_func; - void *closure; -} cairo_output_stream_with_closure_t; - - -static cairo_status_t -closure_write (cairo_output_stream_t *stream, - const unsigned char *data, unsigned int length) -{ - cairo_output_stream_with_closure_t *stream_with_closure = - (cairo_output_stream_with_closure_t *) stream; - - if (stream_with_closure->write_func == NULL) - return CAIRO_STATUS_SUCCESS; - - return stream_with_closure->write_func (stream_with_closure->closure, - data, length); -} - -static cairo_status_t -closure_close (cairo_output_stream_t *stream) -{ - cairo_output_stream_with_closure_t *stream_with_closure = - (cairo_output_stream_with_closure_t *) stream; - - if (stream_with_closure->close_func != NULL) - return stream_with_closure->close_func (stream_with_closure->closure); - else - return CAIRO_STATUS_SUCCESS; -} - -cairo_output_stream_t * -_cairo_output_stream_create (cairo_write_func_t write_func, - cairo_close_func_t close_func, - void *closure) -{ - cairo_output_stream_with_closure_t *stream; - - stream = 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 . - * 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 - */ -static void -_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) -{ - struct lconv *locale_data; - const char *decimal_point; - int decimal_point_len; - char *p; - int decimal_len; - int num_zeros, decimal_digits; - - /* Omit the minus sign from negative zero. */ - if (d == 0.0) - d = 0.0; - -#ifdef HAVE_LOCALECONV - locale_data = localeconv (); - decimal_point = locale_data->decimal_point; - decimal_point_len = strlen (decimal_point); -#else - decimal_point = "."; - decimal_point_len = 1; -#endif - - assert (decimal_point_len != 0); - - if (limited_precision) { - snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d); - } else { - /* Using "%f" to print numbers less than 0.1 will result in - * reduced precision due to the default 6 digits after the - * decimal point. - * - * For numbers is < 0.1, we print with maximum precision and count - * the number of zeros between the decimal point and the first - * significant digit. We then print the number again with the - * number of decimal places that gives us the required number of - * significant digits. This ensures the number is correctly - * rounded. - */ - if (fabs (d) >= 0.1) { - snprintf (buffer, size, "%f", d); - } else { - snprintf (buffer, size, "%.18f", d); - p = buffer; - - if (*p == '+' || *p == '-') - p++; - - while (_cairo_isdigit (*p)) - p++; - - if (strncmp (p, decimal_point, decimal_point_len) == 0) - p += decimal_point_len; - - num_zeros = 0; - while (*p++ == '0') - num_zeros++; - - decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL; - - if (decimal_digits < 18) - snprintf (buffer, size, "%.*f", decimal_digits, d); - } - } - p = buffer; - - if (*p == '+' || *p == '-') - p++; - - while (_cairo_isdigit (*p)) - p++; - - if (strncmp (p, decimal_point, decimal_point_len) == 0) { - *p = '.'; - decimal_len = strlen (p + decimal_point_len); - memmove (p + 1, p + decimal_point_len, decimal_len); - p[1 + decimal_len] = 0; - - /* Remove trailing zeros and decimal point if possible. */ - for (p = p + decimal_len; *p == '0'; p--) - *p = 0; - - if (*p == '.') { - *p = 0; - p--; - } - } -} - -enum { - LENGTH_MODIFIER_LONG = 0x100 -}; - -/* Here's a limited reimplementation of printf. The reason for doing - * this is primarily to special case handling of doubles. We want - * locale independent formatting of doubles and we want to trim - * trailing zeros. This is handled by dtostr() above, and the code - * below handles everything else by calling snprintf() to do the - * formatting. This functionality is only for internal use and we - * only implement the formats we actually use. - */ -void -_cairo_output_stream_vprintf (cairo_output_stream_t *stream, - const char *fmt, va_list ap) -{ -#define SINGLE_FMT_BUFFER_SIZE 32 - char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE]; - int single_fmt_length; - char *p; - const char *f, *start; - int length_modifier, width; - cairo_bool_t var_width; - - if (stream->status) - return; - - f = fmt; - p = buffer; - while (*f != '\0') { - if (p == buffer + sizeof (buffer)) { - _cairo_output_stream_write (stream, buffer, sizeof (buffer)); - p = buffer; - } - - if (*f != '%') { - *p++ = *f++; - continue; - } - - start = f; - f++; - - if (*f == '0') - f++; - - var_width = FALSE; - if (*f == '*') { - var_width = TRUE; - f++; - } - - while (_cairo_isdigit (*f)) - f++; - - length_modifier = 0; - if (*f == 'l') { - length_modifier = LENGTH_MODIFIER_LONG; - f++; - } - - /* The only format strings exist in the cairo implementation - * itself. So there's an internal consistency problem if any - * of them is larger than our format buffer size. */ - single_fmt_length = f - start + 1; - assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE); - - /* Reuse the format string for this conversion. */ - memcpy (single_fmt, start, single_fmt_length); - single_fmt[single_fmt_length] = '\0'; - - /* Flush contents of buffer before snprintf()'ing into it. */ - _cairo_output_stream_write (stream, buffer, p - buffer); - - /* We group signed and unsigned together in this switch, the - * only thing that matters here is the size of the arguments, - * since we're just passing the data through to sprintf(). */ - switch (*f | length_modifier) { - case '%': - buffer[0] = *f; - buffer[1] = 0; - break; - case 'd': - case 'u': - case 'o': - case 'x': - case 'X': - if (var_width) { - width = va_arg (ap, int); - snprintf (buffer, sizeof buffer, - single_fmt, width, va_arg (ap, int)); - } else { - snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int)); - } - break; - case 'd' | LENGTH_MODIFIER_LONG: - case 'u' | LENGTH_MODIFIER_LONG: - case 'o' | LENGTH_MODIFIER_LONG: - case 'x' | LENGTH_MODIFIER_LONG: - case 'X' | LENGTH_MODIFIER_LONG: - if (var_width) { - width = va_arg (ap, int); - snprintf (buffer, sizeof buffer, - single_fmt, width, va_arg (ap, long int)); - } else { - snprintf (buffer, sizeof buffer, - single_fmt, va_arg (ap, long int)); - } - break; - case 's': - snprintf (buffer, sizeof buffer, - single_fmt, va_arg (ap, const char *)); - break; - case 'f': - _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE); - break; - case 'g': - _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE); - break; - case 'c': - buffer[0] = va_arg (ap, int); - buffer[1] = 0; - break; - default: - ASSERT_NOT_REACHED; - } - p = buffer + strlen (buffer); - f++; - } - - _cairo_output_stream_write (stream, buffer, p - buffer); -} - -void -_cairo_output_stream_printf (cairo_output_stream_t *stream, - const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - - _cairo_output_stream_vprintf (stream, fmt, ap); - - va_end (ap); -} - -long -_cairo_output_stream_get_position (cairo_output_stream_t *stream) -{ - return stream->position; -} - -cairo_status_t -_cairo_output_stream_get_status (cairo_output_stream_t *stream) -{ - return stream->status; -} - -/* Maybe this should be a configure time option, so embedded targets - * don't have to pull in stdio. */ - - -typedef struct _stdio_stream { - cairo_output_stream_t base; - FILE *file; -} stdio_stream_t; - -static cairo_status_t -stdio_write (cairo_output_stream_t *base, - const unsigned char *data, unsigned int length) -{ - stdio_stream_t *stream = (stdio_stream_t *) base; - - if (fwrite (data, 1, length, stream->file) != length) - return _cairo_error (CAIRO_STATUS_WRITE_ERROR); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -stdio_flush (cairo_output_stream_t *base) -{ - stdio_stream_t *stream = (stdio_stream_t *) base; - - fflush (stream->file); - - if (ferror (stream->file)) - return _cairo_error (CAIRO_STATUS_WRITE_ERROR); - else - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -stdio_close (cairo_output_stream_t *base) -{ - cairo_status_t status; - stdio_stream_t *stream = (stdio_stream_t *) base; - - status = stdio_flush (base); - - fclose (stream->file); - - return status; -} - -cairo_output_stream_t * -_cairo_output_stream_create_for_file (FILE *file) -{ - stdio_stream_t *stream; - - if (file == NULL) { - _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); - return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; - } - - stream = malloc (sizeof *stream); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - stdio_write, stdio_flush, stdio_flush); - stream->file = file; - - return &stream->base; -} - -cairo_output_stream_t * -_cairo_output_stream_create_for_filename (const char *filename) -{ - stdio_stream_t *stream; - FILE *file; - - if (filename == NULL) - return _cairo_null_stream_create (); - - file = fopen (filename, "wb"); - if (file == NULL) { - switch (errno) { - case ENOMEM: - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - default: - _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); - return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; - } - } - - stream = malloc (sizeof *stream); - if (unlikely (stream == NULL)) { - fclose (file); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - stdio_write, stdio_flush, stdio_close); - stream->file = file; - - return &stream->base; -} - - -typedef struct _memory_stream { - cairo_output_stream_t base; - cairo_array_t array; -} memory_stream_t; - -static cairo_status_t -memory_write (cairo_output_stream_t *base, - const unsigned char *data, unsigned int length) -{ - memory_stream_t *stream = (memory_stream_t *) base; - - return _cairo_array_append_multiple (&stream->array, data, length); -} - -static cairo_status_t -memory_close (cairo_output_stream_t *base) -{ - memory_stream_t *stream = (memory_stream_t *) base; - - _cairo_array_fini (&stream->array); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_output_stream_t * -_cairo_memory_stream_create (void) -{ - memory_stream_t *stream; - - stream = malloc (sizeof *stream); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); - _cairo_array_init (&stream->array, 1); - - return &stream->base; -} - -cairo_status_t -_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, - unsigned char **data_out, - unsigned long *length_out) -{ - memory_stream_t *stream; - cairo_status_t status; - - status = abstract_stream->status; - if (unlikely (status)) - return _cairo_output_stream_destroy (abstract_stream); - - stream = (memory_stream_t *) abstract_stream; - - *length_out = _cairo_array_num_elements (&stream->array); - *data_out = malloc (*length_out); - if (unlikely (*data_out == NULL)) { - status = _cairo_output_stream_destroy (abstract_stream); - assert (status == CAIRO_STATUS_SUCCESS); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out); - - return _cairo_output_stream_destroy (abstract_stream); -} - -void -_cairo_memory_stream_copy (cairo_output_stream_t *base, - cairo_output_stream_t *dest) -{ - memory_stream_t *stream = (memory_stream_t *) base; - - if (dest->status) - return; - - if (base->status) { - dest->status = base->status; - return; - } - - _cairo_output_stream_write (dest, - _cairo_array_index (&stream->array, 0), - _cairo_array_num_elements (&stream->array)); -} - -int -_cairo_memory_stream_length (cairo_output_stream_t *base) -{ - memory_stream_t *stream = (memory_stream_t *) base; - - return _cairo_array_num_elements (&stream->array); -} - -static cairo_status_t -null_write (cairo_output_stream_t *base, - const unsigned char *data, unsigned int length) -{ - return CAIRO_STATUS_SUCCESS; -} - -cairo_output_stream_t * -_cairo_null_stream_create (void) -{ - cairo_output_stream_t *stream; - - stream = malloc (sizeof *stream); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (stream, null_write, NULL, NULL); - - return stream; -} diff --git a/libs/cairo/cairo/src/cairo-paginated-private.h b/libs/cairo/cairo/src/cairo-paginated-private.h deleted file mode 100644 index 5687cf85c..000000000 --- a/libs/cairo/cairo/src/cairo-paginated-private.h +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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__surface_create(). The paginated backend will be careful - * to not let the user see that they really got a "wrapped" - * surface. See test-paginated-surface.c for a fairly minimal example - * of a paginated-using surface. That should be a reasonable example - * to follow. - * - * What the paginated surface does is first save all drawing - * operations for a page into a recording-surface. Then when the user calls - * cairo_show_page(), the paginated surface performs the following - * sequence of operations (using the backend functions passed to - * cairo_paginated_surface_create()): - * - * 1. Calls start_page() (if not %NULL). At this point, it is appropriate - * for the target to emit any page-specific header information into - * its output. - * - * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE - * - * 3. Replays the recording-surface to the target surface, (with an - * analysis surface inserted between which watches the return value - * from each operation). This analysis stage is used to decide which - * operations will require fallbacks. - * - * 4. Calls set_bounding_box() to provide the target surface with the - * tight bounding box of the page. - * - * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER - * - * 6. Replays a subset of the recording-surface operations to the target surface - * - * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK - * - * 8. Replays the remaining operations to an image surface, sets an - * appropriate clip on the target, then paints the resulting image - * surface to the target. - * - * So, the target will see drawing operations during three separate - * stages, (ANALYZE, RENDER and FALLBACK). During the ANALYZE phase - * the target should not actually perform any rendering, (for example, - * if performing output to a file, no output should be generated - * during this stage). Instead the drawing functions simply need to - * return %CAIRO_STATUS_SUCCESS or %CAIRO_INT_STATUS_UNSUPPORTED to - * indicate whether rendering would be supported. And it should do - * this as quickly as possible. The FALLBACK phase allows the surface - * to distinguish fallback images from native rendering in case they - * need to be handled as a special case. - * - * Note: The paginated surface layer assumes that the target surface - * is "blank" by default at the beginning of each page, without any - * need for an explicit erase operation, (as opposed to an image - * surface, for example, which might have uninitialized content - * originally). As such, it optimizes away CLEAR operations that - * happen at the beginning of each page---the target surface will not - * even see these operations. - */ -cairo_private cairo_surface_t * -_cairo_paginated_surface_create (cairo_surface_t *target, - cairo_content_t content, - const cairo_paginated_surface_backend_t *backend); - -cairo_private cairo_surface_t * -_cairo_paginated_surface_get_target (cairo_surface_t *surface); - -cairo_private cairo_bool_t -_cairo_surface_is_paginated (cairo_surface_t *surface); - -cairo_private cairo_status_t -_cairo_paginated_surface_set_size (cairo_surface_t *surface, - int width, - int height); - -#endif /* CAIRO_PAGINATED_H */ diff --git a/libs/cairo/cairo/src/cairo-paginated-surface-private.h b/libs/cairo/cairo/src/cairo-paginated-surface-private.h deleted file mode 100644 index 0b4e81f23..000000000 --- a/libs/cairo/cairo/src/cairo-paginated-surface-private.h +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-paginated-surface.c b/libs/cairo/cairo/src/cairo-paginated-surface.c deleted file mode 100644 index febcd05fa..000000000 --- a/libs/cairo/cairo/src/cairo-paginated-surface.c +++ /dev/null @@ -1,617 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* The paginated surface layer exists to provide as much code sharing - * as possible for the various paginated surface backends in cairo - * (PostScript, PDF, etc.). See cairo-paginated-private.h for - * more details on how it works and how to use it. - */ - -#include "cairoint.h" - -#include "cairo-paginated-private.h" -#include "cairo-paginated-surface-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" - -static const cairo_surface_backend_t cairo_paginated_surface_backend; - -static cairo_int_status_t -_cairo_paginated_surface_show_page (void *abstract_surface); - -static cairo_surface_t * -_cairo_paginated_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_rectangle_t rect; - rect.x = rect.y = 0.; - rect.width = width; - rect.height = height; - return cairo_recording_surface_create (content, &rect); -} - -static cairo_surface_t * -_create_recording_surface_for_target (cairo_surface_t *target, - cairo_content_t content) -{ - cairo_rectangle_int_t rect; - - if (_cairo_surface_get_extents (target, &rect)) { - cairo_rectangle_t recording_extents; - - recording_extents.x = rect.x; - recording_extents.y = rect.y; - recording_extents.width = rect.width; - recording_extents.height = rect.height; - - return cairo_recording_surface_create (content, &recording_extents); - } else { - return cairo_recording_surface_create (content, NULL); - } -} - -cairo_surface_t * -_cairo_paginated_surface_create (cairo_surface_t *target, - cairo_content_t content, - const cairo_paginated_surface_backend_t *backend) -{ - cairo_paginated_surface_t *surface; - cairo_status_t status; - - surface = malloc (sizeof (cairo_paginated_surface_t)); - if (unlikely (surface == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_surface_init (&surface->base, - &cairo_paginated_surface_backend, - NULL, /* device */ - content); - - /* Override surface->base.type with target's type so we don't leak - * evidence of the paginated wrapper out to the user. */ - surface->base.type = target->type; - - surface->target = cairo_surface_reference (target); - - surface->content = content; - surface->backend = backend; - - surface->recording_surface = _create_recording_surface_for_target (target, content); - status = surface->recording_surface->status; - if (unlikely (status)) - goto FAIL_CLEANUP_SURFACE; - - surface->page_num = 1; - surface->base.is_clear = TRUE; - - return &surface->base; - - FAIL_CLEANUP_SURFACE: - cairo_surface_destroy (target); - free (surface); - FAIL: - return _cairo_surface_create_in_error (status); -} - -cairo_bool_t -_cairo_surface_is_paginated (cairo_surface_t *surface) -{ - return surface->backend == &cairo_paginated_surface_backend; -} - -cairo_surface_t * -_cairo_paginated_surface_get_target (cairo_surface_t *surface) -{ - cairo_paginated_surface_t *paginated_surface; - - assert (_cairo_surface_is_paginated (surface)); - - paginated_surface = (cairo_paginated_surface_t *) surface; - - return paginated_surface->target; -} - -static cairo_status_t -_cairo_paginated_surface_finish (void *abstract_surface) -{ - cairo_paginated_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (! surface->base.is_clear || surface->page_num == 1) { - /* Bypass some of the sanity checking in cairo-surface.c, as we - * know that the surface is finished... - */ - status = _cairo_paginated_surface_show_page (surface); - } - - /* XXX We want to propagate any errors from destroy(), but those are not - * returned via the api. So we need to explicitly finish the target, - * and check the status afterwards. However, we can only call finish() - * on the target, if we own it. - */ - if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1) - cairo_surface_finish (surface->target); - if (status == CAIRO_STATUS_SUCCESS) - status = cairo_surface_status (surface->target); - cairo_surface_destroy (surface->target); - - cairo_surface_finish (surface->recording_surface); - if (status == CAIRO_STATUS_SUCCESS) - status = cairo_surface_status (surface->recording_surface); - cairo_surface_destroy (surface->recording_surface); - - return status; -} - -static cairo_surface_t * -_cairo_paginated_surface_create_image_surface (void *abstract_surface, - int width, - int height) -{ - cairo_paginated_surface_t *surface = abstract_surface; - cairo_surface_t *image; - cairo_font_options_t options; - - image = _cairo_image_surface_create_with_content (surface->content, - width, - height); - - cairo_surface_get_font_options (&surface->base, &options); - _cairo_surface_set_font_options (image, &options); - - return image; -} - -static cairo_status_t -_cairo_paginated_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_paginated_surface_t *surface = abstract_surface; - cairo_bool_t is_bounded; - cairo_surface_t *image; - cairo_status_t status; - cairo_rectangle_int_t extents; - - is_bounded = _cairo_surface_get_extents (surface->target, &extents); - if (! is_bounded) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = _cairo_paginated_surface_create_image_surface (surface, - extents.width, - extents.height); - - status = _cairo_recording_surface_replay (surface->recording_surface, image); - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - *image_out = (cairo_image_surface_t*) image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_paginated_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_int_status_t -_paint_fallback_image (cairo_paginated_surface_t *surface, - cairo_rectangle_int_t *rect) -{ - double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; - double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; - int x, y, width, height; - cairo_status_t status; - cairo_surface_t *image; - cairo_surface_pattern_t pattern; - cairo_clip_t clip; - - x = rect->x; - y = rect->y; - width = rect->width; - height = rect->height; - image = _cairo_paginated_surface_create_image_surface (surface, - ceil (width * x_scale), - ceil (height * y_scale)); - _cairo_surface_set_device_scale (image, x_scale, y_scale); - /* set_device_offset just sets the x0/y0 components of the matrix; - * so we have to do the scaling manually. */ - cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); - - status = _cairo_recording_surface_replay (surface->recording_surface, image); - if (unlikely (status)) - goto CLEANUP_IMAGE; - - _cairo_pattern_init_for_surface (&pattern, image); - cairo_matrix_init (&pattern.base.matrix, - x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); - /* the fallback should be rendered at native resolution, so disable - * filtering (if possible) to avoid introducing potential artifacts. */ - pattern.base.filter = CAIRO_FILTER_NEAREST; - - _cairo_clip_init (&clip); - status = _cairo_clip_rectangle (&clip, rect); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_paint (surface->target, - CAIRO_OPERATOR_SOURCE, - &pattern.base, &clip); - } - - _cairo_clip_fini (&clip); - _cairo_pattern_fini (&pattern.base); - -CLEANUP_IMAGE: - cairo_surface_destroy (image); - - return status; -} - -static cairo_int_status_t -_paint_page (cairo_paginated_surface_t *surface) -{ - cairo_surface_t *analysis; - cairo_status_t status; - cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; - - if (unlikely (surface->target->status)) - return surface->target->status; - - analysis = _cairo_analysis_surface_create (surface->target); - if (unlikely (analysis->status)) - return _cairo_surface_set_error (surface->target, analysis->status); - - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_ANALYZE); - status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, - analysis); - if (status || analysis->status) { - if (status == CAIRO_STATUS_SUCCESS) - status = analysis->status; - goto FAIL; - } - - if (surface->backend->set_bounding_box) { - cairo_box_t bbox; - - _cairo_analysis_surface_get_bounding_box (analysis, &bbox); - status = surface->backend->set_bounding_box (surface->target, &bbox); - if (unlikely (status)) - goto FAIL; - } - - if (surface->backend->set_fallback_images_required) { - cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis); - - status = surface->backend->set_fallback_images_required (surface->target, - has_fallbacks); - if (unlikely (status)) - goto FAIL; - } - - /* Finer grained fallbacks are currently only supported for some - * surface types */ - if (surface->backend->supports_fine_grained_fallbacks != NULL && - surface->backend->supports_fine_grained_fallbacks (surface->target)) - { - has_supported = _cairo_analysis_surface_has_supported (analysis); - has_page_fallback = FALSE; - has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis); - } - else - { - if (_cairo_analysis_surface_has_unsupported (analysis)) { - has_supported = FALSE; - has_page_fallback = TRUE; - } else { - has_supported = TRUE; - has_page_fallback = FALSE; - } - has_finegrained_fallback = FALSE; - } - - if (has_supported) { - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_RENDER); - - status = _cairo_recording_surface_replay_region (surface->recording_surface, - NULL, - surface->target, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - goto FAIL; - } - - if (has_page_fallback) { - cairo_rectangle_int_t extents; - cairo_bool_t is_bounded; - - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_FALLBACK); - - is_bounded = _cairo_surface_get_extents (surface->target, &extents); - if (! is_bounded) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - - status = _paint_fallback_image (surface, &extents); - if (unlikely (status)) - goto FAIL; - } - - if (has_finegrained_fallback) { - cairo_region_t *region; - int num_rects, i; - - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_FALLBACK); - - region = _cairo_analysis_surface_get_unsupported (analysis); - - num_rects = cairo_region_num_rectangles (region); - for (i = 0; i < num_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - status = _paint_fallback_image (surface, &rect); - if (unlikely (status)) - goto FAIL; - } - } - - FAIL: - cairo_surface_destroy (analysis); - - return _cairo_surface_set_error (surface->target, status); -} - -static cairo_status_t -_start_page (cairo_paginated_surface_t *surface) -{ - if (surface->target->status) - return surface->target->status; - - if (! surface->backend->start_page) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_set_error (surface->target, - surface->backend->start_page (surface->target)); -} - -static cairo_int_status_t -_cairo_paginated_surface_copy_page (void *abstract_surface) -{ - cairo_status_t status; - cairo_paginated_surface_t *surface = abstract_surface; - - status = _start_page (surface); - if (unlikely (status)) - return status; - - status = _paint_page (surface); - if (unlikely (status)) - return status; - - surface->page_num++; - - /* XXX: It might make sense to add some support here for calling - * cairo_surface_copy_page on the target surface. It would be an - * optimization for the output, but the interaction with image - * fallbacks gets tricky. For now, we just let the target see a - * show_page and we implement the copying by simply not destroying - * the recording-surface. */ - - cairo_surface_show_page (surface->target); - return cairo_surface_status (surface->target); -} - -static cairo_int_status_t -_cairo_paginated_surface_show_page (void *abstract_surface) -{ - cairo_status_t status; - cairo_paginated_surface_t *surface = abstract_surface; - - status = _start_page (surface); - if (unlikely (status)) - return status; - - status = _paint_page (surface); - if (unlikely (status)) - return status; - - cairo_surface_show_page (surface->target); - status = surface->target->status; - if (unlikely (status)) - return status; - - status = surface->recording_surface->status; - if (unlikely (status)) - return status; - - if (! surface->base.finished) { - cairo_surface_destroy (surface->recording_surface); - - surface->recording_surface = _create_recording_surface_for_target (surface->target, - surface->content); - status = surface->recording_surface->status; - if (unlikely (status)) - return status; - - surface->page_num++; - surface->base.is_clear = TRUE; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_paginated_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->target, rectangle); -} - -static void -_cairo_paginated_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - cairo_surface_get_font_options (surface->target, options); -} - -static cairo_int_status_t -_cairo_paginated_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_paint (surface->recording_surface, op, source, clip); -} - -static cairo_int_status_t -_cairo_paginated_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip); -} - -static cairo_int_status_t -_cairo_paginated_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_stroke (surface->recording_surface, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); -} - -static cairo_int_status_t -_cairo_paginated_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_fill (surface->recording_surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); -} - -static cairo_bool_t -_cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return cairo_surface_has_show_text_glyphs (surface->target); -} - -static cairo_int_status_t -_cairo_paginated_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); -} - -static cairo_surface_t * -_cairo_paginated_surface_snapshot (void *abstract_other) -{ - cairo_paginated_surface_t *other = abstract_other; - - return _cairo_surface_snapshot (other->recording_surface); -} - -static const cairo_surface_backend_t cairo_paginated_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, - _cairo_paginated_surface_create_similar, - _cairo_paginated_surface_finish, - _cairo_paginated_surface_acquire_source_image, - _cairo_paginated_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - _cairo_paginated_surface_copy_page, - _cairo_paginated_surface_show_page, - _cairo_paginated_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_paginated_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _cairo_paginated_surface_paint, - _cairo_paginated_surface_mask, - _cairo_paginated_surface_stroke, - _cairo_paginated_surface_fill, - NULL, /* show_glyphs */ - _cairo_paginated_surface_snapshot, - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _cairo_paginated_surface_has_show_text_glyphs, - _cairo_paginated_surface_show_text_glyphs -}; diff --git a/libs/cairo/cairo/src/cairo-path-bounds.c b/libs/cairo/cairo/src/cairo-path-bounds.c deleted file mode 100644 index c752fbca1..000000000 --- a/libs/cairo/cairo/src/cairo-path-bounds.c +++ /dev/null @@ -1,318 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-path-fixed-private.h" - -typedef struct cairo_path_bounder { - cairo_point_t current_point; - cairo_bool_t has_initial_point; - cairo_bool_t has_point; - - cairo_box_t extents; -} cairo_path_bounder_t; - -static void -_cairo_path_bounder_init (cairo_path_bounder_t *bounder) -{ - bounder->has_initial_point = FALSE; - bounder->has_point = FALSE; -} - -static void -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, - const cairo_point_t *point) -{ - if (bounder->has_point) { - if (point->x < bounder->extents.p1.x) - bounder->extents.p1.x = point->x; - - if (point->y < bounder->extents.p1.y) - bounder->extents.p1.y = point->y; - - if (point->x > bounder->extents.p2.x) - bounder->extents.p2.x = point->x; - - if (point->y > bounder->extents.p2.y) - bounder->extents.p2.y = point->y; - } else { - bounder->extents.p1.x = point->x; - bounder->extents.p1.y = point->y; - bounder->extents.p2.x = point->x; - bounder->extents.p2.y = point->y; - bounder->has_point = TRUE; - } -} - -static cairo_status_t -_cairo_path_bounder_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_path_bounder_t *bounder = closure; - - bounder->current_point = *point; - bounder->has_initial_point = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_line_to (void *closure, - const cairo_point_t *point) -{ - cairo_path_bounder_t *bounder = closure; - - if (bounder->has_initial_point) { - _cairo_path_bounder_add_point (bounder, &bounder->current_point); - bounder->has_initial_point = FALSE; - } - - _cairo_path_bounder_add_point (bounder, point); - bounder->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_path_bounder_t *bounder = closure; - - /* If the bbox of the control points is entirely inside, then we - * do not need to further evaluate the spline. - */ - if (! bounder->has_point || - b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x || - b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y || - c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x || - c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y || - d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x || - d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y) - { - return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder, - &bounder->current_point, b, c, d); - } - else - { - /* All control points are within the current extents. */ - bounder->current_point = *d; - return CAIRO_STATUS_SUCCESS; - } -} - -static cairo_status_t -_cairo_path_bounder_close_path (void *closure) -{ - return CAIRO_STATUS_SUCCESS; -} - -/* This computes the extents of all the points in the path, not those of - * the damage area (i.e it does not consider winding and it only inspects - * the control points of the curves, not the flattened path). - */ -void -_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents) -{ - if (path->extents.p1.x < path->extents.p2.x) { - _cairo_box_round_to_rectangle (&path->extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -/* A slightly better approximation than above - we actually decompose the - * Bezier, but we continue to ignore winding. - */ -void -_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents) -{ - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - bounder.extents = path->extents; - bounder.has_point = path->extents.p1.x < path->extents.p2.x; - } else { - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - } - - if (bounder.has_point) { - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -void -_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_rectangle_int_t *extents) -{ - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - bounder.extents = path->extents; - bounder.has_point = path->extents.p1.x < path->extents.p2.x; - } else { - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_close_path, - &bounder, tolerance); - assert (status == CAIRO_STATUS_SUCCESS); - } - - if (bounder.has_point) { - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -/* Adjusts the fill extents (above) by the device-space pen. */ -void -_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - cairo_rectangle_int_t *extents) -{ - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - bounder.extents = path->extents; - - /* include trailing move-to for degenerate segments */ - if (path->has_last_move_point) { - const cairo_point_t *point = &path->last_move_point; - - if (point->x < bounder.extents.p1.x) - bounder.extents.p1.x = point->x; - if (point->y < bounder.extents.p1.y) - bounder.extents.p1.y = point->y; - - if (point->x > bounder.extents.p2.x) - bounder.extents.p2.x = point->x; - if (point->y > bounder.extents.p2.y) - bounder.extents.p2.y = point->y; - } - - bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x; - bounder.has_initial_point = FALSE; - } else { - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - } - - if (bounder.has_point) { - double dx, dy; - - _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); - - bounder.extents.p1.x -= _cairo_fixed_from_double (dx); - bounder.extents.p2.x += _cairo_fixed_from_double (dx); - bounder.extents.p1.y -= _cairo_fixed_from_double (dy); - bounder.extents.p2.y += _cairo_fixed_from_double (dy); - - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else if (bounder.has_initial_point) { - double dx, dy; - - /* accommodate capping of degenerate paths */ - - _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); - - bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx); - bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx); - bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy); - bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy); - - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -cairo_status_t -_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_rectangle_int_t *extents) -{ - cairo_traps_t traps; - cairo_box_t bbox; - cairo_status_t status; - - _cairo_traps_init (&traps); - - status = _cairo_path_fixed_stroke_to_traps (path, - stroke_style, - ctm, - ctm_inverse, - tolerance, - &traps); - - _cairo_traps_extents (&traps, &bbox); - _cairo_traps_fini (&traps); - - _cairo_box_round_to_rectangle (&bbox, extents); - - return status; -} - -cairo_bool_t -_cairo_path_fixed_extents (const cairo_path_fixed_t *path, - cairo_box_t *box) -{ - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - *box = path->extents; - return path->extents.p1.x <= path->extents.p2.x; - } - - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - - *box = bounder.extents; - return bounder.has_point; -} diff --git a/libs/cairo/cairo/src/cairo-path-fill.c b/libs/cairo/cairo/src/cairo-path-fill.c deleted file mode 100644 index 40d41157a..000000000 --- a/libs/cairo/cairo/src/cairo-path-fill.c +++ /dev/null @@ -1,433 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-boxes-private.h" -#include "cairo-error-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-region-private.h" - -typedef struct cairo_filler { - double tolerance; - cairo_polygon_t *polygon; -} cairo_filler_t; - -static void -_cairo_filler_init (cairo_filler_t *filler, - double tolerance, - cairo_polygon_t *polygon) -{ - filler->tolerance = tolerance; - filler->polygon = polygon; -} - -static void -_cairo_filler_fini (cairo_filler_t *filler) -{ -} - -static cairo_status_t -_cairo_filler_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = filler->polygon; - - return _cairo_polygon_close (polygon) || - _cairo_polygon_move_to (polygon, point); -} - -static cairo_status_t -_cairo_filler_line_to (void *closure, - const cairo_point_t *point) -{ - cairo_filler_t *filler = closure; - return _cairo_polygon_line_to (filler->polygon, point); -} - -static cairo_status_t -_cairo_filler_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_filler_t *filler = closure; - cairo_spline_t spline; - - if (! _cairo_spline_init (&spline, - _cairo_filler_line_to, filler, - &filler->polygon->current_point, b, c, d)) - { - return _cairo_filler_line_to (closure, d); - } - - return _cairo_spline_decompose (&spline, filler->tolerance); -} - -static cairo_status_t -_cairo_filler_close_path (void *closure) -{ - cairo_filler_t *filler = closure; - return _cairo_polygon_close (filler->polygon); -} - -cairo_status_t -_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, - double tolerance, - cairo_polygon_t *polygon) -{ - cairo_filler_t filler; - cairo_status_t status; - - _cairo_filler_init (&filler, tolerance, polygon); - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_filler_move_to, - _cairo_filler_line_to, - _cairo_filler_curve_to, - _cairo_filler_close_path, - &filler); - if (unlikely (status)) - return status; - - status = _cairo_polygon_close (polygon); - _cairo_filler_fini (&filler); - - return status; -} - -cairo_status_t -_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_traps_t *traps) -{ - cairo_polygon_t polygon; - cairo_status_t status; - - if (path->is_empty_fill) - return CAIRO_STATUS_SUCCESS; - - _cairo_polygon_init (&polygon); - if (traps->num_limits) - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); - - status = _cairo_path_fixed_fill_to_polygon (path, - tolerance, - &polygon); - if (unlikely (status || polygon.num_edges == 0)) - goto CLEANUP; - - if (path->is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps, - &polygon, - fill_rule); - } else { - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &polygon, - fill_rule); - } - - CLEANUP: - _cairo_polygon_fini (&polygon); - return status; -} - -static cairo_region_t * -_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_rectangle_int_t *extents) -{ - cairo_box_t box; - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_status_t status; - cairo_region_t *region; - - /* first try to bypass fill-to-polygon */ - _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (_cairo_status_is_error (status)) - goto CLEANUP_TRAPS; - - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_traps_extract_region (&traps, ®ion); - goto CLEANUP_TRAPS; - } - - /* path is not rectangular, try extracting clipped rectilinear edges */ - _cairo_polygon_init (&polygon); - if (extents != NULL) { - _cairo_box_from_rectangle (&box, extents); - _cairo_polygon_limit (&polygon, &box, 1); - } - - /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); - if (unlikely (status)) - goto CLEANUP_POLYGON; - - if (polygon.num_edges == 0) { - region = cairo_region_create (); - } else { - status = - _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - status = _cairo_traps_extract_region (&traps, ®ion); - } - - CLEANUP_POLYGON: - _cairo_polygon_fini (&polygon); - - CLEANUP_TRAPS: - _cairo_traps_fini (&traps); - - if (unlikely (status)) - region = _cairo_region_create_in_error (status); - - return region; -} - -/* This special-case filler supports only a path that describes a - * device-axis aligned rectangle. It exists to avoid the overhead of - * the general tessellator when drawing very common rectangles. - * - * If the path described anything but a device-axis aligned rectangle, - * this function will abort. - */ -cairo_region_t * -_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_rectangle_int_t *extents) -{ - cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_box_t box; - cairo_region_t *region = NULL; - - assert (path->maybe_fill_region); - assert (! path->is_empty_fill); - - if (_cairo_path_fixed_is_box (path, &box)) { - rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x); - rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y); - rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) - - rectangle_stack[0].x; - rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) - - rectangle_stack[0].y; - if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents)) - region = cairo_region_create (); - else - region = cairo_region_create_rectangle (&rectangle_stack[0]); - } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { - cairo_rectangle_int_t *rects = rectangle_stack; - cairo_path_fixed_iter_t iter; - int last_cw = -1; - int size = ARRAY_LENGTH (rectangle_stack); - int count = 0; - - /* Support a series of rectangles as can be expected to describe a - * GdkRegion clip region during exposes. - */ - _cairo_path_fixed_iter_init (&iter, path); - while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - int cw = 0; - - if (box.p1.x > box.p2.x) { - cairo_fixed_t t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - - cw = ! cw; - } - - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - - cw = ! cw; - } - - if (last_cw < 0) - last_cw = cw; - else if (last_cw != cw) - goto TESSELLATE; - - if (count == size) { - cairo_rectangle_int_t *new_rects; - - size *= 4; - if (rects == rectangle_stack) { - new_rects = _cairo_malloc_ab (size, - sizeof (cairo_rectangle_int_t)); - if (unlikely (new_rects == NULL)) { - /* XXX _cairo_region_nil */ - break; - } - memcpy (new_rects, rects, sizeof (rectangle_stack)); - } else { - new_rects = _cairo_realloc_ab (rects, size, - sizeof (cairo_rectangle_int_t)); - if (unlikely (new_rects == NULL)) { - /* XXX _cairo_region_nil */ - break; - } - } - rects = new_rects; - } - - rects[count].x = _cairo_fixed_integer_part (box.p1.x); - rects[count].y = _cairo_fixed_integer_part (box.p1.y); - rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x; - rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y; - if (_cairo_rectangle_intersect (&rects[count], extents)) - count++; - } - - if (_cairo_path_fixed_iter_at_end (&iter)) - region = cairo_region_create_rectangles (rects, count); - -TESSELLATE: - if (rects != rectangle_stack) - free (rects); - } - - if (region == NULL) { - /* Hmm, complex polygon */ - region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path, - fill_rule, - extents); - - - } - - return region; -} - -cairo_int_status_t -_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) -{ - cairo_box_t box; - cairo_status_t status; - - traps->is_rectilinear = TRUE; - traps->is_rectangular = TRUE; - - if (_cairo_path_fixed_is_box (path, &box)) { - return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); - } else { - cairo_path_fixed_iter_t iter; - - _cairo_path_fixed_iter_init (&iter, path); - while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - } - - status = _cairo_traps_tessellate_rectangle (traps, - &box.p1, &box.p2); - if (unlikely (status)) { - _cairo_traps_clear (traps); - return status; - } - } - - if (_cairo_path_fixed_iter_at_end (&iter)) - return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule); - - _cairo_traps_clear (traps); - return CAIRO_INT_STATUS_UNSUPPORTED; - } -} - -static cairo_status_t -_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *boxes) -{ - cairo_polygon_t polygon; - cairo_status_t status; - - _cairo_polygon_init (&polygon); - if (boxes->num_limits) { - _cairo_polygon_limit (&polygon, boxes->limits, boxes->num_limits); - boxes->num_limits = 0; - } - - /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = - _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon, - fill_rule, - boxes); - } - - _cairo_polygon_fini (&polygon); - - return status; -} - -cairo_status_t -_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *boxes) -{ - cairo_path_fixed_iter_t iter; - cairo_status_t status; - cairo_box_t box; - - if (_cairo_path_fixed_is_box (path, &box)) - return _cairo_boxes_add (boxes, &box); - - _cairo_path_fixed_iter_init (&iter, path); - while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) - continue; - - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - } - - status = _cairo_boxes_add (boxes, &box); - if (unlikely (status)) - return status; - } - - if (_cairo_path_fixed_iter_at_end (&iter)) - return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes); - - /* path is not rectangular, try extracting clipped rectilinear edges */ - _cairo_boxes_clear (boxes); - return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path, - fill_rule, - boxes); -} diff --git a/libs/cairo/cairo/src/cairo-path-fixed-private.h b/libs/cairo/cairo/src/cairo-path-fixed-private.h deleted file mode 100644 index 69972505a..000000000 --- a/libs/cairo/cairo/src/cairo-path-fixed-private.h +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PATH_FIXED_PRIVATE_H -#define CAIRO_PATH_FIXED_PRIVATE_H - -#include "cairo-types-private.h" -#include "cairo-compiler-private.h" -#include "cairo-list-private.h" - -#define WATCH_PATH 0 -#if WATCH_PATH -#include -#endif - -enum cairo_path_op { - CAIRO_PATH_OP_MOVE_TO = 0, - CAIRO_PATH_OP_LINE_TO = 1, - CAIRO_PATH_OP_CURVE_TO = 2, - CAIRO_PATH_OP_CLOSE_PATH = 3 -}; - -/* we want to make sure a single byte is used for the enum */ -typedef char cairo_path_op_t; - -/* make _cairo_path_fixed fit into ~512 bytes -- about 50 items */ -#define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \ - / (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t))) - -typedef struct _cairo_path_buf { - cairo_list_t link; - unsigned int num_ops; - unsigned int size_ops; - unsigned int num_points; - unsigned int size_points; - - cairo_path_op_t *op; - cairo_point_t *points; -} cairo_path_buf_t; - -typedef struct _cairo_path_buf_fixed { - cairo_path_buf_t base; - - cairo_path_op_t op[CAIRO_PATH_BUF_SIZE]; - cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE]; -} cairo_path_buf_fixed_t; - -struct _cairo_path_fixed { - cairo_point_t last_move_point; - cairo_point_t current_point; - unsigned int has_current_point : 1; - unsigned int has_last_move_point : 1; - unsigned int has_curve_to : 1; - unsigned int is_rectilinear : 1; - unsigned int maybe_fill_region : 1; - unsigned int is_empty_fill : 1; - - cairo_box_t extents; - - cairo_path_buf_fixed_t buf; -}; - -cairo_private void -_cairo_path_fixed_translate (cairo_path_fixed_t *path, - cairo_fixed_t offx, - cairo_fixed_t offy); - -cairo_private cairo_status_t -_cairo_path_fixed_append (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other, - cairo_direction_t dir, - cairo_fixed_t tx, - cairo_fixed_t ty); - -cairo_private unsigned long -_cairo_path_fixed_hash (const cairo_path_fixed_t *path); - -cairo_private unsigned long -_cairo_path_fixed_size (const cairo_path_fixed_t *path); - -cairo_private cairo_bool_t -_cairo_path_fixed_equal (const cairo_path_fixed_t *a, - const cairo_path_fixed_t *b); - -typedef struct _cairo_path_fixed_iter { - const cairo_path_buf_t *first; - const cairo_path_buf_t *buf; - unsigned int n_op; - unsigned int n_point; -} cairo_path_fixed_iter_t; - -cairo_private void -_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, - const cairo_path_fixed_t *path); - -cairo_private cairo_bool_t -_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, - cairo_box_t *box); - -cairo_private cairo_bool_t -_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); - -static inline cairo_bool_t -_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) -{ - return path->is_empty_fill; -} - -static inline cairo_bool_t -_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path) -{ - if (! path->is_rectilinear) - return 0; - - if (! path->has_current_point) - return 1; - - /* check whether the implicit close preserves the rectilinear property */ - return path->current_point.x == path->last_move_point.x || - path->current_point.y == path->last_move_point.y; -} - -static inline cairo_bool_t -_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path) -{ -#if WATCH_PATH - fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n", - path->maybe_fill_region ? "true" : "false"); -#endif - return path->maybe_fill_region; -} - -#endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-path-fixed.c b/libs/cairo/cairo/src/cairo-path-fixed.c deleted file mode 100644 index 3ba5f75e0..000000000 --- a/libs/cairo/cairo/src/cairo-path-fixed.c +++ /dev/null @@ -1,1390 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-slope-private.h" - -static cairo_status_t -_cairo_path_fixed_add (cairo_path_fixed_t *path, - cairo_path_op_t op, - const cairo_point_t *points, - int num_points); - -static void -_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, - cairo_path_buf_t *buf); - -static cairo_path_buf_t * -_cairo_path_buf_create (int size_ops, int size_points); - -static void -_cairo_path_buf_destroy (cairo_path_buf_t *buf); - -static void -_cairo_path_buf_add_op (cairo_path_buf_t *buf, - cairo_path_op_t op); - -static void -_cairo_path_buf_add_points (cairo_path_buf_t *buf, - const cairo_point_t *points, - int num_points); - -#define cairo_path_head(path__) (&(path__)->buf.base) -#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__)) - -#define cairo_path_buf_next(pos__) \ - cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link) -#define cairo_path_buf_prev(pos__) \ - cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link) - -#define cairo_path_foreach_buf_start(pos__, path__) \ - pos__ = cairo_path_head (path__); do -#define cairo_path_foreach_buf_end(pos__, path__) \ - while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__)) - -void -_cairo_path_fixed_init (cairo_path_fixed_t *path) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); - - cairo_list_init (&path->buf.base.link); - - path->buf.base.num_ops = 0; - path->buf.base.num_points = 0; - path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); - path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); - path->buf.base.op = path->buf.op; - path->buf.base.points = path->buf.points; - - path->current_point.x = 0; - path->current_point.y = 0; - path->last_move_point = path->current_point; - path->has_last_move_point = FALSE; - path->has_current_point = FALSE; - path->has_curve_to = FALSE; - path->is_rectilinear = TRUE; - path->maybe_fill_region = TRUE; - path->is_empty_fill = TRUE; - - path->extents.p1.x = path->extents.p1.y = INT_MAX; - path->extents.p2.x = path->extents.p2.y = INT_MIN; -} - -cairo_status_t -_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other) -{ - cairo_path_buf_t *buf, *other_buf; - unsigned int num_points, num_ops; - - VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); - - cairo_list_init (&path->buf.base.link); - - path->buf.base.op = path->buf.op; - path->buf.base.points = path->buf.points; - path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); - path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); - - path->current_point = other->current_point; - path->last_move_point = other->last_move_point; - path->has_last_move_point = other->has_last_move_point; - path->has_current_point = other->has_current_point; - path->has_curve_to = other->has_curve_to; - path->is_rectilinear = other->is_rectilinear; - path->maybe_fill_region = other->maybe_fill_region; - path->is_empty_fill = other->is_empty_fill; - - path->extents = other->extents; - - path->buf.base.num_ops = other->buf.base.num_ops; - path->buf.base.num_points = other->buf.base.num_points; - memcpy (path->buf.op, other->buf.base.op, - other->buf.base.num_ops * sizeof (other->buf.op[0])); - memcpy (path->buf.points, other->buf.points, - other->buf.base.num_points * sizeof (other->buf.points[0])); - - num_points = num_ops = 0; - for (other_buf = cairo_path_buf_next (cairo_path_head (other)); - other_buf != cairo_path_head (other); - other_buf = cairo_path_buf_next (other_buf)) - { - num_ops += other_buf->num_ops; - num_points += other_buf->num_points; - } - - if (num_ops) { - buf = _cairo_path_buf_create (num_ops, num_points); - if (unlikely (buf == NULL)) { - _cairo_path_fixed_fini (path); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (other_buf = cairo_path_buf_next (cairo_path_head (other)); - other_buf != cairo_path_head (other); - other_buf = cairo_path_buf_next (other_buf)) - { - memcpy (buf->op + buf->num_ops, other_buf->op, - other_buf->num_ops * sizeof (buf->op[0])); - buf->num_ops += other_buf->num_ops; - - memcpy (buf->points + buf->num_points, other_buf->points, - other_buf->num_points * sizeof (buf->points[0])); - buf->num_points += other_buf->num_points; - } - - _cairo_path_fixed_add_buf (path, buf); - } - - return CAIRO_STATUS_SUCCESS; -} - -unsigned long -_cairo_path_fixed_hash (const cairo_path_fixed_t *path) -{ - unsigned long hash = _CAIRO_HASH_INIT_VALUE; - const cairo_path_buf_t *buf; - int num_points, num_ops; - - hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents)); - - num_ops = num_points = 0; - cairo_path_foreach_buf_start (buf, path) { - hash = _cairo_hash_bytes (hash, buf->op, - buf->num_ops * sizeof (buf->op[0])); - hash = _cairo_hash_bytes (hash, buf->points, - buf->num_points * sizeof (buf->points[0])); - - num_ops += buf->num_ops; - num_points += buf->num_points; - } cairo_path_foreach_buf_end (buf, path); - - hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops)); - hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points)); - - return hash; -} - -unsigned long -_cairo_path_fixed_size (const cairo_path_fixed_t *path) -{ - const cairo_path_buf_t *buf; - int num_points, num_ops; - - num_ops = num_points = 0; - cairo_path_foreach_buf_start (buf, path) { - num_ops += buf->num_ops; - num_points += buf->num_points; - } cairo_path_foreach_buf_end (buf, path); - - return num_ops * sizeof (buf->op[0]) + - num_points * sizeof (buf->points[0]); -} - -cairo_bool_t -_cairo_path_fixed_equal (const cairo_path_fixed_t *a, - const cairo_path_fixed_t *b) -{ - const cairo_path_buf_t *buf_a, *buf_b; - const cairo_path_op_t *ops_a, *ops_b; - const cairo_point_t *points_a, *points_b; - int num_points_a, num_ops_a; - int num_points_b, num_ops_b; - - if (a == b) - return TRUE; - - /* use the flags to quickly differentiate based on contents */ - if (a->is_empty_fill != b->is_empty_fill || - a->has_curve_to != b->has_curve_to || - a->maybe_fill_region != b->maybe_fill_region || - a->is_rectilinear != b->is_rectilinear) - { - return FALSE; - } - - if (a->extents.p1.x != b->extents.p1.x || - a->extents.p1.y != b->extents.p1.y || - a->extents.p2.x != b->extents.p2.x || - a->extents.p2.y != b->extents.p2.y) - { - return FALSE; - } - - num_ops_a = num_points_a = 0; - cairo_path_foreach_buf_start (buf_a, a) { - num_ops_a += buf_a->num_ops; - num_points_a += buf_a->num_points; - } cairo_path_foreach_buf_end (buf_a, a); - - num_ops_b = num_points_b = 0; - cairo_path_foreach_buf_start (buf_b, b) { - num_ops_b += buf_b->num_ops; - num_points_b += buf_b->num_points; - } cairo_path_foreach_buf_end (buf_b, b); - - if (num_ops_a == 0 && num_ops_b == 0) - return TRUE; - - if (num_ops_a != num_ops_b || num_points_a != num_points_b) - return FALSE; - - buf_a = cairo_path_head (a); - num_points_a = buf_a->num_points; - num_ops_a = buf_a->num_ops; - ops_a = buf_a->op; - points_a = buf_a->points; - - buf_b = cairo_path_head (b); - num_points_b = buf_b->num_points; - num_ops_b = buf_b->num_ops; - ops_b = buf_b->op; - points_b = buf_b->points; - - while (TRUE) { - int num_ops = MIN (num_ops_a, num_ops_b); - int num_points = MIN (num_points_a, num_points_b); - - if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) - return FALSE; - if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) - return FALSE; - - num_ops_a -= num_ops; - ops_a += num_ops; - num_points_a -= num_points; - points_a += num_points; - if (num_ops_a == 0 || num_points_a == 0) { - if (num_ops_a || num_points_a) - return FALSE; - - buf_a = cairo_path_buf_next (buf_a); - if (buf_a == cairo_path_head (a)) - break; - - num_points_a = buf_a->num_points; - num_ops_a = buf_a->num_ops; - ops_a = buf_a->op; - points_a = buf_a->points; - } - - num_ops_b -= num_ops; - ops_b += num_ops; - num_points_b -= num_points; - points_b += num_points; - if (num_ops_b == 0 || num_points_b == 0) { - if (num_ops_b || num_points_b) - return FALSE; - - buf_b = cairo_path_buf_next (buf_b); - if (buf_b == cairo_path_head (b)) - break; - - num_points_b = buf_b->num_points; - num_ops_b = buf_b->num_ops; - ops_b = buf_b->op; - points_b = buf_b->points; - } - } - - return TRUE; -} - -cairo_path_fixed_t * -_cairo_path_fixed_create (void) -{ - cairo_path_fixed_t *path; - - path = malloc (sizeof (cairo_path_fixed_t)); - if (!path) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - _cairo_path_fixed_init (path); - return path; -} - -void -_cairo_path_fixed_fini (cairo_path_fixed_t *path) -{ - cairo_path_buf_t *buf; - - buf = cairo_path_buf_next (cairo_path_head (path)); - while (buf != cairo_path_head (path)) { - cairo_path_buf_t *this = buf; - buf = cairo_path_buf_next (buf); - _cairo_path_buf_destroy (this); - } - - VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t))); -} - -void -_cairo_path_fixed_destroy (cairo_path_fixed_t *path) -{ - _cairo_path_fixed_fini (path); - free (path); -} - -static cairo_path_op_t -_cairo_path_last_op (cairo_path_fixed_t *path) -{ - cairo_path_buf_t *buf; - - buf = cairo_path_tail (path); - if (buf->num_ops == 0) - return -1; - - return buf->op[buf->num_ops - 1]; -} - -static inline void -_cairo_path_fixed_extents_add (cairo_path_fixed_t *path, - const cairo_point_t *point) -{ - if (point->x < path->extents.p1.x) - path->extents.p1.x = point->x; - if (point->y < path->extents.p1.y) - path->extents.p1.y = point->y; - - if (point->x > path->extents.p2.x) - path->extents.p2.x = point->x; - if (point->y > path->extents.p2.y) - path->extents.p2.y = point->y; -} - -cairo_status_t -_cairo_path_fixed_move_to (cairo_path_fixed_t *path, - cairo_fixed_t x, - cairo_fixed_t y) -{ - cairo_status_t status; - cairo_point_t point; - - point.x = x; - point.y = y; - - /* If the previous op was also a MOVE_TO, then just change its - * point rather than adding a new op. */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) { - cairo_path_buf_t *buf; - - buf = cairo_path_tail (path); - buf->points[buf->num_points - 1] = point; - } else { - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); - if (unlikely (status)) - return status; - - if (path->has_current_point && path->is_rectilinear) { - /* a move-to is first an implicit close */ - path->is_rectilinear = path->current_point.x == path->last_move_point.x || - path->current_point.y == path->last_move_point.y; - path->maybe_fill_region &= path->is_rectilinear; - } - if (path->maybe_fill_region) { - path->maybe_fill_region = - _cairo_fixed_is_integer (path->last_move_point.x) && - _cairo_fixed_is_integer (path->last_move_point.y); - } - } - - path->current_point = point; - path->last_move_point = point; - path->has_last_move_point = TRUE; - path->has_current_point = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path) -{ - path->has_current_point = FALSE; -} - -cairo_status_t -_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, - cairo_fixed_t dx, - cairo_fixed_t dy) -{ - if (unlikely (! path->has_current_point)) - return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); - - return _cairo_path_fixed_move_to (path, - path->current_point.x + dx, - path->current_point.y + dy); - -} - -cairo_status_t -_cairo_path_fixed_line_to (cairo_path_fixed_t *path, - cairo_fixed_t x, - cairo_fixed_t y) -{ - cairo_status_t status; - cairo_point_t point; - - point.x = x; - point.y = y; - - /* When there is not yet a current point, the line_to operation - * becomes a move_to instead. Note: We have to do this by - * explicitly calling into _cairo_path_fixed_move_to to ensure - * that the last_move_point state is updated properly. - */ - if (! path->has_current_point) - return _cairo_path_fixed_move_to (path, point.x, point.y); - - /* If the previous op was but the initial MOVE_TO and this segment - * is degenerate, then we can simply skip this point. Note that - * a move-to followed by a degenerate line-to is a valid path for - * stroking, but at all other times is simply a degenerate segment. - */ - if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { - if (x == path->current_point.x && y == path->current_point.y) - return CAIRO_STATUS_SUCCESS; - } - - /* If the previous op was also a LINE_TO with the same gradient, - * then just change its end-point rather than adding a new op. - */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { - cairo_path_buf_t *buf; - const cairo_point_t *p; - - buf = cairo_path_tail (path); - if (likely (buf->num_points >= 2)) { - p = &buf->points[buf->num_points-2]; - } else { - cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); - p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; - } - - if (p->x == path->current_point.x && p->y == path->current_point.y) { - /* previous line element was degenerate, replace */ - buf->points[buf->num_points - 1] = point; - goto FLAGS; - } else { - cairo_slope_t prev, self; - - _cairo_slope_init (&prev, p, &path->current_point); - _cairo_slope_init (&self, &path->current_point, &point); - if (_cairo_slope_equal (&prev, &self) && - /* cannot trim anti-parallel segments whilst stroking */ - ! _cairo_slope_backwards (&prev, &self)) - { - buf->points[buf->num_points - 1] = point; - goto FLAGS; - } - } - } - - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); - if (unlikely (status)) - return status; - - FLAGS: - if (path->is_rectilinear) { - path->is_rectilinear = path->current_point.x == x || - path->current_point.y == y; - path->maybe_fill_region &= path->is_rectilinear; - } - if (path->maybe_fill_region) { - path->maybe_fill_region = _cairo_fixed_is_integer (x) && - _cairo_fixed_is_integer (y); - } - if (path->is_empty_fill) { - path->is_empty_fill = path->current_point.x == x && - path->current_point.y == y; - } - - path->current_point = point; - if (path->has_last_move_point) { - _cairo_path_fixed_extents_add (path, &path->last_move_point); - path->has_last_move_point = FALSE; - } - _cairo_path_fixed_extents_add (path, &point); - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, - cairo_fixed_t dx, - cairo_fixed_t dy) -{ - if (unlikely (! path->has_current_point)) - return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); - - return _cairo_path_fixed_line_to (path, - path->current_point.x + dx, - path->current_point.y + dy); -} - -cairo_status_t -_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, - cairo_fixed_t x0, cairo_fixed_t y0, - cairo_fixed_t x1, cairo_fixed_t y1, - cairo_fixed_t x2, cairo_fixed_t y2) -{ - cairo_status_t status; - cairo_point_t point[3]; - - /* make sure subpaths are started properly */ - if (! path->has_current_point) { - status = _cairo_path_fixed_move_to (path, x0, y0); - if (unlikely (status)) - return status; - } - - point[0].x = x0; point[0].y = y0; - point[1].x = x1; point[1].y = y1; - point[2].x = x2; point[2].y = y2; - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); - if (unlikely (status)) - return status; - - path->current_point = point[2]; - path->has_current_point = TRUE; - path->is_empty_fill = FALSE; - path->has_curve_to = TRUE; - path->is_rectilinear = FALSE; - path->maybe_fill_region = FALSE; - - /* coarse bounds */ - if (path->has_last_move_point) { - _cairo_path_fixed_extents_add (path, &path->last_move_point); - path->has_last_move_point = FALSE; - } - _cairo_path_fixed_extents_add (path, &point[0]); - _cairo_path_fixed_extents_add (path, &point[1]); - _cairo_path_fixed_extents_add (path, &point[2]); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, - cairo_fixed_t dx0, cairo_fixed_t dy0, - cairo_fixed_t dx1, cairo_fixed_t dy1, - cairo_fixed_t dx2, cairo_fixed_t dy2) -{ - if (unlikely (! path->has_current_point)) - return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); - - return _cairo_path_fixed_curve_to (path, - path->current_point.x + dx0, - path->current_point.y + dy0, - - path->current_point.x + dx1, - path->current_point.y + dy1, - - path->current_point.x + dx2, - path->current_point.y + dy2); -} - -cairo_status_t -_cairo_path_fixed_close_path (cairo_path_fixed_t *path) -{ - cairo_status_t status; - - if (! path->has_current_point) - return CAIRO_STATUS_SUCCESS; - - /* If the previous op was also a LINE_TO back to the start, discard it */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { - if (path->current_point.x == path->last_move_point.x && - path->current_point.y == path->last_move_point.y) - { - cairo_path_buf_t *buf; - cairo_point_t *p; - - buf = cairo_path_tail (path); - if (likely (buf->num_points >= 2)) { - p = &buf->points[buf->num_points-2]; - } else { - cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); - p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; - } - - path->current_point = *p; - buf->num_ops--; - buf->num_points--; - } - } - - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); - if (unlikely (status)) - return status; - - return _cairo_path_fixed_move_to (path, - path->last_move_point.x, - path->last_move_point.y); -} - -cairo_bool_t -_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, - cairo_fixed_t *x, - cairo_fixed_t *y) -{ - if (! path->has_current_point) - return FALSE; - - *x = path->current_point.x; - *y = path->current_point.y; - - return TRUE; -} - -static cairo_status_t -_cairo_path_fixed_add (cairo_path_fixed_t *path, - cairo_path_op_t op, - const cairo_point_t *points, - int num_points) -{ - cairo_path_buf_t *buf = cairo_path_tail (path); - - if (buf->num_ops + 1 > buf->size_ops || - buf->num_points + num_points > buf->size_points) - { - buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2); - if (unlikely (buf == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_path_fixed_add_buf (path, buf); - } - - if (WATCH_PATH) { - const char *op_str[] = { - "move-to", - "line-to", - "curve-to", - "close-path", - }; - char buf[1024]; - int len = 0; - int i; - - len += snprintf (buf + len, sizeof (buf), "["); - for (i = 0; i < num_points; i++) { - if (i != 0) - len += snprintf (buf + len, sizeof (buf), " "); - len += snprintf (buf + len, sizeof (buf), "(%f, %f)", - _cairo_fixed_to_double (points[i].x), - _cairo_fixed_to_double (points[i].y)); - } - len += snprintf (buf + len, sizeof (buf), "]"); - - fprintf (stderr, - "_cairo_path_fixed_add (%s, %s)\n", - op_str[(int) op], buf); - } - - _cairo_path_buf_add_op (buf, op); - _cairo_path_buf_add_points (buf, points, num_points); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, - cairo_path_buf_t *buf) -{ - cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link); -} - -COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1); -static cairo_path_buf_t * -_cairo_path_buf_create (int size_ops, int size_points) -{ - cairo_path_buf_t *buf; - - /* adjust size_ops to ensure that buf->points is naturally aligned */ - size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double)); - buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t)); - if (buf) { - buf->num_ops = 0; - buf->num_points = 0; - buf->size_ops = size_ops; - buf->size_points = size_points; - - buf->op = (cairo_path_op_t *) (buf + 1); - buf->points = (cairo_point_t *) (buf->op + size_ops); - } - - return buf; -} - -static void -_cairo_path_buf_destroy (cairo_path_buf_t *buf) -{ - free (buf); -} - -static void -_cairo_path_buf_add_op (cairo_path_buf_t *buf, - cairo_path_op_t op) -{ - buf->op[buf->num_ops++] = op; -} - -static void -_cairo_path_buf_add_points (cairo_path_buf_t *buf, - const cairo_point_t *points, - int num_points) -{ - memcpy (buf->points + buf->num_points, - points, - sizeof (points[0]) * num_points); - buf->num_points += num_points; -} - -cairo_status_t -_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, - cairo_direction_t dir, - cairo_path_fixed_move_to_func_t *move_to, - cairo_path_fixed_line_to_func_t *line_to, - cairo_path_fixed_curve_to_func_t *curve_to, - cairo_path_fixed_close_path_func_t *close_path, - void *closure) -{ - const uint8_t num_args[] = { - 1, /* cairo_path_move_to */ - 1, /* cairo_path_op_line_to */ - 3, /* cairo_path_op_curve_to */ - 0, /* cairo_path_op_close_path */ - }; - cairo_status_t status; - const cairo_path_buf_t *buf, *first; - cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD); - int step = forward ? 1 : -1; - - buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path); - do { - cairo_point_t *points; - int start, stop, i; - - if (forward) { - start = 0; - stop = buf->num_ops; - points = buf->points; - } else { - start = buf->num_ops - 1; - stop = -1; - points = buf->points + buf->num_points; - } - - for (i = start; i != stop; i += step) { - cairo_path_op_t op = buf->op[i]; - - if (! forward) - points -= num_args[(int) op]; - - switch (op) { - case CAIRO_PATH_OP_MOVE_TO: - status = (*move_to) (closure, &points[0]); - break; - case CAIRO_PATH_OP_LINE_TO: - status = (*line_to) (closure, &points[0]); - break; - case CAIRO_PATH_OP_CURVE_TO: - status = (*curve_to) (closure, &points[0], &points[1], &points[2]); - break; - default: - ASSERT_NOT_REACHED; - case CAIRO_PATH_OP_CLOSE_PATH: - status = (*close_path) (closure); - break; - } - if (unlikely (status)) - return status; - - if (forward) - points += num_args[(int) op]; - } - } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first); - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_path_fixed_append_closure { - cairo_point_t offset; - cairo_path_fixed_t *path; -} cairo_path_fixed_append_closure_t; - -static cairo_status_t -_append_move_to (void *abstract_closure, - const cairo_point_t *point) -{ - cairo_path_fixed_append_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_move_to (closure->path, - point->x + closure->offset.x, - point->y + closure->offset.y); -} - -static cairo_status_t -_append_line_to (void *abstract_closure, - const cairo_point_t *point) -{ - cairo_path_fixed_append_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_line_to (closure->path, - point->x + closure->offset.x, - point->y + closure->offset.y); -} - -static cairo_status_t -_append_curve_to (void *abstract_closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - cairo_path_fixed_append_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_curve_to (closure->path, - p0->x + closure->offset.x, - p0->y + closure->offset.y, - p1->x + closure->offset.x, - p1->y + closure->offset.y, - p2->x + closure->offset.x, - p2->y + closure->offset.y); -} - -static cairo_status_t -_append_close_path (void *abstract_closure) -{ - cairo_path_fixed_append_closure_t *closure = abstract_closure; - - return _cairo_path_fixed_close_path (closure->path); -} - -cairo_status_t -_cairo_path_fixed_append (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other, - cairo_direction_t dir, - cairo_fixed_t tx, - cairo_fixed_t ty) -{ - cairo_path_fixed_append_closure_t closure; - - closure.path = path; - closure.offset.x = tx; - closure.offset.y = ty; - - return _cairo_path_fixed_interpret (other, dir, - _append_move_to, - _append_line_to, - _append_curve_to, - _append_close_path, - &closure); -} - -static void -_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, - cairo_fixed_t offx, - cairo_fixed_t offy, - cairo_fixed_t scalex, - cairo_fixed_t scaley) -{ - cairo_path_buf_t *buf; - unsigned int i; - - if (path->maybe_fill_region) { - path->maybe_fill_region = _cairo_fixed_is_integer (offx) && - _cairo_fixed_is_integer (offy) && - _cairo_fixed_is_integer (scalex) && - _cairo_fixed_is_integer (scaley); - } - - cairo_path_foreach_buf_start (buf, path) { - for (i = 0; i < buf->num_points; i++) { - if (scalex != CAIRO_FIXED_ONE) - buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex); - buf->points[i].x += offx; - - if (scaley != CAIRO_FIXED_ONE) - buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley); - buf->points[i].y += offy; - } - } cairo_path_foreach_buf_end (buf, path); - - path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx; - path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx; - - path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy; - path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy; -} - -void -_cairo_path_fixed_translate (cairo_path_fixed_t *path, - cairo_fixed_t offx, - cairo_fixed_t offy) -{ - cairo_path_buf_t *buf; - unsigned int i; - - if (offx == 0 && offy == 0) - return; - - if (path->maybe_fill_region && - ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy))) - { - path->maybe_fill_region = FALSE; - } - - path->last_move_point.x += offx; - path->last_move_point.y += offy; - path->current_point.x += offx; - path->current_point.y += offy; - - cairo_path_foreach_buf_start (buf, path) { - for (i = 0; i < buf->num_points; i++) { - buf->points[i].x += offx; - buf->points[i].y += offy; - } - } cairo_path_foreach_buf_end (buf, path); - - path->extents.p1.x += offx; - path->extents.p1.y += offy; - path->extents.p2.x += offx; - path->extents.p2.y += offy; -} - -/** - * _cairo_path_fixed_transform: - * @path: a #cairo_path_fixed_t to be transformed - * @matrix: a #cairo_matrix_t - * - * Transform the fixed-point path according to the given matrix. - * There is a fast path for the case where @matrix has no rotation - * or shear. - **/ -void -_cairo_path_fixed_transform (cairo_path_fixed_t *path, - const cairo_matrix_t *matrix) -{ - cairo_path_buf_t *buf; - unsigned int i; - double dx, dy; - - /* XXX current_point, last_move_to */ - - if (matrix->yx == 0.0 && matrix->xy == 0.0) { - /* Fast path for the common case of scale+transform */ - if (matrix->xx == 1. && matrix->yy == 1.) { - _cairo_path_fixed_translate (path, - _cairo_fixed_from_double (matrix->x0), - _cairo_fixed_from_double (matrix->y0)); - } else { - _cairo_path_fixed_offset_and_scale (path, - _cairo_fixed_from_double (matrix->x0), - _cairo_fixed_from_double (matrix->y0), - _cairo_fixed_from_double (matrix->xx), - _cairo_fixed_from_double (matrix->yy)); - } - return; - } - - path->extents.p1.x = path->extents.p1.y = INT_MAX; - path->extents.p2.x = path->extents.p2.y = INT_MIN; - path->maybe_fill_region = FALSE; - cairo_path_foreach_buf_start (buf, path) { - for (i = 0; i < buf->num_points; i++) { - dx = _cairo_fixed_to_double (buf->points[i].x); - dy = _cairo_fixed_to_double (buf->points[i].y); - - cairo_matrix_transform_point (matrix, &dx, &dy); - - buf->points[i].x = _cairo_fixed_from_double (dx); - buf->points[i].y = _cairo_fixed_from_double (dy); - - /* XXX need to eliminate surplus move-to's? */ - _cairo_path_fixed_extents_add (path, &buf->points[i]); - } - } cairo_path_foreach_buf_end (buf, path); -} - -cairo_bool_t -_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, - const cairo_path_fixed_t *other) -{ - const cairo_path_buf_t *path_buf, *other_buf; - - if (path->current_point.x != other->current_point.x || - path->current_point.y != other->current_point.y || - path->has_current_point != other->has_current_point || - path->has_curve_to != other->has_curve_to || - path->is_rectilinear != other->is_rectilinear || - path->maybe_fill_region != other->maybe_fill_region || - path->last_move_point.x != other->last_move_point.x || - path->last_move_point.y != other->last_move_point.y) - { - return FALSE; - } - - other_buf = cairo_path_head (other); - cairo_path_foreach_buf_start (path_buf, path) { - if (path_buf->num_ops != other_buf->num_ops || - path_buf->num_points != other_buf->num_points || - memcmp (path_buf->op, other_buf->op, - sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 || - memcmp (path_buf->points, other_buf->points, - sizeof (cairo_point_t) * path_buf->num_points) != 0) - { - return FALSE; - } - other_buf = cairo_path_buf_next (other_buf); - } cairo_path_foreach_buf_end (path_buf, path); - - return TRUE; -} - -/* Closure for path flattening */ -typedef struct cairo_path_flattener { - double tolerance; - cairo_point_t current_point; - cairo_path_fixed_move_to_func_t *move_to; - cairo_path_fixed_line_to_func_t *line_to; - cairo_path_fixed_close_path_func_t *close_path; - void *closure; -} cpf_t; - -static cairo_status_t -_cpf_move_to (void *closure, - const cairo_point_t *point) -{ - cpf_t *cpf = closure; - - cpf->current_point = *point; - - return cpf->move_to (cpf->closure, point); -} - -static cairo_status_t -_cpf_line_to (void *closure, - const cairo_point_t *point) -{ - cpf_t *cpf = closure; - - cpf->current_point = *point; - - return cpf->line_to (cpf->closure, point); -} - -static cairo_status_t -_cpf_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - cpf_t *cpf = closure; - cairo_spline_t spline; - - cairo_point_t *p0 = &cpf->current_point; - - if (! _cairo_spline_init (&spline, - cpf->line_to, - cpf->closure, - p0, p1, p2, p3)) - { - return _cpf_line_to (closure, p3); - } - - cpf->current_point = *p3; - - return _cairo_spline_decompose (&spline, cpf->tolerance); -} - -static cairo_status_t -_cpf_close_path (void *closure) -{ - cpf_t *cpf = closure; - - return cpf->close_path (cpf->closure); -} - -cairo_status_t -_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, - cairo_direction_t dir, - cairo_path_fixed_move_to_func_t *move_to, - cairo_path_fixed_line_to_func_t *line_to, - cairo_path_fixed_close_path_func_t *close_path, - void *closure, - double tolerance) -{ - cpf_t flattener; - - if (! path->has_curve_to) { - return _cairo_path_fixed_interpret (path, dir, - move_to, - line_to, - NULL, - close_path, - closure); - } - - flattener.tolerance = tolerance; - flattener.move_to = move_to; - flattener.line_to = line_to; - flattener.close_path = close_path; - flattener.closure = closure; - return _cairo_path_fixed_interpret (path, dir, - _cpf_move_to, - _cpf_line_to, - _cpf_curve_to, - _cpf_close_path, - &flattener); -} - -static inline void -_canonical_box (cairo_box_t *box, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - if (p1->x <= p2->x) { - box->p1.x = p1->x; - box->p2.x = p2->x; - } else { - box->p1.x = p2->x; - box->p2.x = p1->x; - } - - if (p1->y <= p2->y) { - box->p1.y = p1->y; - box->p2.y = p2->y; - } else { - box->p1.y = p2->y; - box->p2.y = p1->y; - } -} - -/* - * Check whether the given path contains a single rectangle. - */ -cairo_bool_t -_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, - cairo_box_t *box) -{ - const cairo_path_buf_t *buf = cairo_path_head (path); - - if (! path->is_rectilinear) - return FALSE; - - /* Do we have the right number of ops? */ - if (buf->num_ops < 4 || buf->num_ops > 6) - return FALSE; - - /* Check whether the ops are those that would be used for a rectangle */ - if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO || - buf->op[1] != CAIRO_PATH_OP_LINE_TO || - buf->op[2] != CAIRO_PATH_OP_LINE_TO || - buf->op[3] != CAIRO_PATH_OP_LINE_TO) - { - return FALSE; - } - - /* we accept an implicit close for filled paths */ - if (buf->num_ops > 4) { - /* Now, there are choices. The rectangle might end with a LINE_TO - * (to the original point), but this isn't required. If it - * doesn't, then it must end with a CLOSE_PATH. */ - if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { - if (buf->points[4].x != buf->points[0].x || - buf->points[4].y != buf->points[0].y) - return FALSE; - } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { - return FALSE; - } - - if (buf->num_ops == 6) { - /* A trailing CLOSE_PATH or MOVE_TO is ok */ - if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && - buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) - return FALSE; - } - } - - /* Ok, we may have a box, if the points line up */ - if (buf->points[0].y == buf->points[1].y && - buf->points[1].x == buf->points[2].x && - buf->points[2].y == buf->points[3].y && - buf->points[3].x == buf->points[0].x) - { - _canonical_box (box, &buf->points[0], &buf->points[2]); - return TRUE; - } - - if (buf->points[0].x == buf->points[1].x && - buf->points[1].y == buf->points[2].y && - buf->points[2].x == buf->points[3].x && - buf->points[3].y == buf->points[0].y) - { - _canonical_box (box, &buf->points[0], &buf->points[2]); - return TRUE; - } - - return FALSE; -} - -/* - * Check whether the given path contains a single rectangle - * that is logically equivalent to: - * - * cairo_move_to (cr, x, y); - * cairo_rel_line_to (cr, width, 0); - * cairo_rel_line_to (cr, 0, height); - * cairo_rel_line_to (cr, -width, 0); - * cairo_close_path (cr); - * - */ -cairo_bool_t -_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, - cairo_box_t *box) -{ - const cairo_path_buf_t *buf; - - if (! _cairo_path_fixed_is_box (path, box)) - return FALSE; - - buf = cairo_path_head (path); - if (buf->points[0].y == buf->points[1].y) - return TRUE; - - return FALSE; -} - -void -_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, - const cairo_path_fixed_t *path) -{ - iter->first = iter->buf = cairo_path_head (path); - iter->n_op = 0; - iter->n_point = 0; -} - -static cairo_bool_t -_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) -{ - if (++iter->n_op >= iter->buf->num_ops) { - iter->buf = cairo_path_buf_next (iter->buf); - if (iter->buf == iter->first) { - iter->buf = NULL; - return FALSE; - } - - iter->n_op = 0; - iter->n_point = 0; - } - - return TRUE; -} - -cairo_bool_t -_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, - cairo_box_t *box) -{ - cairo_point_t points[5]; - cairo_path_fixed_iter_t iter; - - if (_iter->buf == NULL) - return FALSE; - - iter = *_iter; - - if (iter.n_op == iter.buf->num_ops && - ! _cairo_path_fixed_iter_next_op (&iter)) - { - return FALSE; - } - - /* Check whether the ops are those that would be used for a rectangle */ - if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) - return FALSE; - points[0] = iter.buf->points[iter.n_point++]; - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; - - if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) - return FALSE; - points[1] = iter.buf->points[iter.n_point++]; - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; - - if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) - return FALSE; - points[2] = iter.buf->points[iter.n_point++]; - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; - - if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) - return FALSE; - points[3] = iter.buf->points[iter.n_point++]; - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; - - /* Now, there are choices. The rectangle might end with a LINE_TO - * (to the original point), but this isn't required. If it - * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */ - if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) - { - points[4] = iter.buf->points[iter.n_point++]; - if (points[4].x != points[0].x || points[4].y != points[0].y) - return FALSE; - } - else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH || - iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO)) - { - return FALSE; - } - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; - - /* Ok, we may have a box, if the points line up */ - if (points[0].y == points[1].y && - points[1].x == points[2].x && - points[2].y == points[3].y && - points[3].x == points[0].x) - { - box->p1 = points[0]; - box->p2 = points[2]; - *_iter = iter; - return TRUE; - } - - if (points[0].x == points[1].x && - points[1].y == points[2].y && - points[2].x == points[3].x && - points[3].y == points[0].y) - { - box->p1 = points[1]; - box->p2 = points[3]; - *_iter = iter; - return TRUE; - } - - return FALSE; -} - -cairo_bool_t -_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) -{ - if (iter->buf == NULL) - return TRUE; - - if (iter->n_op == iter->buf->num_ops) - return TRUE; - - if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO && - iter->buf->num_ops == iter->n_op + 1) - { - return TRUE; - } - - return FALSE; -} diff --git a/libs/cairo/cairo/src/cairo-path-in-fill.c b/libs/cairo/cairo/src/cairo-path-in-fill.c deleted file mode 100644 index c303d8596..000000000 --- a/libs/cairo/cairo/src/cairo-path-in-fill.c +++ /dev/null @@ -1,260 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-path-fixed-private.h" - -typedef struct cairo_in_fill { - double tolerance; - cairo_bool_t on_edge; - int winding; - - cairo_fixed_t x, y; - - cairo_bool_t has_current_point; - cairo_point_t current_point; - cairo_point_t first_point; -} cairo_in_fill_t; - -static void -_cairo_in_fill_init (cairo_in_fill_t *in_fill, - double tolerance, - double x, - double y) -{ - in_fill->on_edge = FALSE; - in_fill->winding = 0; - in_fill->tolerance = tolerance; - - in_fill->x = _cairo_fixed_from_double (x); - in_fill->y = _cairo_fixed_from_double (y); - - in_fill->has_current_point = FALSE; - in_fill->current_point.x = 0; - in_fill->current_point.y = 0; -} - -static void -_cairo_in_fill_fini (cairo_in_fill_t *in_fill) -{ -} - -static int -edge_compare_for_y_against_x (const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_fixed_t y, - cairo_fixed_t x) -{ - cairo_fixed_t adx, ady; - cairo_fixed_t dx, dy; - cairo_int64_t L, R; - - adx = p2->x - p1->x; - dx = x - p1->x; - - if (adx == 0) - return -dx; - if ((adx ^ dx) < 0) - return adx; - - dy = y - p1->y; - ady = p2->y - p1->y; - - L = _cairo_int32x32_64_mul (dy, adx); - R = _cairo_int32x32_64_mul (dx, ady); - - return _cairo_int64_cmp (L, R); -} - -static void -_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - int dir; - - if (in_fill->on_edge) - return; - - /* count the number of edge crossing to -∞ */ - - dir = 1; - if (p2->y < p1->y) { - const cairo_point_t *tmp; - - tmp = p1; - p1 = p2; - p2 = tmp; - - dir = -1; - } - - /* First check whether the query is on an edge */ - if ((p1->x == in_fill->x && p1->y == in_fill->y) || - (p2->x == in_fill->x && p2->y == in_fill->y) || - (! (p2->y < in_fill->y || p1->y > in_fill->y || - (p1->x > in_fill->x && p2->x > in_fill->x) || - (p1->x < in_fill->x && p2->x < in_fill->x)) && - edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0)) - { - in_fill->on_edge = TRUE; - return; - } - - /* edge is entirely above or below, note the shortening rule */ - if (p2->y <= in_fill->y || p1->y > in_fill->y) - return; - - /* edge lies wholly to the right */ - if (p1->x >= in_fill->x && p2->x >= in_fill->x) - return; - - if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || - edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0) - { - in_fill->winding += dir; - } -} - -static cairo_status_t -_cairo_in_fill_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_in_fill_t *in_fill = closure; - - /* implicit close path */ - if (in_fill->has_current_point) { - _cairo_in_fill_add_edge (in_fill, - &in_fill->current_point, - &in_fill->first_point); - } - - in_fill->first_point = *point; - in_fill->current_point = *point; - in_fill->has_current_point = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_in_fill_line_to (void *closure, - const cairo_point_t *point) -{ - cairo_in_fill_t *in_fill = closure; - - if (in_fill->has_current_point) - _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point); - - in_fill->current_point = *point; - in_fill->has_current_point = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_in_fill_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_in_fill_t *in_fill = closure; - cairo_spline_t spline; - cairo_fixed_t top, bot, left; - - /* first reject based on bbox */ - bot = top = in_fill->current_point.y; - if (b->y < top) top = b->y; - if (b->y > bot) bot = b->y; - if (c->y < top) top = c->y; - if (c->y > bot) bot = c->y; - if (d->y < top) top = d->y; - if (d->y > bot) bot = d->y; - if (bot < in_fill->y || top > in_fill->y) { - in_fill->current_point = *d; - return CAIRO_STATUS_SUCCESS; - } - - left = in_fill->current_point.x; - if (b->x < left) left = b->x; - if (c->x < left) left = c->x; - if (d->x < left) left = d->x; - if (left > in_fill->x) { - in_fill->current_point = *d; - return CAIRO_STATUS_SUCCESS; - } - - /* XXX Investigate direct inspection of the inflections? */ - if (! _cairo_spline_init (&spline, - _cairo_in_fill_line_to, - in_fill, - &in_fill->current_point, b, c, d)) - { - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_spline_decompose (&spline, in_fill->tolerance); -} - -static cairo_status_t -_cairo_in_fill_close_path (void *closure) -{ - cairo_in_fill_t *in_fill = closure; - - if (in_fill->has_current_point) { - _cairo_in_fill_add_edge (in_fill, - &in_fill->current_point, - &in_fill->first_point); - - in_fill->has_current_point = FALSE; - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - double x, - double y) -{ - cairo_in_fill_t in_fill; - cairo_status_t status; - cairo_bool_t is_inside; - - if (path->is_empty_fill) - return FALSE; - - _cairo_in_fill_init (&in_fill, tolerance, x, y); - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_in_fill_move_to, - _cairo_in_fill_line_to, - _cairo_in_fill_curve_to, - _cairo_in_fill_close_path, - &in_fill); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_in_fill_close_path (&in_fill); - - if (in_fill.on_edge) { - is_inside = TRUE; - } else switch (fill_rule) { - case CAIRO_FILL_RULE_EVEN_ODD: - is_inside = in_fill.winding & 1; - break; - case CAIRO_FILL_RULE_WINDING: - is_inside = in_fill.winding != 0; - break; - default: - ASSERT_NOT_REACHED; - is_inside = FALSE; - break; - } - - _cairo_in_fill_fini (&in_fill); - - return is_inside; -} diff --git a/libs/cairo/cairo/src/cairo-path-private.h b/libs/cairo/cairo/src/cairo-path-private.h deleted file mode 100644 index c28612a32..000000000 --- a/libs/cairo/cairo/src/cairo-path-private.h +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PATH_PRIVATE_H -#define CAIRO_PATH_PRIVATE_H - -#include "cairoint.h" - -cairo_private cairo_path_t * -_cairo_path_create (cairo_path_fixed_t *path, - cairo_gstate_t *gstate); - -cairo_private cairo_path_t * -_cairo_path_create_flat (cairo_path_fixed_t *path, - cairo_gstate_t *gstate); - -cairo_private cairo_path_t * -_cairo_path_create_in_error (cairo_status_t status); - -cairo_private cairo_status_t -_cairo_path_append_to_context (const cairo_path_t *path, - cairo_t *cr); - -#endif /* CAIRO_PATH_DATA_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-path-stroke.c b/libs/cairo/cairo/src/cairo-path-stroke.c deleted file mode 100644 index 6d1d6f205..000000000 --- a/libs/cairo/cairo/src/cairo-path-stroke.c +++ /dev/null @@ -1,2109 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for hypot() */ -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-error-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-slope-private.h" - -typedef struct _cairo_stroker_dash { - cairo_bool_t dashed; - unsigned int dash_index; - cairo_bool_t dash_on; - cairo_bool_t dash_starts_on; - double dash_remain; - - double dash_offset; - const double *dashes; - unsigned int num_dashes; -} cairo_stroker_dash_t; - -typedef struct cairo_stroker { - cairo_stroke_style_t style; - - const cairo_matrix_t *ctm; - const cairo_matrix_t *ctm_inverse; - double tolerance; - double ctm_determinant; - cairo_bool_t ctm_det_positive; - - void *closure; - cairo_status_t (*add_external_edge) (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2); - cairo_status_t (*add_triangle) (void *closure, - const cairo_point_t triangle[3]); - cairo_status_t (*add_triangle_fan) (void *closure, - const cairo_point_t *midpt, - const cairo_point_t *points, - int npoints); - cairo_status_t (*add_convex_quad) (void *closure, - const cairo_point_t quad[4]); - - cairo_pen_t pen; - - cairo_point_t current_point; - cairo_point_t first_point; - - cairo_bool_t has_initial_sub_path; - - cairo_bool_t has_current_face; - cairo_stroke_face_t current_face; - - cairo_bool_t has_first_face; - cairo_stroke_face_t first_face; - - cairo_stroker_dash_t dash; - - cairo_bool_t has_bounds; - cairo_box_t bounds; -} cairo_stroker_t; - -static void -_cairo_stroker_dash_start (cairo_stroker_dash_t *dash) -{ - double offset; - cairo_bool_t on = TRUE; - unsigned int i = 0; - - if (! dash->dashed) - return; - - offset = dash->dash_offset; - - /* We stop searching for a starting point as soon as the - offset reaches zero. Otherwise when an initial dash - segment shrinks to zero it will be skipped over. */ - while (offset > 0.0 && offset >= dash->dashes[i]) { - offset -= dash->dashes[i]; - on = !on; - if (++i == dash->num_dashes) - i = 0; - } - - dash->dash_index = i; - dash->dash_on = dash->dash_starts_on = on; - dash->dash_remain = dash->dashes[i] - offset; -} - -static void -_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) -{ - dash->dash_remain -= step; - if (dash->dash_remain <= 0.) { - if (++dash->dash_index == dash->num_dashes) - dash->dash_index = 0; - - dash->dash_on = ! dash->dash_on; - dash->dash_remain = dash->dashes[dash->dash_index]; - } -} - -static void -_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, - const cairo_stroke_style_t *style) -{ - dash->dashed = style->dash != NULL; - if (! dash->dashed) - return; - - dash->dashes = style->dash; - dash->num_dashes = style->num_dashes; - dash->dash_offset = style->dash_offset; - - _cairo_stroker_dash_start (dash); -} - -static cairo_status_t -_cairo_stroker_init (cairo_stroker_t *stroker, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance) -{ - cairo_status_t status; - - stroker->style = *stroke_style; - stroker->ctm = ctm; - stroker->ctm_inverse = ctm_inverse; - stroker->tolerance = tolerance; - - stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); - stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; - - status = _cairo_pen_init (&stroker->pen, - stroke_style->line_width / 2.0, - tolerance, ctm); - if (unlikely (status)) - return status; - - stroker->has_bounds = FALSE; - - stroker->has_current_face = FALSE; - stroker->has_first_face = FALSE; - stroker->has_initial_sub_path = FALSE; - - _cairo_stroker_dash_init (&stroker->dash, stroke_style); - - stroker->add_external_edge = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_stroker_limit (cairo_stroker_t *stroker, - const cairo_box_t *boxes, - int num_boxes) -{ - double dx, dy; - cairo_fixed_t fdx, fdy; - - stroker->has_bounds = TRUE; - _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); - - /* Extend the bounds in each direction to account for the maximum area - * we might generate trapezoids, to capture line segments that are outside - * of the bounds but which might generate rendering that's within bounds. - */ - - _cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm, - &dx, &dy); - - fdx = _cairo_fixed_from_double (dx); - fdy = _cairo_fixed_from_double (dy); - - stroker->bounds.p1.x -= fdx; - stroker->bounds.p2.x += fdx; - - stroker->bounds.p1.y -= fdy; - stroker->bounds.p2.y += fdy; -} - -static void -_cairo_stroker_fini (cairo_stroker_t *stroker) -{ - _cairo_pen_fini (&stroker->pen); -} - -static void -_translate_point (cairo_point_t *point, const cairo_point_t *offset) -{ - point->x += offset->x; - point->y += offset->y; -} - -static int -_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, - const cairo_stroke_face_t *out) -{ - cairo_slope_t in_slope, out_slope; - - _cairo_slope_init (&in_slope, &in->point, &in->cw); - _cairo_slope_init (&out_slope, &out->point, &out->cw); - - return _cairo_slope_compare (&in_slope, &out_slope) < 0; -} - -/** - * _cairo_slope_compare_sgn - * - * Return -1, 0 or 1 depending on the relative slopes of - * two lines. - */ -static int -_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) -{ - double c = (dx1 * dy2 - dx2 * dy1); - - if (c > 0) return 1; - if (c < 0) return -1; - return 0; -} - -static inline int -_range_step (int i, int step, int max) -{ - i += step; - if (i < 0) - i = max - 1; - if (i >= max) - i = 0; - return i; -} - -/* - * Construct a fan around the midpoint using the vertices from pen between - * inpt and outpt. - */ -static cairo_status_t -_tessellate_fan (cairo_stroker_t *stroker, - const cairo_slope_t *in_vector, - const cairo_slope_t *out_vector, - const cairo_point_t *midpt, - const cairo_point_t *inpt, - const cairo_point_t *outpt, - cairo_bool_t clockwise) -{ - cairo_point_t stack_points[64], *points = stack_points; - int start, stop, step, i, npoints; - cairo_status_t status; - - if (clockwise) { - step = -1; - - start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, - in_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, - in_vector) < 0) - start = _range_step (start, -1, stroker->pen.num_vertices); - - stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, - out_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, - out_vector) > 0) - { - stop = _range_step (stop, 1, stroker->pen.num_vertices); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, - in_vector) < 0) - { - goto BEVEL; - } - } - - npoints = start - stop; - } else { - step = 1; - - start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, - in_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, - in_vector) < 0) - start = _range_step (start, 1, stroker->pen.num_vertices); - - stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, - out_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, - out_vector) > 0) - { - stop = _range_step (stop, -1, stroker->pen.num_vertices); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, - in_vector) < 0) - { - goto BEVEL; - } - } - - npoints = stop - start; - } - stop = _range_step (stop, step, stroker->pen.num_vertices); - - if (npoints < 0) - npoints += stroker->pen.num_vertices; - npoints += 3; - - if (npoints <= 1) - goto BEVEL; - - if (npoints > ARRAY_LENGTH (stack_points)) { - points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); - if (unlikely (points == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - - /* Construct the fan. */ - npoints = 0; - points[npoints++] = *inpt; - for (i = start; - i != stop; - i = _range_step (i, step, stroker->pen.num_vertices)) - { - points[npoints] = *midpt; - _translate_point (&points[npoints], &stroker->pen.vertices[i].point); - npoints++; - } - points[npoints++] = *outpt; - - if (stroker->add_external_edge != NULL) { - for (i = 0; i < npoints - 1; i++) { - if (clockwise) { - status = stroker->add_external_edge (stroker->closure, - &points[i], &points[i+1]); - } else { - status = stroker->add_external_edge (stroker->closure, - &points[i+1], &points[i]); - } - if (unlikely (status)) - break; - } - } else { - status = stroker->add_triangle_fan (stroker->closure, - midpt, points, npoints); - } - - if (points != stack_points) - free (points); - - return status; - -BEVEL: - /* Ensure a leak free connection... */ - if (stroker->add_external_edge != NULL) { - if (clockwise) - return stroker->add_external_edge (stroker->closure, inpt, outpt); - else - return stroker->add_external_edge (stroker->closure, outpt, inpt); - } else { - stack_points[0] = *midpt; - stack_points[1] = *inpt; - stack_points[2] = *outpt; - return stroker->add_triangle (stroker->closure, stack_points); - } -} - -static cairo_status_t -_cairo_stroker_join (cairo_stroker_t *stroker, - const cairo_stroke_face_t *in, - const cairo_stroke_face_t *out) -{ - int clockwise = _cairo_stroker_join_is_clockwise (out, in); - const cairo_point_t *inpt, *outpt; - cairo_point_t points[4]; - cairo_status_t status; - - if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && - in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) - { - return CAIRO_STATUS_SUCCESS; - } - - if (clockwise) { - if (stroker->add_external_edge != NULL) { - status = stroker->add_external_edge (stroker->closure, - &out->cw, &in->point); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &in->point, &in->cw); - if (unlikely (status)) - return status; - } - - inpt = &in->ccw; - outpt = &out->ccw; - } else { - if (stroker->add_external_edge != NULL) { - status = stroker->add_external_edge (stroker->closure, - &in->ccw, &in->point); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &in->point, &out->ccw); - if (unlikely (status)) - return status; - } - - inpt = &in->cw; - outpt = &out->cw; - } - - switch (stroker->style.line_join) { - case CAIRO_LINE_JOIN_ROUND: - /* construct a fan around the common midpoint */ - return _tessellate_fan (stroker, - &in->dev_vector, - &out->dev_vector, - &in->point, inpt, outpt, - clockwise); - - case CAIRO_LINE_JOIN_MITER: - default: { - /* dot product of incoming slope vector with outgoing slope vector */ - double in_dot_out = -in->usr_vector.x * out->usr_vector.x + - -in->usr_vector.y * out->usr_vector.y; - double ml = stroker->style.miter_limit; - - /* Check the miter limit -- lines meeting at an acute angle - * can generate long miters, the limit converts them to bevel - * - * Consider the miter join formed when two line segments - * meet at an angle psi: - * - * /.\ - * /. .\ - * /./ \.\ - * /./psi\.\ - * - * We can zoom in on the right half of that to see: - * - * |\ - * | \ psi/2 - * | \ - * | \ - * | \ - * | \ - * miter \ - * length \ - * | \ - * | .\ - * | . \ - * |. line \ - * \ width \ - * \ \ - * - * - * The right triangle in that figure, (the line-width side is - * shown faintly with three '.' characters), gives us the - * following expression relating miter length, angle and line - * width: - * - * 1 /sin (psi/2) = miter_length / line_width - * - * The right-hand side of this relationship is the same ratio - * in which the miter limit (ml) is expressed. We want to know - * when the miter length is within the miter limit. That is - * when the following condition holds: - * - * 1/sin(psi/2) <= ml - * 1 <= ml sin(psi/2) - * 1 <= ml² sin²(psi/2) - * 2 <= ml² 2 sin²(psi/2) - * 2·sin²(psi/2) = 1-cos(psi) - * 2 <= ml² (1-cos(psi)) - * - * in · out = |in| |out| cos (psi) - * - * in and out are both unit vectors, so: - * - * in · out = cos (psi) - * - * 2 <= ml² (1 - in · out) - * - */ - if (2 <= ml * ml * (1 - in_dot_out)) { - double x1, y1, x2, y2; - double mx, my; - double dx1, dx2, dy1, dy2; - double ix, iy; - double fdx1, fdy1, fdx2, fdy2; - double mdx, mdy; - - /* - * we've got the points already transformed to device - * space, but need to do some computation with them and - * also need to transform the slope from user space to - * device space - */ - /* outer point of incoming line face */ - x1 = _cairo_fixed_to_double (inpt->x); - y1 = _cairo_fixed_to_double (inpt->y); - dx1 = in->usr_vector.x; - dy1 = in->usr_vector.y; - cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); - - /* outer point of outgoing line face */ - x2 = _cairo_fixed_to_double (outpt->x); - y2 = _cairo_fixed_to_double (outpt->y); - dx2 = out->usr_vector.x; - dy2 = out->usr_vector.y; - cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); - - /* - * Compute the location of the outer corner of the miter. - * That's pretty easy -- just the intersection of the two - * outer edges. We've got slopes and points on each - * of those edges. Compute my directly, then compute - * mx by using the edge with the larger dy; that avoids - * dividing by values close to zero. - */ - my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / - (dx1 * dy2 - dx2 * dy1)); - if (fabs (dy1) >= fabs (dy2)) - mx = (my - y1) * dx1 / dy1 + x1; - else - mx = (my - y2) * dx2 / dy2 + x2; - - /* - * When the two outer edges are nearly parallel, slight - * perturbations in the position of the outer points of the lines - * caused by representing them in fixed point form can cause the - * intersection point of the miter to move a large amount. If - * that moves the miter intersection from between the two faces, - * then draw a bevel instead. - */ - - ix = _cairo_fixed_to_double (in->point.x); - iy = _cairo_fixed_to_double (in->point.y); - - /* slope of one face */ - fdx1 = x1 - ix; fdy1 = y1 - iy; - - /* slope of the other face */ - fdx2 = x2 - ix; fdy2 = y2 - iy; - - /* slope from the intersection to the miter point */ - mdx = mx - ix; mdy = my - iy; - - /* - * Make sure the miter point line lies between the two - * faces by comparing the slopes - */ - if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != - _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) - { - if (stroker->add_external_edge != NULL) { - points[0].x = _cairo_fixed_from_double (mx); - points[0].y = _cairo_fixed_from_double (my); - - if (clockwise) { - status = stroker->add_external_edge (stroker->closure, - inpt, &points[0]); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &points[0], outpt); - if (unlikely (status)) - return status; - } else { - status = stroker->add_external_edge (stroker->closure, - outpt, &points[0]); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &points[0], inpt); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; - } else { - points[0] = in->point; - points[1] = *inpt; - points[2].x = _cairo_fixed_from_double (mx); - points[2].y = _cairo_fixed_from_double (my); - points[3] = *outpt; - - return stroker->add_convex_quad (stroker->closure, points); - } - } - } - } - - /* fall through ... */ - - case CAIRO_LINE_JOIN_BEVEL: - if (stroker->add_external_edge != NULL) { - if (clockwise) { - return stroker->add_external_edge (stroker->closure, - inpt, outpt); - } else { - return stroker->add_external_edge (stroker->closure, - outpt, inpt); - } - } else { - points[0] = in->point; - points[1] = *inpt; - points[2] = *outpt; - - return stroker->add_triangle (stroker->closure, points); - } - } -} - -static cairo_status_t -_cairo_stroker_add_cap (cairo_stroker_t *stroker, - const cairo_stroke_face_t *f) -{ - switch (stroker->style.line_cap) { - case CAIRO_LINE_CAP_ROUND: { - cairo_slope_t slope; - - slope.dx = -f->dev_vector.dx; - slope.dy = -f->dev_vector.dy; - - return _tessellate_fan (stroker, - &f->dev_vector, - &slope, - &f->point, &f->cw, &f->ccw, - FALSE); - - } - - case CAIRO_LINE_CAP_SQUARE: { - double dx, dy; - cairo_slope_t fvector; - cairo_point_t quad[4]; - - dx = f->usr_vector.x; - dy = f->usr_vector.y; - dx *= stroker->style.line_width / 2.0; - dy *= stroker->style.line_width / 2.0; - cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); - fvector.dx = _cairo_fixed_from_double (dx); - fvector.dy = _cairo_fixed_from_double (dy); - - quad[0] = f->ccw; - quad[1].x = f->ccw.x + fvector.dx; - quad[1].y = f->ccw.y + fvector.dy; - quad[2].x = f->cw.x + fvector.dx; - quad[2].y = f->cw.y + fvector.dy; - quad[3] = f->cw; - - if (stroker->add_external_edge != NULL) { - cairo_status_t status; - - status = stroker->add_external_edge (stroker->closure, - &quad[0], &quad[1]); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &quad[1], &quad[2]); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &quad[2], &quad[3]); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; - } else { - return stroker->add_convex_quad (stroker->closure, quad); - } - } - - case CAIRO_LINE_CAP_BUTT: - default: - if (stroker->add_external_edge != NULL) { - return stroker->add_external_edge (stroker->closure, - &f->ccw, &f->cw); - } else { - return CAIRO_STATUS_SUCCESS; - } - } -} - -static cairo_status_t -_cairo_stroker_add_leading_cap (cairo_stroker_t *stroker, - const cairo_stroke_face_t *face) -{ - cairo_stroke_face_t reversed; - cairo_point_t t; - - reversed = *face; - - /* The initial cap needs an outward facing vector. Reverse everything */ - reversed.usr_vector.x = -reversed.usr_vector.x; - reversed.usr_vector.y = -reversed.usr_vector.y; - reversed.dev_vector.dx = -reversed.dev_vector.dx; - reversed.dev_vector.dy = -reversed.dev_vector.dy; - t = reversed.cw; - reversed.cw = reversed.ccw; - reversed.ccw = t; - - return _cairo_stroker_add_cap (stroker, &reversed); -} - -static cairo_status_t -_cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, - const cairo_stroke_face_t *face) -{ - return _cairo_stroker_add_cap (stroker, face); -} - -static inline cairo_bool_t -_compute_normalized_device_slope (double *dx, double *dy, - const cairo_matrix_t *ctm_inverse, - double *mag_out) -{ - double dx0 = *dx, dy0 = *dy; - double mag; - - cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0); - - if (dx0 == 0.0 && dy0 == 0.0) { - if (mag_out) - *mag_out = 0.0; - return FALSE; - } - - if (dx0 == 0.0) { - *dx = 0.0; - if (dy0 > 0.0) { - mag = dy0; - *dy = 1.0; - } else { - mag = -dy0; - *dy = -1.0; - } - } else if (dy0 == 0.0) { - *dy = 0.0; - if (dx0 > 0.0) { - mag = dx0; - *dx = 1.0; - } else { - mag = -dx0; - *dx = -1.0; - } - } else { - mag = hypot (dx0, dy0); - *dx = dx0 / mag; - *dy = dy0 / mag; - } - - if (mag_out) - *mag_out = mag; - - return TRUE; -} - -static void -_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, - double slope_dx, double slope_dy, - cairo_stroker_t *stroker, cairo_stroke_face_t *face) -{ - double face_dx, face_dy; - cairo_point_t offset_ccw, offset_cw; - - /* - * rotate to get a line_width/2 vector along the face, note that - * the vector must be rotated the right direction in device space, - * but by 90° in user space. So, the rotation depends on - * whether the ctm reflects or not, and that can be determined - * by looking at the determinant of the matrix. - */ - if (stroker->ctm_det_positive) - { - face_dx = - slope_dy * (stroker->style.line_width / 2.0); - face_dy = slope_dx * (stroker->style.line_width / 2.0); - } - else - { - face_dx = slope_dy * (stroker->style.line_width / 2.0); - face_dy = - slope_dx * (stroker->style.line_width / 2.0); - } - - /* back to device space */ - cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); - - offset_ccw.x = _cairo_fixed_from_double (face_dx); - offset_ccw.y = _cairo_fixed_from_double (face_dy); - offset_cw.x = -offset_ccw.x; - offset_cw.y = -offset_ccw.y; - - face->ccw = *point; - _translate_point (&face->ccw, &offset_ccw); - - face->point = *point; - - face->cw = *point; - _translate_point (&face->cw, &offset_cw); - - face->usr_vector.x = slope_dx; - face->usr_vector.y = slope_dy; - - face->dev_vector = *dev_slope; -} - -static cairo_status_t -_cairo_stroker_add_caps (cairo_stroker_t *stroker) -{ - cairo_status_t status; - - /* check for a degenerative sub_path */ - if (stroker->has_initial_sub_path - && ! stroker->has_first_face - && ! stroker->has_current_face - && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND) - { - /* pick an arbitrary slope to use */ - double dx = 1.0, dy = 0.0; - cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; - cairo_stroke_face_t face; - - _compute_normalized_device_slope (&dx, &dy, - stroker->ctm_inverse, NULL); - - /* arbitrarily choose first_point - * first_point and current_point should be the same */ - _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); - - status = _cairo_stroker_add_leading_cap (stroker, &face); - if (unlikely (status)) - return status; - - status = _cairo_stroker_add_trailing_cap (stroker, &face); - if (unlikely (status)) - return status; - } - - if (stroker->has_first_face) { - status = _cairo_stroker_add_leading_cap (stroker, - &stroker->first_face); - if (unlikely (status)) - return status; - } - - if (stroker->has_current_face) { - status = _cairo_stroker_add_trailing_cap (stroker, - &stroker->current_face); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, - const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_slope_t *dev_slope, - double slope_dx, double slope_dy, - cairo_stroke_face_t *start, - cairo_stroke_face_t *end) -{ - _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); - *end = *start; - - if (p1->x == p2->x && p1->y == p2->y) - return CAIRO_STATUS_SUCCESS; - - end->point = *p2; - end->ccw.x += p2->x - p1->x; - end->ccw.y += p2->y - p1->y; - end->cw.x += p2->x - p1->x; - end->cw.y += p2->y - p1->y; - - if (stroker->add_external_edge != NULL) { - cairo_status_t status; - - status = stroker->add_external_edge (stroker->closure, - &end->cw, &start->cw); - if (unlikely (status)) - return status; - - status = stroker->add_external_edge (stroker->closure, - &start->ccw, &end->ccw); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; - } else { - cairo_point_t quad[4]; - - quad[0] = start->cw; - quad[1] = end->cw; - quad[2] = end->ccw; - quad[3] = start->ccw; - - return stroker->add_convex_quad (stroker->closure, quad); - } -} - -static cairo_status_t -_cairo_stroker_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_stroker_t *stroker = closure; - cairo_status_t status; - - /* reset the dash pattern for new sub paths */ - _cairo_stroker_dash_start (&stroker->dash); - - /* Cap the start and end of the previous sub path as needed */ - status = _cairo_stroker_add_caps (stroker); - if (unlikely (status)) - return status; - - stroker->first_point = *point; - stroker->current_point = *point; - - stroker->has_first_face = FALSE; - stroker->has_current_face = FALSE; - stroker->has_initial_sub_path = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_stroker_line_to (void *closure, - const cairo_point_t *point) -{ - cairo_stroker_t *stroker = closure; - cairo_stroke_face_t start, end; - cairo_point_t *p1 = &stroker->current_point; - cairo_slope_t dev_slope; - double slope_dx, slope_dy; - cairo_status_t status; - - stroker->has_initial_sub_path = TRUE; - - if (p1->x == point->x && p1->y == point->y) - return CAIRO_STATUS_SUCCESS; - - _cairo_slope_init (&dev_slope, p1, point); - slope_dx = _cairo_fixed_to_double (point->x - p1->x); - slope_dy = _cairo_fixed_to_double (point->y - p1->y); - _compute_normalized_device_slope (&slope_dx, &slope_dy, - stroker->ctm_inverse, NULL); - - status = _cairo_stroker_add_sub_edge (stroker, - p1, point, - &dev_slope, - slope_dx, slope_dy, - &start, &end); - if (unlikely (status)) - return status; - - if (stroker->has_current_face) { - /* Join with final face from previous segment */ - status = _cairo_stroker_join (stroker, - &stroker->current_face, - &start); - if (unlikely (status)) - return status; - } else if (! stroker->has_first_face) { - /* Save sub path's first face in case needed for closing join */ - stroker->first_face = start; - stroker->has_first_face = TRUE; - } - stroker->current_face = end; - stroker->has_current_face = TRUE; - - stroker->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -/* - * Dashed lines. Cap each dash end, join around turns when on - */ -static cairo_status_t -_cairo_stroker_line_to_dashed (void *closure, - const cairo_point_t *p2) -{ - cairo_stroker_t *stroker = closure; - double mag, remain, step_length = 0; - double slope_dx, slope_dy; - double dx2, dy2; - cairo_stroke_face_t sub_start, sub_end; - cairo_point_t *p1 = &stroker->current_point; - cairo_slope_t dev_slope; - cairo_line_t segment; - cairo_bool_t fully_in_bounds; - cairo_status_t status; - - stroker->has_initial_sub_path = stroker->dash.dash_starts_on; - - if (p1->x == p2->x && p1->y == p2->y) - return CAIRO_STATUS_SUCCESS; - - fully_in_bounds = TRUE; - if (stroker->has_bounds && - (! _cairo_box_contains_point (&stroker->bounds, p1) || - ! _cairo_box_contains_point (&stroker->bounds, p2))) - { - fully_in_bounds = FALSE; - } - - _cairo_slope_init (&dev_slope, p1, p2); - - slope_dx = _cairo_fixed_to_double (p2->x - p1->x); - slope_dy = _cairo_fixed_to_double (p2->y - p1->y); - - if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, - stroker->ctm_inverse, &mag)) - { - return CAIRO_STATUS_SUCCESS; - } - - remain = mag; - segment.p1 = *p1; - while (remain) { - step_length = MIN (stroker->dash.dash_remain, remain); - remain -= step_length; - dx2 = slope_dx * (mag - remain); - dy2 = slope_dy * (mag - remain); - cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); - segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; - segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; - - if (stroker->dash.dash_on && - (fully_in_bounds || - (! stroker->has_first_face && stroker->dash.dash_starts_on) || - _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) - { - status = _cairo_stroker_add_sub_edge (stroker, - &segment.p1, &segment.p2, - &dev_slope, - slope_dx, slope_dy, - &sub_start, &sub_end); - if (unlikely (status)) - return status; - - if (stroker->has_current_face) - { - /* Join with final face from previous segment */ - status = _cairo_stroker_join (stroker, - &stroker->current_face, - &sub_start); - if (unlikely (status)) - return status; - - stroker->has_current_face = FALSE; - } - else if (! stroker->has_first_face && - stroker->dash.dash_starts_on) - { - /* Save sub path's first face in case needed for closing join */ - stroker->first_face = sub_start; - stroker->has_first_face = TRUE; - } - else - { - /* Cap dash start if not connecting to a previous segment */ - status = _cairo_stroker_add_leading_cap (stroker, &sub_start); - if (unlikely (status)) - return status; - } - - if (remain) { - /* Cap dash end if not at end of segment */ - status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); - if (unlikely (status)) - return status; - } else { - stroker->current_face = sub_end; - stroker->has_current_face = TRUE; - } - } else { - if (stroker->has_current_face) { - /* Cap final face from previous segment */ - status = _cairo_stroker_add_trailing_cap (stroker, - &stroker->current_face); - if (unlikely (status)) - return status; - - stroker->has_current_face = FALSE; - } - } - - _cairo_stroker_dash_step (&stroker->dash, step_length); - segment.p1 = segment.p2; - } - - if (stroker->dash.dash_on && ! stroker->has_current_face) { - /* This segment ends on a transition to dash_on, compute a new face - * and add cap for the beginning of the next dash_on step. - * - * Note: this will create a degenerate cap if this is not the last line - * in the path. Whether this behaviour is desirable or not is debatable. - * On one side these degenerate caps can not be reproduced with regular - * path stroking. - * On the other hand, Acroread 7 also produces the degenerate caps. - */ - _compute_face (p2, &dev_slope, - slope_dx, slope_dy, - stroker, - &stroker->current_face); - - status = _cairo_stroker_add_leading_cap (stroker, - &stroker->current_face); - if (unlikely (status)) - return status; - - stroker->has_current_face = TRUE; - } - - stroker->current_point = *p2; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_stroker_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_stroker_t *stroker = closure; - cairo_spline_t spline; - cairo_line_join_t line_join_save; - cairo_stroke_face_t face; - double slope_dx, slope_dy; - cairo_path_fixed_line_to_func_t *line_to; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - line_to = stroker->dash.dashed ? - _cairo_stroker_line_to_dashed : - _cairo_stroker_line_to; - - if (! _cairo_spline_init (&spline, - line_to, stroker, - &stroker->current_point, b, c, d)) - { - return line_to (closure, d); - } - - /* If the line width is so small that the pen is reduced to a - single point, then we have nothing to do. */ - if (stroker->pen.num_vertices <= 1) - return CAIRO_STATUS_SUCCESS; - - /* Compute the initial face */ - if (! stroker->dash.dashed || stroker->dash.dash_on) { - slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); - slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); - if (_compute_normalized_device_slope (&slope_dx, &slope_dy, - stroker->ctm_inverse, NULL)) - { - _compute_face (&stroker->current_point, - &spline.initial_slope, - slope_dx, slope_dy, - stroker, &face); - } - if (stroker->has_current_face) { - status = _cairo_stroker_join (stroker, - &stroker->current_face, &face); - if (unlikely (status)) - return status; - } else if (! stroker->has_first_face) { - stroker->first_face = face; - stroker->has_first_face = TRUE; - } - - stroker->current_face = face; - stroker->has_current_face = TRUE; - } - - /* Temporarily modify the stroker to use round joins to guarantee - * smooth stroked curves. */ - line_join_save = stroker->style.line_join; - stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; - - status = _cairo_spline_decompose (&spline, stroker->tolerance); - if (unlikely (status)) - return status; - - /* And join the final face */ - if (! stroker->dash.dashed || stroker->dash.dash_on) { - slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); - slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); - if (_compute_normalized_device_slope (&slope_dx, &slope_dy, - stroker->ctm_inverse, NULL)) - { - _compute_face (&stroker->current_point, - &spline.final_slope, - slope_dx, slope_dy, - stroker, &face); - } - - status = _cairo_stroker_join (stroker, &stroker->current_face, &face); - if (unlikely (status)) - return status; - - stroker->current_face = face; - } - - stroker->style.line_join = line_join_save; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_stroker_close_path (void *closure) -{ - cairo_stroker_t *stroker = closure; - cairo_status_t status; - - if (stroker->dash.dashed) - status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); - else - status = _cairo_stroker_line_to (stroker, &stroker->first_point); - if (unlikely (status)) - return status; - - if (stroker->has_first_face && stroker->has_current_face) { - /* Join first and final faces of sub path */ - status = _cairo_stroker_join (stroker, - &stroker->current_face, - &stroker->first_face); - if (unlikely (status)) - return status; - } else { - /* Cap the start and end of the sub path as needed */ - status = _cairo_stroker_add_caps (stroker); - if (unlikely (status)) - return status; - } - - stroker->has_initial_sub_path = FALSE; - stroker->has_first_face = FALSE; - stroker->has_current_face = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_status_t (*add_triangle) (void *closure, - const cairo_point_t triangle[3]), - cairo_status_t (*add_triangle_fan) (void *closure, - const cairo_point_t *midpt, - const cairo_point_t *points, - int npoints), - cairo_status_t (*add_convex_quad) (void *closure, - const cairo_point_t quad[4]), - void *closure) -{ - cairo_stroker_t stroker; - cairo_status_t status; - - status = _cairo_stroker_init (&stroker, stroke_style, - ctm, ctm_inverse, tolerance); - if (unlikely (status)) - return status; - - stroker.add_triangle = add_triangle; - stroker.add_triangle_fan = add_triangle_fan; - stroker.add_convex_quad = add_convex_quad; - stroker.closure = closure; - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - stroker.dash.dashed ? - _cairo_stroker_line_to_dashed : - _cairo_stroker_line_to, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); - - if (unlikely (status)) - goto BAIL; - - /* Cap the start and end of the final sub path as needed */ - status = _cairo_stroker_add_caps (&stroker); - -BAIL: - _cairo_stroker_fini (&stroker); - - return status; -} - -cairo_status_t -_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_polygon_t *polygon) -{ - cairo_stroker_t stroker; - cairo_status_t status; - - status = _cairo_stroker_init (&stroker, stroke_style, - ctm, ctm_inverse, tolerance); - if (unlikely (status)) - return status; - - stroker.add_external_edge = _cairo_polygon_add_external_edge, - stroker.closure = polygon; - - if (polygon->num_limits) - _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - stroker.dash.dashed ? - _cairo_stroker_line_to_dashed : - _cairo_stroker_line_to, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); - - if (unlikely (status)) - goto BAIL; - - /* Cap the start and end of the final sub path as needed */ - status = _cairo_stroker_add_caps (&stroker); - -BAIL: - _cairo_stroker_fini (&stroker); - - return status; -} - -cairo_status_t -_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_traps_t *traps) -{ - cairo_status_t status; - cairo_polygon_t polygon; - - /* Before we do anything else, we attempt the rectilinear - * stroker. It's careful to generate trapezoids that align to - * device-pixel boundaries when possible. Many backends can render - * those much faster than non-aligned trapezoids, (by using clip - * regions, etc.) */ - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - _cairo_polygon_init (&polygon); - if (traps->num_limits) - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); - - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, - ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto BAIL; - - status = _cairo_polygon_status (&polygon); - if (unlikely (status)) - goto BAIL; - - status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, - CAIRO_FILL_RULE_WINDING); - -BAIL: - _cairo_polygon_fini (&polygon); - - return status; -} - -typedef struct _segment_t { - cairo_point_t p1, p2; - cairo_bool_t is_horizontal; - cairo_bool_t has_join; -} segment_t; - -typedef struct _cairo_rectilinear_stroker { - const cairo_stroke_style_t *stroke_style; - const cairo_matrix_t *ctm; - - cairo_fixed_t half_line_width; - cairo_bool_t do_traps; - void *container; - cairo_point_t current_point; - cairo_point_t first_point; - cairo_bool_t open_sub_path; - - cairo_stroker_dash_t dash; - - cairo_bool_t has_bounds; - cairo_box_t bounds; - - int num_segments; - int segments_size; - segment_t *segments; - segment_t segments_embedded[8]; /* common case is a single rectangle */ -} cairo_rectilinear_stroker_t; - -static void -_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, - const cairo_box_t *boxes, - int num_boxes) -{ - stroker->has_bounds = TRUE; - _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); - - stroker->bounds.p1.x -= stroker->half_line_width; - stroker->bounds.p2.x += stroker->half_line_width; - - stroker->bounds.p1.y -= stroker->half_line_width; - stroker->bounds.p2.y += stroker->half_line_width; -} - -static cairo_bool_t -_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_bool_t do_traps, - void *container) -{ - /* This special-case rectilinear stroker only supports - * miter-joined lines (not curves) and a translation-only matrix - * (though it could probably be extended to support a matrix with - * uniform, integer scaling). - * - * It also only supports horizontal and vertical line_to - * elements. But we don't catch that here, but instead return - * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any - * non-rectilinear line_to is encountered. - */ - if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) - return FALSE; - - /* If the miter limit turns right angles into bevels, then we - * can't use this optimization. Remember, the ratio is - * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, - * which we round for safety. */ - if (stroke_style->miter_limit < M_SQRT2) - return FALSE; - - if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || - stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) - { - return FALSE; - } - - if (! _cairo_matrix_has_unity_scale (ctm)) - return FALSE; - - stroker->stroke_style = stroke_style; - stroker->ctm = ctm; - - stroker->half_line_width = - _cairo_fixed_from_double (stroke_style->line_width / 2.0); - stroker->open_sub_path = FALSE; - stroker->segments = stroker->segments_embedded; - stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); - stroker->num_segments = 0; - - _cairo_stroker_dash_init (&stroker->dash, stroke_style); - - stroker->has_bounds = FALSE; - - stroker->do_traps = do_traps; - stroker->container = container; - - return TRUE; -} - -static void -_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) -{ - if (stroker->segments != stroker->segments_embedded) - free (stroker->segments); -} - -static cairo_status_t -_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, - const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_bool_t is_horizontal, - cairo_bool_t has_join) -{ - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (stroker->num_segments == stroker->segments_size) { - int new_size = stroker->segments_size * 2; - segment_t *new_segments; - - if (stroker->segments == stroker->segments_embedded) { - new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); - if (unlikely (new_segments == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (new_segments, stroker->segments, - stroker->num_segments * sizeof (segment_t)); - } else { - new_segments = _cairo_realloc_ab (stroker->segments, - new_size, sizeof (segment_t)); - if (unlikely (new_segments == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - stroker->segments_size = new_size; - stroker->segments = new_segments; - } - - stroker->segments[stroker->num_segments].p1 = *p1; - stroker->segments[stroker->num_segments].p2 = *p2; - stroker->segments[stroker->num_segments].has_join = has_join; - stroker->segments[stroker->num_segments].is_horizontal = is_horizontal; - stroker->num_segments++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) -{ - cairo_status_t status; - cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; - cairo_fixed_t half_line_width = stroker->half_line_width; - int i; - - for (i = 0; i < stroker->num_segments; i++) { - cairo_point_t *a, *b; - cairo_bool_t lengthen_initial, shorten_final, lengthen_final; - - a = &stroker->segments[i].p1; - b = &stroker->segments[i].p2; - - /* For each segment we generate a single rectangular - * trapezoid. This rectangle is based on a perpendicular - * extension (by half the line width) of the segment endpoints - * after some adjustments of the endpoints to account for caps - * and joins. - */ - - /* We adjust the initial point of the segment to extend the - * rectangle to include the previous cap or join, (this - * adjustment applies to all segments except for the first - * segment of open, butt-capped paths). - */ - lengthen_initial = TRUE; - if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) - lengthen_initial = FALSE; - - /* The adjustment of the final point is trickier. For all but - * the last segment we shorten the segment at the final - * endpoint to not overlap with the subsequent join. For the - * last segment we do the same shortening if the path is - * closed. If the path is open and butt-capped we do no - * adjustment, while if it's open and square-capped we do a - * lengthening adjustment instead to include the cap. - */ - shorten_final = TRUE; - lengthen_final = FALSE; - if (i == stroker->num_segments - 1 && stroker->open_sub_path) { - shorten_final = FALSE; - if (line_cap == CAIRO_LINE_CAP_SQUARE) - lengthen_final = TRUE; - } - - /* Perform the adjustments of the endpoints. */ - if (a->y == b->y) { - if (a->x < b->x) { - if (lengthen_initial) - a->x -= half_line_width; - if (shorten_final) - b->x -= half_line_width; - else if (lengthen_final) - b->x += half_line_width; - } else { - if (lengthen_initial) - a->x += half_line_width; - if (shorten_final) - b->x += half_line_width; - else if (lengthen_final) - b->x -= half_line_width; - } - - if (a->x > b->x) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - } else { - if (a->y < b->y) { - if (lengthen_initial) - a->y -= half_line_width; - if (shorten_final) - b->y -= half_line_width; - else if (lengthen_final) - b->y += half_line_width; - } else { - if (lengthen_initial) - a->y += half_line_width; - if (shorten_final) - b->y += half_line_width; - else if (lengthen_final) - b->y -= half_line_width; - } - - if (a->y > b->y) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - } - - /* Form the rectangle by expanding by half the line width in - * either perpendicular direction. */ - if (a->y == b->y) { - a->y -= half_line_width; - b->y += half_line_width; - } else { - a->x -= half_line_width; - b->x += half_line_width; - } - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); - } else { - cairo_box_t box; - - box.p1 = *a; - box.p2 = *b; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - stroker->num_segments = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) -{ - cairo_status_t status; - cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; - cairo_fixed_t half_line_width = stroker->half_line_width; - int i; - - for (i = 0; i < stroker->num_segments; i++) { - cairo_point_t *a, *b; - cairo_bool_t is_horizontal; - - a = &stroker->segments[i].p1; - b = &stroker->segments[i].p2; - - is_horizontal = stroker->segments[i].is_horizontal; - - /* Handle the joins for a potentially degenerate segment. */ - if (line_cap == CAIRO_LINE_CAP_BUTT && - stroker->segments[i].has_join && - (i != stroker->num_segments - 1 || - (! stroker->open_sub_path && stroker->dash.dash_starts_on))) - { - cairo_point_t p1 = stroker->segments[i].p1; - cairo_point_t p2 = stroker->segments[i].p2; - cairo_slope_t out_slope; - int j = (i + 1) % stroker->num_segments; - - _cairo_slope_init (&out_slope, - &stroker->segments[j].p1, - &stroker->segments[j].p2); - - if (is_horizontal) { - if (p1.x <= p2.x) { - p1.x = p2.x; - p2.x += half_line_width; - } else { - p1.x = p2.x - half_line_width; - } - if (out_slope.dy >= 0) - p1.y -= half_line_width; - if (out_slope.dy <= 0) - p2.y += half_line_width; - } else { - if (p1.y <= p2.y) { - p1.y = p2.y; - p2.y += half_line_width; - } else { - p1.y = p2.y - half_line_width; - } - if (out_slope.dx >= 0) - p1.x -= half_line_width; - if (out_slope.dx <= 0) - p2.x += half_line_width; - } - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2); - } else { - cairo_box_t box; - - box.p1 = p1; - box.p2 = p2; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - /* Perform the adjustments of the endpoints. */ - if (is_horizontal) { - if (line_cap == CAIRO_LINE_CAP_SQUARE) { - if (a->x <= b->x) { - a->x -= half_line_width; - b->x += half_line_width; - } else { - a->x += half_line_width; - b->x -= half_line_width; - } - } - - if (a->x > b->x) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - - a->y -= half_line_width; - b->y += half_line_width; - } else { - if (line_cap == CAIRO_LINE_CAP_SQUARE) { - if (a->y <= b->y) { - a->y -= half_line_width; - b->y += half_line_width; - } else { - a->y += half_line_width; - b->y -= half_line_width; - } - } - - if (a->y > b->y) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - - a->x -= half_line_width; - b->x += half_line_width; - } - - if (a->x == b->x && a->y == b->y) - continue; - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); - } else { - cairo_box_t box; - - box.p1 = *a; - box.p2 = *b; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - stroker->num_segments = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_status_t status; - - if (stroker->dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (unlikely (status)) - return status; - - /* reset the dash pattern for new sub paths */ - _cairo_stroker_dash_start (&stroker->dash); - - stroker->current_point = *point; - stroker->first_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_line_to (void *closure, - const cairo_point_t *b) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_point_t *a = &stroker->current_point; - cairo_status_t status; - - /* We only support horizontal or vertical elements. */ - assert (a->x == b->x || a->y == b->y); - - /* We don't draw anything for degenerate paths. */ - if (a->x == b->x && a->y == b->y) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, - a->y == b->y, - TRUE); - - stroker->current_point = *b; - stroker->open_sub_path = TRUE; - - return status; -} - -static cairo_status_t -_cairo_rectilinear_stroker_line_to_dashed (void *closure, - const cairo_point_t *point) -{ - cairo_rectilinear_stroker_t *stroker = closure; - const cairo_point_t *a = &stroker->current_point; - const cairo_point_t *b = point; - cairo_bool_t fully_in_bounds; - double sign, remain; - cairo_fixed_t mag; - cairo_status_t status; - cairo_line_t segment; - cairo_bool_t dash_on = FALSE; - cairo_bool_t is_horizontal; - - /* We don't draw anything for degenerate paths. */ - if (a->x == b->x && a->y == b->y) - return CAIRO_STATUS_SUCCESS; - - /* We only support horizontal or vertical elements. */ - assert (a->x == b->x || a->y == b->y); - - fully_in_bounds = TRUE; - if (stroker->has_bounds && - (! _cairo_box_contains_point (&stroker->bounds, a) || - ! _cairo_box_contains_point (&stroker->bounds, b))) - { - fully_in_bounds = FALSE; - } - - is_horizontal = a->y == b->y; - if (is_horizontal) - mag = b->x - a->x; - else - mag = b->y - a->y; - if (mag < 0) { - remain = _cairo_fixed_to_double (-mag); - sign = 1.; - } else { - remain = _cairo_fixed_to_double (mag); - sign = -1.; - } - - segment.p2 = segment.p1 = *a; - while (remain > 0.) { - double step_length; - - step_length = MIN (stroker->dash.dash_remain, remain); - remain -= step_length; - - mag = _cairo_fixed_from_double (sign*remain); - if (is_horizontal) - segment.p2.x = b->x + mag; - else - segment.p2.y = b->y + mag; - - if (stroker->dash.dash_on && - (fully_in_bounds || - _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) - { - status = _cairo_rectilinear_stroker_add_segment (stroker, - &segment.p1, - &segment.p2, - is_horizontal, - remain <= 0.); - if (unlikely (status)) - return status; - - dash_on = TRUE; - } - else - { - dash_on = FALSE; - } - - _cairo_stroker_dash_step (&stroker->dash, step_length); - segment.p1 = segment.p2; - } - - if (stroker->dash.dash_on && ! dash_on && - (fully_in_bounds || - _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) - { - - /* This segment ends on a transition to dash_on, compute a new face - * and add cap for the beginning of the next dash_on step. - */ - - status = _cairo_rectilinear_stroker_add_segment (stroker, - &segment.p1, - &segment.p1, - is_horizontal, - TRUE); - if (unlikely (status)) - return status; - } - - stroker->current_point = *point; - stroker->open_sub_path = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_close_path (void *closure) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_status_t status; - - /* We don't draw anything for degenerate paths. */ - if (! stroker->open_sub_path) - return CAIRO_STATUS_SUCCESS; - - if (stroker->dash.dashed) { - status = _cairo_rectilinear_stroker_line_to_dashed (stroker, - &stroker->first_point); - } else { - status = _cairo_rectilinear_stroker_line_to (stroker, - &stroker->first_point); - } - if (unlikely (status)) - return status; - - stroker->open_sub_path = FALSE; - - if (stroker->dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps) -{ - cairo_rectilinear_stroker_t rectilinear_stroker; - cairo_int_status_t status; - - assert (path->is_rectilinear); - - if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, - stroke_style, ctm, - TRUE, traps)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (traps->num_limits) { - _cairo_rectilinear_stroker_limit (&rectilinear_stroker, - traps->limits, - traps->num_limits); - } - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_rectilinear_stroker_move_to, - rectilinear_stroker.dash.dashed ? - _cairo_rectilinear_stroker_line_to_dashed : - _cairo_rectilinear_stroker_line_to, - NULL, - _cairo_rectilinear_stroker_close_path, - &rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - if (rectilinear_stroker.dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); - - traps->is_rectilinear = 1; - traps->is_rectangular = 1; - /* As we incrementally tessellate, we do not eliminate self-intersections */ - traps->has_intersections = traps->num_traps > 1; -BAIL: - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - - if (unlikely (status)) - _cairo_traps_clear (traps); - - return status; -} - -cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_boxes_t *boxes) -{ - cairo_rectilinear_stroker_t rectilinear_stroker; - cairo_int_status_t status; - - assert (path->is_rectilinear); - - if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, - stroke_style, ctm, - FALSE, boxes)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (boxes->num_limits) { - _cairo_rectilinear_stroker_limit (&rectilinear_stroker, - boxes->limits, - boxes->num_limits); - } - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_rectilinear_stroker_move_to, - rectilinear_stroker.dash.dashed ? - _cairo_rectilinear_stroker_line_to_dashed : - _cairo_rectilinear_stroker_line_to, - NULL, - _cairo_rectilinear_stroker_close_path, - &rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - if (rectilinear_stroker.dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - /* As we incrementally tessellate, we do not eliminate self-intersections */ - status = _cairo_bentley_ottmann_tessellate_boxes (boxes, - CAIRO_FILL_RULE_WINDING, - boxes); - if (unlikely (status)) - goto BAIL; - - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - - return CAIRO_STATUS_SUCCESS; - -BAIL: - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - _cairo_boxes_clear (boxes); - return status; -} diff --git a/libs/cairo/cairo/src/cairo-path.c b/libs/cairo/cairo/src/cairo-path.c deleted file mode 100644 index 49b479a99..000000000 --- a/libs/cairo/cairo/src/cairo-path.c +++ /dev/null @@ -1,504 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-private.h" -#include "cairo-error-private.h" -#include "cairo-path-private.h" -#include "cairo-path-fixed-private.h" - -/** - * SECTION:cairo-paths - * @Title: Paths - * @Short_Description: Creating paths and manipulating path data - * - * Paths are the most basic drawing tools and are primarily used to implicitly - * generate simple masks. - */ - -static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; - -/* Closure for path interpretation. */ -typedef struct cairo_path_count { - int count; - cairo_point_t current_point; -} cpc_t; - -static cairo_status_t -_cpc_move_to (void *closure, - const cairo_point_t *point) -{ - cpc_t *cpc = closure; - - cpc->count += 2; - - cpc->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpc_line_to (void *closure, - const cairo_point_t *point) -{ - cpc_t *cpc = closure; - - cpc->count += 2; - - cpc->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpc_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - cpc_t *cpc = closure; - - cpc->count += 4; - - cpc->current_point = *p3; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpc_close_path (void *closure) -{ - cpc_t *cpc = closure; - - cpc->count += 1; - - return CAIRO_STATUS_SUCCESS; -} - -static int -_cairo_path_count (cairo_path_t *path, - cairo_path_fixed_t *path_fixed, - double tolerance, - cairo_bool_t flatten) -{ - cairo_status_t status; - cpc_t cpc; - - cpc.count = 0; - cpc.current_point.x = 0; - cpc.current_point.y = 0; - - if (flatten) { - status = _cairo_path_fixed_interpret_flat (path_fixed, - CAIRO_DIRECTION_FORWARD, - _cpc_move_to, - _cpc_line_to, - _cpc_close_path, - &cpc, - tolerance); - } else { - status = _cairo_path_fixed_interpret (path_fixed, - CAIRO_DIRECTION_FORWARD, - _cpc_move_to, - _cpc_line_to, - _cpc_curve_to, - _cpc_close_path, - &cpc); - } - - if (unlikely (status)) - return -1; - - return cpc.count; -} - -/* Closure for path interpretation. */ -typedef struct cairo_path_populate { - cairo_path_data_t *data; - cairo_gstate_t *gstate; - cairo_point_t current_point; -} cpp_t; - -static cairo_status_t -_cpp_move_to (void *closure, - const cairo_point_t *point) -{ - cpp_t *cpp = closure; - cairo_path_data_t *data = cpp->data; - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - - _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); - - data->header.type = CAIRO_PATH_MOVE_TO; - data->header.length = 2; - - /* We index from 1 to leave room for data->header */ - data[1].point.x = x; - data[1].point.y = y; - - cpp->data += data->header.length; - - cpp->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpp_line_to (void *closure, - const cairo_point_t *point) -{ - cpp_t *cpp = closure; - cairo_path_data_t *data = cpp->data; - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - - _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); - - data->header.type = CAIRO_PATH_LINE_TO; - data->header.length = 2; - - /* We index from 1 to leave room for data->header */ - data[1].point.x = x; - data[1].point.y = y; - - cpp->data += data->header.length; - - cpp->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpp_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - cpp_t *cpp = closure; - cairo_path_data_t *data = cpp->data; - double x1, y1; - double x2, y2; - double x3, y3; - - x1 = _cairo_fixed_to_double (p1->x); - y1 = _cairo_fixed_to_double (p1->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x1, &y1); - - x2 = _cairo_fixed_to_double (p2->x); - y2 = _cairo_fixed_to_double (p2->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2); - - x3 = _cairo_fixed_to_double (p3->x); - y3 = _cairo_fixed_to_double (p3->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x3, &y3); - - data->header.type = CAIRO_PATH_CURVE_TO; - data->header.length = 4; - - /* We index from 1 to leave room for data->header */ - data[1].point.x = x1; - data[1].point.y = y1; - - data[2].point.x = x2; - data[2].point.y = y2; - - data[3].point.x = x3; - data[3].point.y = y3; - - cpp->data += data->header.length; - - cpp->current_point = *p3; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cpp_close_path (void *closure) -{ - cpp_t *cpp = closure; - cairo_path_data_t *data = cpp->data; - - data->header.type = CAIRO_PATH_CLOSE_PATH; - data->header.length = 1; - - cpp->data += data->header.length; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_populate (cairo_path_t *path, - cairo_path_fixed_t *path_fixed, - cairo_gstate_t *gstate, - cairo_bool_t flatten) -{ - cairo_status_t status; - cpp_t cpp; - - cpp.data = path->data; - cpp.gstate = gstate; - cpp.current_point.x = 0; - cpp.current_point.y = 0; - - if (flatten) { - double tolerance = _cairo_gstate_get_tolerance (gstate); - status = _cairo_path_fixed_interpret_flat (path_fixed, - CAIRO_DIRECTION_FORWARD, - _cpp_move_to, - _cpp_line_to, - _cpp_close_path, - &cpp, - tolerance); - } else { - status = _cairo_path_fixed_interpret (path_fixed, - CAIRO_DIRECTION_FORWARD, - _cpp_move_to, - _cpp_line_to, - _cpp_curve_to, - _cpp_close_path, - &cpp); - } - - if (unlikely (status)) - return status; - - /* Sanity check the count */ - assert (cpp.data - path->data == path->num_data); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_path_t * -_cairo_path_create_in_error (cairo_status_t status) -{ - cairo_path_t *path; - - /* special case NO_MEMORY so as to avoid allocations */ - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_path_t*) &_cairo_path_nil; - - path = malloc (sizeof (cairo_path_t)); - if (unlikely (path == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_path_t*) &_cairo_path_nil; - } - - path->num_data = 0; - path->data = NULL; - path->status = status; - - return path; -} - -static cairo_path_t * -_cairo_path_create_internal (cairo_path_fixed_t *path_fixed, - cairo_gstate_t *gstate, - cairo_bool_t flatten) -{ - cairo_path_t *path; - - path = malloc (sizeof (cairo_path_t)); - if (unlikely (path == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_path_t*) &_cairo_path_nil; - } - - path->num_data = _cairo_path_count (path, path_fixed, - _cairo_gstate_get_tolerance (gstate), - flatten); - if (path->num_data < 0) { - free (path); - return (cairo_path_t*) &_cairo_path_nil; - } - - if (path->num_data) { - path->data = _cairo_malloc_ab (path->num_data, - sizeof (cairo_path_data_t)); - if (unlikely (path->data == NULL)) { - free (path); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_path_t*) &_cairo_path_nil; - } - - path->status = _cairo_path_populate (path, path_fixed, - gstate, flatten); - } else { - path->data = NULL; - path->status = CAIRO_STATUS_SUCCESS; - } - - return path; -} - -/** - * cairo_path_destroy: - * @path: a path previously returned by either cairo_copy_path() or - * cairo_copy_path_flat(). - * - * Immediately releases all memory associated with @path. After a call - * to cairo_path_destroy() the @path pointer is no longer valid and - * should not be used further. - * - * Note: cairo_path_destroy() should only be called with a - * pointer to a #cairo_path_t returned by a cairo function. Any path - * that is created manually (ie. outside of cairo) should be destroyed - * manually as well. - **/ -void -cairo_path_destroy (cairo_path_t *path) -{ - if (path == NULL || path == &_cairo_path_nil) - return; - - if (path->data) - free (path->data); - - free (path); -} - -/** - * _cairo_path_create: - * @path: a fixed-point, device-space path to be converted and copied - * @gstate: the current graphics state - * - * Creates a user-space #cairo_path_t copy of the given device-space - * @path. The @gstate parameter provides the inverse CTM for the - * conversion. - * - * Return value: the new copy of the path. If there is insufficient - * memory a pointer to a special static nil #cairo_path_t will be - * returned instead with status==%CAIRO_STATUS_NO_MEMORY and - * data==%NULL. - **/ -cairo_path_t * -_cairo_path_create (cairo_path_fixed_t *path, - cairo_gstate_t *gstate) -{ - return _cairo_path_create_internal (path, gstate, FALSE); -} - -/** - * _cairo_path_create_flat: - * @path: a fixed-point, device-space path to be flattened, converted and copied - * @gstate: the current graphics state - * - * Creates a flattened, user-space #cairo_path_t copy of the given - * device-space @path. The @gstate parameter provide the inverse CTM - * for the conversion, as well as the tolerance value to control the - * accuracy of the flattening. - * - * Return value: the flattened copy of the path. If there is insufficient - * memory a pointer to a special static nil #cairo_path_t will be - * returned instead with status==%CAIRO_STATUS_NO_MEMORY and - * data==%NULL. - **/ -cairo_path_t * -_cairo_path_create_flat (cairo_path_fixed_t *path, - cairo_gstate_t *gstate) -{ - return _cairo_path_create_internal (path, gstate, TRUE); -} - -/** - * _cairo_path_append_to_context: - * @path: the path data to be appended - * @cr: a cairo context - * - * Append @path to the current path within @cr. - * - * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path - * is invalid, and %CAIRO_STATUS_SUCCESS otherwise. - **/ -cairo_status_t -_cairo_path_append_to_context (const cairo_path_t *path, - cairo_t *cr) -{ - const cairo_path_data_t *p, *end; - cairo_fixed_t x1_fixed, y1_fixed; - cairo_fixed_t x2_fixed, y2_fixed; - cairo_fixed_t x3_fixed, y3_fixed; - cairo_matrix_t user_to_backend; - cairo_status_t status; - double x, y; - - user_to_backend = cr->gstate->ctm; - cairo_matrix_multiply (&user_to_backend, - &user_to_backend, - &cr->gstate->target->device_transform); - - end = &path->data[path->num_data]; - for (p = &path->data[0]; p < end; p += p->header.length) { - switch (p->header.type) { - case CAIRO_PATH_MOVE_TO: - if (unlikely (p->header.length < 2)) - return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed); - break; - - case CAIRO_PATH_LINE_TO: - if (unlikely (p->header.length < 2)) - return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed); - break; - - case CAIRO_PATH_CURVE_TO: - if (unlikely (p->header.length < 4)) - return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - x = p[2].point.x, y = p[2].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x2_fixed = _cairo_fixed_from_double (x); - y2_fixed = _cairo_fixed_from_double (y); - - x = p[3].point.x, y = p[3].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x3_fixed = _cairo_fixed_from_double (x); - y3_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_curve_to (cr->path, - x1_fixed, y1_fixed, - x2_fixed, y2_fixed, - x3_fixed, y3_fixed); - break; - - case CAIRO_PATH_CLOSE_PATH: - if (unlikely (p->header.length < 1)) - return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - - status = _cairo_path_fixed_close_path (cr->path); - break; - - default: - return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - } - - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-pattern.c b/libs/cairo/cairo/src/cairo-pattern.c deleted file mode 100644 index 502344f3c..000000000 --- a/libs/cairo/cairo/src/cairo-pattern.c +++ /dev/null @@ -1,3202 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" -#include "cairo-freed-pool-private.h" - -/** - * SECTION:cairo-pattern - * @Title: cairo_pattern_t - * @Short_Description: Sources for drawing - * @See_Also: #cairo_t, #cairo_surface_t - * - * #cairo_pattern_t is the paint with which cairo draws. - * The primary use of patterns is as the source for all cairo drawing - * operations, although they can also be used as masks, that is, as the - * brush too. - * - * A cairo pattern is created by using one of the many constructors, - * of the form cairo_pattern_create_type() - * or implicitly through - * cairo_set_source_type() functions. - */ - -#if HAS_FREED_POOL -static freed_pool_t freed_pattern_pool[4]; -#endif - -static const cairo_solid_pattern_t _cairo_pattern_nil = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NO_MEMORY, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ -}; - -static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NULL_POINTER, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ -}; - -const cairo_solid_pattern_t _cairo_pattern_black = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_SUCCESS, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ - { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ -}; - -const cairo_solid_pattern_t _cairo_pattern_clear = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_SUCCESS, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ - { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */ -}; - -const cairo_solid_pattern_t _cairo_pattern_white = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_SUCCESS, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ - { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */ -}; - -/** - * _cairo_pattern_set_error: - * @pattern: a pattern - * @status: a status value indicating an error - * - * Atomically sets pattern->status to @status and calls _cairo_error; - * Does nothing if status is %CAIRO_STATUS_SUCCESS. - * - * All assignments of an error status to pattern->status should happen - * through _cairo_pattern_set_error(). Note that due to the nature of - * the atomic operation, it is not safe to call this function on the nil - * objects. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - **/ -static cairo_status_t -_cairo_pattern_set_error (cairo_pattern_t *pattern, - cairo_status_t status) -{ - if (status == CAIRO_STATUS_SUCCESS) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&pattern->status, status); - - return _cairo_error (status); -} - -static void -_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) -{ -#if HAVE_VALGRIND - switch (type) { - case CAIRO_PATTERN_TYPE_SOLID: - VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); - break; - } -#endif - - pattern->type = type; - pattern->status = CAIRO_STATUS_SUCCESS; - - /* Set the reference count to zero for on-stack patterns. - * Callers needs to explicitly increment the count for heap allocations. */ - CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); - - _cairo_user_data_array_init (&pattern->user_data); - - if (type == CAIRO_PATTERN_TYPE_SURFACE) - pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT; - else - pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT; - - pattern->filter = CAIRO_FILTER_DEFAULT; - - pattern->has_component_alpha = FALSE; - - cairo_matrix_init_identity (&pattern->matrix); -} - -static cairo_status_t -_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, - const cairo_gradient_pattern_t *other) -{ - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; - cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; - - *dst = *src; - } - else - { - cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; - cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; - - *dst = *src; - } - - if (other->stops == other->stops_embedded) - pattern->stops = pattern->stops_embedded; - else if (other->stops) - { - pattern->stops = _cairo_malloc_ab (other->stops_size, - sizeof (cairo_gradient_stop_t)); - if (unlikely (pattern->stops == NULL)) { - pattern->stops_size = 0; - pattern->n_stops = 0; - return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); - } - - memcpy (pattern->stops, other->stops, - other->n_stops * sizeof (cairo_gradient_stop_t)); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_pattern_init_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other) -{ - if (other->status) - return _cairo_pattern_set_error (pattern, other->status); - - switch (other->type) { - case CAIRO_PATTERN_TYPE_SOLID: { - cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; - cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; - - VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); - - *dst = *src; - } break; - case CAIRO_PATTERN_TYPE_SURFACE: { - cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; - cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; - - VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); - - *dst = *src; - cairo_surface_reference (dst->surface); - } break; - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: { - cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; - cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; - cairo_status_t status; - - if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { - VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); - } else { - VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); - } - - status = _cairo_gradient_pattern_init_copy (dst, src); - if (unlikely (status)) - return status; - - } break; - } - - /* The reference count and user_data array are unique to the copy. */ - CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); - _cairo_user_data_array_init (&pattern->user_data); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other) -{ - int size; - - assert (other->status == CAIRO_STATUS_SUCCESS); - - switch (other->type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - size = sizeof (cairo_solid_pattern_t); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - size = sizeof (cairo_surface_pattern_t); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - size = sizeof (cairo_linear_pattern_t); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - size = sizeof (cairo_radial_pattern_t); - break; - } - - memcpy (pattern, other, size); - - CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); - _cairo_user_data_array_init (&pattern->user_data); -} - -cairo_status_t -_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, - const cairo_pattern_t *other) -{ - cairo_status_t status; - - /* We don't bother doing any fancy copy-on-write implementation - * for the pattern's data. It's generally quite tiny. */ - status = _cairo_pattern_init_copy (pattern, other); - if (unlikely (status)) - return status; - - /* But we do let the surface snapshot stuff be as fancy as it - * would like to be. */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) pattern; - cairo_surface_t *surface = surface_pattern->surface; - - surface_pattern->surface = _cairo_surface_snapshot (surface); - - cairo_surface_destroy (surface); - - if (surface_pattern->surface->status) - return surface_pattern->surface->status; - } - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_fini (cairo_pattern_t *pattern) -{ - _cairo_user_data_array_fini (&pattern->user_data); - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - break; - case CAIRO_PATTERN_TYPE_SURFACE: { - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) pattern; - - cairo_surface_destroy (surface_pattern->surface); - } break; - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - - if (gradient->stops && gradient->stops != gradient->stops_embedded) - free (gradient->stops); - } break; - } - -#if HAVE_VALGRIND - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); - break; - } -#endif -} - -cairo_status_t -_cairo_pattern_create_copy (cairo_pattern_t **pattern_out, - const cairo_pattern_t *other) -{ - cairo_pattern_t *pattern; - cairo_status_t status; - - if (other->status) - return other->status; - - switch (other->type) { - case CAIRO_PATTERN_TYPE_SOLID: - pattern = malloc (sizeof (cairo_solid_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - pattern = malloc (sizeof (cairo_surface_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - pattern = malloc (sizeof (cairo_linear_pattern_t)); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - pattern = malloc (sizeof (cairo_radial_pattern_t)); - break; - default: - ASSERT_NOT_REACHED; - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - } - if (unlikely (pattern == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_pattern_init_copy (pattern, other); - if (unlikely (status)) { - free (pattern); - return status; - } - - CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); - *pattern_out = pattern; - return CAIRO_STATUS_SUCCESS; -} - - -void -_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - const cairo_color_t *color) -{ - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); - pattern->color = *color; -} - -void -_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, - cairo_surface_t *surface) -{ - if (surface->status) { - /* Force to solid to simplify the pattern_fini process. */ - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); - _cairo_pattern_set_error (&pattern->base, surface->status); - return; - } - - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); - - pattern->surface = cairo_surface_reference (surface); -} - -static void -_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, - cairo_pattern_type_t type) -{ - _cairo_pattern_init (&pattern->base, type); - - pattern->n_stops = 0; - pattern->stops_size = 0; - pattern->stops = NULL; -} - -void -_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, - double x0, double y0, double x1, double y1) -{ - _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR); - - pattern->p1.x = _cairo_fixed_from_double (x0); - pattern->p1.y = _cairo_fixed_from_double (y0); - pattern->p2.x = _cairo_fixed_from_double (x1); - pattern->p2.y = _cairo_fixed_from_double (y1); -} - -void -_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, - double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1) -{ - _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL); - - pattern->c1.x = _cairo_fixed_from_double (cx0); - pattern->c1.y = _cairo_fixed_from_double (cy0); - pattern->r1 = _cairo_fixed_from_double (fabs (radius0)); - pattern->c2.x = _cairo_fixed_from_double (cx1); - pattern->c2.y = _cairo_fixed_from_double (cy1); - pattern->r2 = _cairo_fixed_from_double (fabs (radius1)); -} - -cairo_pattern_t * -_cairo_pattern_create_solid (const cairo_color_t *color) -{ - cairo_solid_pattern_t *pattern; - - pattern = - _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); - if (unlikely (pattern == NULL)) { - /* None cached, need to create a new pattern. */ - pattern = malloc (sizeof (cairo_solid_pattern_t)); - if (unlikely (pattern == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *) &_cairo_pattern_nil; - } - } - - _cairo_pattern_init_solid (pattern, color); - CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); - - return &pattern->base; -} - -cairo_pattern_t * -_cairo_pattern_create_in_error (cairo_status_t status) -{ - cairo_pattern_t *pattern; - - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_pattern_t *)&_cairo_pattern_nil.base; - - CAIRO_MUTEX_INITIALIZE (); - - pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); - if (pattern->status == CAIRO_STATUS_SUCCESS) - status = _cairo_pattern_set_error (pattern, status); - - return pattern; -} - -/** - * cairo_pattern_create_rgb: - * @red: red component of the color - * @green: green component of the color - * @blue: blue component of the color - * - * Creates a new #cairo_pattern_t corresponding to an opaque color. The - * color components are floating point numbers in the range 0 to 1. - * If the values passed in are outside that range, they will be - * clamped. - * - * Return value: the newly created #cairo_pattern_t if successful, or - * an error pattern in case of no memory. The caller owns the - * returned object and should call cairo_pattern_destroy() when - * finished with it. - * - * This function will always return a valid pointer, but if an error - * occurred the pattern status will be set to an error. To inspect - * the status of a pattern use cairo_pattern_status(). - **/ -cairo_pattern_t * -cairo_pattern_create_rgb (double red, double green, double blue) -{ - cairo_color_t color; - - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - - _cairo_color_init_rgb (&color, red, green, blue); - - CAIRO_MUTEX_INITIALIZE (); - - return _cairo_pattern_create_solid (&color); -} -slim_hidden_def (cairo_pattern_create_rgb); - -/** - * cairo_pattern_create_rgba: - * @red: red component of the color - * @green: green component of the color - * @blue: blue component of the color - * @alpha: alpha component of the color - * - * Creates a new #cairo_pattern_t corresponding to a translucent color. - * The color components are floating point numbers in the range 0 to - * 1. If the values passed in are outside that range, they will be - * clamped. - * - * Return value: the newly created #cairo_pattern_t if successful, or - * an error pattern in case of no memory. The caller owns the - * returned object and should call cairo_pattern_destroy() when - * finished with it. - * - * This function will always return a valid pointer, but if an error - * occurred the pattern status will be set to an error. To inspect - * the status of a pattern use cairo_pattern_status(). - **/ -cairo_pattern_t * -cairo_pattern_create_rgba (double red, double green, double blue, - double alpha) -{ - cairo_color_t color; - - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - alpha = _cairo_restrict_value (alpha, 0.0, 1.0); - - _cairo_color_init_rgba (&color, red, green, blue, alpha); - - CAIRO_MUTEX_INITIALIZE (); - - return _cairo_pattern_create_solid (&color); -} -slim_hidden_def (cairo_pattern_create_rgba); - -/** - * cairo_pattern_create_for_surface: - * @surface: the surface - * - * Create a new #cairo_pattern_t for the given surface. - * - * Return value: the newly created #cairo_pattern_t if successful, or - * an error pattern in case of no memory. The caller owns the - * returned object and should call cairo_pattern_destroy() when - * finished with it. - * - * This function will always return a valid pointer, but if an error - * occurred the pattern status will be set to an error. To inspect - * the status of a pattern use cairo_pattern_status(). - **/ -cairo_pattern_t * -cairo_pattern_create_for_surface (cairo_surface_t *surface) -{ - cairo_surface_pattern_t *pattern; - - if (surface == NULL) { - _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); - return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer; - } - - if (surface->status) - return _cairo_pattern_create_in_error (surface->status); - - pattern = - _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); - if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_surface_pattern_t)); - if (unlikely (pattern == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *)&_cairo_pattern_nil.base; - } - } - - CAIRO_MUTEX_INITIALIZE (); - - _cairo_pattern_init_for_surface (pattern, surface); - CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); - - return &pattern->base; -} -slim_hidden_def (cairo_pattern_create_for_surface); - -/** - * cairo_pattern_create_linear: - * @x0: x coordinate of the start point - * @y0: y coordinate of the start point - * @x1: x coordinate of the end point - * @y1: y coordinate of the end point - * - * Create a new linear gradient #cairo_pattern_t along the line defined - * by (x0, y0) and (x1, y1). Before using the gradient pattern, a - * number of color stops should be defined using - * cairo_pattern_add_color_stop_rgb() or - * cairo_pattern_add_color_stop_rgba(). - * - * Note: The coordinates here are in pattern space. For a new pattern, - * pattern space is identical to user space, but the relationship - * between the spaces can be changed with cairo_pattern_set_matrix(). - * - * Return value: the newly created #cairo_pattern_t if successful, or - * an error pattern in case of no memory. The caller owns the - * returned object and should call cairo_pattern_destroy() when - * finished with it. - * - * This function will always return a valid pointer, but if an error - * occurred the pattern status will be set to an error. To inspect - * the status of a pattern use cairo_pattern_status(). - **/ -cairo_pattern_t * -cairo_pattern_create_linear (double x0, double y0, double x1, double y1) -{ - cairo_linear_pattern_t *pattern; - - pattern = - _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); - if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_linear_pattern_t)); - if (unlikely (pattern == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *) &_cairo_pattern_nil.base; - } - } - - CAIRO_MUTEX_INITIALIZE (); - - _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); - CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); - - return &pattern->base.base; -} - -/** - * cairo_pattern_create_radial: - * @cx0: x coordinate for the center of the start circle - * @cy0: y coordinate for the center of the start circle - * @radius0: radius of the start circle - * @cx1: x coordinate for the center of the end circle - * @cy1: y coordinate for the center of the end circle - * @radius1: radius of the end circle - * - * Creates a new radial gradient #cairo_pattern_t between the two - * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the - * gradient pattern, a number of color stops should be defined using - * cairo_pattern_add_color_stop_rgb() or - * cairo_pattern_add_color_stop_rgba(). - * - * Note: The coordinates here are in pattern space. For a new pattern, - * pattern space is identical to user space, but the relationship - * between the spaces can be changed with cairo_pattern_set_matrix(). - * - * Return value: the newly created #cairo_pattern_t if successful, or - * an error pattern in case of no memory. The caller owns the - * returned object and should call cairo_pattern_destroy() when - * finished with it. - * - * This function will always return a valid pointer, but if an error - * occurred the pattern status will be set to an error. To inspect - * the status of a pattern use cairo_pattern_status(). - **/ -cairo_pattern_t * -cairo_pattern_create_radial (double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1) -{ - cairo_radial_pattern_t *pattern; - - pattern = - _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); - if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_radial_pattern_t)); - if (unlikely (pattern == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *) &_cairo_pattern_nil.base; - } - } - - CAIRO_MUTEX_INITIALIZE (); - - _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); - CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); - - return &pattern->base.base; -} - -/** - * cairo_pattern_reference: - * @pattern: a #cairo_pattern_t - * - * Increases the reference count on @pattern by one. This prevents - * @pattern from being destroyed until a matching call to - * cairo_pattern_destroy() is made. - * - * The number of references to a #cairo_pattern_t can be get using - * cairo_pattern_get_reference_count(). - * - * Return value: the referenced #cairo_pattern_t. - **/ -cairo_pattern_t * -cairo_pattern_reference (cairo_pattern_t *pattern) -{ - if (pattern == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) - return pattern; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); - - _cairo_reference_count_inc (&pattern->ref_count); - - return pattern; -} -slim_hidden_def (cairo_pattern_reference); - -/** - * cairo_pattern_get_type: - * @pattern: a #cairo_pattern_t - * - * This function returns the type a pattern. - * See #cairo_pattern_type_t for available types. - * - * Return value: The type of @pattern. - * - * Since: 1.2 - **/ -cairo_pattern_type_t -cairo_pattern_get_type (cairo_pattern_t *pattern) -{ - return pattern->type; -} - -/** - * cairo_pattern_status: - * @pattern: a #cairo_pattern_t - * - * Checks whether an error has previously occurred for this - * pattern. - * - * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. - **/ -cairo_status_t -cairo_pattern_status (cairo_pattern_t *pattern) -{ - return pattern->status; -} - -/** - * cairo_pattern_destroy: - * @pattern: a #cairo_pattern_t - * - * Decreases the reference count on @pattern by one. If the result is - * zero, then @pattern and all associated resources are freed. See - * cairo_pattern_reference(). - **/ -void -cairo_pattern_destroy (cairo_pattern_t *pattern) -{ - cairo_pattern_type_t type; - - if (pattern == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) - return; - - type = pattern->type; - _cairo_pattern_fini (pattern); - - /* maintain a small cache of freed patterns */ - _freed_pool_put (&freed_pattern_pool[type], pattern); -} -slim_hidden_def (cairo_pattern_destroy); - -/** - * cairo_pattern_get_reference_count: - * @pattern: a #cairo_pattern_t - * - * Returns the current reference count of @pattern. - * - * Return value: the current reference count of @pattern. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.4 - **/ -unsigned int -cairo_pattern_get_reference_count (cairo_pattern_t *pattern) -{ - if (pattern == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count); -} - -/** - * cairo_pattern_get_user_data: - * @pattern: a #cairo_pattern_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @pattern using the - * specified key. If no user data has been attached with the given - * key this function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - * - * Since: 1.4 - **/ -void * -cairo_pattern_get_user_data (cairo_pattern_t *pattern, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&pattern->user_data, - key); -} - -/** - * cairo_pattern_set_user_data: - * @pattern: a #cairo_pattern_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the #cairo_pattern_t - * @destroy: a #cairo_destroy_func_t which will be called when the - * #cairo_t is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @pattern. To remove user data from a surface, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_set_user_data (cairo_pattern_t *pattern, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) - return pattern->status; - - return _cairo_user_data_array_set_data (&pattern->user_data, - key, user_data, destroy); -} - -/* make room for at least one more color stop */ -static cairo_status_t -_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) -{ - cairo_gradient_stop_t *new_stops; - int old_size = pattern->stops_size; - int embedded_size = ARRAY_LENGTH (pattern->stops_embedded); - int new_size = 2 * MAX (old_size, 4); - - /* we have a local buffer at pattern->stops_embedded. try to fulfill the request - * from there. */ - if (old_size < embedded_size) { - pattern->stops = pattern->stops_embedded; - pattern->stops_size = embedded_size; - return CAIRO_STATUS_SUCCESS; - } - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - assert (pattern->n_stops <= pattern->stops_size); - - if (pattern->stops == pattern->stops_embedded) { - new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t)); - if (new_stops) - memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t)); - } else { - new_stops = _cairo_realloc_ab (pattern->stops, - new_size, - sizeof (cairo_gradient_stop_t)); - } - - if (unlikely (new_stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pattern->stops = new_stops; - pattern->stops_size = new_size; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) -{ - cairo_gradient_stop_t *stops; - unsigned int i; - - if (pattern->n_stops >= pattern->stops_size) { - cairo_status_t status = _cairo_pattern_gradient_grow (pattern); - if (unlikely (status)) { - status = _cairo_pattern_set_error (&pattern->base, status); - return; - } - } - - stops = pattern->stops; - - for (i = 0; i < pattern->n_stops; i++) - { - if (offset < stops[i].offset) - { - memmove (&stops[i + 1], &stops[i], - sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i)); - - break; - } - } - - stops[i].offset = offset; - - stops[i].color.red = red; - stops[i].color.green = green; - stops[i].color.blue = blue; - stops[i].color.alpha = alpha; - - stops[i].color.red_short = _cairo_color_double_to_short (red); - stops[i].color.green_short = _cairo_color_double_to_short (green); - stops[i].color.blue_short = _cairo_color_double_to_short (blue); - stops[i].color.alpha_short = _cairo_color_double_to_short (alpha); - - pattern->n_stops++; -} - -/** - * cairo_pattern_add_color_stop_rgb: - * @pattern: a #cairo_pattern_t - * @offset: an offset in the range [0.0 .. 1.0] - * @red: red component of color - * @green: green component of color - * @blue: blue component of color - * - * Adds an opaque color stop to a gradient pattern. The offset - * specifies the location along the gradient's control vector. For - * example, a linear gradient's control vector is from (x0,y0) to - * (x1,y1) while a radial gradient's control vector is from any point - * on the start circle to the corresponding point on the end circle. - * - * The color is specified in the same way as in cairo_set_source_rgb(). - * - * If two (or more) stops are specified with identical offset values, - * they will be sorted according to the order in which the stops are - * added, (stops added earlier will compare less than stops added - * later). This can be useful for reliably making sharp color - * transitions instead of the typical blend. - * - * - * Note: If the pattern is not a gradient pattern, (eg. a linear or - * radial pattern), then the pattern will be put into an error status - * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. - **/ -void -cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, - double offset, - double red, - double green, - double blue) -{ - if (pattern->status) - return; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && - pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - { - _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - return; - } - - offset = _cairo_restrict_value (offset, 0.0, 1.0); - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - - _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, - offset, red, green, blue, 1.0); -} - -/** - * cairo_pattern_add_color_stop_rgba: - * @pattern: a #cairo_pattern_t - * @offset: an offset in the range [0.0 .. 1.0] - * @red: red component of color - * @green: green component of color - * @blue: blue component of color - * @alpha: alpha component of color - * - * Adds a translucent color stop to a gradient pattern. The offset - * specifies the location along the gradient's control vector. For - * example, a linear gradient's control vector is from (x0,y0) to - * (x1,y1) while a radial gradient's control vector is from any point - * on the start circle to the corresponding point on the end circle. - * - * The color is specified in the same way as in cairo_set_source_rgba(). - * - * If two (or more) stops are specified with identical offset values, - * they will be sorted according to the order in which the stops are - * added, (stops added earlier will compare less than stops added - * later). This can be useful for reliably making sharp color - * transitions instead of the typical blend. - * - * Note: If the pattern is not a gradient pattern, (eg. a linear or - * radial pattern), then the pattern will be put into an error status - * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. - */ -void -cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) -{ - if (pattern->status) - return; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && - pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - { - _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - return; - } - - offset = _cairo_restrict_value (offset, 0.0, 1.0); - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - alpha = _cairo_restrict_value (alpha, 0.0, 1.0); - - _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, - offset, red, green, blue, alpha); -} - -/** - * cairo_pattern_set_matrix: - * @pattern: a #cairo_pattern_t - * @matrix: a #cairo_matrix_t - * - * Sets the pattern's transformation matrix to @matrix. This matrix is - * a transformation from user space to pattern space. - * - * When a pattern is first created it always has the identity matrix - * for its transformation matrix, which means that pattern space is - * initially identical to user space. - * - * Important: Please note that the direction of this transformation - * matrix is from user space to pattern space. This means that if you - * imagine the flow from a pattern to user space (and on to device - * space), then coordinates in that flow will be transformed by the - * inverse of the pattern matrix. - * - * For example, if you want to make a pattern appear twice as large as - * it does by default the correct code to use is: - * - * - * cairo_matrix_init_scale (&matrix, 0.5, 0.5); - * cairo_pattern_set_matrix (pattern, &matrix); - * - * - * Meanwhile, using values of 2.0 rather than 0.5 in the code above - * would cause the pattern to appear at half of its default size. - * - * Also, please note the discussion of the user-space locking - * semantics of cairo_set_source(). - **/ -void -cairo_pattern_set_matrix (cairo_pattern_t *pattern, - const cairo_matrix_t *matrix) -{ - cairo_matrix_t inverse; - cairo_status_t status; - - if (pattern->status) - return; - - if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) - return; - - pattern->matrix = *matrix; - - inverse = *matrix; - status = cairo_matrix_invert (&inverse); - if (unlikely (status)) - status = _cairo_pattern_set_error (pattern, status); -} -slim_hidden_def (cairo_pattern_set_matrix); - -/** - * cairo_pattern_get_matrix: - * @pattern: a #cairo_pattern_t - * @matrix: return value for the matrix - * - * Stores the pattern's transformation matrix into @matrix. - **/ -void -cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) -{ - *matrix = pattern->matrix; -} - -/** - * cairo_pattern_set_filter: - * @pattern: a #cairo_pattern_t - * @filter: a #cairo_filter_t describing the filter to use for resizing - * the pattern - * - * Sets the filter to be used for resizing when using this pattern. - * See #cairo_filter_t for details on each filter. - * - * * Note that you might want to control filtering even when you do not - * have an explicit #cairo_pattern_t object, (for example when using - * cairo_set_source_surface()). In these cases, it is convenient to - * use cairo_get_source() to get access to the pattern that cairo - * creates implicitly. For example: - * - * - * cairo_set_source_surface (cr, image, x, y); - * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); - * - **/ -void -cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) -{ - if (pattern->status) - return; - - pattern->filter = filter; -} - -/** - * cairo_pattern_get_filter: - * @pattern: a #cairo_pattern_t - * - * Gets the current filter for a pattern. See #cairo_filter_t - * for details on each filter. - * - * Return value: the current filter used for resizing the pattern. - **/ -cairo_filter_t -cairo_pattern_get_filter (cairo_pattern_t *pattern) -{ - return pattern->filter; -} - -/** - * cairo_pattern_set_extend: - * @pattern: a #cairo_pattern_t - * @extend: a #cairo_extend_t describing how the area outside of the - * pattern will be drawn - * - * Sets the mode to be used for drawing outside the area of a pattern. - * See #cairo_extend_t for details on the semantics of each extend - * strategy. - * - * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns - * and %CAIRO_EXTEND_PAD for gradient patterns. - **/ -void -cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) -{ - if (pattern->status) - return; - - pattern->extend = extend; -} - -/** - * cairo_pattern_get_extend: - * @pattern: a #cairo_pattern_t - * - * Gets the current extend mode for a pattern. See #cairo_extend_t - * for details on the semantics of each extend strategy. - * - * Return value: the current extend strategy used for drawing the - * pattern. - **/ -cairo_extend_t -cairo_pattern_get_extend (cairo_pattern_t *pattern) -{ - return pattern->extend; -} -slim_hidden_def (cairo_pattern_get_extend); - -void -_cairo_pattern_transform (cairo_pattern_t *pattern, - const cairo_matrix_t *ctm_inverse) -{ - if (pattern->status) - return; - - cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); -} - -static void -_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, - double offset_x, - double offset_y, - int width, - int height, - cairo_bool_t *is_horizontal, - cairo_bool_t *is_vertical) -{ - cairo_point_double_t point0, point1; - double a, b, c, d, tx, ty; - double scale, start, dx, dy; - cairo_fixed_t factors[3]; - int i; - - /* To classify a pattern as horizontal or vertical, we first - * compute the (fixed point) factors at the corners of the - * pattern. We actually only need 3/4 corners, so we skip the - * fourth. - */ - point0.x = _cairo_fixed_to_double (pattern->p1.x); - point0.y = _cairo_fixed_to_double (pattern->p1.y); - point1.x = _cairo_fixed_to_double (pattern->p2.x); - point1.y = _cairo_fixed_to_double (pattern->p2.y); - - _cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - dx = point1.x - point0.x; - dy = point1.y - point0.y; - scale = dx * dx + dy * dy; - scale = (scale) ? 1.0 / scale : 1.0; - - start = dx * point0.x + dy * point0.y; - - for (i = 0; i < 3; i++) { - double qx_device = (i % 2) * (width - 1) + offset_x; - double qy_device = (i / 2) * (height - 1) + offset_y; - - /* transform fragment into pattern space */ - double qx = a * qx_device + c * qy_device + tx; - double qy = b * qx_device + d * qy_device + ty; - - factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); - } - - /* We consider a pattern to be vertical if the fixed point factor - * at the two upper corners is the same. We could accept a small - * change, but determining what change is acceptable would require - * sorting the stops in the pattern and looking at the differences. - * - * Horizontal works the same way with the two left corners. - */ - - *is_vertical = factors[1] == factors[0]; - *is_horizontal = factors[2] == factors[0]; -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_image_surface_t *image; - pixman_image_t *pixman_image; - pixman_transform_t pixman_transform; - cairo_status_t status; - cairo_bool_t repeat = FALSE; - cairo_bool_t opaque = TRUE; - - pixman_gradient_stop_t pixman_stops_static[2]; - pixman_gradient_stop_t *pixman_stops = pixman_stops_static; - unsigned int i; - int clone_offset_x, clone_offset_y; - cairo_matrix_t matrix = pattern->base.matrix; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, - sizeof(pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < pattern->n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); - pixman_stops[i].color.red = pattern->stops[i].color.red_short; - pixman_stops[i].color.green = pattern->stops[i].color.green_short; - pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; - pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; - if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha)) - opaque = FALSE; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - pixman_point_fixed_t p1, p2; - double x0, y0, x1, y1, maxabs; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ - x0 = _cairo_fixed_to_double (linear->p1.x); - y0 = _cairo_fixed_to_double (linear->p1.y); - x1 = _cairo_fixed_to_double (linear->p2.x); - y1 = _cairo_fixed_to_double (linear->p2.y); - cairo_matrix_transform_point (&matrix, &x0, &y0); - cairo_matrix_transform_point (&matrix, &x1, &y1); - maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); - -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ - if (maxabs > PIXMAN_MAX_INT) - { - double sf; - cairo_matrix_t scale; - - sf = PIXMAN_MAX_INT / maxabs; - - p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - /* cairo_matrix_scale does a pre-scale, we want a post-scale */ - cairo_matrix_init_scale (&scale, sf, sf); - cairo_matrix_multiply (&matrix, &matrix, &scale); - } - else - { - p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - pixman_image = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - pattern->n_stops); - } - else - { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - pixman_point_fixed_t c1, c2; - pixman_fixed_t r1, r2; - - c1.x = _cairo_fixed_to_16_16 (radial->c1.x); - c1.y = _cairo_fixed_to_16_16 (radial->c1.y); - r1 = _cairo_fixed_to_16_16 (radial->r1); - - c2.x = _cairo_fixed_to_16_16 (radial->c2.x); - c2.y = _cairo_fixed_to_16_16 (radial->c2.y); - r2 = _cairo_fixed_to_16_16 (radial->r2); - - pixman_image = pixman_image_create_radial_gradient (&c1, &c2, - r1, r2, - pixman_stops, - pattern->n_stops); - } - - if (pixman_stops != pixman_stops_static) - free (pixman_stops); - - if (unlikely (pixman_image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (_cairo_surface_is_image (dst)) - { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_for_pixman_image (pixman_image, - PIXMAN_a8r8g8b8); - if (image->base.status) - { - pixman_image_unref (pixman_image); - return image->base.status; - } - - attr->x_offset = attr->y_offset = 0; - attr->matrix = matrix; - attr->extend = pattern->base.extend; - attr->filter = CAIRO_FILTER_NEAREST; - attr->has_component_alpha = pattern->base.has_component_alpha; - - *out = &image->base; - - return CAIRO_STATUS_SUCCESS; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_bool_t is_horizontal; - cairo_bool_t is_vertical; - - _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, - x, y, width, height, - &is_horizontal, &is_vertical); - if (is_horizontal) { - height = 1; - repeat = TRUE; - } - /* width-1 repeating patterns are quite slow with scan-line based - * compositing code, so we use a wider strip and spend some extra - * expense in computing the gradient. It's possible that for narrow - * gradients we'd be better off using a 2 or 4 pixel strip; the - * wider the gradient, the more it's worth spending extra time - * computing a sample. - */ - if (is_vertical && width > 8) { - width = 8; - repeat = TRUE; - } - } - - if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, - NULL, 0)) - { - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - image = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - if (image->base.status) { - pixman_image_unref (pixman_image); - return image->base.status; - } - - _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform, - width/2., height/2.); - if (!pixman_image_set_transform (pixman_image, &pixman_transform)) { - cairo_surface_destroy (&image->base); - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - switch (pattern->base.extend) { - case CAIRO_EXTEND_NONE: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE); - break; - case CAIRO_EXTEND_REPEAT: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL); - break; - case CAIRO_EXTEND_REFLECT: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT); - break; - case CAIRO_EXTEND_PAD: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD); - break; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - pixman_image, - NULL, - image->pixman_image, - x, y, - 0, 0, - 0, 0, - width, height); - - pixman_image_unref (pixman_image); - - _cairo_debug_check_image_surface_is_defined (&image->base); - - status = _cairo_surface_clone_similar (dst, &image->base, - 0, 0, width, height, - &clone_offset_x, - &clone_offset_y, - out); - - cairo_surface_destroy (&image->base); - - attr->x_offset = -x; - attr->y_offset = -y; - cairo_matrix_init_identity (&attr->matrix); - attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; - attr->filter = CAIRO_FILTER_NEAREST; - attr->has_component_alpha = pattern->base.has_component_alpha; - - return status; -} - -/* We maintain a small cache here, because we don't want to constantly - * recreate surfaces for simple solid colors. */ -#define MAX_SURFACE_CACHE_SIZE 16 -static struct { - struct _cairo_pattern_solid_surface_cache{ - cairo_color_t color; - cairo_surface_t *surface; - } cache[MAX_SURFACE_CACHE_SIZE]; - int size; -} solid_surface_cache; - -static cairo_bool_t -_cairo_pattern_solid_surface_matches ( - const struct _cairo_pattern_solid_surface_cache *cache, - const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst) -{ - if (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color)) - return FALSE; - - if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1) - return FALSE; - - if (! _cairo_surface_is_similar (cache->surface, dst)) - return FALSE; - - return TRUE; -} - -static cairo_bool_t -_cairo_pattern_solid_surface_matches_color ( - const struct _cairo_pattern_solid_surface_cache *cache, - const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst) -{ - if (! _cairo_color_equal (&cache->color, &pattern->color)) - return FALSE; - - return _cairo_pattern_solid_surface_matches (cache, pattern, dst); -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attribs) -{ - static int i; - - cairo_surface_t *surface, *to_destroy = NULL; - cairo_status_t status; - - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* Check cache first */ - if (i < solid_surface_cache.size && - _cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], - pattern, - dst)) - { - goto DONE; - } - - for (i = 0 ; i < solid_surface_cache.size; i++) { - if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], - pattern, - dst)) - { - goto DONE; - } - } - - /* Choose a surface to repaint/evict */ - surface = NULL; - if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) { - i = rand () % MAX_SURFACE_CACHE_SIZE; - surface = solid_surface_cache.cache[i].surface; - - if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i], - pattern, - dst)) - { - /* Reuse the surface instead of evicting */ - status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); - if (unlikely (status)) - goto EVICT; - - cairo_surface_reference (surface); - } - else - { - EVICT: - surface = NULL; - } - } - - if (surface == NULL) { - /* Not cached, need to create new */ - surface = _cairo_surface_create_solid_pattern_surface (dst, pattern); - if (surface == NULL) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto UNLOCK; - } - if (unlikely (surface->status)) { - status = surface->status; - goto UNLOCK; - } - - if (unlikely (! _cairo_surface_is_similar (surface, dst))) - { - /* In the rare event of a substitute surface being returned, - * don't cache the fallback. - */ - *out = surface; - goto NOCACHE; - } - } - - if (i == solid_surface_cache.size) - solid_surface_cache.size++; - - to_destroy = solid_surface_cache.cache[i].surface; - solid_surface_cache.cache[i].surface = surface; - solid_surface_cache.cache[i].color = pattern->color; - -DONE: - *out = cairo_surface_reference (solid_surface_cache.cache[i].surface); - -NOCACHE: - attribs->x_offset = attribs->y_offset = 0; - cairo_matrix_init_identity (&attribs->matrix); - attribs->extend = CAIRO_EXTEND_REPEAT; - attribs->filter = CAIRO_FILTER_NEAREST; - attribs->has_component_alpha = pattern->base.has_component_alpha; - - status = CAIRO_STATUS_SUCCESS; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); - - if (to_destroy) - cairo_surface_destroy (to_destroy); - - return status; -} - -static void -_cairo_pattern_reset_solid_surface_cache (void) -{ - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* remove surfaces starting from the end so that solid_surface_cache.cache - * is always in a consistent state when we release the mutex. */ - while (solid_surface_cache.size) { - cairo_surface_t *surface; - - solid_surface_cache.size--; - surface = solid_surface_cache.cache[solid_surface_cache.size].surface; - solid_surface_cache.cache[solid_surface_cache.size].surface = NULL; - - /* release the lock to avoid the possibility of a recursive - * deadlock when the surface destroy closure gets called */ - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); - cairo_surface_destroy (surface); - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - } - - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); -} - -static void -_extents_to_linear_parameter (const cairo_linear_pattern_t *linear, - const cairo_rectangle_int_t *extents, - double t[2]) -{ - double t0, tdx, tdy; - double p1x, p1y, pdx, pdy, invsqnorm; - - p1x = _cairo_fixed_to_double (linear->p1.x); - p1y = _cairo_fixed_to_double (linear->p1.y); - pdx = _cairo_fixed_to_double (linear->p2.x) - p1x; - pdy = _cairo_fixed_to_double (linear->p2.y) - p1y; - invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); - pdx *= invsqnorm; - pdy *= invsqnorm; - - t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy; - tdx = extents->width * pdx; - tdy = extents->height * pdy; - - t[0] = t[1] = t0; - if (tdx < 0) - t[0] += tdx; - else - t[1] += tdx; - - if (tdy < 0) - t[0] += tdy; - else - t[1] += tdy; -} - -static cairo_bool_t -_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) -{ - return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y; -} - -static cairo_bool_t -_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial) -{ - return radial->r1 == radial->r2 && - (radial->r1 == 0 /* && radial->r2 == 0 */ || - (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y)); -} - -static cairo_bool_t -_gradient_is_clear (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents) -{ - unsigned int i; - - assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || - gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); - - if (gradient->n_stops == 0 || - (gradient->base.extend == CAIRO_EXTEND_NONE && - gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) - return TRUE; - - /* Check if the extents intersect the drawn part of the pattern. */ - if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - if (gradient->base.extend == CAIRO_EXTEND_NONE) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; - /* EXTEND_NONE degenerate linear gradients are clear */ - if (_linear_pattern_is_degenerate (linear)) - return TRUE; - - if (extents != NULL) { - double t[2]; - _extents_to_linear_parameter (linear, extents, t); - if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0)) - return TRUE; - } - } - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; - /* degenerate radial gradients are clear */ - if (_radial_pattern_is_degenerate (radial) && FALSE) - return TRUE; - /* TODO: check actual intersection */ - } - - for (i = 0; i < gradient->n_stops; i++) - if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color)) - return FALSE; - - return TRUE; -} - -/** - * _cairo_gradient_pattern_is_solid - * - * Convenience function to determine whether a gradient pattern is - * a solid color within the given extents. In this case the color - * argument is initialized to the color the pattern represents. - * This functions doesn't handle completely transparent gradients, - * thus it should be called only after _cairo_pattern_is_clear has - * returned FALSE. - * - * Return value: %TRUE if the pattern is a solid color. - **/ -cairo_bool_t -_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents, - cairo_color_t *color) -{ - unsigned int i; - - assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || - gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); - - /* TODO: radial, degenerate linear */ - if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - if (gradient->base.extend == CAIRO_EXTEND_NONE) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; - double t[2]; - - /* We already know that the pattern is not clear, thus if some - * part of it is clear, the whole is not solid. - */ - - if (extents == NULL) - return FALSE; - - _extents_to_linear_parameter (linear, extents, t); - if (t[0] < 0.0 || t[1] > 1.0) - return FALSE; - } - } - - for (i = 1; i < gradient->n_stops; i++) - if (! _cairo_color_stop_equal (&gradient->stops[0].color, - &gradient->stops[i].color)) - return FALSE; - - _cairo_color_init_rgba (color, - gradient->stops[0].color.red, - gradient->stops[0].color.green, - gradient->stops[0].color.blue, - gradient->stops[0].color.alpha); - - return TRUE; -} - -/** - * _cairo_pattern_is_opaque_solid - * - * Convenience function to determine whether a pattern is an opaque - * (alpha==1.0) solid color pattern. This is done by testing whether - * the pattern's alpha value when converted to a byte is 255, so if a - * backend actually supported deep alpha channels this function might - * not do the right thing. - * - * Return value: %TRUE if the pattern is an opaque, solid color. - **/ -cairo_bool_t -_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) -{ - cairo_solid_pattern_t *solid; - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return FALSE; - - solid = (cairo_solid_pattern_t *) pattern; - - return CAIRO_COLOR_IS_OPAQUE (&solid->color); -} - -static cairo_bool_t -_surface_is_opaque (const cairo_surface_pattern_t *pattern, - const cairo_rectangle_int_t *r) -{ - if (pattern->surface->content & CAIRO_CONTENT_ALPHA) - return FALSE; - - if (pattern->base.extend != CAIRO_EXTEND_NONE) - return TRUE; - - if (r != NULL) { - cairo_rectangle_int_t extents; - - if (! _cairo_surface_get_extents (pattern->surface, &extents)) - return TRUE; - - if (r->x >= extents.x && - r->y >= extents.y && - r->x + r->width <= extents.x + extents.width && - r->y + r->height <= extents.y + extents.height) - { - return TRUE; - } - } - - return FALSE; -} - -static cairo_bool_t -_surface_is_clear (const cairo_surface_pattern_t *pattern) -{ - cairo_rectangle_int_t extents; - - if (_cairo_surface_get_extents (pattern->surface, &extents) && - (extents.width == 0 || extents.height == 0)) - return TRUE; - - return pattern->surface->is_clear && - pattern->surface->content & CAIRO_CONTENT_ALPHA; -} - -static cairo_bool_t -_gradient_is_opaque (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents) -{ - unsigned int i; - - assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || - gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); - - if (gradient->n_stops == 0 || - (gradient->base.extend == CAIRO_EXTEND_NONE && - gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) - return FALSE; - - if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - if (gradient->base.extend == CAIRO_EXTEND_NONE) { - double t[2]; - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; - - /* EXTEND_NONE degenerate radial gradients are clear */ - if (_linear_pattern_is_degenerate (linear)) - return FALSE; - - if (extents == NULL) - return FALSE; - - _extents_to_linear_parameter (linear, extents, t); - if (t[0] < 0.0 || t[1] > 1.0) - return FALSE; - } - } - - for (i = 0; i < gradient->n_stops; i++) - if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) - return FALSE; - - return TRUE; -} - -/** - * _cairo_pattern_is_opaque - * - * Convenience function to determine whether a pattern is an opaque - * pattern (of any type). The same caveats that apply to - * _cairo_pattern_is_opaque_solid apply here as well. - * - * Return value: %TRUE if the pattern is a opaque. - **/ -cairo_bool_t -_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, - const cairo_rectangle_int_t *extents) -{ - const cairo_pattern_union_t *pattern; - - if (abstract_pattern->has_component_alpha) - return FALSE; - - pattern = (cairo_pattern_union_t *) abstract_pattern; - switch (pattern->base.type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_pattern_is_opaque_solid (abstract_pattern); - case CAIRO_PATTERN_TYPE_SURFACE: - return _surface_is_opaque (&pattern->surface, extents); - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - return _gradient_is_opaque (&pattern->gradient.base, extents); - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -cairo_bool_t -_cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) -{ - const cairo_pattern_union_t *pattern; - - if (abstract_pattern->has_component_alpha) - return FALSE; - - pattern = (cairo_pattern_union_t *) abstract_pattern; - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color); - case CAIRO_PATTERN_TYPE_SURFACE: - return _surface_is_clear (&pattern->surface); - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - return _gradient_is_clear (&pattern->gradient.base, NULL); - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -/** - * _cairo_pattern_analyze_filter: - * @pattern: surface pattern - * @pad_out: location to store necessary padding in the source image, or %NULL - * Returns: the optimized #cairo_filter_t to use with @pattern. - * - * Analyze the filter to determine how much extra needs to be sampled - * from the source image to account for the filter radius and whether - * we can optimize the filter to a simpler value. - * - * XXX: We don't actually have any way of querying the backend for - * the filter radius, so we just guess base on what we know that - * backends do currently (see bug #10508) - */ -cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out) -{ - double pad; - cairo_filter_t optimized_filter; - - switch (pattern->filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - /* If source pixels map 1:1 onto destination pixels, we do - * not need to filter (and do not want to filter, since it - * will cause blurriness) - */ - if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { - pad = 0.; - optimized_filter = CAIRO_FILTER_NEAREST; - } else { - /* 0.5 is enough for a bilinear filter. It's possible we - * should defensively use more for CAIRO_FILTER_BEST, but - * without a single example, it's hard to know how much - * more would be defensive... - */ - pad = 0.5; - optimized_filter = pattern->filter; - } - break; - - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - default: - pad = 0.; - optimized_filter = pattern->filter; - break; - } - - if (pad_out) - *pad_out = pad; - - return optimized_filter; -} - - -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_surface_t *surface; - cairo_rectangle_int_t extents; - cairo_rectangle_int_t sampled_area; - double x1, y1, x2, y2; - int tx, ty; - double pad; - cairo_bool_t is_identity; - cairo_bool_t is_empty; - cairo_bool_t is_bounded; - cairo_int_status_t status; - - surface = cairo_surface_reference (pattern->surface); - - is_identity = FALSE; - attr->matrix = pattern->base.matrix; - attr->extend = pattern->base.extend; - attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); - attr->has_component_alpha = pattern->base.has_component_alpha; - - attr->x_offset = attr->y_offset = tx = ty = 0; - if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { - cairo_matrix_init_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - is_identity = TRUE; - } else if (attr->filter == CAIRO_FILTER_NEAREST) { - /* - * For NEAREST, we can remove the fractional translation component - * from the transformation - this ensures that the pattern will always - * hit fast-paths in the backends for simple transformations that - * become (almost) identity, without loss of quality. - */ - attr->matrix.x0 = 0; - attr->matrix.y0 = 0; - if (_cairo_matrix_is_pixel_exact (&attr->matrix)) { - /* The rounding here is rather peculiar as it needs to match the - * rounding performed on the sample coordinate used by pixman. - */ - attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0); - attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0); - } else { - attr->matrix.x0 = pattern->base.matrix.x0; - attr->matrix.y0 = pattern->base.matrix.y0; - } - - if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { - cairo_matrix_init_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - is_identity = TRUE; - } - } - - /* XXX: Hack: - * - * The way we currently support CAIRO_EXTEND_REFLECT is to create - * an image twice bigger on each side, and create a pattern of four - * images such that the new image, when repeated, has the same effect - * of reflecting the original pattern. - */ - if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT && - attr->extend == CAIRO_EXTEND_REFLECT) - { - cairo_t *cr; - cairo_surface_t *src; - int w, h; - - is_bounded = _cairo_surface_get_extents (surface, &extents); - assert (is_bounded); - - status = _cairo_surface_clone_similar (dst, surface, - extents.x, extents.y, - extents.width, extents.height, - &extents.x, &extents.y, &src); - if (unlikely (status)) - goto BAIL; - - w = 2 * extents.width; - h = 2 * extents.height; - - if (is_identity) { - attr->x_offset = -x; - x += tx; - while (x <= -w) - x += w; - while (x >= w) - x -= w; - extents.x += x; - tx = x = 0; - - attr->y_offset = -y; - y += ty; - while (y <= -h) - y += h; - while (y >= h) - y -= h; - extents.y += y; - ty = y = 0; - } - - cairo_surface_destroy (surface); - surface = _cairo_surface_create_similar_solid (dst, - dst->content, w, h, - CAIRO_COLOR_TRANSPARENT, - FALSE); - if (surface == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (surface->status)) { - cairo_surface_destroy (src); - return surface->status; - } - - surface->device_transform = pattern->surface->device_transform; - surface->device_transform_inverse = pattern->surface->device_transform_inverse; - - cr = cairo_create (surface); - - cairo_set_source_surface (cr, src, -extents.x, -extents.y); - cairo_paint (cr); - - cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, src, extents.x-w, -extents.y); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, -extents.y); - cairo_paint (cr); - - cairo_scale (cr, +1, -1); - cairo_set_source_surface (cr, src, extents.x-w, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x-w, extents.y); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, extents.y); - cairo_paint (cr); - - cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, src, -extents.x, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, -extents.x, extents.y); - cairo_paint (cr); - - status = cairo_status (cr); - cairo_destroy (cr); - - cairo_surface_destroy (src); - - if (unlikely (status)) - goto BAIL; - - attr->extend = CAIRO_EXTEND_REPEAT; - } - - /* We first transform the rectangle to the coordinate space of the - * source surface so that we only need to clone that portion of the - * surface that will be read. - */ - x1 = x; - y1 = y; - x2 = x + (int) width; - y2 = y + (int) height; - if (! is_identity) { - _cairo_matrix_transform_bounding_box (&attr->matrix, - &x1, &y1, &x2, &y2, - NULL); - } - - sampled_area.x = floor (x1 - pad); - sampled_area.y = floor (y1 - pad); - sampled_area.width = ceil (x2 + pad) - sampled_area.x; - sampled_area.height = ceil (y2 + pad) - sampled_area.y; - - sampled_area.x += tx; - sampled_area.y += ty; - - if ( _cairo_surface_get_extents (surface, &extents)) { - if (attr->extend == CAIRO_EXTEND_NONE) { - /* Never acquire a larger area than the source itself */ - is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); - } else { - int trim = 0; - - if (sampled_area.x >= extents.x && - sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) - { - /* source is horizontally contained within extents, trim */ - extents.x = sampled_area.x; - extents.width = sampled_area.width; - trim |= 0x1; - } - - if (sampled_area.y >= extents.y && - sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) - { - /* source is vertically contained within extents, trim */ - extents.y = sampled_area.y; - extents.height = sampled_area.height; - trim |= 0x2; - } - - if (trim == 0x3) { - /* source is wholly contained within extents, drop the REPEAT */ - attr->extend = CAIRO_EXTEND_NONE; - } - - is_empty = extents.width == 0 || extents.height == 0; - } - } - - /* XXX can we use is_empty? */ - - status = _cairo_surface_clone_similar (dst, surface, - extents.x, extents.y, - extents.width, extents.height, - &x, &y, out); - if (unlikely (status)) - goto BAIL; - - if (x != 0 || y != 0) { - if (is_identity) { - attr->x_offset -= x; - attr->y_offset -= y; - } else { - cairo_matrix_t m; - - x -= attr->x_offset; - y -= attr->y_offset; - attr->x_offset = 0; - attr->y_offset = 0; - - cairo_matrix_init_translate (&m, -x, -y); - cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); - } - } - - /* reduce likelihood of range overflow with large downscaling */ - if (! is_identity) { - cairo_matrix_t m; - cairo_status_t invert_status; - - m = attr->matrix; - invert_status = cairo_matrix_invert (&m); - assert (invert_status == CAIRO_STATUS_SUCCESS); - - if (m.x0 != 0. || m.y0 != 0.) { - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - x = floor (m.x0 / 2); - y = floor (m.y0 / 2); - attr->x_offset -= x; - attr->y_offset -= y; - cairo_matrix_init_translate (&m, x, y); - cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix); - } - } - - BAIL: - cairo_surface_destroy (surface); - return status; -} - -/** - * _cairo_pattern_acquire_surface: - * @pattern: a #cairo_pattern_t - * @dst: destination surface - * @x: X coordinate in source corresponding to left side of destination area - * @y: Y coordinate in source corresponding to top side of destination area - * @width: width of destination area - * @height: height of destination area - * @surface_out: location to store a pointer to a surface - * @attributes: surface attributes that destination backend should apply to - * the returned surface - * - * A convenience function to obtain a surface to use as the source for - * drawing on @dst. - * - * Note that this function is only suitable for use when the destination - * surface is pixel based and 1 device unit maps to one pixel. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. - **/ -cairo_int_status_t -_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **surface_out, - cairo_surface_attributes_t *attributes) -{ - if (unlikely (pattern->status)) { - *surface_out = NULL; - return pattern->status; - } - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern, - dst, x, y, width, height, - surface_out, - attributes); - - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern, - dst, x, y, width, height, - surface_out, - attributes); - - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern, - dst, x, y, width, height, - flags, - surface_out, - attributes); - - default: - ASSERT_NOT_REACHED; - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - } -} - -/** - * _cairo_pattern_release_surface: - * @pattern: a #cairo_pattern_t - * @surface: a surface obtained by _cairo_pattern_acquire_surface - * @attributes: attributes obtained by _cairo_pattern_acquire_surface - * - * Releases resources obtained by _cairo_pattern_acquire_surface. - **/ -void -_cairo_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - cairo_surface_destroy (surface); -} - -cairo_int_status_t -_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **src_out, - cairo_surface_t **mask_out, - cairo_surface_attributes_t *src_attributes, - cairo_surface_attributes_t *mask_attributes) -{ - cairo_int_status_t status; - cairo_pattern_union_t src_tmp; - - if (unlikely (src->status)) - return src->status; - if (unlikely (mask != NULL && mask->status)) - return mask->status; - - /* If src and mask are both solid, then the mask alpha can be - * combined into src and mask can be ignored. */ - - if (src->type == CAIRO_PATTERN_TYPE_SOLID && - mask && - ! mask->has_component_alpha && - mask->type == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_color_t combined; - cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; - cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; - - combined = src_solid->color; - _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - - _cairo_pattern_init_solid (&src_tmp.solid, &combined); - - src = &src_tmp.base; - mask = NULL; - } - - status = _cairo_pattern_acquire_surface (src, dst, - src_x, src_y, - width, height, - flags, - src_out, src_attributes); - if (unlikely (status)) - goto BAIL; - - if (mask == NULL) { - *mask_out = NULL; - goto BAIL; - } - - status = _cairo_pattern_acquire_surface (mask, dst, - mask_x, mask_y, - width, height, - flags, - mask_out, mask_attributes); - if (unlikely (status)) - _cairo_pattern_release_surface (src, *src_out, src_attributes); - - BAIL: - if (src == &src_tmp.base) - _cairo_pattern_fini (&src_tmp.base); - - return status; -} - -/** - * _cairo_pattern_get_extents: - * - * Return the "target-space" extents of @pattern in @extents. - * - * For unbounded patterns, the @extents will be initialized with - * "infinite" extents, (minimum and maximum fixed-point values). - * - * XXX: Currently, bounded gradient patterns will also return - * "infinite" extents, though it would be possible to optimize these - * with a little more work. - **/ -void -_cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents) -{ - double x1, y1, x2, y2; - cairo_status_t status; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - goto UNBOUNDED; - - case CAIRO_PATTERN_TYPE_SURFACE: - { - cairo_rectangle_int_t surface_extents; - const cairo_surface_pattern_t *surface_pattern = - (const cairo_surface_pattern_t *) pattern; - cairo_surface_t *surface = surface_pattern->surface; - double pad; - - if (! _cairo_surface_get_extents (surface, &surface_extents)) - goto UNBOUNDED; - - if (surface_extents.width == 0 || surface_extents.height == 0) - goto EMPTY; - - if (pattern->extend != CAIRO_EXTEND_NONE) - goto UNBOUNDED; - - /* The filter can effectively enlarge the extents of the - * pattern, so extend as necessary. - */ - _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); - x1 = surface_extents.x - pad; - y1 = surface_extents.y - pad; - x2 = surface_extents.x + (int) surface_extents.width + pad; - y2 = surface_extents.y + (int) surface_extents.height + pad; - } - break; - - case CAIRO_PATTERN_TYPE_RADIAL: - { - const cairo_radial_pattern_t *radial = - (const cairo_radial_pattern_t *) pattern; - double cx1, cy1; - double cx2, cy2; - double r, D; - - if (radial->r1 == 0 && radial->r2 == 0) - goto EMPTY; - - cx1 = _cairo_fixed_to_double (radial->c1.x); - cy1 = _cairo_fixed_to_double (radial->c1.y); - r = _cairo_fixed_to_double (radial->r1); - x1 = cx1 - r; x2 = cx1 + r; - y1 = cy1 - r; y2 = cy1 + r; - - cx2 = _cairo_fixed_to_double (radial->c2.x); - cy2 = _cairo_fixed_to_double (radial->c2.y); - r = fabs (_cairo_fixed_to_double (radial->r2)); - - if (pattern->extend != CAIRO_EXTEND_NONE) - goto UNBOUNDED; - - /* We need to be careful, as if the circles are not - * self-contained, then the solution is actually unbounded. - */ - D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2); - if (D > r*r - 1e-5) - goto UNBOUNDED; - - if (cx2 - r < x1) - x1 = cx2 - r; - if (cx2 + r > x2) - x2 = cx2 + r; - - if (cy2 - r < y1) - y1 = cy2 - r; - if (cy2 + r > y2) - y2 = cy2 + r; - } - break; - - case CAIRO_PATTERN_TYPE_LINEAR: - { - const cairo_linear_pattern_t *linear = - (const cairo_linear_pattern_t *) pattern; - - if (pattern->extend != CAIRO_EXTEND_NONE) - goto UNBOUNDED; - - if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y) - goto EMPTY; - - if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) - goto UNBOUNDED; - - if (linear->p1.x == linear->p2.x) { - x1 = -HUGE_VAL; - x2 = HUGE_VAL; - y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y)); - y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y)); - } else if (linear->p1.y == linear->p2.y) { - x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x)); - x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x)); - y1 = -HUGE_VAL; - y2 = HUGE_VAL; - } else { - goto UNBOUNDED; - } - } - break; - - default: - ASSERT_NOT_REACHED; - } - - if (_cairo_matrix_is_translation (&pattern->matrix)) { - x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0; - y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0; - } else { - cairo_matrix_t imatrix; - - imatrix = pattern->matrix; - status = cairo_matrix_invert (&imatrix); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_matrix_transform_bounding_box (&imatrix, - &x1, &y1, &x2, &y2, - NULL); - } - - x1 = floor (x1); - if (x1 < CAIRO_RECT_INT_MIN) - x1 = CAIRO_RECT_INT_MIN; - y1 = floor (y1); - if (y1 < CAIRO_RECT_INT_MIN) - y1 = CAIRO_RECT_INT_MIN; - - x2 = ceil (x2); - if (x2 > CAIRO_RECT_INT_MAX) - x2 = CAIRO_RECT_INT_MAX; - y2 = ceil (y2); - if (y2 > CAIRO_RECT_INT_MAX) - y2 = CAIRO_RECT_INT_MAX; - - extents->x = x1; extents->width = x2 - x1; - extents->y = y1; extents->height = y2 - y1; - return; - - UNBOUNDED: - /* unbounded patterns -> 'infinite' extents */ - _cairo_unbounded_rectangle_init (extents); - return; - - EMPTY: - extents->x = extents->y = 0; - extents->width = extents->height = 0; - return; -} - - -static unsigned long -_cairo_solid_pattern_hash (unsigned long hash, - const cairo_pattern_t *pattern) -{ - const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - - hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); - - return hash; -} - -static unsigned long -_cairo_gradient_color_stops_hash (unsigned long hash, - const cairo_gradient_pattern_t *gradient) -{ - unsigned int n; - - hash = _cairo_hash_bytes (hash, - &gradient->n_stops, - sizeof (gradient->n_stops)); - - for (n = 0; n < gradient->n_stops; n++) { - hash = _cairo_hash_bytes (hash, - &gradient->stops[n].offset, - sizeof (double)); - hash = _cairo_hash_bytes (hash, - &gradient->stops[n].color, - sizeof (cairo_color_t)); - } - - return hash; -} - -unsigned long -_cairo_linear_pattern_hash (unsigned long hash, - const cairo_linear_pattern_t *linear) -{ - hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); - hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); - - return _cairo_gradient_color_stops_hash (hash, &linear->base); -} - -unsigned long -_cairo_radial_pattern_hash (unsigned long hash, - const cairo_radial_pattern_t *radial) -{ - hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); - hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); - hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); - hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2)); - - return _cairo_gradient_color_stops_hash (hash, &radial->base); -} - -static unsigned long -_cairo_surface_pattern_hash (unsigned long hash, - const cairo_pattern_t *pattern) -{ - const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; - - hash ^= surface->surface->unique_id; - - return hash; -} - -unsigned long -_cairo_pattern_hash (const cairo_pattern_t *pattern) -{ - unsigned long hash = _CAIRO_HASH_INIT_VALUE; - - if (pattern->status) - return 0; - - hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - hash = _cairo_hash_bytes (hash, - &pattern->matrix, sizeof (pattern->matrix)); - hash = _cairo_hash_bytes (hash, - &pattern->filter, sizeof (pattern->filter)); - hash = _cairo_hash_bytes (hash, - &pattern->extend, sizeof (pattern->extend)); - hash = _cairo_hash_bytes (hash, - &pattern->has_component_alpha, - sizeof (pattern->has_component_alpha)); - } - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_solid_pattern_hash (hash, pattern); - case CAIRO_PATTERN_TYPE_LINEAR: - return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); - case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_surface_pattern_hash (hash, pattern); - default: - ASSERT_NOT_REACHED; - return FALSE; - } -} - -static unsigned long -_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern) -{ - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; - - return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t)); -} - -unsigned long -_cairo_pattern_size (const cairo_pattern_t *pattern) -{ - if (pattern->status) - return 0; - - /* XXX */ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return sizeof (cairo_solid_pattern_t); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - return sizeof (cairo_surface_pattern_t); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - return sizeof (cairo_linear_pattern_t) + - _cairo_gradient_pattern_color_stops_size (pattern); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - return sizeof (cairo_radial_pattern_t) + - _cairo_gradient_pattern_color_stops_size (pattern); - default: - ASSERT_NOT_REACHED; - return 0; - } -} - - -static cairo_bool_t -_cairo_solid_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) -{ - const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A; - const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B; - - return _cairo_color_equal (&a->color, &b->color); -} - -static cairo_bool_t -_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, - const cairo_gradient_pattern_t *b) -{ - unsigned int n; - - if (a->n_stops != b->n_stops) - return FALSE; - - for (n = 0; n < a->n_stops; n++) { - if (a->stops[n].offset != b->stops[n].offset) - return FALSE; - if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) - return FALSE; - } - - return TRUE; -} - -cairo_bool_t -_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, - const cairo_linear_pattern_t *b) -{ - if (a->p1.x != b->p1.x) - return FALSE; - - if (a->p1.y != b->p1.y) - return FALSE; - - if (a->p2.x != b->p2.x) - return FALSE; - - if (a->p2.y != b->p2.y) - return FALSE; - - return _cairo_gradient_color_stops_equal (&a->base, &b->base); -} - -cairo_bool_t -_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, - const cairo_radial_pattern_t *b) -{ - if (a->c1.x != b->c1.x) - return FALSE; - - if (a->c1.y != b->c1.y) - return FALSE; - - if (a->r1 != b->r1) - return FALSE; - - if (a->c2.x != b->c2.x) - return FALSE; - - if (a->c2.y != b->c2.y) - return FALSE; - - if (a->r2 != b->r2) - return FALSE; - - return _cairo_gradient_color_stops_equal (&a->base, &b->base); -} - -static cairo_bool_t -_cairo_surface_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) -{ - const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A; - const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B; - - return a->surface->unique_id == b->surface->unique_id; -} - -cairo_bool_t -_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) -{ - if (a->status || b->status) - return FALSE; - - if (a == b) - return TRUE; - - if (a->type != b->type) - return FALSE; - - if (a->has_component_alpha != b->has_component_alpha) - return FALSE; - - if (a->type != CAIRO_PATTERN_TYPE_SOLID) { - if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) - return FALSE; - - if (a->filter != b->filter) - return FALSE; - - if (a->extend != b->extend) - return FALSE; - } - - switch (a->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_solid_pattern_equal (a, b); - case CAIRO_PATTERN_TYPE_LINEAR: - return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, - (cairo_linear_pattern_t *) b); - case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, - (cairo_radial_pattern_t *) b); - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_surface_pattern_equal (a, b); - default: - ASSERT_NOT_REACHED; - return FALSE; - } -} - -/** - * cairo_pattern_get_rgba - * @pattern: a #cairo_pattern_t - * @red: return value for red component of color, or %NULL - * @green: return value for green component of color, or %NULL - * @blue: return value for blue component of color, or %NULL - * @alpha: return value for alpha component of color, or %NULL - * - * Gets the solid color for a solid color pattern. - * - * Return value: %CAIRO_STATUS_SUCCESS, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid - * color pattern. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_get_rgba (cairo_pattern_t *pattern, - double *red, double *green, - double *blue, double *alpha) -{ - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; - double r0, g0, b0, a0; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0); - - if (red) - *red = r0; - if (green) - *green = g0; - if (blue) - *blue = b0; - if (alpha) - *alpha = a0; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_pattern_get_surface - * @pattern: a #cairo_pattern_t - * @surface: return value for surface of pattern, or %NULL - * - * Gets the surface of a surface pattern. The reference returned in - * @surface is owned by the pattern; the caller should call - * cairo_surface_reference() if the surface is to be retained. - * - * Return value: %CAIRO_STATUS_SUCCESS, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface - * pattern. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_get_surface (cairo_pattern_t *pattern, - cairo_surface_t **surface) -{ - cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_STATUS_PATTERN_TYPE_MISMATCH; - - if (surface) - *surface = spat->surface; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_pattern_get_color_stop_rgba - * @pattern: a #cairo_pattern_t - * @index: index of the stop to return data for - * @offset: return value for the offset of the stop, or %NULL - * @red: return value for red component of color, or %NULL - * @green: return value for green component of color, or %NULL - * @blue: return value for blue component of color, or %NULL - * @alpha: return value for alpha component of color, or %NULL - * - * Gets the color and offset information at the given @index for a - * gradient pattern. Values of @index are 0 to 1 less than the number - * returned by cairo_pattern_get_color_stop_count(). - * - * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX - * if @index is not valid for the given pattern. If the pattern is - * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is - * returned. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, - int index, double *offset, - double *red, double *green, - double *blue, double *alpha) -{ - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && - pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - if (index < 0 || (unsigned int) index >= gradient->n_stops) - return _cairo_error (CAIRO_STATUS_INVALID_INDEX); - - if (offset) - *offset = gradient->stops[index].offset; - if (red) - *red = gradient->stops[index].color.red; - if (green) - *green = gradient->stops[index].color.green; - if (blue) - *blue = gradient->stops[index].color.blue; - if (alpha) - *alpha = gradient->stops[index].color.alpha; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_pattern_get_color_stop_count - * @pattern: a #cairo_pattern_t - * @count: return value for the number of color stops, or %NULL - * - * Gets the number of color stops specified in the given gradient - * pattern. - * - * Return value: %CAIRO_STATUS_SUCCESS, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient - * pattern. - * - * Since: 1.4 - */ -cairo_status_t -cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, - int *count) -{ - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && - pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - if (count) - *count = gradient->n_stops; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_pattern_get_linear_points - * @pattern: a #cairo_pattern_t - * @x0: return value for the x coordinate of the first point, or %NULL - * @y0: return value for the y coordinate of the first point, or %NULL - * @x1: return value for the x coordinate of the second point, or %NULL - * @y1: return value for the y coordinate of the second point, or %NULL - * - * Gets the gradient endpoints for a linear gradient. - * - * Return value: %CAIRO_STATUS_SUCCESS, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear - * gradient pattern. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_get_linear_points (cairo_pattern_t *pattern, - double *x0, double *y0, - double *x1, double *y1) -{ - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - if (x0) - *x0 = _cairo_fixed_to_double (linear->p1.x); - if (y0) - *y0 = _cairo_fixed_to_double (linear->p1.y); - if (x1) - *x1 = _cairo_fixed_to_double (linear->p2.x); - if (y1) - *y1 = _cairo_fixed_to_double (linear->p2.y); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_pattern_get_radial_circles - * @pattern: a #cairo_pattern_t - * @x0: return value for the x coordinate of the center of the first circle, or %NULL - * @y0: return value for the y coordinate of the center of the first circle, or %NULL - * @r0: return value for the radius of the first circle, or %NULL - * @x1: return value for the x coordinate of the center of the second circle, or %NULL - * @y1: return value for the y coordinate of the center of the second circle, or %NULL - * @r1: return value for the radius of the second circle, or %NULL - * - * Gets the gradient endpoint circles for a radial gradient, each - * specified as a center coordinate and a radius. - * - * Return value: %CAIRO_STATUS_SUCCESS, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial - * gradient pattern. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, - double *x0, double *y0, double *r0, - double *x1, double *y1, double *r1) -{ - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern; - - if (pattern->status) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - if (x0) - *x0 = _cairo_fixed_to_double (radial->c1.x); - if (y0) - *y0 = _cairo_fixed_to_double (radial->c1.y); - if (r0) - *r0 = _cairo_fixed_to_double (radial->r1); - if (x1) - *x1 = _cairo_fixed_to_double (radial->c2.x); - if (y1) - *y1 = _cairo_fixed_to_double (radial->c2.y); - if (r1) - *r1 = _cairo_fixed_to_double (radial->r2); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_reset_static_data (void) -{ -#if HAS_FREED_POOL - int i; - - for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) - _freed_pool_reset (&freed_pattern_pool[i]); -#endif - - _cairo_pattern_reset_solid_surface_cache (); -} diff --git a/libs/cairo/cairo/src/cairo-pdf-operators-private.h b/libs/cairo/cairo/src/cairo-pdf-operators-private.h deleted file mode 100644 index d0051433b..000000000 --- a/libs/cairo/cairo/src/cairo-pdf-operators-private.h +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PDF_OPERATORS_H -#define CAIRO_PDF_OPERATORS_H - -#include "cairo-compiler-private.h" -#include "cairo-types-private.h" - -/* The glyph buffer size is based on the expected maximum glyphs in a - * line so that an entire line can be emitted in as one string. If the - * glyphs in a line exceeds this size the only downside is the slight - * overhead of emitting two strings. - */ -#define PDF_GLYPH_BUFFER_SIZE 200 - -typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id, - unsigned int subset_id, - void *closure); - -typedef struct _cairo_pdf_glyph { - unsigned int glyph_index; - double x_position; - double x_advance; -} cairo_pdf_glyph_t; - -typedef struct _cairo_pdf_operators { - cairo_output_stream_t *stream; - cairo_matrix_t cairo_to_pdf; - cairo_scaled_font_subsets_t *font_subsets; - cairo_pdf_operators_use_font_subset_t use_font_subset; - void *use_font_subset_closure; - cairo_bool_t use_actual_text; - cairo_bool_t in_text_object; /* inside BT/ET pair */ - - /* PDF text state */ - cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */ - unsigned int font_id; - unsigned int subset_id; - cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */ - cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */ - cairo_matrix_t font_matrix_inverse; - double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */ - double cur_y; - int hex_width; - int num_glyphs; - double glyph_buf_x_pos; - cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE]; - - /* PDF line style */ - cairo_bool_t has_line_style; - double line_width; - cairo_line_cap_t line_cap; - cairo_line_join_t line_join; - double miter_limit; - cairo_bool_t has_dashes; -} cairo_pdf_operators_t; - -cairo_private void -_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream, - cairo_matrix_t *cairo_to_pdf, - cairo_scaled_font_subsets_t *font_subsets); - -cairo_private cairo_status_t -_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators); - -cairo_private void -_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, - cairo_pdf_operators_use_font_subset_t use_font_subset, - void *closure); - -cairo_private void -_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream); - - -cairo_private void -_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, - cairo_matrix_t *cairo_to_pdf); - -cairo_private void -_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, - cairo_bool_t enable); - -cairo_private cairo_status_t -_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators); - -cairo_private void -_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, - const cairo_stroke_style_t *style, - double scale); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse); - -cairo_private cairo_int_status_t -_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font); - -#endif /* CAIRO_PDF_OPERATORS_H */ diff --git a/libs/cairo/cairo/src/cairo-pdf-operators.c b/libs/cairo/cairo/src/cairo-pdf-operators.c deleted file mode 100644 index 4da9d573c..000000000 --- a/libs/cairo/cairo/src/cairo-pdf-operators.c +++ /dev/null @@ -1,1433 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#if CAIRO_HAS_PDF_OPERATORS - -#include "cairo-error-private.h" -#include "cairo-pdf-operators-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-scaled-font-subsets-private.h" - -static cairo_status_t -_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); - - -void -_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream, - cairo_matrix_t *cairo_to_pdf, - cairo_scaled_font_subsets_t *font_subsets) -{ - pdf_operators->stream = stream; - pdf_operators->cairo_to_pdf = *cairo_to_pdf; - pdf_operators->font_subsets = font_subsets; - pdf_operators->use_font_subset = NULL; - pdf_operators->use_font_subset_closure = NULL; - pdf_operators->in_text_object = FALSE; - pdf_operators->num_glyphs = 0; - pdf_operators->has_line_style = FALSE; - pdf_operators->use_actual_text = FALSE; -} - -cairo_status_t -_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators) -{ - return _cairo_pdf_operators_flush (pdf_operators); -} - -void -_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, - cairo_pdf_operators_use_font_subset_t use_font_subset, - void *closure) -{ - pdf_operators->use_font_subset = use_font_subset; - pdf_operators->use_font_subset_closure = closure; -} - -/* Change the output stream to a different stream. - * _cairo_pdf_operators_flush() should always be called before calling - * this function. - */ -void -_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream) -{ - pdf_operators->stream = stream; - pdf_operators->has_line_style = FALSE; -} - -void -_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, - cairo_matrix_t *cairo_to_pdf) -{ - pdf_operators->cairo_to_pdf = *cairo_to_pdf; - pdf_operators->has_line_style = FALSE; -} - -cairo_private void -_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, - cairo_bool_t enable) -{ - pdf_operators->use_actual_text = enable; -} - -/* Finish writing out any pending commands to the stream. This - * function must be called by the surface before emitting anything - * into the PDF stream. - * - * pdf_operators may leave the emitted PDF for some operations - * unfinished in case subsequent operations can be merged. This - * function will finish off any incomplete operation so the stream - * will be in a state where the surface may emit its own PDF - * operations (eg changing patterns). - * - */ -cairo_status_t -_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (pdf_operators->in_text_object) - status = _cairo_pdf_operators_end_text (pdf_operators); - - return status; -} - -/* Reset the known graphics state of the PDF consumer. ie no - * assumptions will be made about the state. The next time a - * particular graphics state is required (eg line width) the state - * operator is always emitted and then remembered for subsequent - * operatations. - * - * This should be called when starting a new stream or after emitting - * the 'Q' operator (where pdf-operators functions were called inside - * the q/Q pair). - */ -void -_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) -{ - pdf_operators->has_line_style = FALSE; -} - -/* A word wrap stream can be used as a filter to do word wrapping on - * top of an existing output stream. The word wrapping is quite - * simple, using isspace to determine characters that separate - * words. Any word that will cause the column count exceed the given - * max_column will have a '\n' character emitted before it. - * - * The stream is careful to maintain integrity for words that cross - * the boundary from one call to write to the next. - * - * Note: This stream does not guarantee that the output will never - * exceed max_column. In particular, if a single word is larger than - * max_column it will not be broken up. - */ -typedef struct _word_wrap_stream { - cairo_output_stream_t base; - cairo_output_stream_t *output; - int max_column; - int column; - cairo_bool_t last_write_was_space; - cairo_bool_t in_hexstring; - cairo_bool_t empty_hexstring; -} word_wrap_stream_t; - -static int -_count_word_up_to (const unsigned char *s, int length) -{ - int word = 0; - - while (length--) { - if (! (_cairo_isspace (*s) || *s == '<')) { - s++; - word++; - } else { - return word; - } - } - - return word; -} - - -/* Count up to either the end of the ASCII hexstring or the number - * of columns remaining. - */ -static int -_count_hexstring_up_to (const unsigned char *s, int length, int columns) -{ - int word = 0; - - while (length--) { - if (*s++ != '>') - word++; - else - return word; - - columns--; - if (columns < 0 && word > 1) - return word; - } - - return word; -} - -static cairo_status_t -_word_wrap_stream_write (cairo_output_stream_t *base, - const unsigned char *data, - unsigned int length) -{ - word_wrap_stream_t *stream = (word_wrap_stream_t *) base; - cairo_bool_t newline; - int word; - - while (length) { - if (*data == '<') { - stream->in_hexstring = TRUE; - stream->empty_hexstring = TRUE; - stream->last_write_was_space = FALSE; - data++; - length--; - _cairo_output_stream_printf (stream->output, "<"); - stream->column++; - } else if (*data == '>') { - stream->in_hexstring = FALSE; - stream->last_write_was_space = FALSE; - data++; - length--; - _cairo_output_stream_printf (stream->output, ">"); - stream->column++; - } else if (_cairo_isspace (*data)) { - newline = (*data == '\n' || *data == '\r'); - if (! newline && stream->column >= stream->max_column) { - _cairo_output_stream_printf (stream->output, "\n"); - stream->column = 0; - } - _cairo_output_stream_write (stream->output, data, 1); - data++; - length--; - if (newline) { - stream->column = 0; - } - else - stream->column++; - stream->last_write_was_space = TRUE; - } else { - if (stream->in_hexstring) { - word = _count_hexstring_up_to (data, length, - MAX (stream->max_column - stream->column, 0)); - } else { - word = _count_word_up_to (data, length); - } - /* Don't wrap if this word is a continuation of a non hex - * string word from a previous call to write. */ - if (stream->column + word >= stream->max_column) { - if (stream->last_write_was_space || - (stream->in_hexstring && !stream->empty_hexstring)) - { - _cairo_output_stream_printf (stream->output, "\n"); - stream->column = 0; - } - } - _cairo_output_stream_write (stream->output, data, word); - data += word; - length -= word; - stream->column += word; - stream->last_write_was_space = FALSE; - if (stream->in_hexstring) - stream->empty_hexstring = FALSE; - } - } - - return _cairo_output_stream_get_status (stream->output); -} - -static cairo_status_t -_word_wrap_stream_close (cairo_output_stream_t *base) -{ - word_wrap_stream_t *stream = (word_wrap_stream_t *) base; - - return _cairo_output_stream_get_status (stream->output); -} - -static cairo_output_stream_t * -_word_wrap_stream_create (cairo_output_stream_t *output, int max_column) -{ - word_wrap_stream_t *stream; - - if (output->status) - return _cairo_output_stream_create_in_error (output->status); - - stream = malloc (sizeof (word_wrap_stream_t)); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - _word_wrap_stream_write, - NULL, - _word_wrap_stream_close); - stream->output = output; - stream->max_column = max_column; - stream->column = 0; - stream->last_write_was_space = FALSE; - stream->in_hexstring = FALSE; - stream->empty_hexstring = TRUE; - - return &stream->base; -} - -typedef struct _pdf_path_info { - cairo_output_stream_t *output; - cairo_matrix_t *path_transform; - cairo_line_cap_t line_cap; - cairo_point_t last_move_to_point; - cairo_bool_t has_sub_path; -} pdf_path_info_t; - -static cairo_status_t -_cairo_pdf_path_move_to (void *closure, - const cairo_point_t *point) -{ - pdf_path_info_t *info = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - info->last_move_to_point = *point; - info->has_sub_path = FALSE; - cairo_matrix_transform_point (info->path_transform, &x, &y); - _cairo_output_stream_printf (info->output, - "%g %g m ", x, y); - - return _cairo_output_stream_get_status (info->output); -} - -static cairo_status_t -_cairo_pdf_path_line_to (void *closure, - const cairo_point_t *point) -{ - pdf_path_info_t *info = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (info->line_cap != CAIRO_LINE_CAP_ROUND && - ! info->has_sub_path && - point->x == info->last_move_to_point.x && - point->y == info->last_move_to_point.y) - { - return CAIRO_STATUS_SUCCESS; - } - - info->has_sub_path = TRUE; - cairo_matrix_transform_point (info->path_transform, &x, &y); - _cairo_output_stream_printf (info->output, - "%g %g l ", x, y); - - return _cairo_output_stream_get_status (info->output); -} - -static cairo_status_t -_cairo_pdf_path_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - pdf_path_info_t *info = closure; - double bx = _cairo_fixed_to_double (b->x); - double by = _cairo_fixed_to_double (b->y); - double cx = _cairo_fixed_to_double (c->x); - double cy = _cairo_fixed_to_double (c->y); - double dx = _cairo_fixed_to_double (d->x); - double dy = _cairo_fixed_to_double (d->y); - - info->has_sub_path = TRUE; - cairo_matrix_transform_point (info->path_transform, &bx, &by); - cairo_matrix_transform_point (info->path_transform, &cx, &cy); - cairo_matrix_transform_point (info->path_transform, &dx, &dy); - _cairo_output_stream_printf (info->output, - "%g %g %g %g %g %g c ", - bx, by, cx, cy, dx, dy); - return _cairo_output_stream_get_status (info->output); -} - -static cairo_status_t -_cairo_pdf_path_close_path (void *closure) -{ - pdf_path_info_t *info = closure; - - if (info->line_cap != CAIRO_LINE_CAP_ROUND && - ! info->has_sub_path) - { - return CAIRO_STATUS_SUCCESS; - } - - _cairo_output_stream_printf (info->output, - "h\n"); - - return _cairo_output_stream_get_status (info->output); -} - -static cairo_status_t -_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) -{ - double x1 = _cairo_fixed_to_double (box->p1.x); - double y1 = _cairo_fixed_to_double (box->p1.y); - double x2 = _cairo_fixed_to_double (box->p2.x); - double y2 = _cairo_fixed_to_double (box->p2.y); - - cairo_matrix_transform_point (info->path_transform, &x1, &y1); - cairo_matrix_transform_point (info->path_transform, &x2, &y2); - _cairo_output_stream_printf (info->output, - "%g %g %g %g re ", - x1, y1, x2 - x1, y2 - y1); - - return _cairo_output_stream_get_status (info->output); -} - -/* The line cap value is needed to workaround the fact that PostScript - * and PDF semantics for stroking degenerate sub-paths do not match - * cairo semantics. (PostScript draws something for any line cap - * value, while cairo draws something only for round caps). - * - * When using this function to emit a path to be filled, rather than - * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that - * the stroke workaround will not modify the path being emitted. - */ -static cairo_status_t -_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_matrix_t *path_transform, - cairo_line_cap_t line_cap) -{ - cairo_output_stream_t *word_wrap; - cairo_status_t status, status2; - pdf_path_info_t info; - cairo_box_t box; - - word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); - status = _cairo_output_stream_get_status (word_wrap); - if (unlikely (status)) - return _cairo_output_stream_destroy (word_wrap); - - info.output = word_wrap; - info.path_transform = path_transform; - info.line_cap = line_cap; - if (_cairo_path_fixed_is_rectangle (path, &box)) { - status = _cairo_pdf_path_rectangle (&info, &box); - } else { - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_pdf_path_move_to, - _cairo_pdf_path_line_to, - _cairo_pdf_path_curve_to, - _cairo_pdf_path_close_path, - &info); - } - - status2 = _cairo_output_stream_destroy (word_wrap); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - -cairo_int_status_t -_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule) -{ - const char *pdf_operator; - cairo_status_t status; - - if (pdf_operators->in_text_object) { - status = _cairo_pdf_operators_end_text (pdf_operators); - if (unlikely (status)) - return status; - } - - if (! path->has_current_point) { - /* construct an empty path */ - _cairo_output_stream_printf (pdf_operators->stream, "0 0 m "); - } else { - status = _cairo_pdf_operators_emit_path (pdf_operators, - path, - &pdf_operators->cairo_to_pdf, - CAIRO_LINE_CAP_ROUND); - if (unlikely (status)) - return status; - } - - switch (fill_rule) { - default: - ASSERT_NOT_REACHED; - case CAIRO_FILL_RULE_WINDING: - pdf_operator = "W"; - break; - case CAIRO_FILL_RULE_EVEN_ODD: - pdf_operator = "W*"; - break; - } - - _cairo_output_stream_printf (pdf_operators->stream, - "%s n\n", - pdf_operator); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -static int -_cairo_pdf_line_cap (cairo_line_cap_t cap) -{ - switch (cap) { - case CAIRO_LINE_CAP_BUTT: - return 0; - case CAIRO_LINE_CAP_ROUND: - return 1; - case CAIRO_LINE_CAP_SQUARE: - return 2; - default: - ASSERT_NOT_REACHED; - return 0; - } -} - -static int -_cairo_pdf_line_join (cairo_line_join_t join) -{ - switch (join) { - case CAIRO_LINE_JOIN_MITER: - return 0; - case CAIRO_LINE_JOIN_ROUND: - return 1; - case CAIRO_LINE_JOIN_BEVEL: - return 2; - default: - ASSERT_NOT_REACHED; - return 0; - } -} - -cairo_int_status_t -_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, - const cairo_stroke_style_t *style, - double scale) -{ - double *dash = style->dash; - int num_dashes = style->num_dashes; - double dash_offset = style->dash_offset; - double line_width = style->line_width * scale; - - /* PostScript has "special needs" when it comes to zero-length - * dash segments with butt caps. It apparently (at least - * according to ghostscript) draws hairlines for this - * case. That's not what the cairo semantics want, so we first - * touch up the array to eliminate any 0.0 values that will - * result in "on" segments. - */ - if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { - int i; - - /* If there's an odd number of dash values they will each get - * interpreted as both on and off. So we first explicitly - * expand the array to remove the duplicate usage so that we - * can modify some of the values. - */ - if (num_dashes % 2) { - dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); - if (unlikely (dash == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (dash, style->dash, num_dashes * sizeof (double)); - memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double)); - - num_dashes *= 2; - } - - for (i = 0; i < num_dashes; i += 2) { - if (dash[i] == 0.0) { - /* Do not modify the dashes in-place, as we may need to also - * replay this stroke to an image fallback. - */ - if (dash == style->dash) { - dash = _cairo_malloc_ab (num_dashes, sizeof (double)); - if (unlikely (dash == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - memcpy (dash, style->dash, num_dashes * sizeof (double)); - } - - /* If we're at the front of the list, we first rotate - * two elements from the end of the list to the front - * of the list before folding away the 0.0. Or, if - * there are only two dash elements, then there is - * nothing at all to draw. - */ - if (i == 0) { - double last_two[2]; - - if (num_dashes == 2) { - free (dash); - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - /* The cases of num_dashes == 0, 1, or 3 elements - * cannot exist, so the rotation of 2 elements - * will always be safe */ - memcpy (last_two, dash + num_dashes - 2, sizeof (last_two)); - memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double)); - memcpy (dash, last_two, sizeof (last_two)); - dash_offset += dash[0] + dash[1]; - i = 2; - } - dash[i-1] += dash[i+1]; - num_dashes -= 2; - memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); - /* If we might have just rotated, it's possible that - * we rotated a 0.0 value to the front of the list. - * Set i to -2 so it will get incremented to 0. */ - if (i == 2) - i = -2; - } - } - } - - if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) { - _cairo_output_stream_printf (pdf_operators->stream, - "%f w\n", - line_width); - pdf_operators->line_width = line_width; - } - - if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) { - _cairo_output_stream_printf (pdf_operators->stream, - "%d J\n", - _cairo_pdf_line_cap (style->line_cap)); - pdf_operators->line_cap = style->line_cap; - } - - if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) { - _cairo_output_stream_printf (pdf_operators->stream, - "%d j\n", - _cairo_pdf_line_join (style->line_join)); - pdf_operators->line_join = style->line_join; - } - - if (num_dashes) { - int d; - - _cairo_output_stream_printf (pdf_operators->stream, "["); - for (d = 0; d < num_dashes; d++) - _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale); - _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n", - dash_offset * scale); - pdf_operators->has_dashes = TRUE; - } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) { - _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n"); - pdf_operators->has_dashes = FALSE; - } - if (dash != style->dash) - free (dash); - - if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) { - _cairo_output_stream_printf (pdf_operators->stream, - "%f M ", - style->miter_limit < 1.0 ? 1.0 : style->miter_limit); - pdf_operators->miter_limit = style->miter_limit; - } - pdf_operators->has_line_style = TRUE; - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -/* Scale the matrix so the largest absolute value of the non - * translation components is 1.0. Return the scale required to restore - * the matrix to the original values. - * - * eg the matrix [ 100 0 0 50 20 10 ] - * - * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] - * and the scale returned is 100 - */ -static void -_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) -{ - double s; - - s = fabs (m->xx); - if (fabs (m->xy) > s) - s = fabs (m->xy); - if (fabs (m->yx) > s) - s = fabs (m->yx); - if (fabs (m->yy) > s) - s = fabs (m->yy); - *scale = s; - s = 1.0/s; - cairo_matrix_scale (m, s, s); -} - -static cairo_int_status_t -_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - const char *pdf_operator) -{ - cairo_status_t status; - cairo_matrix_t m, path_transform; - cairo_bool_t has_ctm = TRUE; - double scale = 1.0; - - if (pdf_operators->in_text_object) { - status = _cairo_pdf_operators_end_text (pdf_operators); - if (unlikely (status)) - return status; - } - - /* Optimize away the stroke ctm when it does not affect the - * stroke. There are other ctm cases that could be optimized - * however this is the most common. - */ - if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 && - fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0) - { - has_ctm = FALSE; - } - - /* The PDF CTM is transformed to the user space CTM when stroking - * so the correct pen shape will be used. This also requires that - * the path be transformed to user space when emitted. The - * conversion of path coordinates to user space may cause rounding - * errors. For example the device space point (1.234, 3.142) when - * transformed to a user space CTM of [100 0 0 100 0 0] will be - * emitted as (0.012, 0.031). - * - * To avoid the rounding problem we scale the user space CTM - * matrix so that all the non translation components of the matrix - * are <= 1. The line width and and dashes are scaled by the - * inverse of the scale applied to the CTM. This maintains the - * shape of the stroke pen while keeping the user space CTM within - * the range that maximizes the precision of the emitted path. - */ - if (has_ctm) { - m = *ctm; - /* Zero out the translation since it does not affect the pen - * shape however it may cause unnecessary digits to be emitted. - */ - m.x0 = 0.0; - m.y0 = 0.0; - _cairo_matrix_factor_out_scale (&m, &scale); - path_transform = m; - status = cairo_matrix_invert (&path_transform); - if (unlikely (status)) - return status; - - cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); - } - - status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - if (has_ctm) { - _cairo_output_stream_printf (pdf_operators->stream, - "q %f %f %f %f %f %f cm\n", - m.xx, m.yx, m.xy, m.yy, - m.x0, m.y0); - } else { - path_transform = pdf_operators->cairo_to_pdf; - } - - status = _cairo_pdf_operators_emit_path (pdf_operators, - path, - &path_transform, - style->line_cap); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); - if (has_ctm) - _cairo_output_stream_printf (pdf_operators->stream, " Q"); - - _cairo_output_stream_printf (pdf_operators->stream, "\n"); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -cairo_int_status_t -_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse) -{ - return _cairo_pdf_operators_emit_stroke (pdf_operators, - path, - style, - ctm, - ctm_inverse, - "S"); -} - -cairo_int_status_t -_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule) -{ - const char *pdf_operator; - cairo_status_t status; - - if (pdf_operators->in_text_object) { - status = _cairo_pdf_operators_end_text (pdf_operators); - if (unlikely (status)) - return status; - } - - status = _cairo_pdf_operators_emit_path (pdf_operators, - path, - &pdf_operators->cairo_to_pdf, - CAIRO_LINE_CAP_ROUND); - if (unlikely (status)) - return status; - - switch (fill_rule) { - default: - ASSERT_NOT_REACHED; - case CAIRO_FILL_RULE_WINDING: - pdf_operator = "f"; - break; - case CAIRO_FILL_RULE_EVEN_ODD: - pdf_operator = "f*"; - break; - } - - _cairo_output_stream_printf (pdf_operators->stream, - "%s\n", - pdf_operator); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -cairo_int_status_t -_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse) -{ - const char *operator; - - switch (fill_rule) { - default: - ASSERT_NOT_REACHED; - case CAIRO_FILL_RULE_WINDING: - operator = "B"; - break; - case CAIRO_FILL_RULE_EVEN_ODD: - operator = "B*"; - break; - } - - return _cairo_pdf_operators_emit_stroke (pdf_operators, - path, - style, - ctm, - ctm_inverse, - operator); -} - -#define GLYPH_POSITION_TOLERANCE 0.001 - -/* Emit the string of glyphs using the 'Tj' operator. This requires - * that the glyphs are positioned at their natural glyph advances. */ -static cairo_status_t -_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream) -{ - int i; - - _cairo_output_stream_printf (stream, "<"); - for (i = 0; i < pdf_operators->num_glyphs; i++) { - _cairo_output_stream_printf (stream, - "%0*x", - pdf_operators->hex_width, - pdf_operators->glyphs[i].glyph_index); - pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; - } - _cairo_output_stream_printf (stream, ">Tj\n"); - - return _cairo_output_stream_get_status (stream); -} - -/* Emit the string of glyphs using the 'TJ' operator. - * - * The TJ operator takes an array of strings of glyphs. Each string of - * glyphs is displayed using the glyph advances of each glyph to - * position the glyphs. A relative adjustment to the glyph advance may - * be specified by including the adjustment between two strings. The - * adjustment is in units of text space * -1000. - */ -static cairo_status_t -_cairo_pdf_operators_emit_glyph_string_with_positioning ( - cairo_pdf_operators_t *pdf_operators, - cairo_output_stream_t *stream) -{ - int i; - - _cairo_output_stream_printf (stream, "[<"); - for (i = 0; i < pdf_operators->num_glyphs; i++) { - if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) - { - double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; - int rounded_delta; - - delta = -1000.0*delta; - /* As the delta is in 1/1000 of a unit of text space, - * rounding to an integer should still provide sufficient - * precision. We round the delta before adding to Tm_x so - * that we keep track of the accumulated rounding error in - * the PDF interpreter and compensate for it when - * calculating subsequent deltas. - */ - rounded_delta = _cairo_lround (delta); - if (rounded_delta != 0) { - _cairo_output_stream_printf (stream, - ">%d<", - rounded_delta); - } - - /* Convert the rounded delta back to text - * space before adding to the current text - * position. */ - delta = rounded_delta/-1000.0; - pdf_operators->cur_x += delta; - } - - _cairo_output_stream_printf (stream, - "%0*x", - pdf_operators->hex_width, - pdf_operators->glyphs[i].glyph_index); - pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; - } - _cairo_output_stream_printf (stream, ">]TJ\n"); - - return _cairo_output_stream_get_status (stream); -} - -static cairo_status_t -_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) -{ - cairo_output_stream_t *word_wrap_stream; - cairo_status_t status, status2; - int i; - double x; - - if (pdf_operators->num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - - word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); - status = _cairo_output_stream_get_status (word_wrap_stream); - if (unlikely (status)) - return _cairo_output_stream_destroy (word_wrap_stream); - - /* Check if glyph advance used to position every glyph */ - x = pdf_operators->cur_x; - for (i = 0; i < pdf_operators->num_glyphs; i++) { - if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) - break; - x += pdf_operators->glyphs[i].x_advance; - } - if (i == pdf_operators->num_glyphs) { - status = _cairo_pdf_operators_emit_glyph_string (pdf_operators, - word_wrap_stream); - } else { - status = _cairo_pdf_operators_emit_glyph_string_with_positioning ( - pdf_operators, word_wrap_stream); - } - - pdf_operators->num_glyphs = 0; - pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x; - status2 = _cairo_output_stream_destroy (word_wrap_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - -static cairo_status_t -_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, - cairo_scaled_font_subsets_glyph_t *glyph, - double x_position) -{ - double x, y; - - x = glyph->x_advance; - y = glyph->y_advance; - if (glyph->is_scaled) - cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); - - pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; - pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; - pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x; - pdf_operators->glyph_buf_x_pos += x; - pdf_operators->num_glyphs++; - if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE) - return _cairo_pdf_operators_flush_glyphs (pdf_operators); - - return CAIRO_STATUS_SUCCESS; -} - -/* Use 'Tm' operator to set the PDF text matrix. */ -static cairo_status_t -_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, - cairo_matrix_t *matrix) -{ - cairo_matrix_t inverse; - cairo_status_t status; - - /* We require the matrix to be invertable. */ - inverse = *matrix; - status = cairo_matrix_invert (&inverse); - if (unlikely (status)) - return status; - - pdf_operators->text_matrix = *matrix; - pdf_operators->cur_x = 0; - pdf_operators->cur_y = 0; - pdf_operators->glyph_buf_x_pos = 0; - _cairo_output_stream_printf (pdf_operators->stream, - "%f %f %f %f %f %f Tm\n", - pdf_operators->text_matrix.xx, - pdf_operators->text_matrix.yx, - pdf_operators->text_matrix.xy, - pdf_operators->text_matrix.yy, - pdf_operators->text_matrix.x0, - pdf_operators->text_matrix.y0); - - pdf_operators->cairo_to_pdftext = *matrix; - status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); - assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, - &pdf_operators->cairo_to_pdf, - &pdf_operators->cairo_to_pdftext); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -#define TEXT_MATRIX_TOLERANCE 1e-6 - -/* Set the translation components of the PDF text matrix to x, y. The - * 'Td' operator is used to transform the text matrix. - */ -static cairo_status_t -_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, - double x, - double y) -{ - cairo_matrix_t translate, inverse; - cairo_status_t status; - - /* The Td operator transforms the text_matrix with: - * - * text_matrix' = T x text_matrix - * - * where T is a translation matrix with the translation components - * set to the Td operands tx and ty. - */ - inverse = pdf_operators->text_matrix; - status = cairo_matrix_invert (&inverse); - assert (status == CAIRO_STATUS_SUCCESS); - pdf_operators->text_matrix.x0 = x; - pdf_operators->text_matrix.y0 = y; - cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); - if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE) - translate.x0 = 0.0; - if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE) - translate.y0 = 0.0; - _cairo_output_stream_printf (pdf_operators->stream, - "%f %f Td\n", - translate.x0, - translate.y0); - pdf_operators->cur_x = 0; - pdf_operators->cur_y = 0; - pdf_operators->glyph_buf_x_pos = 0; - - pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix; - status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); - assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, - &pdf_operators->cairo_to_pdf, - &pdf_operators->cairo_to_pdftext); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -/* Select the font using the 'Tf' operator. The font size is set to 1 - * as we use the 'Tm' operator to set the font scale. - */ -static cairo_status_t -_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators, - cairo_scaled_font_subsets_glyph_t *subset_glyph) -{ - cairo_status_t status; - - _cairo_output_stream_printf (pdf_operators->stream, - "/f-%d-%d 1 Tf\n", - subset_glyph->font_id, - subset_glyph->subset_id); - if (pdf_operators->use_font_subset) { - status = pdf_operators->use_font_subset (subset_glyph->font_id, - subset_glyph->subset_id, - pdf_operators->use_font_subset_closure); - if (unlikely (status)) - return status; - } - pdf_operators->font_id = subset_glyph->font_id; - pdf_operators->subset_id = subset_glyph->subset_id; - - if (subset_glyph->is_composite) - pdf_operators->hex_width = 4; - else - pdf_operators->hex_width = 2; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) -{ - _cairo_output_stream_printf (pdf_operators->stream, "BT\n"); - - pdf_operators->in_text_object = TRUE; - pdf_operators->num_glyphs = 0; - pdf_operators->glyph_buf_x_pos = 0; - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -static cairo_status_t -_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) -{ - cairo_status_t status; - - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); - - pdf_operators->in_text_object = FALSE; - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -/* Compare the scale components of two matrices. The translation - * components are ignored. */ -static cairo_bool_t -_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) -{ - return (a->xx == b->xx && - a->xy == b->xy && - a->yx == b->yx && - a->yy == b->yy); -} - -static cairo_status_t -_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, - const char *utf8, - int utf8_len) -{ - uint16_t *utf16; - int utf16_len; - cairo_status_t status; - int i; - - _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText stream, - "%04x", (int) (utf16[i])); - } - free (utf16); - } - _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -static cairo_status_t -_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) -{ - _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -static cairo_status_t -_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, - cairo_glyph_t *glyph, - cairo_scaled_font_subsets_glyph_t *subset_glyph) -{ - double x, y; - cairo_status_t status; - - if (pdf_operators->is_new_text_object || - pdf_operators->font_id != subset_glyph->font_id || - pdf_operators->subset_id != subset_glyph->subset_id) - { - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); - if (unlikely (status)) - return status; - - pdf_operators->is_new_text_object = FALSE; - } - - x = glyph->x; - y = glyph->y; - cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y); - - /* The TJ operator for displaying text strings can only set - * the horizontal position of the glyphs. If the y position - * (in text space) changes, use the Td operator to change the - * current position to the next glyph. We also use the Td - * operator to move the current position if the horizontal - * position changes by more than 10 (in text space - * units). This is becauses the horizontal glyph positioning - * in the TJ operator is intended for kerning and there may be - * PDF consumers that do not handle very large position - * adjustments in TJ. - */ - if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 || - fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) - { - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - x = glyph->x; - y = glyph->y; - cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); - status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); - if (unlikely (status)) - return status; - - x = 0.0; - y = 0.0; - } - - status = _cairo_pdf_operators_add_glyph (pdf_operators, - subset_glyph, - x); - return status; -} - -/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an - * empty string. - */ -static cairo_int_status_t -_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) -{ - cairo_scaled_font_subsets_glyph_t subset_glyph; - cairo_glyph_t *cur_glyph; - cairo_status_t status; - int i; - - /* If the cluster maps 1 glyph to 1 or more unicode characters, we - * first try _map_glyph() with the unicode string to see if it can - * use toUnicode to map our glyph to the unicode. This will fail - * if the glyph is already mapped to a different unicode string. - * - * We also go through this path if no unicode mapping was - * supplied (utf8_len < 0). - * - * Mapping a glyph to a zero length unicode string requires the - * use of ActualText. - */ - if (num_glyphs == 1 && utf8_len != 0) { - status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, - scaled_font, - glyphs->index, - utf8, - utf8_len, - &subset_glyph); - if (unlikely (status)) - return status; - - if (subset_glyph.utf8_is_mapped || utf8_len < 0) { - status = _cairo_pdf_operators_emit_glyph (pdf_operators, - glyphs, - &subset_glyph); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; - } - } - - /* Fallback to using ActualText to map zero or more glyphs to a - * unicode string. */ - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); - if (unlikely (status)) - return status; - - cur_glyph = glyphs; - /* XXX - * If no glyphs, we should put *something* here for the text to be selectable. */ - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, - scaled_font, - cur_glyph->index, - NULL, -1, - &subset_glyph); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_emit_glyph (pdf_operators, - cur_glyph, - &subset_glyph); - if (unlikely (status)) - return status; - - if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) - cur_glyph--; - else - cur_glyph++; - } - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_end_actualtext (pdf_operators); - - return status; -} - -cairo_int_status_t -_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) -{ - cairo_status_t status; - int i; - cairo_matrix_t text_matrix, invert_y_axis; - double x, y; - const char *cur_text; - cairo_glyph_t *cur_glyph; - - pdf_operators->font_matrix_inverse = scaled_font->font_matrix; - status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); - if (status == CAIRO_STATUS_INVALID_MATRIX) - return CAIRO_STATUS_SUCCESS; - assert (status == CAIRO_STATUS_SUCCESS); - - pdf_operators->is_new_text_object = FALSE; - if (pdf_operators->in_text_object == FALSE) { - status = _cairo_pdf_operators_begin_text (pdf_operators); - if (unlikely (status)) - return status; - - /* Force Tm and Tf to be emitted when starting a new text - * object.*/ - pdf_operators->is_new_text_object = TRUE; - } - - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - text_matrix = scaled_font->scale; - - /* Invert y axis in font space */ - cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); - - /* Invert y axis in device space */ - cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); - - if (pdf_operators->is_new_text_object || - ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) - { - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - - x = glyphs[0].x; - y = glyphs[0].y; - cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); - text_matrix.x0 = x; - text_matrix.y0 = y; - status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); - if (status == CAIRO_STATUS_INVALID_MATRIX) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - } - - if (num_clusters > 0) { - cur_text = utf8; - if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) - cur_glyph = glyphs + num_glyphs; - else - cur_glyph = glyphs; - for (i = 0; i < num_clusters; i++) { - if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) - cur_glyph -= clusters[i].num_glyphs; - status = _cairo_pdf_operators_emit_cluster (pdf_operators, - cur_text, - clusters[i].num_bytes, - cur_glyph, - clusters[i].num_glyphs, - cluster_flags, - scaled_font); - if (unlikely (status)) - return status; - - cur_text += clusters[i].num_bytes; - if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) - cur_glyph += clusters[i].num_glyphs; - } - } else { - for (i = 0; i < num_glyphs; i++) { - status = _cairo_pdf_operators_emit_cluster (pdf_operators, - NULL, - -1, /* no unicode string available */ - &glyphs[i], - 1, - FALSE, - scaled_font); - if (unlikely (status)) - return status; - } - } - - return _cairo_output_stream_get_status (pdf_operators->stream); -} - -#endif /* CAIRO_HAS_PDF_OPERATORS */ diff --git a/libs/cairo/cairo/src/cairo-pdf-surface-private.h b/libs/cairo/cairo/src/cairo-pdf-surface-private.h deleted file mode 100644 index ab5befa52..000000000 --- a/libs/cairo/cairo/src/cairo-pdf-surface-private.h +++ /dev/null @@ -1,159 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PDF_SURFACE_PRIVATE_H -#define CAIRO_PDF_SURFACE_PRIVATE_H - -#include "cairo-pdf.h" - -#include "cairo-surface-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-pdf-operators-private.h" -#include "cairo-path-fixed-private.h" - -typedef struct _cairo_pdf_resource { - unsigned int id; -} cairo_pdf_resource_t; - -#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1) - -typedef struct _cairo_pdf_group_resources { - cairo_bool_t operators[CAIRO_NUM_OPERATORS]; - cairo_array_t alphas; - cairo_array_t smasks; - cairo_array_t patterns; - cairo_array_t xobjects; - cairo_array_t fonts; -} cairo_pdf_group_resources_t; - -typedef struct _cairo_pdf_source_surface_entry { - cairo_hash_entry_t base; - unsigned int id; - cairo_bool_t interpolate; - cairo_pdf_resource_t surface_res; - int width; - int height; -} cairo_pdf_source_surface_entry_t; - -typedef struct _cairo_pdf_source_surface { - cairo_surface_t *surface; - cairo_pdf_source_surface_entry_t *hash_entry; -} cairo_pdf_source_surface_t; - -typedef struct _cairo_pdf_pattern { - double width; - double height; - cairo_rectangle_int_t extents; - cairo_pattern_t *pattern; - cairo_pdf_resource_t pattern_res; - cairo_pdf_resource_t gstate_res; -} cairo_pdf_pattern_t; - -typedef enum _cairo_pdf_operation { - PDF_PAINT, - PDF_MASK, - PDF_FILL, - PDF_STROKE, - PDF_SHOW_GLYPHS -} cairo_pdf_operation_t; - -typedef struct _cairo_pdf_smask_group { - double width; - double height; - cairo_pdf_resource_t group_res; - cairo_pdf_operation_t operation; - cairo_pattern_t *source; - cairo_pdf_resource_t source_res; - cairo_pattern_t *mask; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - cairo_stroke_style_t style; - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - char *utf8; - int utf8_len; - cairo_glyph_t *glyphs; - int num_glyphs; - cairo_text_cluster_t *clusters; - int num_clusters; - cairo_bool_t cluster_flags; - cairo_scaled_font_t *scaled_font; -} cairo_pdf_smask_group_t; - -typedef struct _cairo_pdf_surface cairo_pdf_surface_t; - -struct _cairo_pdf_surface { - cairo_surface_t base; - - /* Prefer the name "output" here to avoid confusion over the - * structure within a PDF document known as a "stream". */ - cairo_output_stream_t *output; - - double width; - double height; - cairo_matrix_t cairo_to_pdf; - - cairo_array_t objects; - cairo_array_t pages; - cairo_array_t rgb_linear_functions; - cairo_array_t alpha_linear_functions; - cairo_array_t page_patterns; - cairo_array_t page_surfaces; - cairo_hash_table_t *all_surfaces; - cairo_array_t smask_groups; - cairo_array_t knockout_group; - - cairo_scaled_font_subsets_t *font_subsets; - cairo_array_t fonts; - - cairo_pdf_resource_t next_available_resource; - cairo_pdf_resource_t pages_resource; - - cairo_pdf_version_t pdf_version; - cairo_bool_t compress_content; - - cairo_pdf_resource_t content; - cairo_pdf_resource_t content_resources; - cairo_pdf_group_resources_t resources; - cairo_bool_t has_fallback_images; - cairo_bool_t header_emitted; - - struct { - cairo_bool_t active; - cairo_pdf_resource_t self; - cairo_pdf_resource_t length; - long start_offset; - cairo_bool_t compressed; - cairo_output_stream_t *old_output; - } pdf_stream; - - struct { - cairo_bool_t active; - cairo_output_stream_t *stream; - cairo_output_stream_t *mem_stream; - cairo_output_stream_t *old_output; - cairo_pdf_resource_t resource; - cairo_bool_t is_knockout; - } group_stream; - - cairo_surface_clipper_t clipper; - - cairo_pdf_operators_t pdf_operators; - cairo_paginated_mode_t paginated_mode; - cairo_bool_t select_pattern_gstate_saved; - - cairo_bool_t force_fallbacks; - - cairo_operator_t current_operator; - cairo_bool_t current_pattern_is_solid_color; - cairo_bool_t current_color_is_stroke; - double current_color_red; - double current_color_green; - double current_color_blue; - double current_color_alpha; - - cairo_surface_t *paginated_surface; -}; - -#endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-pdf-surface.c b/libs/cairo/cairo/src/cairo-pdf-surface.c deleted file mode 100644 index 3dcf58859..000000000 --- a/libs/cairo/cairo/src/cairo-pdf-surface.c +++ /dev/null @@ -1,6213 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for snprintf() */ -#include "cairoint.h" -#include "cairo-pdf.h" -#include "cairo-pdf-surface-private.h" -#include "cairo-pdf-operators-private.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-image-info-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-paginated-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-surface-subsurface-private.h" -#include "cairo-type3-glyph-surface-private.h" - -#include -#include - -/* Issues: - * - * - We embed an image in the stream each time it's composited. We - * could add generation counters to surfaces and remember the stream - * ID for a particular generation for a particular surface. - * - * - Backend specific meta data. - */ - -/* - * Page Structure of the Generated PDF: - * - * Each page requiring fallbacks images contains a knockout group at - * the top level. The first operation of the knockout group paints a - * group containing all the supported drawing operations. Fallback - * images (if any) are painted in the knockout group. This ensures - * that fallback images do not composite with any content under the - * fallback images. - * - * Streams: - * - * This PDF surface has three types of streams: - * - PDF Stream - * - Content Stream - * - Group Stream - * - * Calling _cairo_output_stream_printf (surface->output, ...) will - * write to the currently open stream. - * - * PDF Stream: - * A PDF Stream may be opened and closed with the following functions: - * _cairo_pdf_surface_open stream () - * _cairo_pdf_surface_close_stream () - * - * PDF Streams are written directly to the PDF file. They are used for - * fonts, images and patterns. - * - * Content Stream: - * The Content Stream is opened and closed with the following functions: - * _cairo_pdf_surface_open_content_stream () - * _cairo_pdf_surface_close_content_stream () - * - * The Content Stream contains the text and graphics operators. - * - * Group Stream: - * A Group Stream may be opened and closed with the following functions: - * _cairo_pdf_surface_open_group () - * _cairo_pdf_surface_close_group () - * - * A Group Stream is a Form XObject. It is used for short sequences - * of operators. As the content is very short the group is stored in - * memory until it is closed. This allows some optimization such as - * including the Resource dictionary and stream length inside the - * XObject instead of using an indirect object. - */ - -/** - * SECTION:cairo-pdf - * @Title: PDF Surfaces - * @Short_Description: Rendering PDF documents - * @See_Also: #cairo_surface_t - * - * The PDF surface is used to render cairo graphics to Adobe - * PDF files and is a multi-page vector surface backend. - */ - -/** - * CAIRO_HAS_PDF_SURFACE: - * - * Defined if the PDF surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -static const cairo_pdf_version_t _cairo_pdf_versions[] = -{ - CAIRO_PDF_VERSION_1_4, - CAIRO_PDF_VERSION_1_5 -}; - -#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions) - -static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = -{ - "PDF 1.4", - "PDF 1.5" -}; - -typedef struct _cairo_pdf_object { - long offset; -} cairo_pdf_object_t; - -typedef struct _cairo_pdf_font { - unsigned int font_id; - unsigned int subset_id; - cairo_pdf_resource_t subset_resource; -} cairo_pdf_font_t; - -typedef struct _cairo_pdf_rgb_linear_function { - cairo_pdf_resource_t resource; - double color1[3]; - double color2[3]; -} cairo_pdf_rgb_linear_function_t; - -typedef struct _cairo_pdf_alpha_linear_function { - cairo_pdf_resource_t resource; - double alpha1; - double alpha2; -} cairo_pdf_alpha_linear_function_t; - -static cairo_pdf_resource_t -_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); - -static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); - -static void -_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); - -static cairo_status_t -_cairo_pdf_surface_add_font (unsigned int font_id, - unsigned int subset_id, - void *closure); - -static void -_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res); - -static cairo_status_t -_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource, - cairo_bool_t compressed, - const char *fmt, - ...) CAIRO_PRINTF_FORMAT(4, 5); -static cairo_status_t -_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); - -static cairo_status_t -_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); - -static void -_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface); - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); - -static long -_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); - -static cairo_status_t -_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); - -static cairo_status_t -_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); - -static cairo_bool_t -_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); - -static const cairo_surface_backend_t cairo_pdf_surface_backend; -static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; - -static cairo_pdf_resource_t -_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t resource; - cairo_status_t status; - cairo_pdf_object_t object; - - object.offset = _cairo_output_stream_get_position (surface->output); - - status = _cairo_array_append (&surface->objects, &object); - if (unlikely (status)) { - resource.id = 0; - return resource; - } - - resource = surface->next_available_resource; - surface->next_available_resource.id++; - - return resource; -} - -static void -_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t resource) -{ - cairo_pdf_object_t *object; - - object = _cairo_array_index (&surface->objects, resource.id - 1); - object->offset = _cairo_output_stream_get_position (surface->output); -} - -static void -_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, - double width, - double height) -{ - surface->width = width; - surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_pdf); -} - -static cairo_bool_t -_path_covers_bbox (cairo_pdf_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_box_t box; - - return _cairo_path_fixed_is_box (path, &box) && - box.p1.x <= 0 && - box.p1.y <= 0 && - box.p2.x >= _cairo_fixed_from_double (surface->width) && - box.p2.y >= _cairo_fixed_from_double (surface->height); -} - -static cairo_status_t -_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_pdf_surface_t *surface = cairo_container_of (clipper, - cairo_pdf_surface_t, - clipper); - cairo_int_status_t status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (path == NULL) { - _cairo_output_stream_printf (surface->output, "Q q\n"); - - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return CAIRO_STATUS_SUCCESS; - } - - if (_path_covers_bbox (surface, path)) - return CAIRO_STATUS_SUCCESS; - - return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); -} - -static cairo_surface_t * -_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, - double width, - double height) -{ - cairo_pdf_surface_t *surface; - cairo_status_t status, status_ignored; - - surface = malloc (sizeof (cairo_pdf_surface_t)); - if (unlikely (surface == NULL)) { - /* destroy stream on behalf of caller */ - status = _cairo_output_stream_destroy (output); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (&surface->base, - &cairo_pdf_surface_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); - - surface->output = output; - surface->width = width; - surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); - - _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); - _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); - _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); - _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); - _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *)); - _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); - - _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); - _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); - surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); - if (unlikely (surface->all_surfaces == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL0; - } - - _cairo_pdf_group_resources_init (&surface->resources); - - surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); - if (! surface->font_subsets) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL1; - } - - surface->next_available_resource.id = 1; - surface->pages_resource = _cairo_pdf_surface_new_object (surface); - if (surface->pages_resource.id == 0) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL2; - } - - surface->pdf_version = CAIRO_PDF_VERSION_1_5; - surface->compress_content = TRUE; - surface->pdf_stream.active = FALSE; - surface->pdf_stream.old_output = NULL; - surface->group_stream.active = FALSE; - surface->group_stream.stream = NULL; - surface->group_stream.mem_stream = NULL; - - surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; - - surface->force_fallbacks = FALSE; - surface->select_pattern_gstate_saved = FALSE; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - surface->header_emitted = FALSE; - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_pdf_surface_clipper_intersect_clip_path); - - _cairo_pdf_operators_init (&surface->pdf_operators, - surface->output, - &surface->cairo_to_pdf, - surface->font_subsets); - _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, - _cairo_pdf_surface_add_font, - surface); - _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); - - surface->paginated_surface = _cairo_paginated_surface_create ( - &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, - &cairo_pdf_surface_paginated_backend); - - status = surface->paginated_surface->status; - if (status == CAIRO_STATUS_SUCCESS) { - /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); - return surface->paginated_surface; - } - -BAIL2: - _cairo_scaled_font_subsets_destroy (surface->font_subsets); -BAIL1: - _cairo_hash_table_destroy (surface->all_surfaces); -BAIL0: - _cairo_array_fini (&surface->objects); - free (surface); - - /* destroy stream on behalf of caller */ - status_ignored = _cairo_output_stream_destroy (output); - - return _cairo_surface_create_in_error (status); -} - -/** - * cairo_pdf_surface_create_for_stream: - * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL - * to indicate a no-op @write_func. With a no-op @write_func, - * the surface may be queried or used as a source without - * generating any temporary files. - * @closure: the closure argument for @write_func - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PDF surface of the specified size in points to be written - * incrementally to the stream represented by @write_func and @closure. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - */ -cairo_surface_t * -cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width_in_points, - double height_in_points) -{ - cairo_output_stream_t *output; - - output = _cairo_output_stream_create (write_func, NULL, closure); - if (_cairo_output_stream_get_status (output)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); - - return _cairo_pdf_surface_create_for_stream_internal (output, - width_in_points, - height_in_points); -} - -/** - * cairo_pdf_surface_create: - * @filename: a filename for the PDF output (must be writable), %NULL may be - * used to specify no output. This will generate a PDF surface that - * may be queried and used as a source, without generating a - * temporary file. - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PDF surface of the specified size in points to be written - * to @filename. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_pdf_surface_create (const char *filename, - double width_in_points, - double height_in_points) -{ - cairo_output_stream_t *output; - - output = _cairo_output_stream_create_for_filename (filename); - if (_cairo_output_stream_get_status (output)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); - - return _cairo_pdf_surface_create_for_stream_internal (output, - width_in_points, - height_in_points); -} - -static cairo_bool_t -_cairo_surface_is_pdf (cairo_surface_t *surface) -{ - return surface->backend == &cairo_pdf_surface_backend; -} - -/* If the abstract_surface is a paginated surface, and that paginated - * surface's target is a pdf_surface, then set pdf_surface to that - * target. Otherwise return FALSE. - */ -static cairo_bool_t -_extract_pdf_surface (cairo_surface_t *surface, - cairo_pdf_surface_t **pdf_surface) -{ - cairo_surface_t *target; - cairo_status_t status_ignored; - - if (surface->status) - return FALSE; - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_paginated (surface)) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - target = _cairo_paginated_surface_get_target (surface); - if (target->status) { - status_ignored = _cairo_surface_set_error (surface, - target->status); - return FALSE; - } - if (target->finished) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_pdf (target)) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - *pdf_surface = (cairo_pdf_surface_t *) target; - return TRUE; -} - -/** - * cairo_pdf_surface_restrict_to_version: - * @surface: a PDF #cairo_surface_t - * @version: PDF version - * - * Restricts the generated PDF file to @version. See cairo_pdf_get_versions() - * for a list of available version values that can be used here. - * - * This function should only be called before any drawing operations - * have been performed on the given surface. The simplest way to do - * this is to call this function immediately after creating the - * surface. - * - * Since: 1.10 - **/ -void -cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface, - cairo_pdf_version_t version) -{ - cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */ - - if (! _extract_pdf_surface (abstract_surface, &surface)) - return; - - if (version < CAIRO_PDF_VERSION_LAST) - surface->pdf_version = version; - - _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, - version >= CAIRO_PDF_VERSION_1_5); -} - -/** - * cairo_pdf_get_versions: - * @versions: supported version list - * @num_versions: list length - * - * Used to retrieve the list of supported versions. See - * cairo_pdf_surface_restrict_to_version(). - * - * Since: 1.10 - **/ -void -cairo_pdf_get_versions (cairo_pdf_version_t const **versions, - int *num_versions) -{ - if (versions != NULL) - *versions = _cairo_pdf_versions; - - if (num_versions != NULL) - *num_versions = CAIRO_PDF_VERSION_LAST; -} - -/** - * cairo_pdf_version_to_string: - * @version: a version id - * - * Get the string representation of the given @version id. This function - * will return %NULL if @version isn't valid. See cairo_pdf_get_versions() - * for a way to get the list of valid version ids. - * - * Return value: the string associated to given version. - * - * Since: 1.10 - **/ -const char * -cairo_pdf_version_to_string (cairo_pdf_version_t version) -{ - if (version >= CAIRO_PDF_VERSION_LAST) - return NULL; - - return _cairo_pdf_version_strings[version]; -} - -/** - * cairo_pdf_surface_set_size: - * @surface: a PDF #cairo_surface_t - * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) - * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) - * - * Changes the size of a PDF surface for the current (and - * subsequent) pages. - * - * This function should only be called before any drawing operations - * have been performed on the current page. The simplest way to do - * this is to call this function immediately after creating the - * surface or immediately after completing a page with either - * cairo_show_page() or cairo_copy_page(). - * - * Since: 1.2 - **/ -void -cairo_pdf_surface_set_size (cairo_surface_t *surface, - double width_in_points, - double height_in_points) -{ - cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ - - if (! _extract_pdf_surface (surface, &pdf_surface)) - return; - - _cairo_pdf_surface_set_size_internal (pdf_surface, - width_in_points, - height_in_points); -} - -static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) -{ - int i, size; - cairo_pdf_pattern_t *pattern; - cairo_pdf_source_surface_t *src_surface; - cairo_pdf_smask_group_t *group; - - size = _cairo_array_num_elements (&surface->page_patterns); - for (i = 0; i < size; i++) { - pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i); - cairo_pattern_destroy (pattern->pattern); - } - _cairo_array_truncate (&surface->page_patterns, 0); - - size = _cairo_array_num_elements (&surface->page_surfaces); - for (i = 0; i < size; i++) { - src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); - cairo_surface_destroy (src_surface->surface); - } - _cairo_array_truncate (&surface->page_surfaces, 0); - - size = _cairo_array_num_elements (&surface->smask_groups); - for (i = 0; i < size; i++) { - _cairo_array_copy_element (&surface->smask_groups, i, &group); - _cairo_pdf_smask_group_destroy (group); - } - _cairo_array_truncate (&surface->smask_groups, 0); - _cairo_array_truncate (&surface->knockout_group, 0); -} - -static void -_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) -{ - int i; - - for (i = 0; i < CAIRO_NUM_OPERATORS; i++) - res->operators[i] = FALSE; - - _cairo_array_init (&res->alphas, sizeof (double)); - _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t)); -} - -static void -_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) -{ - _cairo_array_fini (&res->alphas); - _cairo_array_fini (&res->smasks); - _cairo_array_fini (&res->patterns); - _cairo_array_fini (&res->xobjects); - _cairo_array_fini (&res->fonts); -} - -static void -_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) -{ - int i; - - for (i = 0; i < CAIRO_NUM_OPERATORS; i++) - res->operators[i] = FALSE; - - _cairo_array_truncate (&res->alphas, 0); - _cairo_array_truncate (&res->smasks, 0); - _cairo_array_truncate (&res->patterns, 0); - _cairo_array_truncate (&res->xobjects, 0); - _cairo_array_truncate (&res->fonts, 0); -} - -static void -_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, - cairo_operator_t op) -{ - cairo_pdf_group_resources_t *res = &surface->resources; - - res->operators[op] = TRUE; -} - -static cairo_status_t -_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, - double alpha, - int *index) -{ - int num_alphas, i; - double other; - cairo_status_t status; - cairo_pdf_group_resources_t *res = &surface->resources; - - num_alphas = _cairo_array_num_elements (&res->alphas); - for (i = 0; i < num_alphas; i++) { - _cairo_array_copy_element (&res->alphas, i, &other); - if (alpha == other) { - *index = i; - return CAIRO_STATUS_SUCCESS; - } - } - - status = _cairo_array_append (&res->alphas, &alpha); - if (unlikely (status)) - return status; - - *index = _cairo_array_num_elements (&res->alphas) - 1; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t smask) -{ - return _cairo_array_append (&(surface->resources.smasks), &smask); -} - -static cairo_status_t -_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t pattern) -{ - return _cairo_array_append (&(surface->resources.patterns), &pattern); -} - -static cairo_status_t -_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t xobject) -{ - return _cairo_array_append (&(surface->resources.xobjects), &xobject); -} - -static cairo_status_t -_cairo_pdf_surface_add_font (unsigned int font_id, - unsigned int subset_id, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_pdf_font_t font; - int num_fonts, i; - cairo_status_t status; - cairo_pdf_group_resources_t *res = &surface->resources; - - num_fonts = _cairo_array_num_elements (&res->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&res->fonts, i, &font); - if (font.font_id == font_id && - font.subset_id == subset_id) - return CAIRO_STATUS_SUCCESS; - } - - num_fonts = _cairo_array_num_elements (&surface->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&surface->fonts, i, &font); - if (font.font_id == font_id && - font.subset_id == subset_id) - return _cairo_array_append (&res->fonts, &font); - } - - font.font_id = font_id; - font.subset_id = subset_id; - font.subset_resource = _cairo_pdf_surface_new_object (surface); - if (font.subset_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_array_append (&surface->fonts, &font); - if (unlikely (status)) - return status; - - return _cairo_array_append (&res->fonts, &font); -} - -static cairo_pdf_resource_t -_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, - unsigned int font_id, - unsigned int subset_id) -{ - cairo_pdf_font_t font; - int num_fonts, i; - - num_fonts = _cairo_array_num_elements (&surface->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&surface->fonts, i, &font); - if (font.font_id == font_id && font.subset_id == subset_id) - return font.subset_resource; - } - - font.subset_resource.id = 0; - return font.subset_resource; -} - -static const char * -_cairo_operator_to_pdf_blend_mode (cairo_operator_t op) -{ - switch (op) { - /* The extend blend mode operators */ - case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; - case CAIRO_OPERATOR_SCREEN: return "Screen"; - case CAIRO_OPERATOR_OVERLAY: return "Overlay"; - case CAIRO_OPERATOR_DARKEN: return "Darken"; - case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; - case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; - case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; - case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; - case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; - case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; - case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; - case CAIRO_OPERATOR_HSL_HUE: return "Hue"; - case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; - case CAIRO_OPERATOR_HSL_COLOR: return "Color"; - case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; - - default: - /* The original Porter-Duff set */ - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - return "Normal"; - } -} - -static void -_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, - cairo_pdf_group_resources_t *res) -{ - int num_alphas, num_smasks, num_resources, i; - double alpha; - cairo_pdf_resource_t *smask, *pattern, *xobject; - cairo_pdf_font_t *font; - - _cairo_output_stream_printf (surface->output, "<<\n"); - - num_alphas = _cairo_array_num_elements (&res->alphas); - num_smasks = _cairo_array_num_elements (&res->smasks); - if (num_alphas > 0 || num_smasks > 0) { - _cairo_output_stream_printf (surface->output, - " /ExtGState <<\n"); - - for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { - if (res->operators[i]) { - _cairo_output_stream_printf (surface->output, - " /b%d << /BM /%s >>\n", - i, _cairo_operator_to_pdf_blend_mode(i)); - } - } - - for (i = 0; i < num_alphas; i++) { - _cairo_array_copy_element (&res->alphas, i, &alpha); - _cairo_output_stream_printf (surface->output, - " /a%d << /CA %f /ca %f >>\n", - i, alpha, alpha); - } - - for (i = 0; i < num_smasks; i++) { - smask = _cairo_array_index (&res->smasks, i); - _cairo_output_stream_printf (surface->output, - " /s%d %d 0 R\n", - smask->id, smask->id); - } - - _cairo_output_stream_printf (surface->output, - " >>\n"); - } - - num_resources = _cairo_array_num_elements (&res->patterns); - if (num_resources > 0) { - _cairo_output_stream_printf (surface->output, - " /Pattern <<"); - for (i = 0; i < num_resources; i++) { - pattern = _cairo_array_index (&res->patterns, i); - _cairo_output_stream_printf (surface->output, - " /p%d %d 0 R", - pattern->id, pattern->id); - } - - _cairo_output_stream_printf (surface->output, - " >>\n"); - } - - num_resources = _cairo_array_num_elements (&res->xobjects); - if (num_resources > 0) { - _cairo_output_stream_printf (surface->output, - " /XObject <<"); - - for (i = 0; i < num_resources; i++) { - xobject = _cairo_array_index (&res->xobjects, i); - _cairo_output_stream_printf (surface->output, - " /x%d %d 0 R", - xobject->id, xobject->id); - } - - _cairo_output_stream_printf (surface->output, - " >>\n"); - } - - num_resources = _cairo_array_num_elements (&res->fonts); - if (num_resources > 0) { - _cairo_output_stream_printf (surface->output," /Font <<\n"); - for (i = 0; i < num_resources; i++) { - font = _cairo_array_index (&res->fonts, i); - _cairo_output_stream_printf (surface->output, - " /f-%d-%d %d 0 R\n", - font->font_id, - font->subset_id, - font->subset_resource.id); - } - _cairo_output_stream_printf (surface->output, " >>\n"); - } - - _cairo_output_stream_printf (surface->output, - ">>\n"); -} - -static cairo_pdf_smask_group_t * -_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) -{ - cairo_pdf_smask_group_t *group; - - group = calloc (1, sizeof (cairo_pdf_smask_group_t)); - if (unlikely (group == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - group->group_res = _cairo_pdf_surface_new_object (surface); - if (group->group_res.id == 0) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - free (group); - return NULL; - } - group->width = surface->width; - group->height = surface->height; - - return group; -} - -static void -_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) -{ - if (group->operation == PDF_FILL || group->operation == PDF_STROKE) - _cairo_path_fixed_fini (&group->path); - if (group->source) - cairo_pattern_destroy (group->source); - if (group->mask) - cairo_pattern_destroy (group->mask); - if (group->utf8) - free (group->utf8); - if (group->glyphs) - free (group->glyphs); - if (group->clusters) - free (group->clusters); - if (group->scaled_font) - cairo_scaled_font_destroy (group->scaled_font); - free (group); -} - -static cairo_status_t -_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, - cairo_pdf_smask_group_t *group) -{ - return _cairo_array_append (&surface->smask_groups, &group); -} - -static cairo_bool_t -_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) -{ - const cairo_pdf_source_surface_entry_t *a = key_a; - const cairo_pdf_source_surface_entry_t *b = key_b; - - return (a->id == b->id) && (a->interpolate == b->interpolate); -} - -static void -_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) -{ - key->base.hash = key->id; -} - -static cairo_int_status_t -_get_jpx_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned long *mime_data_length) -{ - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, - mime_data, mime_data_length); - if (*mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); -} - -static cairo_int_status_t -_get_jpeg_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned long *mime_data_length) -{ - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - mime_data, mime_data_length); - if (*mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); -} - -static cairo_status_t -_get_source_surface_size (cairo_surface_t *source, - int *width, - int *height) -{ - cairo_status_t status; - cairo_rectangle_int_t extents; - cairo_image_info_t info; - const unsigned char *mime_data; - unsigned long mime_data_length; - - if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - - *width = sub->extents.width; - *height = sub->extents.height; - - } else { - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; - cairo_box_t bbox; - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - - *width = extents.width; - *height = extents.height; - } - return CAIRO_STATUS_SUCCESS; - } - - status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; - return status; - } - - status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; - return status; - } - - if (! _cairo_surface_get_extents (source, &extents)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - *width = extents.width; - *height = extents.height; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_filter_t filter, - cairo_pdf_resource_t *surface_res, - int *width, - int *height) -{ - cairo_pdf_source_surface_t src_surface; - cairo_pdf_source_surface_entry_t surface_key; - cairo_pdf_source_surface_entry_t *surface_entry; - cairo_status_t status; - cairo_bool_t interpolate; - - switch (filter) { - default: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = TRUE; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = FALSE; - break; - } - - surface_key.id = source->unique_id; - surface_key.interpolate = interpolate; - _cairo_pdf_source_surface_init_key (&surface_key); - surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); - if (surface_entry) { - *surface_res = surface_entry->surface_res; - *width = surface_entry->width; - *height = surface_entry->height; - - return CAIRO_STATUS_SUCCESS; - } - - surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); - if (surface_entry == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - surface_entry->id = surface_key.id; - surface_entry->interpolate = interpolate; - _cairo_pdf_source_surface_init_key (surface_entry); - - src_surface.hash_entry = surface_entry; - src_surface.surface = cairo_surface_reference (source); - surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); - if (surface_entry->surface_res.id == 0) { - cairo_surface_destroy (source); - free (surface_entry); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - status = _get_source_surface_size (source, &surface_entry->width, - &surface_entry->height); - - status = _cairo_array_append (&surface->page_surfaces, &src_surface); - if (unlikely (status)) { - cairo_surface_destroy (source); - free (surface_entry); - return status; - } - - status = _cairo_hash_table_insert (surface->all_surfaces, - &surface_entry->base); - - *surface_res = surface_entry->surface_res; - *width = surface_entry->width; - *height = surface_entry->height; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *pattern_res, - cairo_pdf_resource_t *gstate_res) -{ - cairo_pdf_pattern_t pdf_pattern; - cairo_status_t status; - - /* Solid colors are emitted into the content stream */ - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - pattern_res->id = 0; - gstate_res->id = 0; - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); - if (unlikely (status)) - return status; - - pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern.pattern_res.id == 0) { - cairo_pattern_destroy (pdf_pattern.pattern); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pdf_pattern.gstate_res.id = 0; - - /* gradient patterns require an smask object to implement transparency */ - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - { - if (_cairo_pattern_is_opaque (pattern, extents) == FALSE) { - pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern.gstate_res.id == 0) { - cairo_pattern_destroy (pdf_pattern.pattern); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - } - - pdf_pattern.width = surface->width; - pdf_pattern.height = surface->height; - if (extents != NULL) { - pdf_pattern.extents = *extents; - } else { - pdf_pattern.extents.x = 0; - pdf_pattern.extents.y = 0; - pdf_pattern.extents.width = surface->width; - pdf_pattern.extents.height = surface->height; - } - - *pattern_res = pdf_pattern.pattern_res; - *gstate_res = pdf_pattern.gstate_res; - - status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); - if (unlikely (status)) { - cairo_pattern_destroy (pdf_pattern.pattern); - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource, - cairo_bool_t compressed, - const char *fmt, - ...) -{ - va_list ap; - cairo_pdf_resource_t self, length; - cairo_output_stream_t *output = NULL; - - if (resource) { - self = *resource; - _cairo_pdf_surface_update_object (surface, self); - } else { - self = _cairo_pdf_surface_new_object (surface); - if (self.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - length = _cairo_pdf_surface_new_object (surface); - if (length.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (compressed) { - output = _cairo_deflate_stream_create (surface->output); - if (_cairo_output_stream_get_status (output)) - return _cairo_output_stream_destroy (output); - } - - surface->pdf_stream.active = TRUE; - surface->pdf_stream.self = self; - surface->pdf_stream.length = length; - surface->pdf_stream.compressed = compressed; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Length %d 0 R\n", - surface->pdf_stream.self.id, - surface->pdf_stream.length.id); - if (compressed) - _cairo_output_stream_printf (surface->output, - " /Filter /FlateDecode\n"); - - if (fmt != NULL) { - va_start (ap, fmt); - _cairo_output_stream_vprintf (surface->output, fmt, ap); - va_end (ap); - } - - _cairo_output_stream_printf (surface->output, - ">>\n" - "stream\n"); - - surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output); - - if (compressed) { - assert (surface->pdf_stream.old_output == NULL); - surface->pdf_stream.old_output = surface->output; - surface->output = output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) -{ - cairo_status_t status; - long length; - - if (! surface->pdf_stream.active) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - - if (surface->pdf_stream.compressed) { - cairo_status_t status2; - - status2 = _cairo_output_stream_destroy (surface->output); - if (likely (status == CAIRO_STATUS_SUCCESS)) - status = status2; - - surface->output = surface->pdf_stream.old_output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - surface->pdf_stream.old_output = NULL; - } - - length = _cairo_output_stream_get_position (surface->output) - - surface->pdf_stream.start_offset; - _cairo_output_stream_printf (surface->output, - "\n" - "endstream\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, - surface->pdf_stream.length); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - " %ld\n" - "endobj\n", - surface->pdf_stream.length.id, - length); - - surface->pdf_stream.active = FALSE; - - if (likely (status == CAIRO_STATUS_SUCCESS)) - status = _cairo_output_stream_get_status (surface->output); - - return status; -} - -static void -_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, - cairo_output_stream_t *mem_stream, - cairo_pdf_resource_t resource, - cairo_pdf_group_resources_t *resources, - cairo_bool_t is_knockout_group) -{ - _cairo_pdf_surface_update_object (surface, resource); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /XObject\n" - " /Length %d\n", - resource.id, - _cairo_memory_stream_length (mem_stream)); - - if (surface->compress_content) { - _cairo_output_stream_printf (surface->output, - " /Filter /FlateDecode\n"); - } - - _cairo_output_stream_printf (surface->output, - " /Subtype /Form\n" - " /BBox [ 0 0 %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /CS /DeviceRGB\n", - surface->width, - surface->height); - - if (is_knockout_group) - _cairo_output_stream_printf (surface->output, - " /K true\n"); - - _cairo_output_stream_printf (surface->output, - " >>\n" - " /Resources\n"); - _cairo_pdf_surface_emit_group_resources (surface, resources); - _cairo_output_stream_printf (surface->output, - ">>\n" - "stream\n"); - _cairo_memory_stream_copy (mem_stream, surface->output); - _cairo_output_stream_printf (surface->output, - "endstream\n" - "endobj\n"); -} - -static cairo_status_t -_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource) -{ - cairo_status_t status; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == FALSE); - - surface->group_stream.active = TRUE; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - surface->group_stream.mem_stream = _cairo_memory_stream_create (); - - if (surface->compress_content) { - surface->group_stream.stream = - _cairo_deflate_stream_create (surface->group_stream.mem_stream); - } else { - surface->group_stream.stream = surface->group_stream.mem_stream; - } - status = _cairo_output_stream_get_status (surface->group_stream.stream); - - surface->group_stream.old_output = surface->output; - surface->output = surface->group_stream.stream; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - _cairo_pdf_group_resources_clear (&surface->resources); - - if (resource) { - surface->group_stream.resource = *resource; - } else { - surface->group_stream.resource = _cairo_pdf_surface_new_object (surface); - if (surface->group_stream.resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - surface->group_stream.is_knockout = FALSE; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface) -{ - cairo_status_t status; - - status = _cairo_pdf_surface_open_group (surface, NULL); - if (unlikely (status)) - return status; - - surface->group_stream.is_knockout = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *group) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == TRUE); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (surface->compress_content) { - status = _cairo_output_stream_destroy (surface->group_stream.stream); - surface->group_stream.stream = NULL; - - _cairo_output_stream_printf (surface->group_stream.mem_stream, - "\n"); - } - surface->output = surface->group_stream.old_output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - surface->group_stream.active = FALSE; - _cairo_pdf_surface_write_memory_stream (surface, - surface->group_stream.mem_stream, - surface->group_stream.resource, - &surface->resources, - surface->group_stream.is_knockout); - if (group) - *group = surface->group_stream.resource; - - status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - surface->group_stream.mem_stream = NULL; - surface->group_stream.stream = NULL; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource, - cairo_bool_t is_form) -{ - cairo_status_t status; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == FALSE); - - surface->content_resources = _cairo_pdf_surface_new_object (surface); - if (surface->content_resources.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (is_form) { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ 0 0 %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - surface->width, - surface->height, - surface->content_resources.id); - } else { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - NULL); - } - if (unlikely (status)) - return status; - - surface->content = surface->pdf_stream.self; - - _cairo_output_stream_printf (surface->output, "q\n"); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) -{ - cairo_status_t status; - - assert (surface->pdf_stream.active == TRUE); - assert (surface->group_stream.active == FALSE); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - _cairo_pdf_surface_update_object (surface, surface->content_resources); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n", - surface->content_resources.id); - _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); - _cairo_output_stream_printf (surface->output, - "endobj\n"); - - return _cairo_output_stream_get_status (surface->output); -} - -static void -_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) -{ - cairo_pdf_source_surface_entry_t *surface_entry = entry; - cairo_hash_table_t *patterns = closure; - - _cairo_hash_table_remove (patterns, &surface_entry->base); - free (surface_entry); -} - -static cairo_status_t -_cairo_pdf_surface_finish (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - long offset; - cairo_pdf_resource_t info, catalog; - cairo_status_t status, status2; - - status = surface->base.status; - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_pdf_surface_emit_font_subsets (surface); - - _cairo_pdf_surface_write_pages (surface); - - info = _cairo_pdf_surface_write_info (surface); - if (info.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - catalog = _cairo_pdf_surface_write_catalog (surface); - if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - offset = _cairo_pdf_surface_write_xref (surface); - - _cairo_output_stream_printf (surface->output, - "trailer\n" - "<< /Size %d\n" - " /Root %d 0 R\n" - " /Info %d 0 R\n" - ">>\n", - surface->next_available_resource.id, - catalog.id, - info.id); - - _cairo_output_stream_printf (surface->output, - "startxref\n" - "%ld\n" - "%%%%EOF\n", - offset); - - /* pdf_operators has already been flushed when the last stream was - * closed so we should never be writing anything here - however, - * the stream may itself be in an error state. */ - status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - /* close any active streams still open due to fatal errors */ - status2 = _cairo_pdf_surface_close_stream (surface); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - if (surface->group_stream.stream != NULL) { - status2 = _cairo_output_stream_destroy (surface->group_stream.stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - if (surface->group_stream.mem_stream != NULL) { - status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - if (surface->pdf_stream.active) - surface->output = surface->pdf_stream.old_output; - if (surface->group_stream.active) - surface->output = surface->group_stream.old_output; - - /* and finish the pdf surface */ - status2 = _cairo_output_stream_destroy (surface->output); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_pdf_surface_clear (surface); - _cairo_pdf_group_resources_fini (&surface->resources); - - _cairo_array_fini (&surface->objects); - _cairo_array_fini (&surface->pages); - _cairo_array_fini (&surface->rgb_linear_functions); - _cairo_array_fini (&surface->alpha_linear_functions); - _cairo_array_fini (&surface->page_patterns); - _cairo_array_fini (&surface->page_surfaces); - _cairo_hash_table_foreach (surface->all_surfaces, - _cairo_pdf_source_surface_entry_pluck, - surface->all_surfaces); - _cairo_hash_table_destroy (surface->all_surfaces); - _cairo_array_fini (&surface->smask_groups); - _cairo_array_fini (&surface->fonts); - _cairo_array_fini (&surface->knockout_group); - - if (surface->font_subsets) { - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - surface->font_subsets = NULL; - } - - _cairo_surface_clipper_reset (&surface->clipper); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_start_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - /* Document header */ - if (! surface->header_emitted) { - const char *version; - - switch (surface->pdf_version) { - case CAIRO_PDF_VERSION_1_4: - version = "1.4"; - break; - default: - case CAIRO_PDF_VERSION_1_5: - version = "1.5"; - break; - } - - _cairo_output_stream_printf (surface->output, - "%%PDF-%s\n", version); - _cairo_output_stream_printf (surface->output, - "%%%c%c%c%c\n", 181, 237, 174, 251); - surface->header_emitted = TRUE; - } - - _cairo_pdf_group_resources_clear (&surface->resources); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_has_fallback_images (void *abstract_surface, - cairo_bool_t has_fallbacks) -{ - cairo_status_t status; - cairo_pdf_surface_t *surface = abstract_surface; - - surface->has_fallback_images = has_fallbacks; - status = _cairo_pdf_surface_open_content_stream (surface, NULL, has_fallbacks); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) -{ - return TRUE; -} - -/* Emit alpha channel from the image into the given data, providing - * an id that can be used to reference the resulting SMask object. - * - * In the case that the alpha channel happens to be all opaque, then - * no SMask object will be emitted and *id_ret will be set to 0. - */ -static cairo_status_t -_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image, - cairo_pdf_resource_t *stream_ret) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - char *alpha; - unsigned long alpha_size; - uint32_t *pixel32; - uint8_t *pixel8; - int i, x, y; - cairo_bool_t opaque; - uint8_t a; - - /* This is the only image format we support, which simplifies things. */ - assert (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1 ); - - stream_ret->id = 0; - - if (image->format == CAIRO_FORMAT_A1) { - alpha_size = (image->width + 7) / 8 * image->height; - alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); - } else { - alpha_size = image->height * image->width; - alpha = _cairo_malloc_ab (image->height, image->width); - } - - if (unlikely (alpha == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - opaque = TRUE; - i = 0; - for (y = 0; y < image->height; y++) { - if (image->format == CAIRO_FORMAT_ARGB32) { - pixel32 = (uint32_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel32++) { - a = (*pixel32 & 0xff000000) >> 24; - alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; - } - } else if (image->format == CAIRO_FORMAT_A8){ - pixel8 = (uint8_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel8++) { - a = *pixel8; - alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; - } - } else { /* image->format == CAIRO_FORMAT_A1 */ - pixel8 = (uint8_t *) (image->data + y * image->stride); - - for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { - a = *pixel8; - a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); - alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; - } - } - } - - /* Bail out without emitting smask if it's all opaque. */ - if (opaque) - goto CLEANUP_ALPHA; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace /DeviceGray\n" - " /BitsPerComponent %d\n", - image->width, image->height, - image->format == CAIRO_FORMAT_A1 ? 1 : 8); - if (unlikely (status)) - goto CLEANUP_ALPHA; - - *stream_ret = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, alpha, alpha_size); - status = _cairo_pdf_surface_close_stream (surface); - - CLEANUP_ALPHA: - free (alpha); - CLEANUP: - return status; -} - -/* Emit image data into the given surface, providing a resource that - * can be used to reference the data in image_ret. */ -static cairo_status_t -_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image, - cairo_pdf_resource_t *image_res, - cairo_filter_t filter) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - char *rgb; - unsigned long rgb_size; - uint32_t *pixel; - int i, x, y; - cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ - cairo_bool_t need_smask; - const char *interpolate = "true"; - - /* These are the only image formats we currently support, (which - * makes things a lot simpler here). This is enforced through - * _cairo_pdf_surface_analyze_operation which only accept source surfaces of - * CONTENT_COLOR or CONTENT_COLOR_ALPHA. - */ - assert (image->format == CAIRO_FORMAT_RGB24 || - image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1); - - rgb_size = image->height * image->width * 3; - rgb = _cairo_malloc_abc (image->width, image->height, 3); - if (unlikely (rgb == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - i = 0; - for (y = 0; y < image->height; y++) { - pixel = (uint32_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel++) { - /* XXX: We're un-premultiplying alpha here. My reading of the PDF - * specification suggests that we should be able to avoid having - * to do this by filling in the SMask's Matte dictionary - * appropriately, but my attempts to do that so far have - * failed. */ - if (image->format == CAIRO_FORMAT_ARGB32) { - uint8_t a; - a = (*pixel & 0xff000000) >> 24; - if (a == 0) { - rgb[i++] = 0; - rgb[i++] = 0; - rgb[i++] = 0; - } else { - rgb[i++] = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; - rgb[i++] = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; - rgb[i++] = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; - } - } else if (image->format == CAIRO_FORMAT_RGB24) { - rgb[i++] = (*pixel & 0x00ff0000) >> 16; - rgb[i++] = (*pixel & 0x0000ff00) >> 8; - rgb[i++] = (*pixel & 0x000000ff) >> 0; - } else { - rgb[i++] = 0; - rgb[i++] = 0; - rgb[i++] = 0; - } - } - } - - need_smask = FALSE; - if (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1) { - status = _cairo_pdf_surface_emit_smask (surface, image, &smask); - if (unlikely (status)) - goto CLEANUP_RGB; - - if (smask.id) - need_smask = TRUE; - } - - switch (filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; - break; - } - -#define IMAGE_DICTIONARY " /Type /XObject\n" \ - " /Subtype /Image\n" \ - " /Width %d\n" \ - " /Height %d\n" \ - " /ColorSpace /DeviceRGB\n" \ - " /Interpolate %s\n" \ - " /BitsPerComponent 8\n" - - if (need_smask) - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY - " /SMask %d 0 R\n", - image->width, image->height, - interpolate, - smask.id); - else - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY, - image->width, image->height, - interpolate); - if (unlikely (status)) - goto CLEANUP_RGB; - -#undef IMAGE_DICTIONARY - - _cairo_output_stream_write (surface->output, rgb, rgb_size); - status = _cairo_pdf_surface_close_stream (surface); - -CLEANUP_RGB: - free (rgb); -CLEANUP: - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t res) -{ - cairo_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - - if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); - if (status) - return status; - - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /Filter /JPXDecode\n", - info.width, - info.height); - if (status) - return status; - - _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t res) -{ - cairo_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (unlikely (source->status)) - return source->status; - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - if (info.num_components != 1 && info.num_components != 3) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace %s\n" - " /BitsPerComponent %d\n" - " /Filter /DCTDecode\n", - info.width, - info.height, - info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB", - info.bits_per_component); - if (unlikely (status)) - return status; - - _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t resource, - cairo_bool_t interpolate) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_status_t status; - - status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_surface_acquire_source_image (source, &image, &image_extra); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_image (surface, image, - &resource, interpolate); - if (unlikely (status)) - goto BAIL; - -BAIL: - _cairo_surface_release_source_image (source, image, image_extra); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern, - cairo_pdf_resource_t *resource, - int *width, - int *height, - int *origin_x, - int *origin_y) -{ - cairo_image_surface_t *image; - cairo_surface_t *pad_image; - void *image_extra; - cairo_status_t status; - cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; - int x = 0; - int y = 0; - cairo_bool_t interpolate; - - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (unlikely (status)) - return status; - - pad_image = &image->base; - if (pattern->base.extend == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, &pdf_pattern->extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - x = -rect.x; - y = -rect.y; - - pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, - rect.width, - rect.height); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } - - _cairo_pattern_init_for_surface (&pad_pattern, &image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL, - pad_image, - 0, 0, - 0, 0, - 0, 0, - rect.width, - rect.height, - NULL); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) - goto BAIL; - } - - switch (pdf_pattern->pattern->filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = TRUE; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = FALSE; - break; - } - - *resource = _cairo_pdf_surface_new_object (surface); - if (resource->id == 0) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, - resource, interpolate); - if (unlikely (status)) - goto BAIL; - - *width = ((cairo_image_surface_t *)pad_image)->width; - *height = ((cairo_image_surface_t *)pad_image)->height; - *origin_x = x; - *origin_y = y; - -BAIL: - if (pad_image != &image->base) - cairo_surface_destroy (pad_image); - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - - -static cairo_status_t -_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *recording_surface, - cairo_pdf_resource_t resource) -{ - double old_width, old_height; - cairo_paginated_mode_t old_paginated_mode; - cairo_rectangle_int_t recording_extents; - cairo_bool_t is_bounded; - cairo_status_t status; - int alpha = 0; - - is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents); - assert (is_bounded); - - old_width = surface->width; - old_height = surface->height; - old_paginated_mode = surface->paginated_mode; - - _cairo_pdf_surface_set_size_internal (surface, - recording_extents.width, - recording_extents.height); - /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the recording surface is replayed - * back to this surface. - */ - surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); - if (unlikely (status)) - return status; - - if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", - alpha, - surface->width, - surface->height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - surface->paginated_mode = old_paginated_mode; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_recording_subsurface (cairo_pdf_surface_t *surface, - cairo_surface_t *recording_surface, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t resource) -{ - double old_width, old_height; - cairo_paginated_mode_t old_paginated_mode; - cairo_status_t status; - int alpha = 0; - - old_width = surface->width; - old_height = surface->height; - old_paginated_mode = surface->paginated_mode; - - _cairo_pdf_surface_set_size_internal (surface, - extents->width, - extents->height); - /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the recording surface is replayed - * back to this surface. - */ - surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); - if (unlikely (status)) - return status; - - if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", - alpha, - surface->width, - surface->height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - extents, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - surface->paginated_mode = old_paginated_mode; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, - cairo_pdf_source_surface_t *src_surface) -{ - if (src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (src_surface->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) src_surface->surface; - return _cairo_pdf_surface_emit_recording_subsurface (surface, - sub->target, - &sub->extents, - src_surface->hash_entry->surface_res); - } else { - return _cairo_pdf_surface_emit_recording_surface (surface, - src_surface->surface, - src_surface->hash_entry->surface_res); - } - } else { - return _cairo_pdf_surface_emit_image_surface (surface, - src_surface->surface, - src_surface->hash_entry->surface_res, - src_surface->hash_entry->interpolate); - } -} - -static cairo_status_t -_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; - cairo_status_t status; - cairo_pdf_resource_t pattern_resource = {0}; - cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base); - double xstep, ystep; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ - int origin_x = 0; /* squelch bogus compiler warning */ - int origin_y = 0; /* squelch bogus compiler warning */ - int bbox_x, bbox_y; - char draw_surface[200]; - - if (pattern->base.extend == CAIRO_EXTEND_PAD && - pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) - { - status = _cairo_pdf_surface_emit_padded_image_surface (surface, - pdf_pattern, - &pattern_resource, - &pattern_width, - &pattern_height, - &origin_x, - &origin_y); - } - else - { - status = _cairo_pdf_surface_add_source_surface (surface, - pattern->surface, - pdf_pattern->pattern->filter, - &pattern_resource, - &pattern_width, - &pattern_height); - } - if (unlikely (status)) - return status; - - bbox_x = pattern_width; - bbox_y = pattern_height; - switch (extend) { - case CAIRO_EXTEND_PAD: - case CAIRO_EXTEND_NONE: - { - /* In PS/PDF, (as far as I can tell), all patterns are - * repeating. So we support cairo's EXTEND_NONE semantics - * by setting the repeat step size to a size large enough - * to guarantee that no more than a single occurrence will - * be visible. - * - * First, map the surface extents into pattern space (since - * xstep and ystep are in pattern space). Then use an upper - * bound on the length of the diagonal of the pattern image - * and the surface as repeat size. This guarantees to never - * repeat visibly. - */ - double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, - &x1, &y1, &x2, &y2, - NULL); - - /* Rather than computing precise bounds of the union, just - * add the surface extents unconditionally. We only - * required an answer that's large enough, we don't really - * care if it's not as tight as possible.*/ - xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); - } - break; - case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; - break; - case CAIRO_EXTEND_REFLECT: - bbox_x = pattern_width*2; - bbox_y = pattern_height*2; - xstep = bbox_x; - ystep = bbox_y; - break; - /* All the rest (if any) should have been analyzed away, so this - * case should be unreachable. */ - default: - ASSERT_NOT_REACHED; - xstep = 0; - ystep = 0; - } - - /* At this point, (that is, within the surface backend interface), - * the pattern's matrix maps from cairo's device space to cairo's - * pattern space, (both with their origin at the upper-left, and - * cairo's pattern space of size width,height). - * - * Then, we must emit a PDF pattern object that maps from its own - * pattern space, (which has a size that we establish in the BBox - * dictionary entry), to the PDF page's *initial* space, (which - * does not benefit from the Y-axis flipping matrix that we emit - * on each page). So the PDF patterns matrix maps from a - * (width,height) pattern space to a device space with the origin - * in the lower-left corner. - * - * So to handle all of that, we start with an identity matrix for - * the PDF pattern to device matrix. We translate it up by the - * image height then flip it in the Y direction, (moving us from - * the PDF origin to cairo's origin). We then multiply in the - * inverse of the cairo pattern matrix, (since it maps from device - * to pattern, while we're setting up pattern to device). Finally, - * we translate back down by the image height and flip again to - * end up at the lower-left origin that PDF expects. - * - * Additionally, within the stream that paints the pattern itself, - * we are using a PDF image object that has a size of (1,1) so we - * have to scale it up by the image width and height to fill our - * pattern cell. - */ - cairo_p2d = pattern->base.matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); - cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y); - cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - status = _cairo_pdf_surface_open_stream (surface, - &pdf_pattern->pattern_res, - FALSE, - " /PatternType 1\n" - " /BBox [0 0 %d %d]\n" - " /XStep %f\n" - " /YStep %f\n" - " /TilingType 1\n" - " /PaintType 1\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Resources << /XObject << /x%d %d 0 R >> >>\n", - bbox_x, bbox_y, - xstep, ystep, - pdf_p2d.xx, pdf_p2d.yx, - pdf_p2d.xy, pdf_p2d.yy, - pdf_p2d.x0, pdf_p2d.y0, - pattern_resource.id, - pattern_resource.id); - if (unlikely (status)) - return status; - - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - snprintf(draw_surface, - sizeof (draw_surface), - "/x%d Do\n", - pattern_resource.id); - } else { - snprintf(draw_surface, - sizeof (draw_surface), - "q %d 0 0 %d 0 0 cm /x%d Do Q", - pattern_width, - pattern_height, - pattern_resource.id); - } - - if (extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->output, - "q 0 0 %d %d re W n %s Q\n" - "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n" - "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n" - "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n", - pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_width, pattern_height, - draw_surface, - pattern_height*2, pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_height*2, pattern_width, pattern_height, - draw_surface); - } else { - _cairo_output_stream_printf (surface->output, - " %s \n", - draw_surface); - } - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - return _cairo_output_stream_get_status (surface->output); -} - -typedef struct _cairo_pdf_color_stop { - double offset; - double color[4]; - cairo_pdf_resource_t resource; -} cairo_pdf_color_stop_t; - -static cairo_status_t -cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2, - cairo_pdf_resource_t *function) -{ - int num_elems, i; - cairo_pdf_rgb_linear_function_t elem; - cairo_pdf_resource_t res; - cairo_status_t status; - - num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); - for (i = 0; i < num_elems; i++) { - _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem); - if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0) - continue; - if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0) - continue; - *function = elem.resource; - return CAIRO_STATUS_SUCCESS; - } - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 2\n" - " /Domain [ 0 1 ]\n" - " /C0 [ %f %f %f ]\n" - " /C1 [ %f %f %f ]\n" - " /N 1\n" - ">>\n" - "endobj\n", - res.id, - stop1->color[0], - stop1->color[1], - stop1->color[2], - stop2->color[0], - stop2->color[1], - stop2->color[2]); - - elem.resource = res; - memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3); - memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3); - - status = _cairo_array_append (&surface->rgb_linear_functions, &elem); - *function = res; - - return status; -} - -static cairo_status_t -cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2, - cairo_pdf_resource_t *function) -{ - int num_elems, i; - cairo_pdf_alpha_linear_function_t elem; - cairo_pdf_resource_t res; - cairo_status_t status; - - num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); - for (i = 0; i < num_elems; i++) { - _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem); - if (elem.alpha1 != stop1->color[3]) - continue; - if (elem.alpha2 != stop2->color[3]) - continue; - *function = elem.resource; - return CAIRO_STATUS_SUCCESS; - } - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 2\n" - " /Domain [ 0 1 ]\n" - " /C0 [ %f ]\n" - " /C1 [ %f ]\n" - " /N 1\n" - ">>\n" - "endobj\n", - res.id, - stop1->color[3], - stop2->color[3]); - - elem.resource = res; - elem.alpha1 = stop1->color[3]; - elem.alpha2 = stop2->color[3]; - - status = _cairo_array_append (&surface->alpha_linear_functions, &elem); - *function = res; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, - unsigned int n_stops, - cairo_pdf_color_stop_t *stops, - cairo_bool_t is_alpha, - cairo_pdf_resource_t *function) -{ - cairo_pdf_resource_t res; - unsigned int i; - cairo_status_t status; - - /* emit linear gradients between pairs of subsequent stops... */ - for (i = 0; i < n_stops-1; i++) { - if (is_alpha) { - status = cairo_pdf_surface_emit_alpha_linear_function (surface, - &stops[i], - &stops[i+1], - &stops[i].resource); - if (unlikely (status)) - return status; - } else { - status = cairo_pdf_surface_emit_rgb_linear_function (surface, - &stops[i], - &stops[i+1], - &stops[i].resource); - if (unlikely (status)) - return status; - } - } - - /* ... and stitch them together */ - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 3\n" - " /Domain [ %f %f ]\n", - res.id, - stops[0].offset, - stops[n_stops - 1].offset); - - _cairo_output_stream_printf (surface->output, - " /Functions [ "); - for (i = 0; i < n_stops-1; i++) - _cairo_output_stream_printf (surface->output, - "%d 0 R ", stops[i].resource.id); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Bounds [ "); - for (i = 1; i < n_stops-1; i++) - _cairo_output_stream_printf (surface->output, - "%f ", stops[i].offset); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Encode [ "); - for (i = 1; i < n_stops; i++) - _cairo_output_stream_printf (surface->output, - "0 1 "); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - *function = res; - - return _cairo_output_stream_get_status (surface->output); -} - - -static void -calc_gradient_color (cairo_pdf_color_stop_t *new_stop, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2) -{ - int i; - double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); - - for (i = 0; i < 4; i++) - new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); -} - -#define COLOR_STOP_EPSILON 1e-6 - -static cairo_status_t -_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, - cairo_gradient_pattern_t *pattern, - cairo_pdf_resource_t *color_function, - cairo_pdf_resource_t *alpha_function) -{ - cairo_pdf_color_stop_t *allstops, *stops; - unsigned int n_stops; - unsigned int i; - cairo_bool_t emit_alpha = FALSE; - cairo_status_t status; - - color_function->id = 0; - alpha_function->id = 0; - - allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); - if (unlikely (allstops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - stops = &allstops[1]; - n_stops = pattern->n_stops; - - for (i = 0; i < n_stops; i++) { - stops[i].color[0] = pattern->stops[i].color.red; - stops[i].color[1] = pattern->stops[i].color.green; - stops[i].color[2] = pattern->stops[i].color.blue; - stops[i].color[3] = pattern->stops[i].color.alpha; - if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3])) - emit_alpha = TRUE; - stops[i].offset = pattern->stops[i].offset; - } - - if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) { - if (stops[0].offset > COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) - memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t)); - else - calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); - stops = allstops; - n_stops++; - } - stops[0].offset = 0.0; - - if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { - memcpy (&stops[n_stops], - &stops[n_stops - 1], - sizeof (cairo_pdf_color_stop_t)); - } else { - calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); - } - n_stops++; - } - stops[n_stops-1].offset = 1.0; - } - - if (n_stops <= 2) { - /* no need for stitched function */ - status = cairo_pdf_surface_emit_rgb_linear_function (surface, - &stops[0], - &stops[n_stops - 1], - color_function); - if (unlikely (status)) - goto BAIL; - - if (emit_alpha) { - status = cairo_pdf_surface_emit_alpha_linear_function (surface, - &stops[0], - &stops[n_stops - 1], - alpha_function); - if (unlikely (status)) - goto BAIL; - } - } else { - /* multiple stops: stitch. XXX possible optimization: regularly spaced - * stops do not require stitching. XXX */ - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - n_stops, - stops, - FALSE, - color_function); - if (unlikely (status)) - goto BAIL; - - if (emit_alpha) { - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - n_stops, - stops, - TRUE, - alpha_function); - if (unlikely (status)) - goto BAIL; - } - } - -BAIL: - free (allstops); - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, - cairo_gradient_pattern_t *pattern, - cairo_pdf_resource_t *function, - int begin, - int end) -{ - cairo_pdf_resource_t res; - int i; - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 3\n" - " /Domain [ %d %d ]\n", - res.id, - begin, - end); - - _cairo_output_stream_printf (surface->output, - " /Functions [ "); - for (i = begin; i < end; i++) - _cairo_output_stream_printf (surface->output, - "%d 0 R ", function->id); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Bounds [ "); - for (i = begin + 1; i < end; i++) - _cairo_output_stream_printf (surface->output, - "%d ", i); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Encode [ "); - for (i = begin; i < end; i++) { - if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->output, - "1 0 "); - } else { - _cairo_output_stream_printf (surface->output, - "0 1 "); - } - } - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - *function = res; - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t gstate_resource, - cairo_pdf_resource_t gradient_mask) -{ - cairo_pdf_resource_t smask_resource; - cairo_status_t status; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /FormType 1\n" - " /BBox [ 0 0 %f %f ]\n" - " /Resources\n" - " << /ExtGState\n" - " << /a0 << /ca 1 /CA 1 >>" - " >>\n" - " /Pattern\n" - " << /p%d %d 0 R >>\n" - " >>\n" - " /Group\n" - " << /Type /Group\n" - " /S /Transparency\n" - " /CS /DeviceGray\n" - " >>\n", - surface->width, - surface->height, - gradient_mask.id, - gradient_mask.id); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q\n" - "/a0 gs\n" - "/Pattern cs /p%d scn\n" - "0 0 %f %f re\n" - "f\n" - "Q\n", - gradient_mask.id, - surface->width, - surface->height); - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - smask_resource = _cairo_pdf_surface_new_object (surface); - if (smask_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Mask\n" - " /S /Luminosity\n" - " /G %d 0 R\n" - ">>\n" - "endobj\n", - smask_resource.id, - surface->pdf_stream.self.id); - - /* Create GState which uses the transparency group as an SMask. */ - _cairo_pdf_surface_update_object (surface, gstate_resource); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /ExtGState\n" - " /SMask %d 0 R\n" - " /ca 1\n" - " /CA 1\n" - " /AIS false\n" - ">>\n" - "endobj\n", - gstate_resource.id, - smask_resource.id); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern; - cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2; - double _x1, _y1, _x2, _y2; - cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; - cairo_status_t status; - cairo_gradient_pattern_t *gradient = &pattern->base; - double first_stop, last_stop; - int repeat_begin = 0, repeat_end = 1; - - assert (pattern->base.n_stops != 0); - - extend = cairo_pattern_get_extend (pdf_pattern->pattern); - - pat_to_pdf = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - first_stop = gradient->stops[0].offset; - last_stop = gradient->stops[gradient->n_stops - 1].offset; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - double dx, dy; - int x_rep = 0, y_rep = 0; - - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1); - - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2); - - dx = fabs (x2 - x1); - dy = fabs (y2 - y1); - if (dx > 1e-6) - x_rep = ceil (surface->width/dx); - if (dy > 1e-6) - y_rep = ceil (surface->height/dy); - - repeat_end = MAX (x_rep, y_rep); - repeat_begin = -repeat_end; - first_stop = repeat_begin; - last_stop = repeat_end; - } - - /* PDF requires the first and last stop to be the same as the line - * coordinates. For repeating patterns this moves the line - * coordinates out to the begin/end of the repeating function. For - * non repeating patterns this may move the line coordinates in if - * there are not stops at offset 0 and 1. */ - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - - _x1 = x1 + (x2 - x1)*first_stop; - _y1 = y1 + (y2 - y1)*first_stop; - _x2 = x1 + (x2 - x1)*last_stop; - _y2 = y1 + (y2 - y1)*last_stop; - - x1 = _x1; - x2 = _x2; - y1 = _y1; - y2 = _y2; - - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || - pattern->base.base.extend == CAIRO_EXTEND_PAD) && - gradient->n_stops == 2) { - first_stop = 0.0; - last_stop = 1.0; - } - - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); - if (unlikely (status)) - return status; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, - &color_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - - if (alpha_function.id != 0) { - status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, - &alpha_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - } - } - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, x2, y2, - first_stop, last_stop, - color_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); - - if (alpha_function.id != 0) { - cairo_pdf_resource_t mask_resource; - - assert (pdf_pattern->gstate_res.id != 0); - - /* Create pattern for SMask. */ - mask_resource = _cairo_pdf_surface_new_object (surface); - if (mask_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\n", - mask_resource.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, x2, y2, - first_stop, last_stop, - alpha_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); - status = _cairo_pdf_surface_add_pattern (surface, mask_resource); - if (unlikely (status)) - return status; - - status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->gstate_res, - mask_resource); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2, r1, r2; - cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; - cairo_status_t status; - cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern; - - assert (pattern->base.n_stops != 0); - - extend = cairo_pattern_get_extend (pdf_pattern->pattern); - - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); - if (unlikely (status)) - return status; - - pat_to_pdf = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - x1 = _cairo_fixed_to_double (pattern->c1.x); - y1 = _cairo_fixed_to_double (pattern->c1.y); - r1 = _cairo_fixed_to_double (pattern->r1); - x2 = _cairo_fixed_to_double (pattern->c2.x); - y2 = _cairo_fixed_to_double (pattern->c2.y); - r2 = _cairo_fixed_to_double (pattern->r2); - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, r1, x2, y2, r2, - color_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); - - if (alpha_function.id != 0) { - cairo_pdf_resource_t mask_resource; - - assert (pdf_pattern->gstate_res.id != 0); - - /* Create pattern for SMask. */ - mask_resource = _cairo_pdf_surface_new_object (surface); - if (mask_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\n", - mask_resource.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, r1, x2, y2, r2, - alpha_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); - - status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->gstate_res, - mask_resource); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) -{ - double old_width, old_height; - cairo_status_t status; - - old_width = surface->width; - old_height = surface->height; - _cairo_pdf_surface_set_size_internal (surface, - pdf_pattern->width, - pdf_pattern->height); - - switch (pdf_pattern->pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - ASSERT_NOT_REACHED; - status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - break; - - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern); - break; - - case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern); - break; - - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern); - break; - - default: - ASSERT_NOT_REACHED; - status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - break; - } - - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *source) -{ - cairo_pdf_resource_t surface_res; - int width, height; - cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_status_t status; - int alpha; - - status = _cairo_pdf_surface_add_source_surface (surface, - source->surface, - source->base.filter, - &surface_res, - &width, - &height); - if (unlikely (status)) - return status; - - cairo_p2d = source->base.matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - pdf_p2d = surface->cairo_to_pdf; - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); - cairo_matrix_translate (&pdf_p2d, 0.0, height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING) - cairo_matrix_scale (&pdf_p2d, width, height); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (! _cairo_matrix_is_identity (&pdf_p2d)) { - _cairo_output_stream_printf (surface->output, - "%f %f %f %f %f %f cm\n", - pdf_p2d.xx, pdf_p2d.yx, - pdf_p2d.xy, pdf_p2d.yy, - pdf_p2d.x0, pdf_p2d.y0); - } - - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/a%d gs /x%d Do\n", - alpha, - surface_res.id); - - return _cairo_pdf_surface_add_xobject (surface, surface_res); -} - -static cairo_status_t -_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, - cairo_operator_t op) -{ - cairo_status_t status; - - if (op == surface->current_operator) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/b%d gs\n", op); - surface->current_operator = op; - _cairo_pdf_surface_add_operator (surface, op); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_pdf_resource_t pattern_res, - cairo_bool_t is_stroke) -{ - cairo_status_t status; - int alpha; - const cairo_color_t *solid_color = NULL; - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; - - solid_color = &solid->color; - } - - if (solid_color != NULL) { - if (surface->current_pattern_is_solid_color == FALSE || - surface->current_color_red != solid_color->red || - surface->current_color_green != solid_color->green || - surface->current_color_blue != solid_color->blue || - surface->current_color_is_stroke != is_stroke) - { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "%f %f %f ", - solid_color->red, - solid_color->green, - solid_color->blue); - - if (is_stroke) - _cairo_output_stream_printf (surface->output, "RG "); - else - _cairo_output_stream_printf (surface->output, "rg "); - - surface->current_color_red = solid_color->red; - surface->current_color_green = solid_color->green; - surface->current_color_blue = solid_color->blue; - surface->current_color_is_stroke = is_stroke; - } - - if (surface->current_pattern_is_solid_color == FALSE || - surface->current_color_alpha != solid_color->alpha) - { - status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/a%d gs\n", - alpha); - surface->current_color_alpha = solid_color->alpha; - } - - surface->current_pattern_is_solid_color = TRUE; - } else { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_pattern (surface, pattern_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - /* fill-stroke calls select_pattern twice. Don't save if the - * gstate is already saved. */ - if (!surface->select_pattern_gstate_saved) - _cairo_output_stream_printf (surface->output, "q "); - - if (is_stroke) { - _cairo_output_stream_printf (surface->output, - "/Pattern CS /p%d SCN ", - pattern_res.id); - } else { - _cairo_output_stream_printf (surface->output, - "/Pattern cs /p%d scn ", - pattern_res.id); - } - _cairo_output_stream_printf (surface->output, - "/a%d gs\n", - alpha); - surface->select_pattern_gstate_saved = TRUE; - surface->current_pattern_is_solid_color = FALSE; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) -{ - cairo_int_status_t status; - - if (surface->select_pattern_gstate_saved) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - _cairo_pdf_operators_reset (&surface->pdf_operators); - } - surface->select_pattern_gstate_saved = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_show_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_write_page (surface); - if (unlikely (status)) - return status; - - _cairo_pdf_surface_clear (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_pdf_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the arbitrary limitation of width to a short(!). We - * may need to come up with a better interface for get_size. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); - - return TRUE; -} - -static void -_cairo_pdf_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); - cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); -} - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t info; - - info = _cairo_pdf_surface_new_object (surface); - if (info.id == 0) - return info; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Creator (cairo %s (http://cairographics.org))\n" - " /Producer (cairo %s (http://cairographics.org))\n" - ">>\n" - "endobj\n", - info.id, - cairo_version_string (), - cairo_version_string ()); - - return info; -} - -static void -_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t page; - int num_pages, i; - - _cairo_pdf_surface_update_object (surface, surface->pages_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pages\n" - " /Kids [ ", - surface->pages_resource.id); - - num_pages = _cairo_array_num_elements (&surface->pages); - for (i = 0; i < num_pages; i++) { - _cairo_array_copy_element (&surface->pages, i, &page); - _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id); - } - - _cairo_output_stream_printf (surface->output, "]\n"); - _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages); - - - /* TODO: Figure out which other defaults to be inherited by /Page - * objects. */ - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); -} - -static cairo_status_t -_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, - const char *utf8) -{ - uint16_t *utf16 = NULL; - int utf16_len = 0; - cairo_status_t status; - int i; - - if (utf8 && *utf8) { - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) - return status; - } - - _cairo_output_stream_printf (surface->output, "<"); - if (utf16 == NULL || utf16_len == 0) { - /* According to the "ToUnicode Mapping File Tutorial" - * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf - * - * Glyphs that do not map to a Unicode code point must be - * mapped to 0xfffd "REPLACEMENT CHARACTER". - */ - _cairo_output_stream_printf (surface->output, - "fffd"); - } else { - for (i = 0; i < utf16_len; i++) - _cairo_output_stream_printf (surface->output, - "%04x", (int) (utf16[i])); - } - _cairo_output_stream_printf (surface->output, ">"); - - if (utf16) - free (utf16); - - return CAIRO_STATUS_SUCCESS; -} - -/* Bob Jenkins hash - * - * Public domain code from: - * http://burtleburtle.net/bob/hash/doobs.html - */ - -#define HASH_MIX(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<<8); \ - c -= a; c -= b; c ^= (b>>13); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<16); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>3); \ - b -= c; b -= a; b ^= (a<<10); \ - c -= a; c -= b; c ^= (b>>15); \ -} - -static uint32_t -_hash_data (const unsigned char *data, int length, uint32_t initval) -{ - uint32_t a, b, c, len; - - len = length; - a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - c = initval; /* the previous hash value */ - - while (len >= 12) { - a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); - b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); - c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); - HASH_MIX (a,b,c); - data += 12; - len -= 12; - } - - c += length; - switch(len) { - case 11: c+= ((uint32_t) data[10] << 24); - case 10: c+= ((uint32_t) data[9] << 16); - case 9 : c+= ((uint32_t) data[8] << 8); - case 8 : b+= ((uint32_t) data[7] << 24); - case 7 : b+= ((uint32_t) data[6] << 16); - case 6 : b+= ((uint32_t) data[5] << 8); - case 5 : b+= data[4]; - case 4 : a+= ((uint32_t) data[3] << 24); - case 3 : a+= ((uint32_t) data[2] << 16); - case 2 : a+= ((uint32_t) data[1] << 8); - case 1 : a+= data[0]; - } - HASH_MIX (a,b,c); - - return c; -} - -static void -_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, - const char *font_name, - char *tag) -{ - uint32_t hash; - int i; - long numerator; - ldiv_t d; - - hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); - hash = _hash_data ((unsigned char *) (font_subset->glyphs), - font_subset->num_glyphs * sizeof(unsigned long), hash); - - numerator = abs (hash); - for (i = 0; i < 6; i++) { - d = ldiv (numerator, 26); - numerator = d.quot; - tag[i] = 'A' + d.rem; - } - tag[i] = 0; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_bool_t is_composite, - cairo_pdf_resource_t *stream) -{ - unsigned int i, num_bfchar; - cairo_int_status_t status; - - stream->id = 0; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - NULL); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/CIDInit /ProcSet findresource begin\n" - "12 dict begin\n" - "begincmap\n" - "/CIDSystemInfo\n" - "<< /Registry (Adobe)\n" - " /Ordering (UCS)\n" - " /Supplement 0\n" - ">> def\n" - "/CMapName /Adobe-Identity-UCS def\n" - "/CMapType 2 def\n" - "1 begincodespacerange\n"); - - if (is_composite) { - _cairo_output_stream_printf (surface->output, - "<0000> \n"); - } else { - _cairo_output_stream_printf (surface->output, - "<00> \n"); - } - - _cairo_output_stream_printf (surface->output, - "endcodespacerange\n"); - - if (font_subset->is_scaled) { - /* Type 3 fonts include glyph 0 in the subset */ - num_bfchar = font_subset->num_glyphs; - - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); - - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - _cairo_output_stream_printf (surface->output, "<%02x> ", i); - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i]); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "\n"); - } - } else { - /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ - num_bfchar = font_subset->num_glyphs - 1; - - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); - - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - if (is_composite) - _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); - else - _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); - - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i + 1]); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "\n"); - } - } - - _cairo_output_stream_printf (surface->output, - "endbfchar\n"); - - _cairo_output_stream_printf (surface->output, - "endcmap\n" - "CMapName currentdict /CMap defineresource pop\n" - "end\n" - "end\n"); - - *stream = surface->pdf_stream.self; - return _cairo_pdf_surface_close_stream (surface); -} - -#define PDF_UNITS_PER_EM 1000 - -static cairo_status_t -_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_cff_subset_t *subset) -{ - cairo_pdf_resource_t stream, descriptor, cidfont_dict; - cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - unsigned int i; - cairo_status_t status; - char tag[10]; - - _create_font_subset_tag (font_subset, subset->ps_name, tag); - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Subtype /CIDFontType0C\n"); - if (unlikely (status)) - return status; - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, - subset->data, subset->data_length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, - &to_unicode_stream); - if (_cairo_status_is_error (status)) - return status; - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n", - descriptor.id, - tag, - subset->ps_name); - - if (subset->font_name) { - _cairo_output_stream_printf (surface->output, - " /FontFamily (%s)\n", - subset->font_name); - } - - _cairo_output_stream_printf (surface->output, - " /Flags 4\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile3 %u 0 R\n" - ">>\n" - "endobj\n", - (long)(subset->x_min*PDF_UNITS_PER_EM), - (long)(subset->y_min*PDF_UNITS_PER_EM), - (long)(subset->x_max*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - (long)(subset->ascent*PDF_UNITS_PER_EM), - (long)(subset->descent*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - stream.id); - - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType0\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset->ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset->ps_name, - cidfont_dict.id); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - status = _cairo_array_append (&surface->fonts, &font); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_status_t status; - cairo_cff_subset_t subset; - char name[64]; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_cff_subset_init (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); - - _cairo_cff_subset_fini (&subset); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_status_t status; - cairo_cff_subset_t subset; - char name[64]; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_cff_fallback_init (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); - - _cairo_cff_fallback_fini (&subset); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_type1_subset_t *subset) -{ - cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - cairo_status_t status; - unsigned long length; - unsigned int i; - char tag[10]; - - _create_font_subset_tag (font_subset, subset->base_font, tag); - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - length = subset->header_length + subset->data_length + subset->trailer_length; - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Length1 %lu\n" - " /Length2 %lu\n" - " /Length3 %lu\n", - subset->header_length, - subset->data_length, - subset->trailer_length); - if (unlikely (status)) - return status; - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, subset->data, length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, - &to_unicode_stream); - if (_cairo_status_is_error (status)) - return status; - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n" - " /Flags 4\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile %u 0 R\n" - ">>\n" - "endobj\n", - descriptor.id, - tag, - subset->base_font, - (long)(subset->x_min*PDF_UNITS_PER_EM), - (long)(subset->y_min*PDF_UNITS_PER_EM), - (long)(subset->x_max*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - (long)(subset->ascent*PDF_UNITS_PER_EM), - (long)(subset->descent*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - stream.id); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type1\n" - " /BaseFont /%s+%s\n" - " /FirstChar 0\n" - " /LastChar %d\n" - " /FontDescriptor %d 0 R\n" - " /Widths [", - subset_resource.id, - tag, - subset->base_font, - font_subset->num_glyphs - 1, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]\n"); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - return _cairo_array_append (&surface->fonts, &font); -} - -#if CAIRO_HAS_FT_FONT -static cairo_status_t -_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_status_t status; - cairo_type1_subset_t subset; - char name[64]; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); - - _cairo_type1_subset_fini (&subset); - return status; -} -#endif - -static cairo_status_t -_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_status_t status; - cairo_type1_subset_t subset; - char name[64]; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); - - _cairo_type1_fallback_fini (&subset); - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_pdf_resource_t stream, descriptor, cidfont_dict; - cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_status_t status; - cairo_pdf_font_t font; - cairo_truetype_subset_t subset; - unsigned int i; - char tag[10]; - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_truetype_subset_init (&subset, font_subset); - if (unlikely (status)) - return status; - - _create_font_subset_tag (font_subset, subset.ps_name, tag); - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Length1 %lu\n", - subset.data_length); - if (unlikely (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, - subset.data, subset.data_length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, - &to_unicode_stream); - if (_cairo_status_is_error (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n", - descriptor.id, - tag, - subset.ps_name); - - if (subset.font_name) { - _cairo_output_stream_printf (surface->output, - " /FontFamily (%s)\n", - subset.font_name); - } - - _cairo_output_stream_printf (surface->output, - " /Flags 4\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile2 %u 0 R\n" - ">>\n" - "endobj\n", - (long)(subset.x_min*PDF_UNITS_PER_EM), - (long)(subset.y_min*PDF_UNITS_PER_EM), - (long)(subset.x_max*PDF_UNITS_PER_EM), - (long)(subset.y_max*PDF_UNITS_PER_EM), - (long)(subset.ascent*PDF_UNITS_PER_EM), - (long)(subset.descent*PDF_UNITS_PER_EM), - (long)(subset.y_max*PDF_UNITS_PER_EM), - stream.id); - - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType2\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset.ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset.widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset.ps_name, - cidfont_dict.id); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - status = _cairo_array_append (&surface->fonts, &font); - - _cairo_truetype_subset_fini (&subset); - - return status; -} - -static cairo_status_t -_cairo_pdf_emit_imagemask (cairo_image_surface_t *image, - cairo_output_stream_t *stream) -{ - uint8_t *byte, output_byte; - int row, col, num_cols; - - /* The only image type supported by Type 3 fonts are 1-bit image - * masks */ - assert (image->format == CAIRO_FORMAT_A1); - - _cairo_output_stream_printf (stream, - "BI\n" - "/IM true\n" - "/W %d\n" - "/H %d\n" - "/BPC 1\n" - "/D [1 0]\n", - image->width, - image->height); - - _cairo_output_stream_printf (stream, - "ID "); - - num_cols = (image->width + 7) / 8; - for (row = 0; row < image->height; row++) { - byte = image->data + row * image->stride; - for (col = 0; col < num_cols; col++) { - output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); - _cairo_output_stream_write (stream, &output_byte, 1); - byte++; - } - } - - _cairo_output_stream_printf (stream, - "\nEI\n"); - - return _cairo_output_stream_get_status (stream); -} - -static cairo_status_t -_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_status_t status2; - unsigned int i; - cairo_surface_t *type3_surface; - cairo_output_stream_t *null_stream; - - null_stream = _cairo_null_stream_create (); - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - null_stream, - _cairo_pdf_emit_imagemask, - surface->font_subsets); - if (unlikely (type3_surface->status)) { - status2 = _cairo_output_stream_destroy (null_stream); - return type3_surface->status; - } - - _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, - _cairo_pdf_surface_add_font, - surface); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, - font_subset->glyphs[i]); - if (unlikely (status)) - break; - } - - cairo_surface_destroy (type3_surface); - status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - double *widths; - unsigned int i; - cairo_box_t font_bbox = {{0,0},{0,0}}; - cairo_box_t bbox = {{0,0},{0,0}}; - cairo_surface_t *type3_surface; - - if (font_subset->num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); - if (unlikely (glyphs == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); - if (unlikely (widths == NULL)) { - free (glyphs); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_pdf_group_resources_clear (&surface->resources); - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - NULL, - _cairo_pdf_emit_imagemask, - surface->font_subsets); - if (unlikely (type3_surface->status)) { - free (glyphs); - free (widths); - return type3_surface->status; - } - - _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, - _cairo_pdf_surface_add_font, - surface); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - NULL); - if (unlikely (status)) - break; - - glyphs[i] = surface->pdf_stream.self; - status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, - surface->output, - font_subset->glyphs[i], - &bbox, - &widths[i]); - if (unlikely (status)) - break; - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - break; - - if (i == 0) { - font_bbox.p1.x = bbox.p1.x; - font_bbox.p1.y = bbox.p1.y; - font_bbox.p2.x = bbox.p2.x; - font_bbox.p2.y = bbox.p2.y; - } else { - if (bbox.p1.x < font_bbox.p1.x) - font_bbox.p1.x = bbox.p1.x; - if (bbox.p1.y < font_bbox.p1.y) - font_bbox.p1.y = bbox.p1.y; - if (bbox.p2.x > font_bbox.p2.x) - font_bbox.p2.x = bbox.p2.x; - if (bbox.p2.y > font_bbox.p2.y) - font_bbox.p2.y = bbox.p2.y; - } - } - cairo_surface_destroy (type3_surface); - if (unlikely (status)) { - free (glyphs); - free (widths); - return status; - } - - encoding = _cairo_pdf_surface_new_object (surface); - if (encoding.id == 0) { - free (glyphs); - free (widths); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Encoding\n" - " /Differences [0", encoding.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " /%d", i); - _cairo_output_stream_printf (surface->output, - "]\n" - ">>\n" - "endobj\n"); - - char_procs = _cairo_pdf_surface_new_object (surface); - if (char_procs.id == 0) { - free (glyphs); - free (widths); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<<\n", char_procs.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " /%d %d 0 R\n", - i, glyphs[i].id); - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - free (glyphs); - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, - &to_unicode_stream); - if (_cairo_status_is_error (status)) { - free (widths); - return status; - } - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type3\n" - " /FontBBox [%f %f %f %f]\n" - " /FontMatrix [ 1 0 0 1 0 0 ]\n" - " /Encoding %d 0 R\n" - " /CharProcs %d 0 R\n" - " /FirstChar 0\n" - " /LastChar %d\n", - subset_resource.id, - _cairo_fixed_to_double (font_bbox.p1.x), - - _cairo_fixed_to_double (font_bbox.p2.y), - _cairo_fixed_to_double (font_bbox.p2.x), - - _cairo_fixed_to_double (font_bbox.p1.y), - encoding.id, - char_procs.id, - font_subset->num_glyphs - 1); - - _cairo_output_stream_printf (surface->output, - " /Widths ["); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, " %f", widths[i]); - _cairo_output_stream_printf (surface->output, - "]\n"); - free (widths); - - _cairo_output_stream_printf (surface->output, - " /Resources\n"); - _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - return _cairo_array_append (&surface->fonts, &font); -} - -static cairo_status_t -_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_status_t status; - - if (font_subset->is_composite) { - status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } else { -#if CAIRO_HAS_FT_FONT - status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; -#endif - - status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - } - - ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_status_t status; - - status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) -{ - cairo_status_t status; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_analyze_user_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, - _cairo_pdf_surface_emit_unscaled_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); - -BAIL: - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - surface->font_subsets = NULL; - - return status; -} - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t catalog; - - catalog = _cairo_pdf_surface_new_object (surface); - if (catalog.id == 0) - return catalog; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Catalog\n" - " /Pages %d 0 R\n" - ">>\n" - "endobj\n", - catalog.id, - surface->pages_resource.id); - - return catalog; -} - -static long -_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) -{ - cairo_pdf_object_t *object; - int num_objects, i; - long offset; - char buffer[11]; - - num_objects = _cairo_array_num_elements (&surface->objects); - - offset = _cairo_output_stream_get_position (surface->output); - _cairo_output_stream_printf (surface->output, - "xref\n" - "%d %d\n", - 0, num_objects + 1); - - _cairo_output_stream_printf (surface->output, - "0000000000 65535 f \n"); - for (i = 0; i < num_objects; i++) { - object = _cairo_array_index (&surface->objects, i); - snprintf (buffer, sizeof buffer, "%010ld", object->offset); - _cairo_output_stream_printf (surface->output, - "%s 00000 n \n", buffer); - } - - return offset; -} - -static cairo_status_t -_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, - cairo_pdf_smask_group_t *group) -{ - cairo_pdf_resource_t mask_group; - cairo_pdf_resource_t smask; - cairo_pdf_smask_group_t *smask_group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_status_t status; - - /* Create mask group */ - status = _cairo_pdf_surface_open_group (surface, NULL); - if (unlikely (status)) - return status; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->mask); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - status = _cairo_pdf_surface_close_group (surface, &mask_group); - if (unlikely (status)) - return status; - - /* Create source group */ - status = _cairo_pdf_surface_open_group (surface, &group->source_res); - if (unlikely (status)) - return status; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->source); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - status = _cairo_pdf_surface_close_group (surface, NULL); - if (unlikely (status)) - return status; - - /* Create an smask based on the alpha component of mask_group */ - smask = _cairo_pdf_surface_new_object (surface); - if (smask.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Mask\n" - " /S /Alpha\n" - " /G %d 0 R\n" - ">>\n" - "endobj\n", - smask.id, - mask_group.id); - - /* Create a GState that uses the smask */ - _cairo_pdf_surface_update_object (surface, group->group_res); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /ExtGState\n" - " /SMask %d 0 R\n" - " /ca 1\n" - " /CA 1\n" - " /AIS false\n" - ">>\n" - "endobj\n", - group->group_res.id, - smask.id); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_status_t -_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, - cairo_pdf_smask_group_t *group) -{ - double old_width, old_height; - cairo_status_t status; - - old_width = surface->width; - old_height = surface->height; - _cairo_pdf_surface_set_size_internal (surface, - group->width, - group->height); - /* _mask is a special case that requires two groups - source - * and mask as well as a smask and gstate dictionary */ - if (group->operation == PDF_MASK) { - status = _cairo_pdf_surface_write_mask_group (surface, group); - goto RESTORE_SIZE; - } - - status = _cairo_pdf_surface_open_group (surface, &group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_pattern (surface, - group->source, - group->source_res, - group->operation == PDF_STROKE); - if (unlikely (status)) - return status; - - switch (group->operation) { - case PDF_PAINT: - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); - break; - case PDF_MASK: - ASSERT_NOT_REACHED; - break; - case PDF_FILL: - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - &group->path, - group->fill_rule); - break; - case PDF_STROKE: - status = _cairo_pdf_operators_stroke (&surface->pdf_operators, - &group->path, - &group->style, - &group->ctm, - &group->ctm_inverse); - break; - case PDF_SHOW_GLYPHS: - status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - group->utf8, group->utf8_len, - group->glyphs, group->num_glyphs, - group->clusters, group->num_clusters, - group->cluster_flags, - group->scaled_font); - break; - } - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_group (surface, NULL); - -RESTORE_SIZE: - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface) -{ - cairo_pdf_pattern_t pattern; - cairo_pdf_smask_group_t *group; - cairo_pdf_source_surface_t src_surface; - int pattern_index, group_index, surface_index; - cairo_status_t status; - - /* Writing out PDF_MASK groups will cause additional smask groups - * to be appended to surface->smask_groups. Additional patterns - * may also be appended to surface->patterns. - * - * Writing recording surface patterns will cause additional patterns - * and groups to be appended. - */ - pattern_index = 0; - group_index = 0; - surface_index = 0; - while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || - (group_index < _cairo_array_num_elements (&surface->smask_groups)) || - (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) - { - for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { - _cairo_array_copy_element (&surface->smask_groups, group_index, &group); - status = _cairo_pdf_surface_write_smask_group (surface, group); - if (unlikely (status)) - return status; - } - - for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { - _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); - status = _cairo_pdf_surface_emit_pattern (surface, &pattern); - if (unlikely (status)) - return status; - } - - for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { - _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); - status = _cairo_pdf_surface_emit_surface (surface, &src_surface); - if (unlikely (status)) - return status; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t page, knockout, res; - cairo_status_t status; - int i, len; - - _cairo_pdf_group_resources_clear (&surface->resources); - if (surface->has_fallback_images) { - status = _cairo_pdf_surface_open_knockout_group (surface); - if (unlikely (status)) - return status; - - len = _cairo_array_num_elements (&surface->knockout_group); - for (i = 0; i < len; i++) { - _cairo_array_copy_element (&surface->knockout_group, i, &res); - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - res.id); - status = _cairo_pdf_surface_add_xobject (surface, res); - if (unlikely (status)) - return status; - } - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - surface->content.id); - status = _cairo_pdf_surface_add_xobject (surface, surface->content); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_group (surface, &knockout); - if (unlikely (status)) - return status; - - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, NULL, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - knockout.id); - status = _cairo_pdf_surface_add_xobject (surface, knockout); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - } - - page = _cairo_pdf_surface_new_object (surface); - if (page.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Page\n" - " /Parent %d 0 R\n" - " /MediaBox [ 0 0 %f %f ]\n" - " /Contents %d 0 R\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n" - ">>\n" - "endobj\n", - page.id, - surface->pages_resource.id, - surface->width, - surface->height, - surface->content.id, - surface->content_resources.id); - - status = _cairo_array_append (&surface->pages, &page); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_int_status_t status; - cairo_image_transparency_t transparency; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - transparency = _cairo_image_analyze_transparency (image); - if (transparency == CAIRO_IMAGE_IS_OPAQUE) - status = CAIRO_STATUS_SUCCESS; - else - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_bool_t -_surface_pattern_supported (cairo_surface_pattern_t *pattern) -{ - cairo_extend_t extend; - - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - return TRUE; - - if (pattern->surface->backend->acquire_source_image == NULL) - return FALSE; - - /* Does an ALPHA-only source surface even make sense? Maybe, but I - * don't think it's worth the extra code to support it. */ - -/* XXX: Need to write this function here... - content = cairo_surface_get_content (pattern->surface); - if (content == CAIRO_CONTENT_ALPHA) - return FALSE; -*/ - - extend = cairo_pattern_get_extend (&pattern->base); - switch (extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - case CAIRO_EXTEND_REFLECT: - /* There's no point returning FALSE for EXTEND_PAD, as the image - * surface does not currently implement it either */ - case CAIRO_EXTEND_PAD: - return TRUE; - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -static cairo_bool_t -_gradient_pattern_supported (const cairo_pattern_t *pattern) -{ - cairo_extend_t extend; - - extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); - - - /* Radial gradients are currently only supported with EXTEND_NONE - * and EXTEND_PAD and when one circle is inside the other. */ - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - double x1, y1, x2, y2, r1, r2, d; - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - if (extend == CAIRO_EXTEND_REPEAT || - extend == CAIRO_EXTEND_REFLECT) { - return FALSE; - } - - x1 = _cairo_fixed_to_double (radial->c1.x); - y1 = _cairo_fixed_to_double (radial->c1.y); - r1 = _cairo_fixed_to_double (radial->r1); - x2 = _cairo_fixed_to_double (radial->c2.x); - y2 = _cairo_fixed_to_double (radial->c2.y); - r2 = _cairo_fixed_to_double (radial->r2); - - d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - if (d > fabs(r2 - r1)) { - return FALSE; - } - } - - return TRUE; -} - -static cairo_bool_t -_pattern_supported (const cairo_pattern_t *pattern) -{ - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) - return TRUE; - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - return _gradient_pattern_supported (pattern); - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); - - return FALSE; -} - -static cairo_bool_t -_pdf_operator_supported (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return TRUE; - - default: - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - return FALSE; - } -} - -static cairo_int_status_t -_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - if (surface->force_fallbacks && - surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! _pattern_supported (pattern)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_pdf_operator_supported (op)) { - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->extend == CAIRO_EXTEND_PAD) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } - } - - return CAIRO_STATUS_SUCCESS; - } - - - /* The SOURCE operator is supported if the pattern is opaque or if - * there is nothing painted underneath. */ - if (op == CAIRO_OPERATOR_SOURCE) { - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (_cairo_pattern_is_opaque (pattern, extents)) { - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } else { - /* FIXME: The analysis surface does not yet have - * the capability to analyze a non opaque recording - * surface and mark it supported if there is - * nothing underneath. For now recording surfaces of - * type CONTENT_COLOR_ALPHA painted with - * OPERATOR_SOURCE will result in a fallback - * image. */ - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface, - surface_pattern); - } - } - - if (_cairo_pattern_is_opaque (pattern, extents)) - return CAIRO_STATUS_SUCCESS; - else - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) -{ - cairo_status_t status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_array_append (&surface->knockout_group, &surface->content); - if (unlikely (status)) - return status; - - _cairo_pdf_group_resources_clear (&surface->resources); - return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE); -} - -static cairo_int_status_t -_cairo_pdf_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - - return status; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - return status; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - source->extend == CAIRO_EXTEND_NONE) - { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - return _cairo_output_stream_get_status (surface->output); - } - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - group->operation = PDF_PAINT; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, - pattern_res, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_status_t status; - cairo_composite_rectangles_t extents; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - - return status; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - cairo_status_t source_status, mask_status; - - source_status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - if (_cairo_status_is_error (source_status)) - return source_status; - - if (mask->has_component_alpha) { - mask_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); - if (_cairo_status_is_error (mask_status)) - return mask_status; - } - - return _cairo_analysis_surface_merge_status (source_status, - mask_status); - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - return status; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - group->operation = PDF_MASK; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - status = _cairo_pattern_create_copy (&group->mask, mask); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - group->source_res = _cairo_pdf_surface_new_object (surface); - if (group->source_res.id == 0) { - _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, group->source_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - group->group_res.id, - group->source_res.id); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - - return status; - } - - /* use the more accurate extents */ - if (extents.is_bounded) { - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &extents.mask); - if (unlikely (status)) - return status; - - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - group->operation = PDF_STROKE; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - group->source_res = pattern_res; - status = _cairo_path_fixed_init_copy (&group->path, path); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - group->style = *style; - group->ctm = *ctm; - group->ctm_inverse = *ctm_inverse; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_stroke (&surface->pdf_operators, - path, - style, - ctm, - ctm_inverse); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - - return status; - } - - /* use the more accurate extents */ - if (extents.is_bounded) { - _cairo_path_fixed_fill_extents (path, - fill_rule, - tolerance, - &extents.mask); - - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - return status; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - source->extend == CAIRO_EXTEND_NONE) - { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - return _cairo_output_stream_get_status (surface->output); - } - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - group->operation = PDF_FILL; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - group->source_res = pattern_res; - status = _cairo_path_fixed_init_copy (&group->path, path); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - group->fill_rule = fill_rule; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - path, - fill_rule); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_fill_stroke (void *abstract_surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; - cairo_rectangle_int_t extents; - - /* During analysis we return unsupported and let the _fill and - * _stroke functions that are on the fallback path do the analysis - * for us. During render we may still encounter unsupported - * combinations of fill/stroke patterns. However we can return - * unsupported anytime to let the _fill and _stroke functions take - * over. - */ - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* PDF rendering of fill-stroke is not the same as cairo when - * either the fill or stroke is not opaque. - */ - if ( !_cairo_pattern_is_opaque (fill_source, NULL) || - !_cairo_pattern_is_opaque (stroke_source, NULL)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (fill_op != stroke_op) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, fill_op); - if (unlikely (status)) - return status; - - status = _cairo_surface_fill_extents (&surface->base, - fill_op, fill_source, path, fill_rule, - fill_tolerance, fill_antialias, - clip, &extents); - if (unlikely (status)) - return status; - - - fill_pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - &extents, - &fill_pattern_res, - &gstate_res); - if (unlikely (status)) - return status; - - assert (gstate_res.id == 0); - - status = _cairo_surface_stroke_extents (&surface->base, - stroke_op, stroke_source, path, - stroke_style, stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, stroke_antialias, - clip, &extents); - if (unlikely (status)) - return status; - - stroke_pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, - stroke_source, - &extents, - &stroke_pattern_res, - &gstate_res); - if (unlikely (status)) - return status; - - assert (gstate_res.id == 0); - - /* As PDF has separate graphics state for fill and stroke we can - * select both at the same time */ - status = _cairo_pdf_surface_select_pattern (surface, fill_source, - fill_pattern_res, FALSE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_pattern (surface, stroke_source, - stroke_pattern_res, TRUE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, - path, - fill_rule, - stroke_style, - stroke_ctm, - stroke_ctm_inverse); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_bool_t -_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static cairo_int_status_t -_cairo_pdf_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - cairo_bool_t overlap; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - - return status; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - group->operation = PDF_SHOW_GLYPHS; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - group->source_res = pattern_res; - - if (utf8_len) { - group->utf8 = malloc (utf8_len); - if (unlikely (group->utf8 == NULL)) { - _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - memcpy (group->utf8, utf8, utf8_len); - } - group->utf8_len = utf8_len; - - if (num_glyphs) { - group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (group->glyphs == NULL)) { - _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - } - group->num_glyphs = num_glyphs; - - if (num_clusters) { - group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); - if (unlikely (group->clusters == NULL)) { - _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); - } - group->num_clusters = num_clusters; - - group->scaled_font = cairo_scaled_font_reference (scaled_font); - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (unlikely (status)) - return status; - - /* Each call to show_glyphs() with a transclucent pattern must - * be in a separate text object otherwise overlapping text - * from separate calls to show_glyphs will not composite with - * each other. */ - if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - } - - status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - - -static void -_cairo_pdf_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t paginated_mode) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - surface->paginated_mode = paginated_mode; -} - -static const cairo_surface_backend_t cairo_pdf_surface_backend = { - CAIRO_SURFACE_TYPE_PDF, - NULL, /* create similar: handled by wrapper */ - _cairo_pdf_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* _cairo_pdf_surface_copy_page */ - _cairo_pdf_surface_show_page, - _cairo_pdf_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_pdf_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here are the drawing functions */ - - _cairo_pdf_surface_paint, - _cairo_pdf_surface_mask, - _cairo_pdf_surface_stroke, - _cairo_pdf_surface_fill, - NULL, /* show_glyphs */ - NULL, /* snapshot */ - - NULL, /* is_compatible */ - _cairo_pdf_surface_fill_stroke, - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _cairo_pdf_surface_has_show_text_glyphs, - _cairo_pdf_surface_show_text_glyphs, -}; - -static const cairo_paginated_surface_backend_t -cairo_pdf_surface_paginated_backend = { - _cairo_pdf_surface_start_page, - _cairo_pdf_surface_set_paginated_mode, - NULL, /* set_bounding_box */ - _cairo_pdf_surface_has_fallback_images, - _cairo_pdf_surface_supports_fine_grained_fallbacks, -}; diff --git a/libs/cairo/cairo/src/cairo-pdf.h b/libs/cairo/cairo/src/cairo-pdf.h deleted file mode 100644 index 0e85e9804..000000000 --- a/libs/cairo/cairo/src/cairo-pdf.h +++ /dev/null @@ -1,62 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PDF_H -#define CAIRO_PDF_H - -#include "cairo.h" - -#if CAIRO_HAS_PDF_SURFACE - -CAIRO_BEGIN_DECLS - -/** - * cairo_pdf_version_t: - * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. - * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. - * - * #cairo_pdf_version_t is used to describe the version number of the PDF - * specification that a generated PDF file will conform to. - * - * Since 1.10 - */ -typedef enum _cairo_pdf_version { - CAIRO_PDF_VERSION_1_4, - CAIRO_PDF_VERSION_1_5 -} cairo_pdf_version_t; - -cairo_public cairo_surface_t * -cairo_pdf_surface_create (const char *filename, - double width_in_points, - double height_in_points); - -cairo_public cairo_surface_t * -cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width_in_points, - double height_in_points); - -cairo_public void -cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface, - cairo_pdf_version_t version); - -cairo_public void -cairo_pdf_get_versions (cairo_pdf_version_t const **versions, - int *num_versions); - -cairo_public const char * -cairo_pdf_version_to_string (cairo_pdf_version_t version); - -cairo_public void -cairo_pdf_surface_set_size (cairo_surface_t *surface, - double width_in_points, - double height_in_points); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_PDF_SURFACE */ -# error Cairo was not compiled with support for the pdf backend -#endif /* CAIRO_HAS_PDF_SURFACE */ - -#endif /* CAIRO_PDF_H */ diff --git a/libs/cairo/cairo/src/cairo-pen.c b/libs/cairo/cairo/src/cairo-pen.c deleted file mode 100644 index 751b5dc3a..000000000 --- a/libs/cairo/cairo/src/cairo-pen.c +++ /dev/null @@ -1,364 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-slope-private.h" - -static int -_cairo_pen_vertices_needed (double tolerance, - double radius, - const cairo_matrix_t *matrix); - -static void -_cairo_pen_compute_slopes (cairo_pen_t *pen); - -cairo_status_t -_cairo_pen_init (cairo_pen_t *pen, - double radius, - double tolerance, - const cairo_matrix_t *ctm) -{ - int i; - int reflect; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); - - pen->radius = radius; - pen->tolerance = tolerance; - - reflect = _cairo_matrix_compute_determinant (ctm) < 0.; - - pen->num_vertices = _cairo_pen_vertices_needed (tolerance, - radius, - ctm); - - if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { - pen->vertices = _cairo_malloc_ab (pen->num_vertices, - sizeof (cairo_pen_vertex_t)); - if (unlikely (pen->vertices == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - pen->vertices = pen->vertices_embedded; - } - - /* - * Compute pen coordinates. To generate the right ellipse, compute points around - * a circle in user space and transform them to device space. To get a consistent - * orientation in device space, flip the pen if the transformation matrix - * is reflecting - */ - for (i=0; i < pen->num_vertices; i++) { - double theta = 2 * M_PI * i / (double) pen->num_vertices; - double dx = radius * cos (reflect ? -theta : theta); - double dy = radius * sin (reflect ? -theta : theta); - cairo_pen_vertex_t *v = &pen->vertices[i]; - cairo_matrix_transform_distance (ctm, &dx, &dy); - v->point.x = _cairo_fixed_from_double (dx); - v->point.y = _cairo_fixed_from_double (dy); - } - - _cairo_pen_compute_slopes (pen); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pen_fini (cairo_pen_t *pen) -{ - if (pen->vertices != pen->vertices_embedded) - free (pen->vertices); - - - VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t))); -} - -cairo_status_t -_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); - - *pen = *other; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pen->vertices = pen->vertices_embedded; - if (pen->num_vertices) { - if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { - pen->vertices = _cairo_malloc_ab (pen->num_vertices, - sizeof (cairo_pen_vertex_t)); - if (unlikely (pen->vertices == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (pen->vertices, other->vertices, - pen->num_vertices * sizeof (cairo_pen_vertex_t)); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) -{ - cairo_status_t status; - int num_vertices; - int i; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - num_vertices = pen->num_vertices + num_points; - if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || - pen->vertices != pen->vertices_embedded) - { - cairo_pen_vertex_t *vertices; - - if (pen->vertices == pen->vertices_embedded) { - vertices = _cairo_malloc_ab (num_vertices, - sizeof (cairo_pen_vertex_t)); - if (unlikely (vertices == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (vertices, pen->vertices, - pen->num_vertices * sizeof (cairo_pen_vertex_t)); - } else { - vertices = _cairo_realloc_ab (pen->vertices, - num_vertices, - sizeof (cairo_pen_vertex_t)); - if (unlikely (vertices == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pen->vertices = vertices; - } - - pen->num_vertices = num_vertices; - - /* initialize new vertices */ - for (i=0; i < num_points; i++) - pen->vertices[pen->num_vertices-num_points+i].point = point[i]; - - status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); - if (unlikely (status)) - return status; - - _cairo_pen_compute_slopes (pen); - - return CAIRO_STATUS_SUCCESS; -} - -/* -The circular pen in user space is transformed into an ellipse in -device space. - -We construct the pen by computing points along the circumference -using equally spaced angles. - -We show that this approximation to the ellipse has maximum error at the -major axis of the ellipse. - -Set - - M = major axis length - m = minor axis length - -Align 'M' along the X axis and 'm' along the Y axis and draw -an ellipse parameterized by angle 't': - - x = M cos t y = m sin t - -Perturb t by ± d and compute two new points (x+,y+), (x-,y-). -The distance from the average of these two points to (x,y) represents -the maximum error in approximating the ellipse with a polygon formed -from vertices 2∆ radians apart. - - x+ = M cos (t+∆) y+ = m sin (t+∆) - x- = M cos (t-∆) y- = m sin (t-∆) - -Now compute the approximation error, E: - - Ex = (x - (x+ + x-) / 2) - Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) - = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + - cos(t)cos(∆) - sin(t)sin(∆))/2) - = M(cos(t) - cos(t)cos(∆)) - = M cos(t) (1 - cos(∆)) - - Ey = y - (y+ - y-) / 2 - = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 - = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + - sin(t)cos(∆) - cos(t)sin(∆))/2) - = m (sin(t) - sin(t)cos(∆)) - = m sin(t) (1 - cos(∆)) - - E² = Ex² + Ey² - = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² - = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) - = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) - = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² - -Find the extremum by differentiation wrt t and setting that to zero - -∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) - - 0 = 2 cos (t) sin (t) - 0 = sin (2t) - t = nπ - -Which is to say that the maximum and minimum errors occur on the -axes of the ellipse at 0 and π radians: - - E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² - = (1-cos(∆))² M² - E²(π) = (1-cos(∆))² m² - -maximum error = M (1-cos(∆)) -minimum error = m (1-cos(∆)) - -We must make maximum error ≤ tolerance, so compute the ∆ needed: - - tolerance = M (1-cos(∆)) - tolerance / M = 1 - cos (∆) - cos(∆) = 1 - tolerance/M - ∆ = acos (1 - tolerance / M); - -Remembering that ∆ is half of our angle between vertices, -the number of vertices is then - - vertices = ceil(2π/2∆). - = ceil(π/∆). - -Note that this also equation works for M == m (a circle) as it -doesn't matter where on the circle the error is computed. -*/ - -static int -_cairo_pen_vertices_needed (double tolerance, - double radius, - const cairo_matrix_t *matrix) -{ - /* - * the pen is a circle that gets transformed to an ellipse by matrix. - * compute major axis length for a pen with the specified radius. - * we don't need the minor axis length. - */ - - double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, - radius); - - /* - * compute number of vertices needed - */ - int num_vertices; - - /* Where tolerance / M is > 1, we use 4 points */ - if (tolerance >= major_axis) { - num_vertices = 4; - } else { - double delta = acos (1 - tolerance / major_axis); - num_vertices = ceil (M_PI / delta); - - /* number of vertices must be even */ - if (num_vertices % 2) - num_vertices++; - - /* And we must always have at least 4 vertices. */ - if (num_vertices < 4) - num_vertices = 4; - } - - return num_vertices; -} - -static void -_cairo_pen_compute_slopes (cairo_pen_t *pen) -{ - int i, i_prev; - cairo_pen_vertex_t *prev, *v, *next; - - for (i=0, i_prev = pen->num_vertices - 1; - i < pen->num_vertices; - i_prev = i++) { - prev = &pen->vertices[i_prev]; - v = &pen->vertices[i]; - next = &pen->vertices[(i + 1) % pen->num_vertices]; - - _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); - _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); - } -} -/* - * Find active pen vertex for clockwise edge of stroke at the given slope. - * - * The strictness of the inequalities here is delicate. The issue is - * that the slope_ccw member of one pen vertex will be equivalent to - * the slope_cw member of the next pen vertex in a counterclockwise - * order. However, for this function, we care strongly about which - * vertex is returned. - * - * [I think the "care strongly" above has to do with ensuring that the - * pen's "extra points" from the spline's initial and final slopes are - * properly found when beginning the spline stroking.] - */ -int -_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, - const cairo_slope_t *slope) -{ - int i; - - for (i=0; i < pen->num_vertices; i++) { - if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && - (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) - break; - } - - /* If the desired slope cannot be found between any of the pen - * vertices, then we must have a degenerate pen, (such as a pen - * that's been transformed to a line). In that case, we consider - * the first pen vertex as the appropriate clockwise vertex. - */ - if (i == pen->num_vertices) - i = 0; - - return i; -} - -/* Find active pen vertex for counterclockwise edge of stroke at the given slope. - * - * Note: See the comments for _cairo_pen_find_active_cw_vertex_index - * for some details about the strictness of the inequalities here. - */ -int -_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, - const cairo_slope_t *slope) -{ - cairo_slope_t slope_reverse; - int i; - - slope_reverse = *slope; - slope_reverse.dx = -slope_reverse.dx; - slope_reverse.dy = -slope_reverse.dy; - - for (i=pen->num_vertices-1; i >= 0; i--) { - if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && - (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) - break; - } - - /* If the desired slope cannot be found between any of the pen - * vertices, then we must have a degenerate pen, (such as a pen - * that's been transformed to a line). In that case, we consider - * the last pen vertex as the appropriate counterclockwise vertex. - */ - if (i < 0) - i = pen->num_vertices - 1; - - return i; -} diff --git a/libs/cairo/cairo/src/cairo-platform.h b/libs/cairo/cairo/src/cairo-platform.h deleted file mode 100644 index 9d4bc4d1f..000000000 --- a/libs/cairo/cairo/src/cairo-platform.h +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PLATFORM_H -#define CAIRO_PLATFORM_H - -#include "prcpucfg.h" - -/* we're replacing any definition from cairoint.h etc */ -#undef cairo_public - -#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE -#define CVISIBILITY_HIDDEN __attribute__((visibility("hidden"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) -#define CVISIBILITY_HIDDEN __hidden -#else -#define CVISIBILITY_HIDDEN -#endif - -/* In libxul builds we don't ever want to export cairo symbols */ -#define cairo_public extern CVISIBILITY_HIDDEN - -#define CCALLBACK -#define CCALLBACK_DECL -#define CSTATIC_CALLBACK(__x) static __x - -#ifdef MOZILLA_VERSION -#include "cairo-rename.h" -#endif - -#if defined(IS_BIG_ENDIAN) -#define WORDS_BIGENDIAN -#define FLOAT_WORDS_BIGENDIAN -#endif - -#endif /* CAIRO_PLATFORM_H */ diff --git a/libs/cairo/cairo/src/cairo-png.c b/libs/cairo/cairo/src/cairo-png.c deleted file mode 100644 index bbab2c2ae..000000000 --- a/libs/cairo/cairo/src/cairo-png.c +++ /dev/null @@ -1,764 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" - -#include -#include -#include - -/** - * SECTION:cairo-png - * @Title: PNG Support - * @Short_Description: Reading and writing PNG images - * @See_Also: #cairo_surface_t - * - * The PNG functions allow reading PNG images into image surfaces, and writing - * any surface to a PNG file. - */ - -/** - * CAIRO_HAS_PNG_FUNCTIONS: - * - * Defined if the PNG functions are available. - * This macro can be used to conditionally compile code using the cairo - * PNG functions. - */ - -struct png_read_closure_t { - cairo_read_func_t read_func; - void *closure; - cairo_output_stream_t *png_data; -}; - - -/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ -static void -unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) -{ - unsigned int i; - - for (i = 0; i < row_info->rowbytes; i += 4) { - uint8_t *b = &data[i]; - uint32_t pixel; - uint8_t alpha; - - memcpy (&pixel, b, sizeof (uint32_t)); - alpha = (pixel & 0xff000000) >> 24; - if (alpha == 0) { - b[0] = b[1] = b[2] = b[3] = 0; - } else { - b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; - b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; - b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; - b[3] = alpha; - } - } -} - -/* Converts native endian xRGB => RGBx bytes */ -static void -convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) -{ - unsigned int i; - - for (i = 0; i < row_info->rowbytes; i += 4) { - uint8_t *b = &data[i]; - uint32_t pixel; - - memcpy (&pixel, b, sizeof (uint32_t)); - - b[0] = (pixel & 0xff0000) >> 16; - b[1] = (pixel & 0x00ff00) >> 8; - b[2] = (pixel & 0x0000ff) >> 0; - b[3] = 0; - } -} - -/* Use a couple of simple error callbacks that do not print anything to - * stderr and rely on the user to check for errors via the #cairo_status_t - * return. - */ -static void -png_simple_error_callback (png_structp png, - png_const_charp error_msg) -{ - cairo_status_t *error = png_get_error_ptr (png); - - /* default to the most likely error */ - if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); - -#ifdef PNG_SETJMP_SUPPORTED - longjmp (png_jmpbuf (png), 1); -#endif - - /* if we get here, then we have to choice but to abort ... */ -} - -static void -png_simple_warning_callback (png_structp png, - png_const_charp error_msg) -{ - cairo_status_t *error = png_get_error_ptr (png); - - /* default to the most likely error */ - if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* png does not expect to abort and will try to tidy up after a warning */ -} - - -/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn. - * Otherwise, we will segfault if we are writing to a stream. */ -static void -png_simple_output_flush_fn (png_structp png_ptr) -{ -} - -static cairo_status_t -write_png (cairo_surface_t *surface, - png_rw_ptr write_func, - void *closure) -{ - int i; - cairo_status_t status; - cairo_image_surface_t *image; - cairo_image_surface_t * volatile clone; - void *image_extra; - png_struct *png; - png_info *info; - png_byte **volatile rows = NULL; - png_color_16 white; - int png_color_type; - int depth; - - status = _cairo_surface_acquire_source_image (surface, - &image, - &image_extra); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - else if (unlikely (status)) - return status; - - /* PNG complains about "Image width or height is zero in IHDR" */ - if (image->width == 0 || image->height == 0) { - status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); - goto BAIL1; - } - - /* Handle the various fallback formats (e.g. low bit-depth XServers) - * by coercing them to a simpler format using pixman. - */ - clone = _cairo_image_surface_coerce (image); - status = clone->base.status; - if (unlikely (status)) - goto BAIL1; - - rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*)); - if (unlikely (rows == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL2; - } - - for (i = 0; i < clone->height; i++) - rows[i] = (png_byte *) clone->data + i * clone->stride; - - png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status, - png_simple_error_callback, - png_simple_warning_callback); - if (unlikely (png == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL3; - } - - info = png_create_info_struct (png); - if (unlikely (info == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL4; - } - -#ifdef PNG_SETJMP_SUPPORTED - if (setjmp (png_jmpbuf (png))) - goto BAIL4; -#endif - - png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn); - - switch (clone->format) { - case CAIRO_FORMAT_ARGB32: - depth = 8; - if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE) - png_color_type = PNG_COLOR_TYPE_RGB; - else - png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; - break; - case CAIRO_FORMAT_RGB24: - depth = 8; - png_color_type = PNG_COLOR_TYPE_RGB; - break; - case CAIRO_FORMAT_A8: - depth = 8; - png_color_type = PNG_COLOR_TYPE_GRAY; - break; - case CAIRO_FORMAT_A1: - depth = 1; - png_color_type = PNG_COLOR_TYPE_GRAY; -#ifndef WORDS_BIGENDIAN - png_set_packswap (png); -#endif - break; - case CAIRO_FORMAT_INVALID: - case CAIRO_FORMAT_RGB16_565: - default: - status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - goto BAIL4; - } - - png_set_IHDR (png, info, - clone->width, - clone->height, depth, - png_color_type, - PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - - white.gray = (1 << depth) - 1; - white.red = white.blue = white.green = white.gray; - png_set_bKGD (png, info, &white); - - if (0) { /* XXX extract meta-data from surface (i.e. creation date) */ - png_time pt; - - png_convert_from_time_t (&pt, time (NULL)); - png_set_tIME (png, info, &pt); - } - - /* We have to call png_write_info() before setting up the write - * transformation, since it stores data internally in 'png' - * that is needed for the write transformation functions to work. - */ - png_write_info (png, info); - - if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_set_write_user_transform_fn (png, unpremultiply_data); - } else if (png_color_type == PNG_COLOR_TYPE_RGB) { - png_set_write_user_transform_fn (png, convert_data_to_bytes); - png_set_filler (png, 0, PNG_FILLER_AFTER); - } - - png_write_image (png, rows); - png_write_end (png, info); - -BAIL4: - png_destroy_write_struct (&png, &info); -BAIL3: - free (rows); -BAIL2: - cairo_surface_destroy (&clone->base); -BAIL1: - _cairo_surface_release_source_image (surface, image, image_extra); - - return status; -} - -static void -stdio_write_func (png_structp png, png_bytep data, png_size_t size) -{ - FILE *fp; - - fp = png_get_io_ptr (png); - while (size) { - size_t ret = fwrite (data, 1, size, fp); - size -= ret; - data += ret; - if (size && ferror (fp)) { - cairo_status_t *error = png_get_error_ptr (png); - if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR); - png_error (png, NULL); - } - } -} - -/** - * cairo_surface_write_to_png: - * @surface: a #cairo_surface_t with pixel contents - * @filename: the name of a file to write to - * - * Writes the contents of @surface to a new file @filename as a PNG - * image. - * - * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written - * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not - * be allocated for the operation or - * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have - * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs - * while attempting to write the file. - **/ -cairo_status_t -cairo_surface_write_to_png (cairo_surface_t *surface, - const char *filename) -{ - FILE *fp; - cairo_status_t status; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - - fp = fopen (filename, "wb"); - if (fp == NULL) { - switch (errno) { - case ENOMEM: - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - default: - return _cairo_error (CAIRO_STATUS_WRITE_ERROR); - } - } - - status = write_png (surface, stdio_write_func, fp); - - if (fclose (fp) && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); - - return status; -} - -struct png_write_closure_t { - cairo_write_func_t write_func; - void *closure; -}; - -static void -stream_write_func (png_structp png, png_bytep data, png_size_t size) -{ - cairo_status_t status; - struct png_write_closure_t *png_closure; - - png_closure = png_get_io_ptr (png); - status = png_closure->write_func (png_closure->closure, data, size); - if (unlikely (status)) { - cairo_status_t *error = png_get_error_ptr (png); - if (*error == CAIRO_STATUS_SUCCESS) - *error = status; - png_error (png, NULL); - } -} - -/** - * cairo_surface_write_to_png_stream: - * @surface: a #cairo_surface_t with pixel contents - * @write_func: a #cairo_write_func_t - * @closure: closure data for the write function - * - * Writes the image surface to the write function. - * - * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written - * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if - * memory could not be allocated for the operation, - * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have - * pixel contents. - **/ -cairo_status_t -cairo_surface_write_to_png_stream (cairo_surface_t *surface, - cairo_write_func_t write_func, - void *closure) -{ - struct png_write_closure_t png_closure; - - if (surface->status) - return surface->status; - - if (surface->finished) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - - png_closure.write_func = write_func; - png_closure.closure = closure; - - return write_png (surface, stream_write_func, &png_closure); -} -slim_hidden_def (cairo_surface_write_to_png_stream); - -static inline int -multiply_alpha (int alpha, int color) -{ - int temp = (alpha * color) + 0x80; - return ((temp + (temp >> 8)) >> 8); -} - -/* Premultiplies data and converts RGBA bytes => native endian */ -static void -premultiply_data (png_structp png, - png_row_infop row_info, - png_bytep data) -{ - unsigned int i; - - for (i = 0; i < row_info->rowbytes; i += 4) { - uint8_t *base = &data[i]; - uint8_t alpha = base[3]; - uint32_t p; - - if (alpha == 0) { - p = 0; - } else { - uint8_t red = base[0]; - uint8_t green = base[1]; - uint8_t blue = base[2]; - - if (alpha != 0xff) { - red = multiply_alpha (alpha, red); - green = multiply_alpha (alpha, green); - blue = multiply_alpha (alpha, blue); - } - p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); - } - memcpy (base, &p, sizeof (uint32_t)); - } -} - -/* Converts RGBx bytes to native endian xRGB */ -static void -convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) -{ - unsigned int i; - - for (i = 0; i < row_info->rowbytes; i += 4) { - uint8_t *base = &data[i]; - uint8_t red = base[0]; - uint8_t green = base[1]; - uint8_t blue = base[2]; - uint32_t pixel; - - pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); - memcpy (base, &pixel, sizeof (uint32_t)); - } -} - -static cairo_status_t -stdio_read_func (void *closure, unsigned char *data, unsigned int size) -{ - FILE *file = closure; - - while (size) { - size_t ret; - - ret = fread (data, 1, size, file); - size -= ret; - data += ret; - - if (size && (feof (file) || ferror (file))) - return _cairo_error (CAIRO_STATUS_READ_ERROR); - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -stream_read_func (png_structp png, png_bytep data, png_size_t size) -{ - cairo_status_t status; - struct png_read_closure_t *png_closure; - - png_closure = png_get_io_ptr (png); - status = png_closure->read_func (png_closure->closure, data, size); - if (unlikely (status)) { - cairo_status_t *error = png_get_error_ptr (png); - if (*error == CAIRO_STATUS_SUCCESS) - *error = status; - png_error (png, NULL); - } - - _cairo_output_stream_write (png_closure->png_data, data, size); -} - -static cairo_surface_t * -read_png (struct png_read_closure_t *png_closure) -{ - cairo_surface_t *surface; - png_struct *png = NULL; - png_info *info; - png_byte *data = NULL; - png_byte **row_pointers = NULL; - png_uint_32 png_width, png_height; - int depth, color_type, interlace, stride; - unsigned int i; - cairo_format_t format; - cairo_status_t status; - unsigned char *mime_data; - unsigned long mime_data_length; - - png_closure->png_data = _cairo_memory_stream_create (); - - /* XXX: Perhaps we'll want some other error handlers? */ - png = png_create_read_struct (PNG_LIBPNG_VER_STRING, - &status, - png_simple_error_callback, - png_simple_warning_callback); - if (unlikely (png == NULL)) { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto BAIL; - } - - info = png_create_info_struct (png); - if (unlikely (info == NULL)) { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto BAIL; - } - - png_set_read_fn (png, png_closure, stream_read_func); - - status = CAIRO_STATUS_SUCCESS; -#ifdef PNG_SETJMP_SUPPORTED - if (setjmp (png_jmpbuf (png))) { - surface = _cairo_surface_create_in_error (status); - goto BAIL; - } -#endif - - png_read_info (png, info); - - png_get_IHDR (png, info, - &png_width, &png_height, &depth, - &color_type, &interlace, NULL, NULL); - if (unlikely (status)) { /* catch any early warnings */ - surface = _cairo_surface_create_in_error (status); - goto BAIL; - } - - /* convert palette/gray image to rgb */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb (png); - - /* expand gray bit depth if needed */ - if (color_type == PNG_COLOR_TYPE_GRAY) { -#if PNG_LIBPNG_VER >= 10209 - png_set_expand_gray_1_2_4_to_8 (png); -#else - png_set_gray_1_2_4_to_8 (png); -#endif - } - - /* transform transparency to alpha */ - if (png_get_valid (png, info, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha (png); - - if (depth == 16) - png_set_strip_16 (png); - - if (depth < 8) - png_set_packing (png); - - /* convert grayscale to RGB */ - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - png_set_gray_to_rgb (png); - } - - if (interlace != PNG_INTERLACE_NONE) - png_set_interlace_handling (png); - - png_set_filler (png, 0xff, PNG_FILLER_AFTER); - - /* recheck header after setting EXPAND options */ - png_read_update_info (png, info); - png_get_IHDR (png, info, - &png_width, &png_height, &depth, - &color_type, &interlace, NULL, NULL); - if (depth != 8 || - ! (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA)) - { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR)); - goto BAIL; - } - - switch (color_type) { - default: - ASSERT_NOT_REACHED; - /* fall-through just in case ;-) */ - - case PNG_COLOR_TYPE_RGB_ALPHA: - format = CAIRO_FORMAT_ARGB32; - png_set_read_user_transform_fn (png, premultiply_data); - break; - - case PNG_COLOR_TYPE_RGB: - format = CAIRO_FORMAT_RGB24; - png_set_read_user_transform_fn (png, convert_bytes_to_data); - break; - } - - stride = cairo_format_stride_for_width (format, png_width); - if (stride < 0) { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); - goto BAIL; - } - - data = _cairo_malloc_ab (png_height, stride); - if (unlikely (data == NULL)) { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto BAIL; - } - - row_pointers = _cairo_malloc_ab (png_height, sizeof (char *)); - if (unlikely (row_pointers == NULL)) { - surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto BAIL; - } - - for (i = 0; i < png_height; i++) - row_pointers[i] = &data[i * stride]; - - png_read_image (png, row_pointers); - png_read_end (png, info); - - if (unlikely (status)) { /* catch any late warnings - probably hit an error already */ - surface = _cairo_surface_create_in_error (status); - goto BAIL; - } - - surface = cairo_image_surface_create_for_data (data, format, - png_width, png_height, - stride); - if (surface->status) - goto BAIL; - - _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); - data = NULL; - - _cairo_debug_check_image_surface_is_defined (surface); - - status = _cairo_memory_stream_destroy (png_closure->png_data, - &mime_data, - &mime_data_length); - png_closure->png_data = NULL; - if (unlikely (status)) { - cairo_surface_destroy (surface); - surface = _cairo_surface_create_in_error (status); - goto BAIL; - } - - status = cairo_surface_set_mime_data (surface, - CAIRO_MIME_TYPE_PNG, - mime_data, - mime_data_length, - free, - mime_data); - if (unlikely (status)) { - free (mime_data); - cairo_surface_destroy (surface); - surface = _cairo_surface_create_in_error (status); - goto BAIL; - } - - BAIL: - if (row_pointers != NULL) - free (row_pointers); - if (data != NULL) - free (data); - if (png != NULL) - png_destroy_read_struct (&png, &info, NULL); - if (png_closure->png_data != NULL) { - cairo_status_t status_ignored; - - status_ignored = _cairo_output_stream_destroy (png_closure->png_data); - } - - return surface; -} - -/** - * cairo_image_surface_create_from_png: - * @filename: name of PNG file to load - * - * Creates a new image surface and initializes the contents to the - * given PNG file. - * - * Return value: a new #cairo_surface_t initialized with the contents - * of the PNG file, or a "nil" surface if any error occurred. A nil - * surface can be checked for with cairo_surface_status(surface) which - * may return one of the following values: - * - * %CAIRO_STATUS_NO_MEMORY - * %CAIRO_STATUS_FILE_NOT_FOUND - * %CAIRO_STATUS_READ_ERROR - * - * Alternatively, you can allow errors to propagate through the drawing - * operations and check the status on the context upon completion - * using cairo_status(). - **/ -cairo_surface_t * -cairo_image_surface_create_from_png (const char *filename) -{ - struct png_read_closure_t png_closure; - cairo_surface_t *surface; - - png_closure.closure = fopen (filename, "rb"); - if (png_closure.closure == NULL) { - cairo_status_t status; - switch (errno) { - case ENOMEM: - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - case ENOENT: - status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); - break; - default: - status = _cairo_error (CAIRO_STATUS_READ_ERROR); - break; - } - return _cairo_surface_create_in_error (status); - } - - png_closure.read_func = stdio_read_func; - - surface = read_png (&png_closure); - - fclose (png_closure.closure); - - return surface; -} - -/** - * cairo_image_surface_create_from_png_stream: - * @read_func: function called to read the data of the file - * @closure: data to pass to @read_func. - * - * Creates a new image surface from PNG data read incrementally - * via the @read_func function. - * - * Return value: a new #cairo_surface_t initialized with the contents - * of the PNG file or a "nil" surface if the data read is not a valid PNG image - * or memory could not be allocated for the operation. A nil - * surface can be checked for with cairo_surface_status(surface) which - * may return one of the following values: - * - * %CAIRO_STATUS_NO_MEMORY - * %CAIRO_STATUS_READ_ERROR - * - * Alternatively, you can allow errors to propagate through the drawing - * operations and check the status on the context upon completion - * using cairo_status(). - **/ -cairo_surface_t * -cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, - void *closure) -{ - struct png_read_closure_t png_closure; - - png_closure.read_func = read_func; - png_closure.closure = closure; - - return read_png (&png_closure); -} diff --git a/libs/cairo/cairo/src/cairo-polygon.c b/libs/cairo/cairo/src/cairo-polygon.c deleted file mode 100644 index fd71319e9..000000000 --- a/libs/cairo/cairo/src/cairo-polygon.c +++ /dev/null @@ -1,460 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-slope-private.h" - -void -_cairo_polygon_init (cairo_polygon_t *polygon) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); - - polygon->status = CAIRO_STATUS_SUCCESS; - - polygon->num_edges = 0; - - polygon->edges = polygon->edges_embedded; - polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); - - polygon->has_current_point = FALSE; - polygon->has_current_edge = FALSE; - polygon->num_limits = 0; - - polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; - polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; -} - -void -_cairo_polygon_limit (cairo_polygon_t *polygon, - const cairo_box_t *limits, - int num_limits) -{ - int n; - - polygon->limits = limits; - polygon->num_limits = num_limits; - - if (polygon->num_limits) { - polygon->limit = limits[0]; - for (n = 1; n < num_limits; n++) { - if (limits[n].p1.x < polygon->limit.p1.x) - polygon->limit.p1.x = limits[n].p1.x; - - if (limits[n].p1.y < polygon->limit.p1.y) - polygon->limit.p1.y = limits[n].p1.y; - - if (limits[n].p2.x > polygon->limit.p2.x) - polygon->limit.p2.x = limits[n].p2.x; - - if (limits[n].p2.y > polygon->limit.p2.y) - polygon->limit.p2.y = limits[n].p2.y; - } - } -} - -void -_cairo_polygon_fini (cairo_polygon_t *polygon) -{ - if (polygon->edges != polygon->edges_embedded) - free (polygon->edges); - - VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t))); -} - -/* make room for at least one more edge */ -static cairo_bool_t -_cairo_polygon_grow (cairo_polygon_t *polygon) -{ - cairo_edge_t *new_edges; - int old_size = polygon->edges_size; - int new_size = 4 * old_size; - - if (CAIRO_INJECT_FAULT ()) { - polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return FALSE; - } - - if (polygon->edges == polygon->edges_embedded) { - new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); - if (new_edges != NULL) - memcpy (new_edges, polygon->edges, old_size * sizeof (cairo_edge_t)); - } else { - new_edges = _cairo_realloc_ab (polygon->edges, - new_size, sizeof (cairo_edge_t)); - } - - if (unlikely (new_edges == NULL)) { - polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return FALSE; - } - - polygon->edges = new_edges; - polygon->edges_size = new_size; - - return TRUE; -} - -static void -_add_edge (cairo_polygon_t *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - cairo_edge_t *edge; - - assert (top < bottom); - - if (unlikely (polygon->num_edges == polygon->edges_size)) { - if (! _cairo_polygon_grow (polygon)) - return; - } - - edge = &polygon->edges[polygon->num_edges++]; - edge->line.p1 = *p1; - edge->line.p2 = *p2; - edge->top = top; - edge->bottom = bottom; - edge->dir = dir; - - if (top < polygon->extents.p1.y) - polygon->extents.p1.y = top; - if (bottom > polygon->extents.p2.y) - polygon->extents.p2.y = bottom; - - if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) { - cairo_fixed_t x = p1->x; - if (top != p1->y) - x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top); - if (x < polygon->extents.p1.x) - polygon->extents.p1.x = x; - if (x > polygon->extents.p2.x) - polygon->extents.p2.x = x; - } - - if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) { - cairo_fixed_t x = p2->x; - if (bottom != p2->y) - x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom); - if (x < polygon->extents.p1.x) - polygon->extents.p1.x = x; - if (x > polygon->extents.p2.x) - polygon->extents.p2.x = x; - } -} - -static void -_add_clipped_edge (cairo_polygon_t *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2, - const int top, const int bottom, - const int dir) -{ - cairo_point_t p[2]; - int top_y, bot_y; - int n; - - for (n = 0; n < polygon->num_limits; n++) { - const cairo_box_t *limits = &polygon->limits[n]; - - if (top >= limits->p2.y) - continue; - if (bottom <= limits->p1.y) - continue; - - if (p1->x >= limits->p1.x && p2->x >= limits->p1.x && - p1->x <= limits->p2.x && p2->x <= limits->p2.x) - { - top_y = top; - if (top_y < limits->p1.y) - top_y = limits->p1.y; - - bot_y = bottom; - if (bot_y > limits->p2.y) - bot_y = limits->p2.y; - - _add_edge (polygon, p1, p2, top_y, bot_y, dir); - } - else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x) - { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = top; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = bottom; - if (bot_y > p[1].y) - bot_y = p[1].y; - - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - } - else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x) - { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = top; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = bottom; - if (bot_y > p[1].y) - bot_y = p[1].y; - - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - } - else - { - int left_y, right_y; - int p1_y, p2_y; - - left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, - limits->p1.x); - right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, - limits->p2.x); - - p1_y = top; - p2_y = bottom; - - if (p1->x < p2->x) { - if (p1->x < limits->p1.x && left_y > limits->p1.y) { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = p1_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = left_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p1_y = bot_y; - } - - if (p2->x > limits->p2.x && right_y < limits->p2.y) { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = right_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = p2_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p2_y = top_y; - } - } else { - if (p1->x > limits->p2.x && right_y > limits->p1.y) { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = p1_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = right_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p1_y = bot_y; - } - - if (p2->x < limits->p1.x && left_y < limits->p2.y) { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = left_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = p2_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p2_y = top_y; - } - } - - if (p1_y < limits->p1.y) - p1_y = limits->p1.y; - if (p2_y > limits->p2.y) - p2_y = limits->p2.y; - if (p2_y > p1_y) - _add_edge (polygon, p1, p2, p1_y, p2_y, dir); - } - } -} - -static void -_cairo_polygon_add_edge (cairo_polygon_t *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - int dir; - - /* drop horizontal edges */ - if (p1->y == p2->y) - return; - - if (p1->y < p2->y) { - dir = 1; - } else { - const cairo_point_t *t; - t = p1, p1 = p2, p2 = t; - dir = -1; - } - - if (polygon->num_limits) { - if (p2->y <= polygon->limit.p1.y) - return; - - if (p1->y >= polygon->limit.p2.y) - return; - - _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir); - } else - _add_edge (polygon, p1, p2, p1->y, p2->y, dir); -} - -cairo_status_t -_cairo_polygon_add_external_edge (void *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - _cairo_polygon_add_edge (polygon, p1, p2); - return _cairo_polygon_status (polygon); -} - -cairo_status_t -_cairo_polygon_add_line (cairo_polygon_t *polygon, - const cairo_line_t *line, - int top, int bottom, - int dir) -{ - /* drop horizontal edges */ - if (line->p1.y == line->p2.y) - return CAIRO_STATUS_SUCCESS; - - if (bottom <= top) - return CAIRO_STATUS_SUCCESS; - - if (polygon->num_limits) { - if (line->p2.y <= polygon->limit.p1.y) - return CAIRO_STATUS_SUCCESS; - - if (line->p1.y >= polygon->limit.p2.y) - return CAIRO_STATUS_SUCCESS; - - _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir); - } else - _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir); - - return polygon->status; -} - -/* flattened path operations */ - -cairo_status_t -_cairo_polygon_move_to (cairo_polygon_t *polygon, - const cairo_point_t *point) -{ - if (polygon->has_current_edge) { - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - polygon->has_current_edge = FALSE; - } - - if (! polygon->has_current_point) { - polygon->first_point = *point; - polygon->has_current_point = TRUE; - } - - polygon->current_point = *point; - return polygon->status; -} - -cairo_status_t -_cairo_polygon_line_to (cairo_polygon_t *polygon, - const cairo_point_t *point) -{ - /* squash collinear edges */ - if (polygon->has_current_edge) { - if (polygon->current_point.x != point->x || - polygon->current_point.y != point->y) - { - cairo_slope_t this; - - _cairo_slope_init (&this, &polygon->current_point, point); - if (_cairo_slope_equal (&polygon->current_edge, &this)) { - polygon->current_point = *point; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - - polygon->last_point = polygon->current_point; - polygon->current_edge = this; - } - } else if (polygon->has_current_point) { - if (polygon->current_point.x != point->x || - polygon->current_point.y != point->y) - { - polygon->last_point = polygon->current_point; - _cairo_slope_init (&polygon->current_edge, - &polygon->last_point, - point); - polygon->has_current_edge = TRUE; - } - } else { - polygon->first_point = *point; - polygon->has_current_point = TRUE; - } - - polygon->current_point = *point; - return polygon->status; -} - -cairo_status_t -_cairo_polygon_close (cairo_polygon_t *polygon) -{ - cairo_status_t status; - - if (polygon->has_current_point) { - status = _cairo_polygon_line_to (polygon, &polygon->first_point); - polygon->has_current_point = FALSE; - } - - if (polygon->has_current_edge) { - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - polygon->has_current_edge = FALSE; - } - - return polygon->status; -} diff --git a/libs/cairo/cairo/src/cairo-private.h b/libs/cairo/cairo/src/cairo-private.h deleted file mode 100644 index 33b049678..000000000 --- a/libs/cairo/cairo/src/cairo-private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PRIVATE_H -#define CAIRO_PRIVATE_H - -#include "cairo-reference-count-private.h" -#include "cairo-gstate-private.h" -#include "cairo-path-fixed-private.h" - -struct _cairo { - cairo_reference_count_t ref_count; - - cairo_status_t status; - - cairo_user_data_array_t user_data; - - cairo_gstate_t *gstate; - cairo_gstate_t gstate_tail[2]; - cairo_gstate_t *gstate_freelist; - - cairo_path_fixed_t path[1]; -}; - -#endif /* CAIRO_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-ps-surface-private.h b/libs/cairo/cairo/src/cairo-ps-surface-private.h deleted file mode 100644 index 96da53554..000000000 --- a/libs/cairo/cairo/src/cairo-ps-surface-private.h +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PS_SURFACE_PRIVATE_H -#define CAIRO_PS_SURFACE_PRIVATE_H - -#include "cairo-ps.h" - -#include "cairo-surface-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-pdf-operators-private.h" - -#include - -typedef struct cairo_ps_surface { - cairo_surface_t base; - - /* Here final_stream corresponds to the stream/file passed to - * cairo_ps_surface_create surface is built. Meanwhile stream is a - * temporary stream in which the file output is built, (so that - * the header can be built and inserted into the target stream - * before the contents of the temporary stream are copied). */ - cairo_output_stream_t *final_stream; - - FILE *tmpfile; - cairo_output_stream_t *stream; - - cairo_bool_t eps; - cairo_content_t content; - double width; - double height; - cairo_rectangle_int_t page_bbox; - int bbox_x1, bbox_y1, bbox_x2, bbox_y2; - cairo_matrix_t cairo_to_ps; - - /* XXX These 3 are used as temporary storage whilst emitting patterns */ - cairo_image_surface_t *image; - cairo_image_surface_t *acquired_image; - void *image_extra; - - cairo_bool_t use_string_datasource; - - cairo_bool_t current_pattern_is_solid_color; - cairo_color_t current_color; - - int num_pages; - - cairo_paginated_mode_t paginated_mode; - - cairo_bool_t force_fallbacks; - cairo_bool_t has_creation_date; - time_t creation_date; - - cairo_scaled_font_subsets_t *font_subsets; - - cairo_list_t document_media; - cairo_array_t dsc_header_comments; - cairo_array_t dsc_setup_comments; - cairo_array_t dsc_page_setup_comments; - - cairo_array_t *dsc_comment_target; - - cairo_ps_level_t ps_level; - cairo_ps_level_t ps_level_used; - - cairo_surface_clipper_t clipper; - - cairo_pdf_operators_t pdf_operators; - cairo_surface_t *paginated_surface; -} cairo_ps_surface_t; - -#endif /* CAIRO_PS_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-ps-surface.c b/libs/cairo/cairo/src/cairo-ps-surface.c deleted file mode 100644 index 5696d6cb7..000000000 --- a/libs/cairo/cairo/src/cairo-ps-surface.c +++ /dev/null @@ -1,3890 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Design of the PS output: - * - * The PS output is harmonised with the PDF operations using PS procedures - * to emulate the PDF operators. - * - * This has a number of advantages: - * 1. A large chunk of code is shared between the PDF and PS backends. - * See cairo-pdf-operators. - * 2. Using gs to do PS -> PDF and PDF -> PS will always work well. - */ - -#define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-ps.h" -#include "cairo-ps-surface-private.h" -#include "cairo-pdf-operators-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-paginated-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-surface-subsurface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-type3-glyph-surface-private.h" -#include "cairo-image-info-private.h" - -#include -#include -#include -#include -#include - -#define DEBUG_PS 0 - -#if DEBUG_PS -#define DEBUG_FALLBACK(s) \ - fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s)) -#else -#define DEBUG_FALLBACK(s) -#endif - -#ifndef HAVE_CTIME_R -#define ctime_r(T, BUF) ctime (T) -#endif - -/** - * SECTION:cairo-ps - * @Title: PostScript Surfaces - * @Short_Description: Rendering PostScript documents - * @See_Also: #cairo_surface_t - * - * The PostScript surface is used to render cairo graphics to Adobe - * PostScript files and is a multi-page vector surface backend. - */ - -/** - * CAIRO_HAS_PS_SURFACE: - * - * Defined if the PostScript surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -static const cairo_surface_backend_t cairo_ps_surface_backend; -static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; - -static void -_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern); - -static const cairo_ps_level_t _cairo_ps_levels[] = -{ - CAIRO_PS_LEVEL_2, - CAIRO_PS_LEVEL_3 -}; - -#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels) - -static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = -{ - "PS Level 2", - "PS Level 3" -}; - -typedef struct _cairo_page_standard_media { - const char *name; - int width; - int height; -} cairo_page_standard_media_t; - -static const cairo_page_standard_media_t _cairo_page_standard_media[] = -{ - { "A0", 2384, 3371 }, - { "A1", 1685, 2384 }, - { "A2", 1190, 1684 }, - { "A3", 842, 1190 }, - { "A4", 595, 842 }, - { "A5", 420, 595 }, - { "B4", 729, 1032 }, - { "B5", 516, 729 }, - { "Letter", 612, 792 }, - { "Tabloid", 792, 1224 }, - { "Ledger", 1224, 792 }, - { "Legal", 612, 1008 }, - { "Statement", 396, 612 }, - { "Executive", 540, 720 }, - { "Folio", 612, 936 }, - { "Quarto", 610, 780 }, - { "10x14", 720, 1008 }, -}; - -typedef struct _cairo_page_media { - char *name; - int width; - int height; - cairo_list_t link; -} cairo_page_media_t; - -static void -_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) -{ - char ctime_buf[26]; - time_t now; - char **comments; - int i, num_comments; - int level; - const char *eps_header = ""; - - if (surface->has_creation_date) - now = surface->creation_date; - else - now = time (NULL); - - if (surface->ps_level_used == CAIRO_PS_LEVEL_2) - level = 2; - else - level = 3; - - if (surface->eps) - eps_header = " EPSF-3.0"; - - _cairo_output_stream_printf (surface->final_stream, - "%%!PS-Adobe-3.0%s\n" - "%%%%Creator: cairo %s (http://cairographics.org)\n" - "%%%%CreationDate: %s" - "%%%%Pages: %d\n" - "%%%%BoundingBox: %d %d %d %d\n", - eps_header, - cairo_version_string (), - ctime_r (&now, ctime_buf), - surface->num_pages, - surface->bbox_x1, - surface->bbox_y1, - surface->bbox_x2, - surface->bbox_y2); - - _cairo_output_stream_printf (surface->final_stream, - "%%%%DocumentData: Clean7Bit\n" - "%%%%LanguageLevel: %d\n", - level); - - if (!cairo_list_is_empty (&surface->document_media)) { - cairo_page_media_t *page; - cairo_bool_t first = TRUE; - - cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { - if (first) { - _cairo_output_stream_printf (surface->final_stream, - "%%%%DocumentMedia: "); - first = FALSE; - } else { - _cairo_output_stream_printf (surface->final_stream, - "%%%%+ "); - } - _cairo_output_stream_printf (surface->final_stream, - "%s %d %d 0 () ()\n", - page->name, - page->width, - page->height); - } - } - - num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); - comments = _cairo_array_index (&surface->dsc_header_comments, 0); - for (i = 0; i < num_comments; i++) { - _cairo_output_stream_printf (surface->final_stream, - "%s\n", comments[i]); - free (comments[i]); - comments[i] = NULL; - } - - _cairo_output_stream_printf (surface->final_stream, - "%%%%EndComments\n"); - - _cairo_output_stream_printf (surface->final_stream, - "%%%%BeginProlog\n"); - - if (surface->eps) { - _cairo_output_stream_printf (surface->final_stream, - "/cairo_eps_state save def\n" - "/dict_count countdictstack def\n" - "/op_count count 1 sub def\n" - "userdict begin\n"); - } else { - _cairo_output_stream_printf (surface->final_stream, - "/languagelevel where\n" - "{ pop languagelevel } { 1 } ifelse\n" - "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n" - " (This print job requires a PostScript Language Level %d printer.) show\n" - " showpage quit } if\n", - level, - level); - } - - _cairo_output_stream_printf (surface->final_stream, - "/q { gsave } bind def\n" - "/Q { grestore } bind def\n" - "/cm { 6 array astore concat } bind def\n" - "/w { setlinewidth } bind def\n" - "/J { setlinecap } bind def\n" - "/j { setlinejoin } bind def\n" - "/M { setmiterlimit } bind def\n" - "/d { setdash } bind def\n" - "/m { moveto } bind def\n" - "/l { lineto } bind def\n" - "/c { curveto } bind def\n" - "/h { closepath } bind def\n" - "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n" - " 0 exch rlineto 0 rlineto closepath } bind def\n" - "/S { stroke } bind def\n" - "/f { fill } bind def\n" - "/f* { eofill } bind def\n" - "/n { newpath } bind def\n" - "/W { clip } bind def\n" - "/W* { eoclip } bind def\n" - "/BT { } bind def\n" - "/ET { } bind def\n" - "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" - " { globaldict begin /?pdfmark /pop load def /pdfmark\n" - " /cleartomark load def end } ifelse\n" - "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" - "/EMC { mark /EMC pdfmark } bind def\n" - "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n" - "/Tj { show currentpoint cairo_store_point } bind def\n" - "/TJ {\n" - " {\n" - " dup\n" - " type /stringtype eq\n" - " { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n" - " } forall\n" - " currentpoint cairo_store_point\n" - "} bind def\n" - "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n" - " cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n" - "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n" - " { pop cairo_selectfont } if } bind def\n" - "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n" - " /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n" - " /cairo_font where { pop cairo_selectfont } if } bind def\n" - "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n" - " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n" - "/g { setgray } bind def\n" - "/rg { setrgbcolor } bind def\n" - "/d1 { setcachedevice } bind def\n"); - - _cairo_output_stream_printf (surface->final_stream, - "%%%%EndProlog\n"); - - num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); - if (num_comments) { - _cairo_output_stream_printf (surface->final_stream, - "%%%%BeginSetup\n"); - - comments = _cairo_array_index (&surface->dsc_setup_comments, 0); - for (i = 0; i < num_comments; i++) { - _cairo_output_stream_printf (surface->final_stream, - "%s\n", comments[i]); - free (comments[i]); - comments[i] = NULL; - } - - _cairo_output_stream_printf (surface->final_stream, - "%%%%EndSetup\n"); - } -} - -#if CAIRO_HAS_FT_FONT -static cairo_status_t -_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) - - -{ - cairo_type1_subset_t subset; - cairo_status_t status; - int length; - char name[64]; - - snprintf (name, sizeof name, "f-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE); - if (unlikely (status)) - return status; - - /* FIXME: Figure out document structure convention for fonts */ - -#if DEBUG_PS - _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_type1_font_subset\n"); -#endif - - length = subset.header_length + subset.data_length + subset.trailer_length; - _cairo_output_stream_write (surface->final_stream, subset.data, length); - - _cairo_type1_subset_fini (&subset); - - return CAIRO_STATUS_SUCCESS; -} -#endif - -static cairo_status_t -_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_type1_subset_t subset; - cairo_status_t status; - int length; - char name[64]; - - snprintf (name, sizeof name, "f-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_fallback_init_hex (&subset, name, font_subset); - if (unlikely (status)) - return status; - - /* FIXME: Figure out document structure convention for fonts */ - -#if DEBUG_PS - _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_type1_font_fallback\n"); -#endif - - length = subset.header_length + subset.data_length + subset.trailer_length; - _cairo_output_stream_write (surface->final_stream, subset.data, length); - - _cairo_type1_fallback_fini (&subset); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) - - -{ - cairo_truetype_subset_t subset; - cairo_status_t status; - unsigned int i, begin, end; - - status = _cairo_truetype_subset_init (&subset, font_subset); - if (unlikely (status)) - return status; - - /* FIXME: Figure out document structure convention for fonts */ - -#if DEBUG_PS - _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_truetype_font_subset\n"); -#endif - - _cairo_output_stream_printf (surface->final_stream, - "11 dict begin\n" - "/FontType 42 def\n" - "/FontName /%s def\n" - "/PaintType 0 def\n" - "/FontMatrix [ 1 0 0 1 0 0 ] def\n" - "/FontBBox [ 0 0 0 0 ] def\n" - "/Encoding 256 array def\n" - "0 1 255 { Encoding exch /.notdef put } for\n", - subset.ps_name); - - /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ - - for (i = 1; i < font_subset->num_glyphs; i++) { - if (font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /%s put\n", - i, font_subset->glyph_names[i]); - } else { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /g%d put\n", i, i); - } - } - - _cairo_output_stream_printf (surface->final_stream, - "/CharStrings %d dict dup begin\n" - "/.notdef 0 def\n", - font_subset->num_glyphs); - - for (i = 1; i < font_subset->num_glyphs; i++) { - if (font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (surface->final_stream, - "/%s %d def\n", - font_subset->glyph_names[i], i); - } else { - _cairo_output_stream_printf (surface->final_stream, - "/g%d %d def\n", i, i); - } - } - - _cairo_output_stream_printf (surface->final_stream, - "end readonly def\n"); - - _cairo_output_stream_printf (surface->final_stream, - "/sfnts [\n"); - begin = 0; - end = 0; - for (i = 0; i < subset.num_string_offsets; i++) { - end = subset.string_offsets[i]; - _cairo_output_stream_printf (surface->final_stream,"<"); - _cairo_output_stream_write_hex_string (surface->final_stream, - subset.data + begin, end - begin); - _cairo_output_stream_printf (surface->final_stream,"00>\n"); - begin = end; - } - if (subset.data_length > end) { - _cairo_output_stream_printf (surface->final_stream,"<"); - _cairo_output_stream_write_hex_string (surface->final_stream, - subset.data + end, subset.data_length - end); - _cairo_output_stream_printf (surface->final_stream,"00>\n"); - } - - _cairo_output_stream_printf (surface->final_stream, - "] def\n" - "/f-%d-%d currentdict end definefont pop\n", - font_subset->font_id, - font_subset->subset_id); - - _cairo_truetype_subset_fini (&subset); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_emit_imagemask (cairo_image_surface_t *image, - cairo_output_stream_t *stream) -{ - uint8_t *row, *byte; - int rows, cols; - - /* The only image type supported by Type 3 fonts are 1-bit image - * masks */ - assert (image->format == CAIRO_FORMAT_A1); - - _cairo_output_stream_printf (stream, - "<<\n" - " /ImageType 1\n" - " /Width %d\n" - " /Height %d\n" - " /ImageMatrix [%d 0 0 %d 0 %d]\n" - " /Decode [1 0]\n" - " /BitsPerComponent 1\n", - image->width, - image->height, - image->width, - -image->height, - image->height); - - _cairo_output_stream_printf (stream, - " /DataSource {<\n "); - for (row = image->data, rows = image->height; rows; row += image->stride, rows--) { - for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { - uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); - _cairo_output_stream_printf (stream, "%02x ", output_byte); - } - _cairo_output_stream_printf (stream, "\n "); - } - _cairo_output_stream_printf (stream, ">}\n>>\n"); - - _cairo_output_stream_printf (stream, - "imagemask\n"); - - return _cairo_output_stream_get_status (stream); -} - -static cairo_status_t -_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_ps_surface_t *surface = closure; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - unsigned int i; - cairo_surface_t *type3_surface; - - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - NULL, - _cairo_ps_emit_imagemask, - surface->font_subsets); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, - font_subset->glyphs[i]); - if (unlikely (status)) - break; - - } - cairo_surface_finish (type3_surface); - cairo_surface_destroy (type3_surface); - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) - - -{ - cairo_status_t status; - unsigned int i; - cairo_box_t font_bbox = {{0,0},{0,0}}; - cairo_box_t bbox = {{0,0},{0,0}}; - cairo_surface_t *type3_surface; - double width; - - if (font_subset->num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - -#if DEBUG_PS - _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_type3_font_subset\n"); -#endif - - _cairo_output_stream_printf (surface->final_stream, - "8 dict begin\n" - "/FontType 3 def\n" - "/FontMatrix [1 0 0 1 0 0] def\n" - "/Encoding 256 array def\n" - "0 1 255 { Encoding exch /.notdef put } for\n"); - - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - NULL, - _cairo_ps_emit_imagemask, - surface->font_subsets); - status = type3_surface->status; - if (unlikely (status)) - return status; - - for (i = 0; i < font_subset->num_glyphs; i++) { - if (font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /%s put\n", - i, font_subset->glyph_names[i]); - } else { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /g%d put\n", i, i); - } - } - - _cairo_output_stream_printf (surface->final_stream, - "/Glyphs [\n"); - - for (i = 0; i < font_subset->num_glyphs; i++) { - _cairo_output_stream_printf (surface->final_stream, - " { %% %d\n", i); - status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, - surface->final_stream, - font_subset->glyphs[i], - &bbox, - &width); - if (unlikely (status)) - break; - - _cairo_output_stream_printf (surface->final_stream, - " }\n"); - if (i == 0) { - font_bbox.p1.x = bbox.p1.x; - font_bbox.p1.y = bbox.p1.y; - font_bbox.p2.x = bbox.p2.x; - font_bbox.p2.y = bbox.p2.y; - } else { - if (bbox.p1.x < font_bbox.p1.x) - font_bbox.p1.x = bbox.p1.x; - if (bbox.p1.y < font_bbox.p1.y) - font_bbox.p1.y = bbox.p1.y; - if (bbox.p2.x > font_bbox.p2.x) - font_bbox.p2.x = bbox.p2.x; - if (bbox.p2.y > font_bbox.p2.y) - font_bbox.p2.y = bbox.p2.y; - } - } - cairo_surface_finish (type3_surface); - cairo_surface_destroy (type3_surface); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->final_stream, - "] def\n" - "/FontBBox [%f %f %f %f] def\n" - "/BuildChar {\n" - " exch /Glyphs get\n" - " exch get\n" - " 10 dict begin exec end\n" - "} bind def\n" - "currentdict\n" - "end\n" - "/f-%d-%d exch definefont pop\n", - _cairo_fixed_to_double (font_bbox.p1.x), - - _cairo_fixed_to_double (font_bbox.p2.y), - _cairo_fixed_to_double (font_bbox.p2.x), - - _cairo_fixed_to_double (font_bbox.p1.y), - font_subset->font_id, - font_subset->subset_id); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_ps_surface_t *surface = closure; - cairo_status_t status; - - - status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (_cairo_status_is_error (status)) - return status; - -#if CAIRO_HAS_FT_FONT - status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; -#endif - - status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_ps_surface_t *surface = closure; - cairo_status_t status; - - status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (_cairo_status_is_error (status)) - return status; - - status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) -{ - cairo_status_t status; - -#if DEBUG_PS - _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_font_subsets\n"); -#endif - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_ps_surface_analyze_user_font_subset, - surface); - if (unlikely (status)) - return status; - - status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, - _cairo_ps_surface_emit_unscaled_font_subset, - surface); - if (unlikely (status)) - return status; - - status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); - if (unlikely (status)) - return status; - - return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); -} - -static cairo_status_t -_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) -{ - char buf[4096]; - int n; - - if (ferror (surface->tmpfile) != 0) - return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); - - rewind (surface->tmpfile); - while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0) - _cairo_output_stream_write (surface->final_stream, buf, n); - - if (ferror (surface->tmpfile) != 0) - return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) -{ - _cairo_output_stream_printf (surface->final_stream, - "%%%%Trailer\n"); - - if (surface->eps) { - _cairo_output_stream_printf (surface->final_stream, - "count op_count sub {pop} repeat\n" - "countdictstack dict_count sub {end} repeat\n" - "cairo_eps_state restore\n"); - } - - _cairo_output_stream_printf (surface->final_stream, - "%%%%EOF\n"); -} - -static cairo_bool_t -_path_covers_bbox (cairo_ps_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_box_t box; - - if (_cairo_path_fixed_is_box (path, &box)) { - cairo_rectangle_int_t rect; - - _cairo_box_round_to_rectangle (&box, &rect); - - /* skip trivial whole-page clips */ - if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { - if (rect.x == surface->page_bbox.x && - rect.width == surface->page_bbox.width && - rect.y == surface->page_bbox.y && - rect.height == surface->page_bbox.height) - { - return TRUE; - } - } - } - - return FALSE; -} - -static cairo_status_t -_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_ps_surface_t *surface = cairo_container_of (clipper, - cairo_ps_surface_t, - clipper); - cairo_output_stream_t *stream = surface->stream; - cairo_status_t status; - - assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE); - -#if DEBUG_PS - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_intersect_clip_path\n"); -#endif - - if (path == NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (stream, "Q q\n"); - - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return CAIRO_STATUS_SUCCESS; - } - - if (_path_covers_bbox (surface, path)) - return CAIRO_STATUS_SUCCESS; - - return _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); -} - -/* PLRM specifies a tolerance of 5 points when matching page sizes */ -static cairo_bool_t -_ps_page_dimension_equal (int a, int b) -{ - return (abs (a - b) < 5); -} - -static const char * -_cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) -{ - int width, height, i; - char buf[50]; - cairo_page_media_t *page; - const char *page_name; - - width = _cairo_lround (surface->width); - height = _cairo_lround (surface->height); - - /* search previously used page sizes */ - cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { - if (_ps_page_dimension_equal (width, page->width) && - _ps_page_dimension_equal (height, page->height)) - return page->name; - } - - /* search list of standard page sizes */ - page_name = NULL; - for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) { - if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) && - _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height)) - { - page_name = _cairo_page_standard_media[i].name; - width = _cairo_page_standard_media[i].width; - height = _cairo_page_standard_media[i].height; - break; - } - } - - page = malloc (sizeof (cairo_page_media_t)); - if (unlikely (page == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - if (page_name) { - page->name = strdup (page_name); - } else { - snprintf (buf, sizeof (buf), "%dx%dmm", - (int) _cairo_lround (surface->width * 25.4/72), - (int) _cairo_lround (surface->height * 25.4/72)); - page->name = strdup (buf); - } - - if (unlikely (page->name == NULL)) { - free (page); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - page->width = width; - page->height = height; - cairo_list_add_tail (&page->link, &surface->document_media); - - return page->name; -} - -static cairo_surface_t * -_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, - double width, - double height) -{ - cairo_status_t status, status_ignored; - cairo_ps_surface_t *surface; - - surface = malloc (sizeof (cairo_ps_surface_t)); - if (unlikely (surface == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - _cairo_surface_init (&surface->base, - &cairo_ps_surface_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); - - surface->final_stream = stream; - - surface->tmpfile = tmpfile (); - if (surface->tmpfile == NULL) { - switch (errno) { - case ENOMEM: - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - default: - status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); - break; - } - goto CLEANUP_SURFACE; - } - - surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); - status = _cairo_output_stream_get_status (surface->stream); - if (unlikely (status)) - goto CLEANUP_OUTPUT_STREAM; - - surface->font_subsets = _cairo_scaled_font_subsets_create_simple (); - if (unlikely (surface->font_subsets == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_OUTPUT_STREAM; - } - - surface->has_creation_date = FALSE; - surface->eps = FALSE; - surface->ps_level = CAIRO_PS_LEVEL_3; - surface->ps_level_used = CAIRO_PS_LEVEL_2; - surface->width = width; - surface->height = height; - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); - surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; - surface->force_fallbacks = FALSE; - surface->content = CAIRO_CONTENT_COLOR_ALPHA; - surface->use_string_datasource = FALSE; - surface->current_pattern_is_solid_color = FALSE; - - surface->page_bbox.x = 0; - surface->page_bbox.y = 0; - surface->page_bbox.width = width; - surface->page_bbox.height = height; - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_ps_surface_clipper_intersect_clip_path); - - _cairo_pdf_operators_init (&surface->pdf_operators, - surface->stream, - &surface->cairo_to_ps, - surface->font_subsets); - surface->num_pages = 0; - - cairo_list_init (&surface->document_media); - _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); - _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); - _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); - - surface->dsc_comment_target = &surface->dsc_header_comments; - - surface->paginated_surface = _cairo_paginated_surface_create ( - &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, - &cairo_ps_surface_paginated_backend); - status = surface->paginated_surface->status; - if (status == CAIRO_STATUS_SUCCESS) { - /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); - return surface->paginated_surface; - } - - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - CLEANUP_OUTPUT_STREAM: - status_ignored = _cairo_output_stream_destroy (surface->stream); - fclose (surface->tmpfile); - CLEANUP_SURFACE: - free (surface); - CLEANUP: - /* destroy stream on behalf of caller */ - status_ignored = _cairo_output_stream_destroy (stream); - - return _cairo_surface_create_in_error (status); -} - -/** - * cairo_ps_surface_create: - * @filename: a filename for the PS output (must be writable), %NULL may be - * used to specify no output. This will generate a PS surface that - * may be queried and used as a source, without generating a - * temporary file. - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PostScript surface of the specified size in points to be - * written to @filename. See cairo_ps_surface_create_for_stream() for - * a more flexible mechanism for handling the PostScript output than - * simply writing it to a named file. - * - * Note that the size of individual pages of the PostScript output can - * vary. See cairo_ps_surface_set_size(). - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_ps_surface_create (const char *filename, - double width_in_points, - double height_in_points) -{ - cairo_output_stream_t *stream; - - stream = _cairo_output_stream_create_for_filename (filename); - if (_cairo_output_stream_get_status (stream)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); - - return _cairo_ps_surface_create_for_stream_internal (stream, - width_in_points, - height_in_points); -} - -/** - * cairo_ps_surface_create_for_stream: - * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL - * to indicate a no-op @write_func. With a no-op @write_func, - * the surface may be queried or used as a source without - * generating any temporary files. - * @closure: the closure argument for @write_func - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PostScript surface of the specified size in points to be - * written incrementally to the stream represented by @write_func and - * @closure. See cairo_ps_surface_create() for a more convenient way - * to simply direct the PostScript output to a named file. - * - * Note that the size of individual pages of the PostScript - * output can vary. See cairo_ps_surface_set_size(). - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - */ -cairo_surface_t * -cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width_in_points, - double height_in_points) -{ - cairo_output_stream_t *stream; - - stream = _cairo_output_stream_create (write_func, NULL, closure); - if (_cairo_output_stream_get_status (stream)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); - - return _cairo_ps_surface_create_for_stream_internal (stream, - width_in_points, - height_in_points); -} - -static cairo_bool_t -_cairo_surface_is_ps (cairo_surface_t *surface) -{ - return surface->backend == &cairo_ps_surface_backend; -} - -/* If the abstract_surface is a paginated surface, and that paginated - * surface's target is a ps_surface, then set ps_surface to that - * target. Otherwise return FALSE. - */ -static cairo_bool_t -_extract_ps_surface (cairo_surface_t *surface, - cairo_bool_t set_error_on_failure, - cairo_ps_surface_t **ps_surface) -{ - cairo_surface_t *target; - cairo_status_t status_ignored; - - if (surface->status) - return FALSE; - if (surface->finished) { - if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_paginated (surface)) { - if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - target = _cairo_paginated_surface_get_target (surface); - if (target->status) { - if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, target->status); - return FALSE; - } - if (target->finished) { - if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_ps (target)) { - if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - *ps_surface = (cairo_ps_surface_t *) target; - return TRUE; -} - -/** - * cairo_ps_surface_restrict_to_level: - * @surface: a PostScript #cairo_surface_t - * @level: PostScript level - * - * Restricts the generated PostSript file to @level. See - * cairo_ps_get_levels() for a list of available level values that - * can be used here. - * - * This function should only be called before any drawing operations - * have been performed on the given surface. The simplest way to do - * this is to call this function immediately after creating the - * surface. - * - * Since: 1.6 - **/ -void -cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, - cairo_ps_level_t level) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - if (level < CAIRO_PS_LEVEL_LAST) - ps_surface->ps_level = level; -} - -/** - * cairo_ps_get_levels: - * @levels: supported level list - * @num_levels: list length - * - * Used to retrieve the list of supported levels. See - * cairo_ps_surface_restrict_to_level(). - * - * Since: 1.6 - **/ -void -cairo_ps_get_levels (cairo_ps_level_t const **levels, - int *num_levels) -{ - if (levels != NULL) - *levels = _cairo_ps_levels; - - if (num_levels != NULL) - *num_levels = CAIRO_PS_LEVEL_LAST; -} - -/** - * cairo_ps_level_to_string: - * @level: a level id - * - * Get the string representation of the given @level id. This function - * will return %NULL if @level id isn't valid. See cairo_ps_get_levels() - * for a way to get the list of valid level ids. - * - * Return value: the string associated to given level. - * - * Since: 1.6 - **/ -const char * -cairo_ps_level_to_string (cairo_ps_level_t level) -{ - if (level >= CAIRO_PS_LEVEL_LAST) - return NULL; - - return _cairo_ps_level_strings[level]; -} - -/** - * cairo_ps_surface_set_eps: - * @surface: a PostScript #cairo_surface_t - * @eps: %TRUE to output EPS format PostScript - * - * If @eps is %TRUE, the PostScript surface will output Encapsulated - * PostScript. - * - * This function should only be called before any drawing operations - * have been performed on the current page. The simplest way to do - * this is to call this function immediately after creating the - * surface. An Encapsulated PostScript file should never contain more - * than one page. - * - * Since: 1.6 - **/ -void -cairo_ps_surface_set_eps (cairo_surface_t *surface, - cairo_bool_t eps) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - ps_surface->eps = eps; -} - -/** - * cairo_ps_surface_get_eps: - * @surface: a PostScript #cairo_surface_t - * - * Check whether the PostScript surface will output Encapsulated PostScript. - * - * Return value: %TRUE if the surface will output Encapsulated PostScript. - * - * Since: 1.6 - **/ -cairo_public cairo_bool_t -cairo_ps_surface_get_eps (cairo_surface_t *surface) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, FALSE, &ps_surface)) - return FALSE; - - return ps_surface->eps; -} - -/** - * cairo_ps_surface_set_size: - * @surface: a PostScript #cairo_surface_t - * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) - * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) - * - * Changes the size of a PostScript surface for the current (and - * subsequent) pages. - * - * This function should only be called before any drawing operations - * have been performed on the current page. The simplest way to do - * this is to call this function immediately after creating the - * surface or immediately after completing a page with either - * cairo_show_page() or cairo_copy_page(). - * - * Since: 1.2 - **/ -void -cairo_ps_surface_set_size (cairo_surface_t *surface, - double width_in_points, - double height_in_points) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - ps_surface->width = width_in_points; - ps_surface->height = height_in_points; - cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, - &ps_surface->cairo_to_ps); -} - -/** - * cairo_ps_surface_dsc_comment: - * @surface: a PostScript #cairo_surface_t - * @comment: a comment string to be emitted into the PostScript output - * - * Emit a comment into the PostScript output for the given surface. - * - * The comment is expected to conform to the PostScript Language - * Document Structuring Conventions (DSC). Please see that manual for - * details on the available comments and their meanings. In - * particular, the %%IncludeFeature comment allows a - * device-independent means of controlling printer device features. So - * the PostScript Printer Description Files Specification will also be - * a useful reference. - * - * The comment string must begin with a percent character (%) and the - * total length of the string (including any initial percent - * characters) must not exceed 255 characters. Violating either of - * these conditions will place @surface into an error state. But - * beyond these two conditions, this function will not enforce - * conformance of the comment with any particular specification. - * - * The comment string should not have a trailing newline. - * - * The DSC specifies different sections in which particular comments - * can appear. This function provides for comments to be emitted - * within three sections: the header, the Setup section, and the - * PageSetup section. Comments appearing in the first two sections - * apply to the entire document while comments in the BeginPageSetup - * section apply only to a single page. - * - * For comments to appear in the header section, this function should - * be called after the surface is created, but before a call to - * cairo_ps_surface_begin_setup(). - * - * For comments to appear in the Setup section, this function should - * be called after a call to cairo_ps_surface_begin_setup() but before - * a call to cairo_ps_surface_begin_page_setup(). - * - * For comments to appear in the PageSetup section, this function - * should be called after a call to cairo_ps_surface_begin_page_setup(). - * - * Note that it is only necessary to call cairo_ps_surface_begin_page_setup() - * for the first page of any surface. After a call to - * cairo_show_page() or cairo_copy_page() comments are unambiguously - * directed to the PageSetup section of the current page. But it - * doesn't hurt to call this function at the beginning of every page - * as that consistency may make the calling code simpler. - * - * As a final note, cairo automatically generates several comments on - * its own. As such, applications must not manually generate any of - * the following comments: - * - * Header section: %!PS-Adobe-3.0, %%Creator, %%CreationDate, %%Pages, - * %%BoundingBox, %%DocumentData, %%LanguageLevel, %%EndComments. - * - * Setup section: %%BeginSetup, %%EndSetup - * - * PageSetup section: %%BeginPageSetup, %%PageBoundingBox, - * %%EndPageSetup. - * - * Other sections: %%BeginProlog, %%EndProlog, %%Page, %%Trailer, %%EOF - * - * Here is an example sequence showing how this function might be used: - * - * - * #cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); - * ... - * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); - * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") - * ... - * cairo_ps_surface_dsc_begin_setup (surface); - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White"); - * ... - * cairo_ps_surface_dsc_begin_page_setup (surface); - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3"); - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity"); - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy"); - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue"); - * ... draw to first page here .. - * cairo_show_page (cr); - * ... - * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5"); - * ... - * - * - * Since: 1.2 - **/ -void -cairo_ps_surface_dsc_comment (cairo_surface_t *surface, - const char *comment) -{ - cairo_ps_surface_t *ps_surface = NULL; - cairo_status_t status; - char *comment_copy; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - /* A couple of sanity checks on the comment value. */ - if (comment == NULL) { - status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER); - return; - } - - if (comment[0] != '%' || strlen (comment) > 255) { - status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT); - return; - } - - /* Then, copy the comment and store it in the appropriate array. */ - comment_copy = strdup (comment); - if (unlikely (comment_copy == NULL)) { - status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); - return; - } - - status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); - if (unlikely (status)) { - free (comment_copy); - status = _cairo_surface_set_error (surface, status); - return; - } -} - -/** - * cairo_ps_surface_dsc_begin_setup: - * @surface: a PostScript #cairo_surface_t - * - * This function indicates that subsequent calls to - * cairo_ps_surface_dsc_comment() should direct comments to the Setup - * section of the PostScript output. - * - * This function should be called at most once per surface, and must - * be called before any call to cairo_ps_surface_dsc_begin_page_setup() - * and before any drawing is performed to the surface. - * - * See cairo_ps_surface_dsc_comment() for more details. - * - * Since: 1.2 - **/ -void -cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments) - ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments; -} - -/** - * cairo_ps_surface_dsc_begin_page_setup: - * @surface: a PostScript #cairo_surface_t - * - * This function indicates that subsequent calls to - * cairo_ps_surface_dsc_comment() should direct comments to the - * PageSetup section of the PostScript output. - * - * This function call is only needed for the first page of a - * surface. It should be called after any call to - * cairo_ps_surface_dsc_begin_setup() and before any drawing is - * performed to the surface. - * - * See cairo_ps_surface_dsc_comment() for more details. - * - * Since: 1.2 - **/ -void -cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) -{ - cairo_ps_surface_t *ps_surface = NULL; - - if (! _extract_ps_surface (surface, TRUE, &ps_surface)) - return; - - if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments || - ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments) - { - ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments; - } -} - -static cairo_status_t -_cairo_ps_surface_finish (void *abstract_surface) -{ - cairo_status_t status, status2; - cairo_ps_surface_t *surface = abstract_surface; - int i, num_comments; - char **comments; - - status = surface->base.status; - if (unlikely (status)) - goto CLEANUP; - - _cairo_ps_surface_emit_header (surface); - - status = _cairo_ps_surface_emit_font_subsets (surface); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_ps_surface_emit_body (surface); - if (unlikely (status)) - goto CLEANUP; - - _cairo_ps_surface_emit_footer (surface); - -CLEANUP: - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - - status2 = _cairo_output_stream_destroy (surface->stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - fclose (surface->tmpfile); - - status2 = _cairo_output_stream_destroy (surface->final_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - while (! cairo_list_is_empty (&surface->document_media)) { - cairo_page_media_t *page; - - page = cairo_list_first_entry (&surface->document_media, - cairo_page_media_t, - link); - cairo_list_del (&page->link); - free (page->name); - free (page); - } - - num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); - comments = _cairo_array_index (&surface->dsc_header_comments, 0); - for (i = 0; i < num_comments; i++) - free (comments[i]); - _cairo_array_fini (&surface->dsc_header_comments); - - num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); - comments = _cairo_array_index (&surface->dsc_setup_comments, 0); - for (i = 0; i < num_comments; i++) - free (comments[i]); - _cairo_array_fini (&surface->dsc_setup_comments); - - num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); - comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); - for (i = 0; i < num_comments; i++) - free (comments[i]); - _cairo_array_fini (&surface->dsc_page_setup_comments); - - _cairo_surface_clipper_reset (&surface->clipper); - - return status; -} - -static cairo_int_status_t -_cairo_ps_surface_start_page (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - /* Increment before print so page numbers start at 1. */ - surface->num_pages++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_end_page (cairo_ps_surface_t *surface) -{ - cairo_int_status_t status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (surface->clipper.clip.path != NULL) { - _cairo_output_stream_printf (surface->stream, "Q Q\n"); - _cairo_surface_clipper_reset (&surface->clipper); - } else - _cairo_output_stream_printf (surface->stream, "Q\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_show_page (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_ps_surface_end_page (surface); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, "showpage\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -color_is_gray (double red, double green, double blue) -{ - const double epsilon = 0.00001; - - return (fabs (red - green) < epsilon && - fabs (red - blue) < epsilon); -} - -static cairo_int_status_t -_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_int_status_t status; - cairo_image_transparency_t transparency; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - transparency = _cairo_image_analyze_transparency (image); - switch (transparency) { - case CAIRO_IMAGE_IS_OPAQUE: - status = CAIRO_STATUS_SUCCESS; - break; - - case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: - if (surface->ps_level == CAIRO_PS_LEVEL_2) { - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - } else { - surface->ps_level_used = CAIRO_PS_LEVEL_3; - status = CAIRO_STATUS_SUCCESS; - } - break; - - case CAIRO_IMAGE_HAS_ALPHA: - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - break; - - case CAIRO_IMAGE_UNKNOWN: - ASSERT_NOT_REACHED; - } - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_bool_t -surface_pattern_supported (const cairo_surface_pattern_t *pattern) -{ - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - return TRUE; - - if (pattern->surface->backend->acquire_source_image == NULL) - return FALSE; - - /* Does an ALPHA-only source surface even make sense? Maybe, but I - * don't think it's worth the extra code to support it. */ - -/* XXX: Need to write this function here... - content = pattern->surface->content; - if (content == CAIRO_CONTENT_ALPHA) - return FALSE; -*/ - - return TRUE; -} - -static cairo_bool_t -_gradient_pattern_supported (cairo_ps_surface_t *surface, - const cairo_pattern_t *pattern) -{ - const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern; - uint16_t alpha; - cairo_extend_t extend; - unsigned int i; - - if (surface->ps_level == CAIRO_PS_LEVEL_2) - return FALSE; - - if (gradient->n_stops == 0) - return TRUE; - - /* Alpha gradients are only supported (by flattening the alpha) - * if there is no variation in the alpha across the gradient. */ - alpha = gradient->stops[0].color.alpha_short; - for (i = 0; i < gradient->n_stops; i++) { - if (gradient->stops[i].color.alpha_short != alpha) - return FALSE; - } - - extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); - - /* Radial gradients are currently only supported when one circle - * is inside the other. */ - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - double x1, y1, x2, y2, r1, r2, d; - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - if (extend == CAIRO_EXTEND_REPEAT || - extend == CAIRO_EXTEND_REFLECT) { - return FALSE; - } - - x1 = _cairo_fixed_to_double (radial->c1.x); - y1 = _cairo_fixed_to_double (radial->c1.y); - r1 = _cairo_fixed_to_double (radial->r1); - x2 = _cairo_fixed_to_double (radial->c2.x); - y2 = _cairo_fixed_to_double (radial->c2.y); - r2 = _cairo_fixed_to_double (radial->r2); - - d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - if (d > fabs(r2 - r1)) { - return FALSE; - } - } - - surface->ps_level_used = CAIRO_PS_LEVEL_3; - - return TRUE; -} - -static cairo_bool_t -pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) -{ - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) - return TRUE; - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - return _gradient_pattern_supported (surface, pattern); - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - return surface_pattern_supported ((cairo_surface_pattern_t *) pattern); - - return FALSE; -} - -static cairo_int_status_t -_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - if (surface->force_fallbacks && - surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! pattern_supported (surface, pattern)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->extend == CAIRO_EXTEND_PAD) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } - } - - if (op == CAIRO_OPERATOR_SOURCE) - return CAIRO_STATUS_SUCCESS; - - /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If - * the pattern contains transparency, we return - * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis - * surface. If the analysis surface determines that there is - * anything drawn under this operation, a fallback image will be - * used. Otherwise the operation will be replayed during the - * render stage and we blend the transparency into the white - * background to convert the pattern to opaque. - */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, - surface_pattern); - } - - if (_cairo_pattern_is_opaque (pattern, extents)) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; -} - -static cairo_bool_t -_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - return _cairo_ps_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; -} - -/* The "standard" implementation limit for PostScript string sizes is - * 65535 characters (see PostScript Language Reference, Appendix - * B). We go one short of that because we sometimes need two - * characters in a string to represent a single ASCII85 byte, (for the - * escape sequences "\\", "\(", and "\)") and we must not split these - * across two strings. So we'd be in trouble if we went right to the - * limit and one of these escape sequences just happened to land at - * the end. - */ -#define STRING_ARRAY_MAX_STRING_SIZE (65535-1) -#define STRING_ARRAY_MAX_COLUMN 72 - -typedef struct _string_array_stream { - cairo_output_stream_t base; - cairo_output_stream_t *output; - int column; - int string_size; - cairo_bool_t use_strings; -} string_array_stream_t; - -static cairo_status_t -_string_array_stream_write (cairo_output_stream_t *base, - const unsigned char *data, - unsigned int length) -{ - string_array_stream_t *stream = (string_array_stream_t *) base; - unsigned char c; - const unsigned char backslash = '\\'; - - if (length == 0) - return CAIRO_STATUS_SUCCESS; - - while (length--) { - if (stream->string_size == 0 && stream->use_strings) { - _cairo_output_stream_printf (stream->output, "("); - stream->column++; - } - - c = *data++; - if (stream->use_strings) { - switch (c) { - case '\\': - case '(': - case ')': - _cairo_output_stream_write (stream->output, &backslash, 1); - stream->column++; - stream->string_size++; - break; - } - } - /* Have to be careful to never split the final ~> sequence. */ - if (c == '~') { - _cairo_output_stream_write (stream->output, &c, 1); - stream->column++; - stream->string_size++; - - if (length-- == 0) - break; - - c = *data++; - } - _cairo_output_stream_write (stream->output, &c, 1); - stream->column++; - stream->string_size++; - - if (stream->use_strings && - stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) - { - _cairo_output_stream_printf (stream->output, ")\n"); - stream->string_size = 0; - stream->column = 0; - } - if (stream->column >= STRING_ARRAY_MAX_COLUMN) { - _cairo_output_stream_printf (stream->output, "\n "); - stream->string_size += 2; - stream->column = 1; - } - } - - return _cairo_output_stream_get_status (stream->output); -} - -static cairo_status_t -_string_array_stream_close (cairo_output_stream_t *base) -{ - cairo_status_t status; - string_array_stream_t *stream = (string_array_stream_t *) base; - - if (stream->use_strings) - _cairo_output_stream_printf (stream->output, ")\n"); - - status = _cairo_output_stream_get_status (stream->output); - - return status; -} - -/* A string_array_stream wraps an existing output stream. It takes the - * data provided to it and output one or more consecutive string - * objects, each within the standard PostScript implementation limit - * of 65k characters. - * - * The strings are each separated by a space character for easy - * inclusion within an array object, (but the array delimiters are not - * added by the string_array_stream). - * - * The string array stream is also careful to wrap the output within - * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds - * necessary escaping for special characters within a string, - * (specifically '\', '(', and ')'). - */ -static cairo_output_stream_t * -_string_array_stream_create (cairo_output_stream_t *output) -{ - string_array_stream_t *stream; - - stream = malloc (sizeof (string_array_stream_t)); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - _string_array_stream_write, - NULL, - _string_array_stream_close); - stream->output = output; - stream->column = 0; - stream->string_size = 0; - stream->use_strings = TRUE; - - return &stream->base; -} - -/* A base85_array_stream wraps an existing output stream. It wraps the - * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output - * is not enclosed in strings like string_array_stream. - */ -static cairo_output_stream_t * -_base85_array_stream_create (cairo_output_stream_t *output) -{ - string_array_stream_t *stream; - - stream = malloc (sizeof (string_array_stream_t)); - if (unlikely (stream == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_output_stream_t *) &_cairo_output_stream_nil; - } - - _cairo_output_stream_init (&stream->base, - _string_array_stream_write, - NULL, - _string_array_stream_close); - stream->output = output; - stream->column = 0; - stream->string_size = 0; - stream->use_strings = FALSE; - - return &stream->base; -} - - -/* PS Output - this section handles output of the parts of the recording - * surface we can render natively in PS. */ - -static cairo_status_t -_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, - cairo_image_surface_t *image, - cairo_image_surface_t **opaque_image) -{ - cairo_surface_t *opaque; - cairo_surface_pattern_t pattern; - cairo_status_t status; - - opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - image->width, - image->height); - if (unlikely (opaque->status)) - return opaque->status; - - if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { - status = _cairo_surface_paint (opaque, - CAIRO_OPERATOR_SOURCE, - &_cairo_pattern_white.base, - NULL); - if (unlikely (status)) { - cairo_surface_destroy (opaque); - return status; - } - } - - _cairo_pattern_init_for_surface (&pattern, &image->base); - pattern.base.filter = CAIRO_FILTER_NEAREST; - status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) { - cairo_surface_destroy (opaque); - return status; - } - - *opaque_image = (cairo_image_surface_t *) opaque; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, - const unsigned char *data, - unsigned long length, - cairo_bool_t use_strings) -{ - cairo_output_stream_t *base85_stream, *string_array_stream; - cairo_status_t status, status2; - - if (use_strings) - string_array_stream = _string_array_stream_create (surface->stream); - else - string_array_stream = _base85_array_stream_create (surface->stream); - - status = _cairo_output_stream_get_status (string_array_stream); - if (unlikely (status)) - return _cairo_output_stream_destroy (string_array_stream); - - base85_stream = _cairo_base85_stream_create (string_array_stream); - status = _cairo_output_stream_get_status (base85_stream); - if (unlikely (status)) { - status2 = _cairo_output_stream_destroy (string_array_stream); - return _cairo_output_stream_destroy (base85_stream); - } - - _cairo_output_stream_write (base85_stream, data, length); - - status = _cairo_output_stream_destroy (base85_stream); - - /* Mark end of base85 data */ - _cairo_output_stream_printf (string_array_stream, "~>"); - status2 = _cairo_output_stream_destroy (string_array_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, - cairo_image_surface_t *image, - cairo_operator_t op, - cairo_filter_t filter) -{ - cairo_status_t status; - unsigned char *data, *data_compressed; - unsigned long data_size, data_compressed_size; - cairo_image_surface_t *opaque_image = NULL; - int x, y, i; - cairo_image_transparency_t transparency; - cairo_bool_t use_mask; - uint32_t *pixel; - int bit; - const char *interpolate; - - if (image->base.status) - return image->base.status; - - switch (filter) { - default: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; - break; - } - - transparency = _cairo_image_analyze_transparency (image); - - /* PostScript can not represent the alpha channel, so we blend the - current image over a white (or black for CONTENT_COLOR - surfaces) RGB surface to eliminate it. */ - - if (op == CAIRO_OPERATOR_SOURCE || - transparency == CAIRO_IMAGE_HAS_ALPHA || - (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && - surface->ps_level == CAIRO_PS_LEVEL_2)) - { - status = _cairo_ps_surface_flatten_image_transparency (surface, - image, - &opaque_image); - if (unlikely (status)) - return status; - - use_mask = FALSE; - } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) { - opaque_image = image; - use_mask = FALSE; - } else { - use_mask = TRUE; - } - - if (use_mask) { - /* Type 2 (mask and image interleaved) has the mask and image - * samples interleaved by row. The mask row is first, one bit - * per pixel with (bit 7 first). The row is padded to byte - * boundaries. The image data is 3 bytes per pixel RGB - * format. */ - data_size = image->height * ((image->width + 7)/8 + 3*image->width); - } else { - data_size = image->height * image->width * 3; - } - data = malloc (data_size); - if (unlikely (data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto bail1; - } - - if (use_mask) { - i = 0; - for (y = 0; y < image->height; y++) { - /* mask row */ - pixel = (uint32_t *) (image->data + y * image->stride); - bit = 7; - for (x = 0; x < image->width; x++, pixel++) { - if (bit == 7) - data[i] = 0; - if (((*pixel & 0xff000000) >> 24) > 0x80) - data[i] |= (1 << bit); - bit--; - if (bit < 0) { - bit = 7; - i++; - } - } - if (bit != 7) - i++; - - /* image row*/ - pixel = (uint32_t *) (image->data + y * image->stride); - for (x = 0; x < image->width; x++, pixel++) { - data[i++] = (*pixel & 0x00ff0000) >> 16; - data[i++] = (*pixel & 0x0000ff00) >> 8; - data[i++] = (*pixel & 0x000000ff) >> 0; - } - } - } else { - i = 0; - for (y = 0; y < opaque_image->height; y++) { - pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride); - for (x = 0; x < opaque_image->width; x++, pixel++) { - data[i++] = (*pixel & 0x00ff0000) >> 16; - data[i++] = (*pixel & 0x0000ff00) >> 8; - data[i++] = (*pixel & 0x000000ff) >> 0; - } - } - } - - /* XXX: Should fix cairo-lzw to provide a stream-based interface - * instead. */ - data_compressed_size = data_size; - data_compressed = _cairo_lzw_compress (data, &data_compressed_size); - if (unlikely (data_compressed == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto bail2; - } - - if (surface->use_string_datasource) { - /* Emit the image data as a base85-encoded string which will - * be used as the data source for the image operator later. */ - _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); - - status = _cairo_ps_surface_emit_base85_string (surface, - data_compressed, - data_compressed_size, - TRUE); - if (unlikely (status)) - goto bail3; - - _cairo_output_stream_printf (surface->stream, - "] def\n"); - _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); - } - - if (use_mask) { - _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "5 dict dup begin\n" - " /ImageType 3 def\n" - " /InterleaveType 2 def\n" - " /DataDict 8 dict def\n" - " DataDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent 8 def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - image->width, - image->height, - interpolate); - - if (surface->use_string_datasource) { - _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /LZWDecode filter def\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); - } - - _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - " /MaskDict 8 dict def\n" - " MaskDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent 1 def\n" - " /Decode [ 1 0 ] def\n" - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - "end\n" - "image\n", - image->height, - image->width, - image->height, - interpolate, - image->height); - } else { - _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /BitsPerComponent 8 def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - opaque_image->width, - opaque_image->height); - if (surface->use_string_datasource) { - _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /LZWDecode filter def\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); - } - - _cairo_output_stream_printf (surface->stream, - " /Interpolate %s def\n" - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" - "image\n", - interpolate, - opaque_image->height); - } - - if (!surface->use_string_datasource) { - /* Emit the image data as a base85-encoded string which will - * be used as the data source for the image operator. */ - status = _cairo_ps_surface_emit_base85_string (surface, - data_compressed, - data_compressed_size, - FALSE); - _cairo_output_stream_printf (surface->stream, "\n"); - } else { - status = CAIRO_STATUS_SUCCESS; - } - -bail3: - free (data_compressed); - -bail2: - free (data); - -bail1: - if (!use_mask && opaque_image != image) - cairo_surface_destroy (&opaque_image->base); - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, - cairo_surface_t *source, - int width, - int height) -{ - cairo_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (unlikely (source->status)) - return source->status; - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - if (info.num_components != 1 && info.num_components != 3) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->use_string_datasource) { - /* Emit the image data as a base85-encoded string which will - * be used as the data source for the image operator later. */ - _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); - - status = _cairo_ps_surface_emit_base85_string (surface, - mime_data, - mime_data_length, - TRUE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, - "] def\n"); - _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); - } - - _cairo_output_stream_printf (surface->stream, - "/%s setcolorspace\n" - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /BitsPerComponent %d def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - info.num_components == 1 ? "DeviceGray" : "DeviceRGB", - info.width, - info.height, - info.bits_per_component); - - if (surface->use_string_datasource) { - _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /DCTDecode filter def\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); - } - - _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" - "image\n", - info.height); - - if (!surface->use_string_datasource) { - /* Emit the image data as a base85-encoded string which will - * be used as the data source for the image operator. */ - status = _cairo_ps_surface_emit_base85_string (surface, - mime_data, - mime_data_length, - FALSE); - } - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface) -{ - double old_width, old_height; - cairo_matrix_t old_cairo_to_ps; - cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; - cairo_box_t bbox; - cairo_status_t status; - - old_content = surface->content; - old_width = surface->width; - old_height = surface->height; - old_page_bbox = surface->page_bbox; - old_cairo_to_ps = surface->cairo_to_ps; - - status = - _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, - NULL); - if (unlikely (status)) - return status; - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", - _cairo_fixed_to_double (bbox.p1.x), - _cairo_fixed_to_double (bbox.p1.y), - _cairo_fixed_to_double (bbox.p2.x), - _cairo_fixed_to_double (bbox.p2.y)); -#endif - - surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); - _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); - - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - _cairo_output_stream_printf (surface->stream, " q\n"); - - if (recording_surface->content == CAIRO_CONTENT_COLOR) { - surface->content = CAIRO_CONTENT_COLOR; - _cairo_output_stream_printf (surface->stream, - " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, " Q\n"); - surface->content = old_content; - surface->width = old_width; - surface->height = old_height; - surface->page_bbox = old_page_bbox; - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->cairo_to_ps = old_cairo_to_ps; - - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface, - const cairo_rectangle_int_t *extents) -{ - double old_width, old_height; - cairo_matrix_t old_cairo_to_ps; - cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; - cairo_status_t status; - - old_content = surface->content; - old_width = surface->width; - old_height = surface->height; - old_page_bbox = surface->page_bbox; - old_cairo_to_ps = surface->cairo_to_ps; - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n", - extents->x, extents->y, - extents->width, extents->height); -#endif - - surface->page_bbox.x = surface->page_bbox.y = 0; - surface->page_bbox.width = surface->width = extents->width; - surface->page_bbox.height = surface->height = extents->height; - - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - _cairo_output_stream_printf (surface->stream, " q\n"); - - if (recording_surface->content == CAIRO_CONTENT_COLOR) { - surface->content = CAIRO_CONTENT_COLOR; - _cairo_output_stream_printf (surface->stream, - " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - extents, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, " Q\n"); - surface->content = old_content; - surface->width = old_width; - surface->height = old_height; - surface->page_bbox = old_page_bbox; - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->cairo_to_ps = old_cairo_to_ps; - - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface, - const cairo_color_t *color, - double *red, - double *green, - double *blue) -{ - *red = color->red; - *green = color->green; - *blue = color->blue; - - if (! CAIRO_COLOR_IS_OPAQUE (color)) { - *red *= color->alpha; - *green *= color->alpha; - *blue *= color->alpha; - if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { - double one_minus_alpha = 1. - color->alpha; - *red += one_minus_alpha; - *green += one_minus_alpha; - *blue += one_minus_alpha; - } - } -} - -static void -_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, - cairo_solid_pattern_t *pattern) -{ - double red, green, blue; - - _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue); - - if (color_is_gray (red, green, blue)) - _cairo_output_stream_printf (surface->stream, - "%f g\n", - red); - else - _cairo_output_stream_printf (surface->stream, - "%f %f %f rg\n", - red, green, blue); -} - -static cairo_status_t -_cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_rectangle_int_t *extents, - int *width, - int *height, - int *origin_x, - int *origin_y) -{ - cairo_status_t status; - cairo_surface_t *pad_image; - int x = 0; - int y = 0; - - surface->acquired_image = NULL; - surface->image = NULL; - - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) pattern->surface; - - *width = sub->extents.width; - *height = sub->extents.height; - } else { - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; - cairo_box_t bbox; - cairo_rectangle_int_t extents; - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - *width = extents.width; - *height = extents.height; - } - return CAIRO_STATUS_SUCCESS; - } else { - status = _cairo_surface_acquire_source_image (pattern->surface, - &surface->acquired_image, - &surface->image_extra); - if (unlikely (status)) - return status; - - pad_image = &surface->acquired_image->base; - if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - x = -rect.x; - y = -rect.y; - - pad_image = - _cairo_image_surface_create_with_pixman_format (NULL, - surface->acquired_image->pixman_format, - rect.width, rect.height, - 0); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } - - _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_paint (pad_image, - CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) { - if (pad_image != &surface->acquired_image->base) - cairo_surface_destroy (pad_image); - - goto BAIL; - } - } - - surface->image = (cairo_image_surface_t *) pad_image; - *width = surface->image->width; - *height = surface->image->height; - *origin_x = x; - *origin_y = y; - return CAIRO_STATUS_SUCCESS; - } - -BAIL: - _cairo_ps_surface_release_surface (surface, pattern); - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_operator_t op, - int width, - int height) -{ - cairo_status_t status; - - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - cairo_surface_t *source = pattern->surface; - - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents); - } else { - status = _cairo_ps_surface_emit_recording_surface (surface, source); - } - } else { - if (pattern->base.extend != CAIRO_EXTEND_PAD) { - status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface, - width, height); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_ps_surface_emit_image (surface, surface->image, - op, pattern->base.filter); - } - - return status; -} - -static void -_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - if (surface->image != surface->acquired_image) - cairo_surface_destroy (&surface->image->base); - - if (pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) { - _cairo_surface_release_source_image (pattern->surface, - surface->acquired_image, - surface->image_extra); - } - - surface->acquired_image = NULL; - surface->image = NULL; -} - -static void -_path_fixed_init_rectangle (cairo_path_fixed_t *path, - cairo_rectangle_int_t *rect) -{ - cairo_status_t status; - - _cairo_path_fixed_init (path); - - status = _cairo_path_fixed_move_to (path, - _cairo_fixed_from_int (rect->x), - _cairo_fixed_from_int (rect->y)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (0), - _cairo_fixed_from_int (rect->height)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (-rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - - status = _cairo_path_fixed_close_path (path); - assert (status == CAIRO_STATUS_SUCCESS); -} - -static cairo_status_t -_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_rectangle_int_t *extents, - cairo_operator_t op) -{ - cairo_status_t status; - int width, height; - cairo_matrix_t cairo_p2d, ps_p2d; - cairo_path_fixed_t path; - int origin_x = 0; - int origin_y = 0; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_ps_surface_acquire_surface (surface, - pattern, - extents, - &width, &height, - &origin_x, &origin_y); - if (unlikely (status)) - return status; - - _path_fixed_init_rectangle (&path, extents); - status = _cairo_pdf_operators_clip (&surface->pdf_operators, - &path, - CAIRO_FILL_RULE_WINDING); - _cairo_path_fixed_fini (&path); - if (unlikely (status)) - return status; - - cairo_p2d = pattern->base.matrix; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - double scale = cairo_p2d.xx; - - _cairo_output_stream_printf (surface->stream, - "%% Fallback Image: x=%f, y=%f, w=%d, h=%d res=%fdpi size=%ld\n", - -cairo_p2d.x0/scale, - -cairo_p2d.y0/scale, - (int)(width/scale), - (int)(height/scale), - scale*72, - (long)width*height*3); - } else { - if (op == CAIRO_OPERATOR_SOURCE) { - _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", - surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, - surface->width, - surface->height); - } - } - - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - ps_p2d = surface->cairo_to_ps; - cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); - cairo_matrix_translate (&ps_p2d, 0.0, height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); - - if (! _cairo_matrix_is_identity (&ps_p2d)) { - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ] concat\n", - ps_p2d.xx, ps_p2d.yx, - ps_p2d.xy, ps_p2d.yy, - ps_p2d.x0, ps_p2d.y0); - } - - status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height); - _cairo_ps_surface_release_surface (surface, pattern); - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_rectangle_int_t *extents, - cairo_operator_t op) -{ - cairo_status_t status; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ - double xstep, ystep; - cairo_matrix_t cairo_p2d, ps_p2d; - cairo_bool_t old_use_string_datasource; - int origin_x = 0; - int origin_y = 0; - - cairo_p2d = pattern->base.matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - status = _cairo_ps_surface_acquire_surface (surface, - pattern, - extents, - &pattern_width, &pattern_height, - &origin_x, &origin_y); - if (unlikely (status)) - return status; - - switch (pattern->base.extend) { - case CAIRO_EXTEND_PAD: - case CAIRO_EXTEND_NONE: - { - /* In PS/PDF, (as far as I can tell), all patterns are - * repeating. So we support cairo's EXTEND_NONE semantics - * by setting the repeat step size to a size large enough - * to guarantee that no more than a single occurrence will - * be visible. - * - * First, map the surface extents into pattern space (since - * xstep and ystep are in pattern space). Then use an upper - * bound on the length of the diagonal of the pattern image - * and the surface as repeat size. This guarantees to never - * repeat visibly. - */ - double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, - &x1, &y1, &x2, &y2, - NULL); - - /* Rather than computing precise bounds of the union, just - * add the surface extents unconditionally. We only - * required an answer that's large enough, we don't really - * care if it's not as tight as possible.*/ - xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); - break; - } - case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; - break; - case CAIRO_EXTEND_REFLECT: - xstep = pattern_width*2; - ystep = pattern_height*2; - break; - /* All the rest (if any) should have been analyzed away, so these - * cases should be unreachable. */ - default: - ASSERT_NOT_REACHED; - xstep = 0; - ystep = 0; - } - - _cairo_output_stream_printf (surface->stream, - "/CairoPattern {\n"); - - old_use_string_datasource = surface->use_string_datasource; - surface->use_string_datasource = TRUE; - if (op == CAIRO_OPERATOR_SOURCE) { - _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", - surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, - xstep, ystep); - } - status = _cairo_ps_surface_emit_surface (surface, pattern, op, - pattern_width, pattern_height); - if (unlikely (status)) - return status; - - surface->use_string_datasource = old_use_string_datasource; - _cairo_output_stream_printf (surface->stream, - "} bind def\n"); - - _cairo_output_stream_printf (surface->stream, - "<< /PatternType 1\n" - " /PaintType 1\n" - " /TilingType 1\n"); - _cairo_output_stream_printf (surface->stream, - " /XStep %f /YStep %f\n", - xstep, ystep); - - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n" - " /PaintProc {\n" - " CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " [ 1 0 0 -1 0 %d] concat CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " CairoPattern\n" - " } bind\n", - pattern_width*2, pattern_height*2, - pattern_width*2, - pattern_height*2, - pattern_width*2); - } else { - if (op == CAIRO_OPERATOR_SOURCE) { - _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %f %f]\n", - xstep, ystep); - } else { - _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n", - pattern_width, pattern_height); - } - _cairo_output_stream_printf (surface->stream, - " /PaintProc { CairoPattern }\n"); - } - - _cairo_output_stream_printf (surface->stream, - ">>\n"); - - cairo_p2d = pattern->base.matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_init_identity (&ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, surface->height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); - cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); - - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - ps_p2d.xx, ps_p2d.yx, - ps_p2d.xy, ps_p2d.yy, - ps_p2d.x0, ps_p2d.y0); - _cairo_output_stream_printf (surface->stream, - "makepattern setpattern\n"); - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_ps_color_stop { - double offset; - double color[4]; -} cairo_ps_color_stop_t; - -static void -_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface, - cairo_ps_color_stop_t *stop1, - cairo_ps_color_stop_t *stop2) -{ - _cairo_output_stream_printf (surface->stream, - " << /FunctionType 2\n" - " /Domain [ 0 1 ]\n" - " /C0 [ %f %f %f ]\n" - " /C1 [ %f %f %f ]\n" - " /N 1\n" - " >>\n", - stop1->color[0], - stop1->color[1], - stop1->color[2], - stop2->color[0], - stop2->color[1], - stop2->color[2]); -} - -static void -_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface, - unsigned int n_stops, - cairo_ps_color_stop_t stops[]) -{ - unsigned int i; - - _cairo_output_stream_printf (surface->stream, - "<< /FunctionType 3\n" - " /Domain [ 0 1 ]\n" - " /Functions [\n"); - for (i = 0; i < n_stops - 1; i++) - _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]); - - _cairo_output_stream_printf (surface->stream, " ]\n"); - - _cairo_output_stream_printf (surface->stream, " /Bounds [ "); - for (i = 1; i < n_stops-1; i++) - _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset); - _cairo_output_stream_printf (surface->stream, "]\n"); - - _cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n", - n_stops - 1); - - _cairo_output_stream_printf (surface->stream, ">>\n"); -} - -static void -calc_gradient_color (cairo_ps_color_stop_t *new_stop, - cairo_ps_color_stop_t *stop1, - cairo_ps_color_stop_t *stop2) -{ - int i; - double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); - - for (i = 0; i < 4; i++) - new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); -} - -#define COLOR_STOP_EPSILON 1e-6 - -static cairo_status_t -_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, - cairo_gradient_pattern_t *pattern) -{ - cairo_ps_color_stop_t *allstops, *stops; - unsigned int i, n_stops; - - allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t)); - if (unlikely (allstops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - stops = &allstops[1]; - n_stops = pattern->n_stops; - - for (i = 0; i < n_stops; i++) { - cairo_gradient_stop_t *stop = &pattern->stops[i]; - - stops[i].color[0] = stop->color.red; - stops[i].color[1] = stop->color.green; - stops[i].color[2] = stop->color.blue; - stops[i].color[3] = stop->color.alpha; - stops[i].offset = pattern->stops[i].offset; - } - - if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) { - if (stops[0].offset > COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) - memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t)); - else - calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); - stops = allstops; - n_stops++; - } - stops[0].offset = 0.0; - - if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { - memcpy (&stops[n_stops], - &stops[n_stops - 1], - sizeof (cairo_ps_color_stop_t)); - } else { - calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); - } - n_stops++; - } - stops[n_stops-1].offset = 1.0; - } - - for (i = 0; i < n_stops; i++) { - double red, green, blue; - cairo_color_t color; - - _cairo_color_init_rgba (&color, - stops[i].color[0], - stops[i].color[1], - stops[i].color[2], - stops[i].color[3]); - _cairo_ps_surface_flatten_transparency (surface, &color, - &red, &green, &blue); - stops[i].color[0] = red; - stops[i].color[1] = green; - stops[i].color[2] = blue; - } - - _cairo_output_stream_printf (surface->stream, - "/CairoFunction\n"); - if (n_stops == 1) { - /* work around single stop gradients */ - _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[0]); - } else if (n_stops == 2) { - /* no need for stitched function */ - _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]); - } else { - /* multiple stops: stitch. XXX possible optimization: regulary spaced - * stops do not require stitching. XXX */ - _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops,stops); - } - _cairo_output_stream_printf (surface->stream, - "def\n"); - - free (allstops); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface, - cairo_gradient_pattern_t *pattern, - int begin, - int end) -{ - _cairo_output_stream_printf (surface->stream, - "/CairoFunction\n" - "<< /FunctionType 3\n" - " /Domain [ %d %d ]\n" - " /Functions [ %d {CairoFunction} repeat ]\n" - " /Bounds [ %d 1 %d {} for ]\n", - begin, - end, - end - begin, - begin + 1, - end - 1); - - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n", - begin, - end - 1); - } else { - _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n", - begin, - end - 1); - } - - _cairo_output_stream_printf (surface->stream, ">> def\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, - cairo_linear_pattern_t *pattern) -{ - double x1, y1, x2, y2; - double _x1, _y1, _x2, _y2; - cairo_matrix_t pat_to_ps; - cairo_extend_t extend; - cairo_status_t status; - cairo_gradient_pattern_t *gradient = &pattern->base; - double first_stop, last_stop; - int repeat_begin = 0, repeat_end = 1; - - extend = cairo_pattern_get_extend (&pattern->base.base); - - pat_to_ps = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_ps); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); - first_stop = gradient->stops[0].offset; - last_stop = gradient->stops[gradient->n_stops - 1].offset; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - double dx, dy; - int x_rep = 0, y_rep = 0; - - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - cairo_matrix_transform_point (&pat_to_ps, &x1, &y1); - - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_transform_point (&pat_to_ps, &x2, &y2); - - dx = fabs (x2 - x1); - dy = fabs (y2 - y1); - if (dx > 1e-6) - x_rep = ceil (surface->width/dx); - if (dy > 1e-6) - y_rep = ceil (surface->height/dy); - - repeat_end = MAX (x_rep, y_rep); - repeat_begin = -repeat_end; - first_stop = repeat_begin; - last_stop = repeat_end; - } - - /* PS requires the first and last stop to be the same as the line - * coordinates. For repeating patterns this moves the line - * coordinates out to the begin/end of the repeating function. For - * non repeating patterns this may move the line coordinates in if - * there are not stops at offset 0 and 1. */ - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - - _x1 = x1 + (x2 - x1)*first_stop; - _y1 = y1 + (y2 - y1)*first_stop; - _x2 = x1 + (x2 - x1)*last_stop; - _y2 = y1 + (y2 - y1)*last_stop; - - x1 = _x1; - x2 = _x2; - y1 = _y1; - y2 = _y2; - - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || - pattern->base.base.extend == CAIRO_EXTEND_PAD) && - gradient->n_stops == 2) { - first_stop = 0.0; - last_stop = 1.0; - } - - status = _cairo_ps_surface_emit_pattern_stops (surface, - &pattern->base); - if (unlikely (status)) - return status; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - status = _cairo_ps_surface_emit_repeating_function (surface, - &pattern->base, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - } - - _cairo_output_stream_printf (surface->stream, - "<< /PatternType 2\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function CairoFunction\n", - x1, y1, x2, y2, - first_stop, last_stop); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->stream, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->stream, - " >>\n" - ">>\n"); - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - pat_to_ps.xx, pat_to_ps.yx, - pat_to_ps.xy, pat_to_ps.yy, - pat_to_ps.x0, pat_to_ps.y0); - _cairo_output_stream_printf (surface->stream, - "makepattern setpattern\n"); - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface, - cairo_radial_pattern_t *pattern) -{ - double x1, y1, x2, y2, r1, r2; - cairo_matrix_t pat_to_ps; - cairo_extend_t extend; - cairo_status_t status; - - extend = cairo_pattern_get_extend (&pattern->base.base); - - pat_to_ps = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_ps); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); - x1 = _cairo_fixed_to_double (pattern->c1.x); - y1 = _cairo_fixed_to_double (pattern->c1.y); - r1 = _cairo_fixed_to_double (pattern->r1); - x2 = _cairo_fixed_to_double (pattern->c2.x); - y2 = _cairo_fixed_to_double (pattern->c2.y); - r2 = _cairo_fixed_to_double (pattern->r2); - - status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, - "<< /PatternType 2\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function CairoFunction\n", - x1, y1, r1, x2, y2, r2); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->stream, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->stream, - " >>\n" - ">>\n"); - - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - pat_to_ps.xx, pat_to_ps.yx, - pat_to_ps.xy, pat_to_ps.yy, - pat_to_ps.x0, pat_to_ps.y0); - _cairo_output_stream_printf (surface->stream, - "makepattern setpattern\n"); - - return status; -} - -static cairo_status_t -_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents, - cairo_operator_t op) -{ - cairo_status_t status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - - if (surface->current_pattern_is_solid_color == FALSE || - ! _cairo_color_equal (&surface->current_color, &solid->color)) - { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); - - surface->current_pattern_is_solid_color = TRUE; - surface->current_color = solid->color; - } - - return CAIRO_STATUS_SUCCESS; - } - - surface->current_pattern_is_solid_color = FALSE; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - - _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); - break; - - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_ps_surface_emit_surface_pattern (surface, - (cairo_surface_pattern_t *) pattern, - extents, - op); - if (unlikely (status)) - return status; - break; - - case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_ps_surface_emit_linear_pattern (surface, - (cairo_linear_pattern_t *) pattern); - if (unlikely (status)) - return status; - break; - - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_ps_surface_emit_radial_pattern (surface, - (cairo_radial_pattern_t *) pattern); - if (unlikely (status)) - return status; - break; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_ps_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_ps_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the aribitray limitation of width to a short(!). We - * may need to come up with a better interface for get_extents. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); - - return TRUE; -} - -static void -_cairo_ps_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); - cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); -} - -static cairo_int_status_t -_cairo_ps_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_output_stream_t *stream = surface->stream; - cairo_composite_rectangles_t extents; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, clip); - if (unlikely (status)) - return status; - - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); - -#if DEBUG_PS - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_paint\n"); -#endif - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || - source->extend == CAIRO_EXTEND_PAD)) - { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (stream, "q\n"); - status = _cairo_ps_surface_paint_surface (surface, - (cairo_surface_pattern_t *) source, - &extents.bounded, op); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (stream, "Q\n"); - } else { - status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", - extents.bounded.x, extents.bounded.y, - extents.bounded.width, extents.bounded.height); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_int_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; - - /* use the more accurate extents */ - if (extents.is_bounded) { - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &extents.mask); - if (unlikely (status)) - return status; - - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; - } - - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_stroke\n"); -#endif - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); - if (unlikely (status)) - return status; - - return _cairo_pdf_operators_stroke (&surface->pdf_operators, - path, - style, - ctm, - ctm_inverse); -} - -static cairo_int_status_t -_cairo_ps_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_int_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; - - /* use the more accurate extents */ - if (extents.is_bounded) { - _cairo_path_fixed_fill_extents (path, - fill_rule, - tolerance, - &extents.mask); - - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_fill\n"); -#endif - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || - source->extend == CAIRO_EXTEND_PAD)) - { - _cairo_output_stream_printf (surface->stream, "q\n"); - - status = _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); - if (unlikely (status)) - return status; - - status = _cairo_ps_surface_paint_surface (surface, - (cairo_surface_pattern_t *) source, - &extents.bounded, op); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, "Q\n"); - _cairo_pdf_operators_reset (&surface->pdf_operators); - } else { - status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - path, - fill_rule); - } - - return status; -} - -static cairo_int_status_t -_cairo_ps_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_bool_t overlap; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - if (unlikely (status)) - return status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); - -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_show_glyphs\n"); -#endif - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); - if (unlikely (status)) - return status; - - return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE, - scaled_font); -} - -static void -_cairo_ps_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t paginated_mode) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_status_t status; - - surface->paginated_mode = paginated_mode; - - if (surface->clipper.clip.path != NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - - _cairo_output_stream_printf (surface->stream, "Q q\n"); - _cairo_surface_clipper_reset (&surface->clipper); - } -} - -static cairo_int_status_t -_cairo_ps_surface_set_bounding_box (void *abstract_surface, - cairo_box_t *bbox) -{ - cairo_ps_surface_t *surface = abstract_surface; - int i, num_comments; - char **comments; - int x1, y1, x2, y2; - cairo_bool_t has_page_media; - const char *page_media; - - if (surface->eps) { - x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); - y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); - x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); - y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); - } else { - x1 = 0; - y1 = 0; - x2 = ceil (surface->width); - y2 = ceil (surface->height); - } - - surface->page_bbox.x = x1; - surface->page_bbox.y = y1; - surface->page_bbox.width = x2 - x1; - surface->page_bbox.height = y2 - y1; - - _cairo_output_stream_printf (surface->stream, - "%%%%Page: %d %d\n", - surface->num_pages, - surface->num_pages); - - _cairo_output_stream_printf (surface->stream, - "%%%%BeginPageSetup\n"); - - has_page_media = FALSE; - num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); - comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); - for (i = 0; i < num_comments; i++) { - _cairo_output_stream_printf (surface->stream, - "%s\n", comments[i]); - if (strncmp (comments[i], "%%PageMedia:", 11) == 0) - has_page_media = TRUE; - free (comments[i]); - comments[i] = NULL; - } - _cairo_array_truncate (&surface->dsc_page_setup_comments, 0); - - if (!has_page_media && !surface->eps) { - page_media = _cairo_ps_surface_get_page_media (surface); - if (unlikely (page_media == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->stream, - "%%%%PageMedia: %s\n", - page_media); - } - - _cairo_output_stream_printf (surface->stream, - "%%%%PageBoundingBox: %d %d %d %d\n", - x1, y1, x2, y2); - - _cairo_output_stream_printf (surface->stream, - "%%%%EndPageSetup\n" - "q %d %d %d %d rectclip q\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); - - if (surface->num_pages == 1) { - surface->bbox_x1 = x1; - surface->bbox_y1 = y1; - surface->bbox_x2 = x2; - surface->bbox_y2 = y2; - } else { - if (x1 < surface->bbox_x1) - surface->bbox_x1 = x1; - if (y1 < surface->bbox_y1) - surface->bbox_y1 = y1; - if (x2 > surface->bbox_x2) - surface->bbox_x2 = x2; - if (y2 > surface->bbox_y2) - surface->bbox_y2 = y2; - } - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return _cairo_output_stream_get_status (surface->stream); -} - -static cairo_bool_t -_cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) -{ - return TRUE; -} - -static const cairo_surface_backend_t cairo_ps_surface_backend = { - CAIRO_SURFACE_TYPE_PS, - NULL, /* create similar: handled by wrapper */ - _cairo_ps_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* cairo_ps_surface_copy_page */ - _cairo_ps_surface_show_page, - _cairo_ps_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_ps_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here are the drawing functions */ - - _cairo_ps_surface_paint, /* paint */ - NULL, /* mask */ - _cairo_ps_surface_stroke, - _cairo_ps_surface_fill, - _cairo_ps_surface_show_glyphs, - NULL, /* snapshot */ -}; - -static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = { - _cairo_ps_surface_start_page, - _cairo_ps_surface_set_paginated_mode, - _cairo_ps_surface_set_bounding_box, - NULL, /* _cairo_ps_surface_has_fallback_images, */ - _cairo_ps_surface_supports_fine_grained_fallbacks, -}; diff --git a/libs/cairo/cairo/src/cairo-ps.h b/libs/cairo/cairo/src/cairo-ps.h deleted file mode 100644 index 3d609c9d1..000000000 --- a/libs/cairo/cairo/src/cairo-ps.h +++ /dev/null @@ -1,82 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_PS_H -#define CAIRO_PS_H - -#include "cairo.h" - -#if CAIRO_HAS_PS_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -/* PS-surface functions */ - -/** - * cairo_ps_level_t: - * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. - * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. - * - * #cairo_ps_level_t is used to describe the language level of the - * PostScript Language Reference that a generated PostScript file will - * conform to. - */ -typedef enum _cairo_ps_level { - CAIRO_PS_LEVEL_2, - CAIRO_PS_LEVEL_3 -} cairo_ps_level_t; - -cairo_public cairo_surface_t * -cairo_ps_surface_create (const char *filename, - double width_in_points, - double height_in_points); - -cairo_public cairo_surface_t * -cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width_in_points, - double height_in_points); - -cairo_public void -cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, - cairo_ps_level_t level); - -cairo_public void -cairo_ps_get_levels (cairo_ps_level_t const **levels, - int *num_levels); - -cairo_public const char * -cairo_ps_level_to_string (cairo_ps_level_t level); - -cairo_public void -cairo_ps_surface_set_eps (cairo_surface_t *surface, - cairo_bool_t eps); - -cairo_public cairo_bool_t -cairo_ps_surface_get_eps (cairo_surface_t *surface); - -cairo_public void -cairo_ps_surface_set_size (cairo_surface_t *surface, - double width_in_points, - double height_in_points); - -cairo_public void -cairo_ps_surface_dsc_comment (cairo_surface_t *surface, - const char *comment); - -cairo_public void -cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface); - -cairo_public void -cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_PS_SURFACE */ -# error Cairo was not compiled with support for the ps backend -#endif /* CAIRO_HAS_PS_SURFACE */ - -#endif /* CAIRO_PS_H */ diff --git a/libs/cairo/cairo/src/cairo-qt-surface.cpp b/libs/cairo/cairo/src/cairo-qt-surface.cpp deleted file mode 100644 index 9d73395b7..000000000 --- a/libs/cairo/cairo/src/cairo-qt-surface.cpp +++ /dev/null @@ -1,1716 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Get INT16_MIN etc. as per C99 */ -#define __STDC_LIMIT_MACROS - -#include "cairoint.h" -#include "cairo-types-private.h" -#include "cairo-clip-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-region-private.h" - -#include "cairo-ft.h" -#include "cairo-qt.h" -#include "cairo-error-private.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Enable workaround slow regional Qt paths */ -#define ENABLE_FAST_FILL 0 -#define ENABLE_FAST_CLIP 0 - -#if 0 -#define D(x) x -static const char * -_opstr (cairo_operator_t op) -{ - const char *ops[] = { - "CLEAR", - "SOURCE", - "OVER", - "IN", - "OUT", - "ATOP", - "DEST", - "DEST_OVER", - "DEST_IN", - "DEST_OUT", - "DEST_ATOP", - "XOR", - "ADD", - "SATURATE" - }; - - if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE) - return "(\?\?\?)"; - - return ops[op]; -} -#else -#define D(x) do { } while(0) -#endif - -#ifndef CAIRO_INT_STATUS_SUCCESS -#define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS) -#endif - -/* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */ -#define DOT_LENGTH 1.0 -#define DASH_LENGTH 3.0 - -struct cairo_qt_surface_t { - cairo_surface_t base; - - cairo_bool_t supports_porter_duff; - - QPainter *p; - - /* The pixmap/image constructors will store their objects here */ - QPixmap *pixmap; - QImage *image; - - QRect window; - - cairo_surface_clipper_t clipper; - - cairo_surface_t *image_equiv; -}; - -/* Will be true if we ever try to create a QPixmap and end - * up with one without an alpha channel. - */ -static cairo_bool_t _qpixmaps_have_no_alpha = FALSE; - -/** - ** Helper methods - **/ - -static QPainter::CompositionMode -_qpainter_compositionmode_from_cairo_op (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return QPainter::CompositionMode_Clear; - - case CAIRO_OPERATOR_SOURCE: - return QPainter::CompositionMode_Source; - case CAIRO_OPERATOR_OVER: - return QPainter::CompositionMode_SourceOver; - case CAIRO_OPERATOR_IN: - return QPainter::CompositionMode_SourceIn; - case CAIRO_OPERATOR_OUT: - return QPainter::CompositionMode_SourceOut; - case CAIRO_OPERATOR_ATOP: - return QPainter::CompositionMode_SourceAtop; - - case CAIRO_OPERATOR_DEST: - return QPainter::CompositionMode_Destination; - case CAIRO_OPERATOR_DEST_OVER: - return QPainter::CompositionMode_DestinationOver; - case CAIRO_OPERATOR_DEST_IN: - return QPainter::CompositionMode_DestinationIn; - case CAIRO_OPERATOR_DEST_OUT: - return QPainter::CompositionMode_DestinationOut; - case CAIRO_OPERATOR_DEST_ATOP: - return QPainter::CompositionMode_DestinationAtop; - - case CAIRO_OPERATOR_XOR: - return QPainter::CompositionMode_Xor; - - default: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - ASSERT_NOT_REACHED; - } - return QPainter::CompositionMode_Source; -} - -static bool -_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op) -{ - if (qs->supports_porter_duff) { - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - - case CAIRO_OPERATOR_XOR: - return TRUE; - - default: - ASSERT_NOT_REACHED; - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return FALSE; - - } - } else { - return op == CAIRO_OPERATOR_OVER; - } -} - -static cairo_format_t -_cairo_format_from_qimage_format (QImage::Format fmt) -{ - switch (fmt) { - case QImage::Format_ARGB32_Premultiplied: - return CAIRO_FORMAT_ARGB32; - case QImage::Format_RGB32: - return CAIRO_FORMAT_RGB24; - case QImage::Format_Indexed8: // XXX not quite - return CAIRO_FORMAT_A8; -#ifdef WORDS_BIGENDIAN - case QImage::Format_Mono: -#else - case QImage::Format_MonoLSB: -#endif - return CAIRO_FORMAT_A1; - - case QImage::Format_Invalid: -#ifdef WORDS_BIGENDIAN - case QImage::Format_MonoLSB: -#else - case QImage::Format_Mono: -#endif - case QImage::Format_ARGB32: - case QImage::Format_RGB16: - case QImage::Format_ARGB8565_Premultiplied: - case QImage::Format_RGB666: - case QImage::Format_ARGB6666_Premultiplied: - case QImage::Format_RGB555: - case QImage::Format_ARGB8555_Premultiplied: - case QImage::Format_RGB888: - case QImage::Format_RGB444: - case QImage::Format_ARGB4444_Premultiplied: - case QImage::NImageFormats: - default: - ASSERT_NOT_REACHED; - return (cairo_format_t) -1; - } -} - -static QImage::Format -_qimage_format_from_cairo_format (cairo_format_t fmt) -{ - switch (fmt) { - case CAIRO_FORMAT_ARGB32: - return QImage::Format_ARGB32_Premultiplied; - case CAIRO_FORMAT_RGB24: - return QImage::Format_RGB32; - case CAIRO_FORMAT_A8: - return QImage::Format_Indexed8; // XXX not quite - case CAIRO_FORMAT_A1: -#ifdef WORDS_BIGENDIAN - return QImage::Format_Mono; // XXX think we need to choose between this and LSB -#else - return QImage::Format_MonoLSB; -#endif - } - - return QImage::Format_Mono; -} - -static inline QMatrix -_qmatrix_from_cairo_matrix (const cairo_matrix_t& m) -{ - return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); -} - -/** Path conversion **/ -typedef struct _qpainter_path_transform { - QPainterPath path; - const cairo_matrix_t *ctm_inverse; -} qpainter_path_data; - -/* cairo path -> execute in context */ -static cairo_status_t -_cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point) -{ - qpainter_path_data *pdata = static_cast (closure); - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (pdata->ctm_inverse) - cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); - - pdata->path.moveTo(x, y); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point) -{ - qpainter_path_data *pdata = static_cast (closure); - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (pdata->ctm_inverse) - cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); - - pdata->path.lineTo(x, y); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) -{ - qpainter_path_data *pdata = static_cast (closure); - double x0 = _cairo_fixed_to_double (p0->x); - double y0 = _cairo_fixed_to_double (p0->y); - double x1 = _cairo_fixed_to_double (p1->x); - double y1 = _cairo_fixed_to_double (p1->y); - double x2 = _cairo_fixed_to_double (p2->x); - double y2 = _cairo_fixed_to_double (p2->y); - - if (pdata->ctm_inverse) { - cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0); - cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1); - cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2); - } - - pdata->path.cubicTo (x0, y0, x1, y1, x2, y2); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_qpainterpath_close_path (void *closure) -{ - qpainter_path_data *pdata = static_cast (closure); - - pdata->path.closeSubpath(); - - return CAIRO_STATUS_SUCCESS; -} - -static inline QPainterPath -path_to_qt (cairo_path_fixed_t *path, - const cairo_matrix_t *ctm_inverse = NULL) -{ - qpainter_path_data data; - cairo_status_t status; - - if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse)) - ctm_inverse = NULL; - data.ctm_inverse = ctm_inverse; - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_path_to_qpainterpath_move_to, - _cairo_path_to_qpainterpath_line_to, - _cairo_path_to_qpainterpath_curve_to, - _cairo_path_to_qpainterpath_close_path, - &data); - assert (status == CAIRO_STATUS_SUCCESS); - - return data.path; -} - -static inline QPainterPath -path_to_qt (cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_matrix_t *ctm_inverse = NULL) -{ - QPainterPath qpath = path_to_qt (path, ctm_inverse); - - qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ? - Qt::WindingFill : - Qt::OddEvenFill); - - return qpath; -} - -/** - ** Surface backend methods - **/ -static cairo_surface_t * -_cairo_qt_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - bool use_pixmap; - - D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content)); - - use_pixmap = qs->image == NULL; - if (use_pixmap) { - switch (content) { - case CAIRO_CONTENT_ALPHA: - use_pixmap = FALSE; - break; - case CAIRO_CONTENT_COLOR: - break; - case CAIRO_CONTENT_COLOR_ALPHA: - use_pixmap = ! _qpixmaps_have_no_alpha; - break; - } - } - - if (use_pixmap) { - cairo_surface_t *result = - cairo_qt_surface_create_with_qpixmap (content, width, height); - - /* XXX result->content is always content. ??? */ - if (result->content == content) { - D(fprintf(stderr, "qpixmap content: %d\n", content)); - return result; - } - - _qpixmaps_have_no_alpha = TRUE; - cairo_surface_destroy (result); - } - - D(fprintf (stderr, "qimage\n")); - return cairo_qt_surface_create_with_qimage - (_cairo_format_from_content (content), width, height); -} - -static cairo_status_t -_cairo_qt_surface_finish (void *abstract_surface) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] finish\n", abstract_surface)); - - /* Only delete p if we created it */ - if (qs->image || qs->pixmap) - delete qs->p; - else if (qs->p) - qs->p->restore (); - - if (qs->image_equiv) - cairo_surface_destroy (qs->image_equiv); - - _cairo_surface_clipper_reset (&qs->clipper); - - if (qs->image) - delete qs->image; - - if (qs->pixmap) - delete qs->pixmap; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_qimg_destroy (void *closure) -{ - QImage *qimg = (QImage *) closure; - delete qimg; -} - -static cairo_status_t -_cairo_qt_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface)); - - *image_extra = NULL; - - if (qs->image_equiv) { - *image_out = (cairo_image_surface_t*) - cairo_surface_reference (qs->image_equiv); - - return CAIRO_STATUS_SUCCESS; - } - - if (qs->pixmap) { - QImage *qimg = new QImage(qs->pixmap->toImage()); - cairo_surface_t *image; - cairo_status_t status; - - image = cairo_image_surface_create_for_data (qimg->bits(), - _cairo_format_from_qimage_format (qimg->format()), - qimg->width(), qimg->height(), - qimg->bytesPerLine()); - - status = _cairo_user_data_array_set_data (&image->user_data, - (const cairo_user_data_key_t *)&_qimg_destroy, - qimg, - _qimg_destroy); - if (status) { - cairo_surface_destroy (image); - return status; - } - - *image_out = (cairo_image_surface_t *) image; - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); -} - -static void -_cairo_qt_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface)); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_qt_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - QImage *qimg = NULL; - - D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface)); - - *image_extra = NULL; - - if (qs->image_equiv) { - *image_out = (cairo_image_surface_t*) - cairo_surface_reference (qs->image_equiv); - - image_rect->x = qs->window.x(); - image_rect->y = qs->window.y(); - image_rect->width = qs->window.width(); - image_rect->height = qs->window.height(); - - return CAIRO_STATUS_SUCCESS; - } - - QPoint offset; - - if (qs->pixmap) { - qimg = new QImage(qs->pixmap->toImage()); - } else { - // Try to figure out what kind of QPaintDevice we have, and - // how we can grab an image from it - QPaintDevice *pd = qs->p->device(); - if (!pd) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - QPaintDevice *rpd = QPainter::redirected(pd, &offset); - if (rpd) - pd = rpd; - - if (pd->devType() == QInternal::Image) { - qimg = new QImage(((QImage*) pd)->copy()); - } else if (pd->devType() == QInternal::Pixmap) { - qimg = new QImage(((QPixmap*) pd)->toImage()); - } - } - - if (qimg == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *image_out = (cairo_image_surface_t*) - cairo_image_surface_create_for_data (qimg->bits(), - _cairo_format_from_qimage_format (qimg->format()), - qimg->width(), qimg->height(), - qimg->bytesPerLine()); - *image_extra = qimg; - - image_rect->x = qs->window.x() + offset.x(); - image_rect->y = qs->window.y() + offset.y(); - image_rect->width = qs->window.width() - offset.x(); - image_rect->height = qs->window.height() - offset.y(); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_qt_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface)); - - cairo_surface_destroy (&image->base); - - if (image_extra) { - QImage *qimg = (QImage*) image_extra; - - // XXX should I be using setBackgroundMode here instead of setCompositionMode? - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_Source); - - qs->p->drawImage (image_rect->x, image_rect->y, *qimg); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - - delete qimg; - } -} - -static cairo_status_t -_cairo_qt_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - if (src->backend == qs->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - return CAIRO_STATUS_SUCCESS; - } - - return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_cairo_qt_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - extents->x = qs->window.x(); - extents->y = qs->window.y(); - extents->width = qs->window.width(); - extents->height = qs->window.height(); - - return TRUE; -} - -static cairo_status_t -_cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_qt_surface_t *qs = cairo_container_of (clipper, - cairo_qt_surface_t, - clipper); - - if (path == NULL) { - if (qs->pixmap || qs->image) { - // we own p - qs->p->setClipping (false); - } else { - qs->p->restore (); - qs->p->save (); - } - } else { - // XXX Antialiasing is ignored - qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip); - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, - cairo_region_t *clip_region) -{ - _cairo_surface_clipper_reset (&qs->clipper); - - if (clip_region == NULL) { - // How the clip path is reset depends on whether we own p or not - if (qs->pixmap || qs->image) { - // we own p - qs->p->setClipping (false); - } else { - qs->p->restore (); - qs->p->save (); - } - } else { - QRegion qr; - int num_rects = cairo_region_num_rectangles (clip_region); - for (int i = 0; i < num_rects; ++i) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, i, &rect); - - QRect r(rect.x, rect.y, rect.width, rect.height); - qr = qr.united(r); - } - - qs->p->setClipRegion (qr, Qt::IntersectClip); - } -} - -static cairo_int_status_t -_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, - cairo_clip_t *clip) -{ - cairo_int_status_t status; - - D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", qs, clip ? "(path)" : "(clear)")); - - if (clip == NULL) { - _cairo_surface_clipper_reset (&qs->clipper); - // How the clip path is reset depends on whether we own p or not - if (qs->pixmap || qs->image) { - // we own p - qs->p->setClipping (false); - } else { - qs->p->restore (); - qs->p->save (); - } - - return CAIRO_INT_STATUS_SUCCESS; - } - -#if ENABLE_FAST_CLIP - // Qt will implicitly enable clipping, and will use ReplaceClip - // instead of IntersectClip if clipping was disabled before - - // Note: Qt is really bad at dealing with clip paths. It doesn't - // seem to usefully recognize rectangular paths, instead going down - // extremely slow paths whenever a clip path is set. So, - // we do a bunch of work here to try to get rectangles or regions - // down to Qt for clipping. - - cairo_region_t *clip_region = NULL; - - status = _cairo_clip_get_region (clip, &clip_region); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - // We weren't able to extract a region from the traps. - // Just hand the path down to QPainter. - status = (cairo_int_status_t) - _cairo_surface_clipper_set_clip (&qs->clipper, clip); - } else if (status == CAIRO_INT_STATUS_SUCCESS) { - _cairo_qt_surface_set_clip_region (qs, clip_region); - status = CAIRO_INT_STATUS_SUCCESS; - } -#else - status = (cairo_int_status_t) - _cairo_surface_clipper_set_clip (&qs->clipper, clip); -#endif - - return status; -} - -/** - ** Brush conversion - **/ - -struct PatternToBrushConverter { - PatternToBrushConverter (const cairo_pattern_t *pattern) : - mAcquiredImageParent(0), - mAcquiredImage(0), - mAcquiredImageExtra(0) - { - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; - QColor color; - color.setRgbF(solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - mBrush = QBrush(color); - } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; - cairo_surface_t *surface = spattern->surface; - - if (surface->type == CAIRO_SURFACE_TYPE_QT) { - cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - - if (qs->image) { - mBrush = QBrush(*qs->image); - } else if (qs->pixmap) { - mBrush = QBrush(*qs->pixmap); - } else { - // do something smart - mBrush = QBrush(0xff0000ff); - } - } else { - cairo_image_surface_t *isurf = NULL; - - if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { - isurf = (cairo_image_surface_t*) surface; - } else { - void *image_extra; - - if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) { - mAcquiredImageParent = surface; - mAcquiredImage = isurf; - mAcquiredImageExtra = image_extra; - } else { - isurf = NULL; - } - } - - if (isurf) { - mBrush = QBrush (QImage ((const uchar *) isurf->data, - isurf->width, - isurf->height, - isurf->stride, - _qimage_format_from_cairo_format (isurf->format))); - } else { - mBrush = QBrush(0x0000ffff); - } - } - } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - { - QGradient *grad; - cairo_bool_t reverse_stops = FALSE; - cairo_bool_t emulate_reflect = FALSE; - double offset = 0.0; - - cairo_extend_t extend = pattern->extend; - - cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern; - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; - grad = new QLinearGradient (_cairo_fixed_to_double (lpat->p1.x), - _cairo_fixed_to_double (lpat->p1.y), - _cairo_fixed_to_double (lpat->p2.x), - _cairo_fixed_to_double (lpat->p2.y)); - } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern; - - /* Based on the SVG surface code */ - - cairo_point_t *c0, *c1; - cairo_fixed_t radius0, radius1; - - if (rpat->r1 < rpat->r2) { - c0 = &rpat->c1; - c1 = &rpat->c2; - radius0 = rpat->r1; - radius1 = rpat->r2; - reverse_stops = FALSE; - } else { - c0 = &rpat->c2; - c1 = &rpat->c1; - radius0 = rpat->r2; - radius1 = rpat->r1; - reverse_stops = TRUE; - } - - double x0 = _cairo_fixed_to_double (c0->x); - double y0 = _cairo_fixed_to_double (c0->y); - double r0 = _cairo_fixed_to_double (radius0); - double x1 = _cairo_fixed_to_double (c1->x); - double y1 = _cairo_fixed_to_double (c1->y); - double r1 = _cairo_fixed_to_double (radius1); - - if (rpat->r1 == rpat->r2) { - grad = new QRadialGradient (x1, y1, r1, x1, y1); - } else { - double fx = (r1 * x0 - r0 * x1) / (r1 - r0); - double fy = (r1 * y0 - r0 * y1) / (r1 - r0); - - /* QPainter doesn't support the inner circle and use instead a gradient focal. - * That means we need to emulate the cairo behaviour by processing the - * cairo gradient stops. - * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, - * it's just a matter of stop position translation and calculation of - * the corresponding SVG radial gradient focal. - * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new - * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT - * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop - * list that maps to the original cairo stop list. - */ - if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { - double r_org = r1; - double r, x, y; - - if (extend == CAIRO_EXTEND_REFLECT) { - r1 = 2 * r1 - r0; - emulate_reflect = TRUE; - } - - offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; - r = r1 - r0; - - /* New position of outer circle. */ - x = r * (x1 - fx) / r_org + fx; - y = r * (y1 - fy) / r_org + fy; - - x1 = x; - y1 = y; - r1 = r; - r0 = 0.0; - } else { - offset = r0 / r1; - } - - grad = new QRadialGradient (x1, y1, r1, fx, fy); - - if (extend == CAIRO_EXTEND_NONE && r0 != 0.0) - grad->setColorAt (r0 / r1, Qt::transparent); - } - } - - switch (extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_PAD: - grad->setSpread(QGradient::PadSpread); - - grad->setColorAt (0.0, Qt::transparent); - grad->setColorAt (1.0, Qt::transparent); - break; - - case CAIRO_EXTEND_REFLECT: - grad->setSpread(QGradient::ReflectSpread); - break; - - case CAIRO_EXTEND_REPEAT: - grad->setSpread(QGradient::RepeatSpread); - break; - } - - for (unsigned int i = 0; i < gpat->n_stops; i++) { - int index = i; - if (reverse_stops) - index = gpat->n_stops - i - 1; - - double offset = gpat->stops[i].offset; - QColor color; - color.setRgbF (gpat->stops[i].color.red, - gpat->stops[i].color.green, - gpat->stops[i].color.blue, - gpat->stops[i].color.alpha); - - if (emulate_reflect) { - offset = offset / 2.0; - grad->setColorAt (1.0 - offset, color); - } - - grad->setColorAt (offset, color); - } - - mBrush = QBrush(*grad); - - delete grad; - } - - if (mBrush.style() != Qt::NoBrush && - pattern->type != CAIRO_PATTERN_TYPE_SOLID && - ! _cairo_matrix_is_identity (&pattern->matrix)) - { - cairo_matrix_t pm = pattern->matrix; - cairo_status_t status = cairo_matrix_invert (&pm); - assert (status == CAIRO_STATUS_SUCCESS); - mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm)); - } - } - - ~PatternToBrushConverter () { - if (mAcquiredImageParent) - _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra); - } - - operator QBrush& () { - return mBrush; - } - - QBrush mBrush; - - private: - cairo_surface_t *mAcquiredImageParent; - cairo_image_surface_t *mAcquiredImage; - void *mAcquiredImageExtra; -}; - -struct PatternToPenConverter { - PatternToPenConverter (const cairo_pattern_t *source, - const cairo_stroke_style_t *style) : - mBrushConverter(source) - { - Qt::PenJoinStyle join = Qt::MiterJoin; - Qt::PenCapStyle cap = Qt::SquareCap; - - switch (style->line_cap) { - case CAIRO_LINE_CAP_BUTT: - cap = Qt::FlatCap; - break; - case CAIRO_LINE_CAP_ROUND: - cap = Qt::RoundCap; - break; - case CAIRO_LINE_CAP_SQUARE: - cap = Qt::SquareCap; - break; - } - - switch (style->line_join) { - case CAIRO_LINE_JOIN_MITER: - join = Qt::MiterJoin; - break; - case CAIRO_LINE_JOIN_ROUND: - join = Qt::RoundJoin; - break; - case CAIRO_LINE_JOIN_BEVEL: - join = Qt::BevelJoin; - break; - } - - mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join); - mPen.setMiterLimit (style->miter_limit); - - if (style->dash && style->num_dashes) { - Qt::PenStyle pstyle = Qt::NoPen; - - if (style->num_dashes == 2) { - if ((style->dash[0] == style->line_width && - style->dash[1] == style->line_width && style->line_width <= 2.0) || - (style->dash[0] == 0.0 && - style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap)) - { - pstyle = Qt::DotLine; - } else if (style->dash[0] == style->line_width * DASH_LENGTH && - style->dash[1] == style->line_width * DASH_LENGTH && - cap == Qt::FlatCap) - { - pstyle = Qt::DashLine; - } - } - - if (pstyle != Qt::NoPen) { - mPen.setStyle(pstyle); - return; - } - - unsigned int odd_dash = style->num_dashes % 2; - - QVector dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes); - for (unsigned int i = 0; i < odd_dash+1; i++) { - for (unsigned int j = 0; j < style->num_dashes; j++) { - // In Qt, the dash lengths are given in units of line width, whereas - // in cairo, they are in user-space units. We'll always apply the CTM, - // so all we have to do here is divide cairo's dash lengths by the line - // width. - dashes.append (style->dash[j] / style->line_width); - } - } - - mPen.setDashPattern(dashes); - mPen.setDashOffset(style->dash_offset / style->line_width); - } - } - - ~PatternToPenConverter() { } - - operator QPen& () { - return mPen; - } - - QPen mPen; - PatternToBrushConverter mBrushConverter; -}; - -/** - ** Core drawing operations - **/ - -static bool -_cairo_qt_fast_fill (cairo_qt_surface_t *qs, - const cairo_pattern_t *source, - cairo_path_fixed_t *path = NULL, - cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING, - double tolerance = 0.0, - cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE) -{ -#if ENABLE_FAST_FILL - QImage *qsSrc_image = NULL; - QPixmap *qsSrc_pixmap = NULL; - std::auto_ptr qsSrc_image_d; - - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source; - if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) { - cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface; - - qsSrc_image = p->image; - qsSrc_pixmap = p->pixmap; - } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { - cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface; - qsSrc_image = new QImage((const uchar*) p->data, - p->width, - p->height, - p->stride, - _qimage_format_from_cairo_format(p->format)); - qsSrc_image_d.reset(qsSrc_image); - } - } - - if (!qsSrc_image && !qsSrc_pixmap) - return false; - - // We can only drawTiledPixmap; there's no drawTiledImage - if (! qsSrc_pixmap && - (source->extend == CAIRO_EXTEND_REPEAT || - source->extend == CAIRO_EXTEND_REFLECT)) - { - return false; - } - - QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix); - - // We can draw this faster by clipping and calling drawImage/drawPixmap. - // Use our own clipping function so that we can get the - // region handling to end up with the fastest possible clip. - // - // XXX Antialiasing will fail pretty hard here, since we can't clip with AA - // with QPainter. - qs->p->save(); - - if (path) { - cairo_int_status_t status; - - cairo_clip_t clip, old_clip = qs->clipper.clip; - - _cairo_clip_init_copy (&clip, &qs->clipper.clip); - status = (cairo_int_status_t) _cairo_clip_clip (&clip, - path, - fill_rule, - tolerance, - antialias); - if (unlikely (status)) { - qs->p->restore(); - return false; - } - - status = _cairo_qt_surface_set_clip (qs, &clip); - if (unlikely (status)) { - qs->p->restore(); - return false; - } - - _cairo_clip_reset (&clip); - qs->clipper.clip = old_clip; - } - - qs->p->setWorldMatrix (sourceMatrix.inverted(), true); - - switch (source->extend) { - case CAIRO_EXTEND_REPEAT: - // XXX handle reflect by tiling 4 times first - case CAIRO_EXTEND_REFLECT: { - assert (qsSrc_pixmap); - - // Render the tiling to cover the entire destination window (because - // it'll be clipped). Transform the window rect by the inverse - // of the current world transform so that the device coordinates - // end up as the right thing. - QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window)); - QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0)); - - qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin); - } - break; - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough - default: - if (qsSrc_image) - qs->p->drawImage (0, 0, *qsSrc_image); - else if (qsSrc_pixmap) - qs->p->drawPixmap (0, 0, *qsSrc_pixmap); - break; - } - - qs->p->restore(); - - return true; -#else - return false; -#endif -} - -static cairo_int_status_t -_cairo_qt_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - cairo_int_status_t status; - - D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op))); - - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_qt_surface_set_clip (qs, clip); - if (unlikely (status)) - return status; - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - if (! _cairo_qt_fast_fill (qs, source)) { - PatternToBrushConverter brush (source); - qs->p->fillRect (qs->window, brush); - } - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_qt_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op))); - - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip); - if (unlikely (status)) - return status; - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is - // enabled - //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); - qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); - - if (! _cairo_qt_fast_fill (qs, source, - path, fill_rule, tolerance, antialias)) - { - PatternToBrushConverter brush(source); - qs->p->fillPath (path_to_qt (path, fill_rule), brush); - } - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_qt_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op))); - - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip); - if (unlikely (int_status)) - return int_status; - - - QMatrix savedMatrix = qs->p->worldMatrix(); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true); - // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is - // enabled - //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); - qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); - - PatternToPenConverter pen(source, style); - - qs->p->setPen(pen); - qs->p->drawPath(path_to_qt (path, ctm_inverse)); - qs->p->setPen(Qt::black); - - qs->p->setWorldMatrix (savedMatrix, false); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_qt_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_qt_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op))); - - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; - cairo_int_status_t result; - - qs->p->setOpacity (solid_mask->color.alpha); - - result = _cairo_qt_surface_paint (abstract_surface, op, source, clip); - - qs->p->setOpacity (1.0); - - return result; - } - - // otherwise skip for now - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_qt_surface_composite (cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_surface, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - if (mask_pattern) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_qt_surface_set_clip_region (qs, clip_region); - - D(fprintf(stderr, "q[%p] composite op:%s src:%p [%d %d] dst [%d %d] dim [%d %d]\n", - abstract_surface, _opstr(op), (void*)pattern, - src_x, src_y, dst_x, dst_y, width, height)); - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; - - QColor color; - color.setRgbF(solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - qs->p->fillRect (dst_x, dst_y, width, height, color); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; - cairo_surface_t *surface = spattern->surface; - - QImage *qimg = NULL; - QPixmap *qpixmap = NULL; - std::auto_ptr qimg_d; - - if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { - cairo_image_surface_t *isurf = (cairo_image_surface_t*) surface; - qimg = new QImage ((const uchar *) isurf->data, - isurf->width, - isurf->height, - isurf->stride, - _qimage_format_from_cairo_format (isurf->format)); - qimg_d.reset(qimg); - } - - if (surface->type == CAIRO_SURFACE_TYPE_QT) { - cairo_qt_surface_t *qsrc = (cairo_qt_surface_t*) surface; - - if (qsrc->image) - qimg = qsrc->image; - else if (qsrc->pixmap) - qpixmap = qsrc->pixmap; - } - - if (!qimg && !qpixmap) - return CAIRO_INT_STATUS_UNSUPPORTED; - - QMatrix savedMatrix = qs->p->worldMatrix(); - if (! _cairo_matrix_is_identity (&pattern->matrix)) { - cairo_matrix_t pm = pattern->matrix; - cairo_status_t status; - - status = cairo_matrix_invert (&pm); - assert (status == CAIRO_STATUS_SUCCESS); - qs->p->setWorldMatrix(_qmatrix_from_cairo_matrix (pm), true); - } - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - if (qimg) - qs->p->drawImage (dst_x, dst_y, *qimg, src_x, src_y, width, height); - else if (qpixmap) - qs->p->drawPixmap (dst_x, dst_y, *qpixmap, src_x, src_y, width, height); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_qt_surface_mark_dirty (void *abstract_surface, - int x, int y, - int width, int height) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - if (qs->p && !(qs->image || qs->pixmap)) - qs->p->save (); - - return CAIRO_STATUS_SUCCESS; -} - -/** - ** Backend struct - **/ - -static const cairo_surface_backend_t cairo_qt_surface_backend = { - CAIRO_SURFACE_TYPE_QT, - _cairo_qt_surface_create_similar, - _cairo_qt_surface_finish, - _cairo_qt_surface_acquire_source_image, - _cairo_qt_surface_release_source_image, - _cairo_qt_surface_acquire_dest_image, - _cairo_qt_surface_release_dest_image, - _cairo_qt_surface_clone_similar, - - _cairo_qt_surface_composite, - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_qt_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - _cairo_qt_surface_mark_dirty, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_qt_surface_paint, - _cairo_qt_surface_mask, - _cairo_qt_surface_stroke, - _cairo_qt_surface_fill, - _cairo_qt_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - NULL, /* has_show_text_glyphs */ - NULL, /* show_text_glyphs */ -}; - -cairo_surface_t * -cairo_qt_surface_create (QPainter *painter) -{ - cairo_qt_surface_t *qs; - - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); - if (qs == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - memset (qs, 0, sizeof(cairo_qt_surface_t)); - - _cairo_surface_init (&qs->base, - &cairo_qt_surface_backend, - NULL, - CAIRO_CONTENT_COLOR_ALPHA); - - _cairo_surface_clipper_init (&qs->clipper, - _cairo_qt_surface_clipper_intersect_clip_path); - - qs->p = painter; - if (qs->p->paintEngine()) - qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); - else - qs->supports_porter_duff = FALSE; - - // Save so that we can always get back to the original state - qs->p->save(); - - qs->window = painter->window(); - - D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n", - qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), - qs->supports_porter_duff)); - - return &qs->base; -} - -cairo_surface_t * -cairo_qt_surface_create_with_qimage (cairo_format_t format, - int width, - int height) -{ - cairo_qt_surface_t *qs; - - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); - if (qs == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - memset (qs, 0, sizeof(cairo_qt_surface_t)); - - _cairo_surface_init (&qs->base, - &cairo_qt_surface_backend, - NULL, - _cairo_content_from_format (format)); - - _cairo_surface_clipper_init (&qs->clipper, - _cairo_qt_surface_clipper_intersect_clip_path); - - if (CAIRO_FORMAT_A8 == format) { - qs->image = NULL; - qs->image_equiv = cairo_image_surface_create(format, - width, height); - qs->p = NULL; - qs->supports_porter_duff = false; - qs->window = QRect(0, 0, width, height); - return &qs->base; - } - - QImage *image = new QImage (width, height, - _qimage_format_from_cairo_format (format)); - - qs->image = image; - - if (!image->isNull()) { - qs->p = new QPainter(image); - qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); - } - - qs->image_equiv = cairo_image_surface_create_for_data (image->bits(), - format, - width, height, - image->bytesPerLine()); - - qs->window = QRect(0, 0, width, height); - - D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n", - qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), - qs->supports_porter_duff)); - - return &qs->base; -} - -cairo_surface_t * -cairo_qt_surface_create_with_qpixmap (cairo_content_t content, - int width, - int height) -{ - cairo_qt_surface_t *qs; - - if ((content & CAIRO_CONTENT_COLOR) == 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); - if (qs == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - memset (qs, 0, sizeof(cairo_qt_surface_t)); - - QPixmap *pixmap = new QPixmap (width, height); - if (pixmap == NULL) { - free (qs); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - // By default, a QPixmap is opaque; however, if it's filled - // with a color with a transparency component, it is converted - // to a format that preserves transparency. - if (content == CAIRO_CONTENT_COLOR_ALPHA) - pixmap->fill(Qt::transparent); - - _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, NULL, content); - - _cairo_surface_clipper_init (&qs->clipper, - _cairo_qt_surface_clipper_intersect_clip_path); - - qs->pixmap = pixmap; - - if (!pixmap->isNull()) { - qs->p = new QPainter(pixmap); - qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); - } - - qs->window = QRect(0, 0, width, height); - - D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n", - qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), - qs->supports_porter_duff)); - - return &qs->base; -} - -QPainter * -cairo_qt_surface_get_qpainter (cairo_surface_t *surface) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - - if (surface->type != CAIRO_SURFACE_TYPE_QT) - return NULL; - - return qs->p; -} - -QImage * -cairo_qt_surface_get_qimage (cairo_surface_t *surface) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - - if (surface->type != CAIRO_SURFACE_TYPE_QT) - return NULL; - - return qs->image; -} - -cairo_surface_t * -cairo_qt_surface_get_image (cairo_surface_t *surface) -{ - cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - - if (surface->type != CAIRO_SURFACE_TYPE_QT) - return NULL; - - return qs->image_equiv; -} - -/* - * TODO: - * - * - Figure out why QBrush isn't working with non-repeated images - * - * - Correct repeat mode; right now, every surface source is ExtendMode::REPEAT - * - implement EXTEND_NONE (?? probably need to clip to the extents of the source) - * - implement ExtendMode::REFLECT (create temporary and copy 4x, then ExtendMode::REPEAT that) - * - * - stroke-image failure - * - * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN) - * - * - Implement gradient sources - * - * - Make create_similar smarter -- create QPixmaps in more circumstances - * (e.g. if the pixmap can have alpha) - * - * - Implement show_glyphs() in terms of Qt - * - */ diff --git a/libs/cairo/cairo/src/cairo-qt.h b/libs/cairo/cairo/src/cairo-qt.h deleted file mode 100644 index 9bb6cd0e3..000000000 --- a/libs/cairo/cairo/src/cairo-qt.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_QT_H -#define CAIRO_QT_H - -#include "cairo.h" - -#if CAIRO_HAS_QT_SURFACE - -#include -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_qt_surface_create (QPainter *painter); - -cairo_public cairo_surface_t * -cairo_qt_surface_create_with_qimage (cairo_format_t format, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_qt_surface_create_with_qpixmap (cairo_content_t content, - int width, - int height); - -cairo_public QPainter * -cairo_qt_surface_get_qpainter (cairo_surface_t *surface); - -/* XXX needs hooking to generic surface layer, my vote is for -cairo_public cairo_surface_t * -cairo_surface_map_image (cairo_surface_t *surface); -cairo_public void -cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image); -*/ -cairo_public cairo_surface_t * -cairo_qt_surface_get_image (cairo_surface_t *surface); - -cairo_public QImage * -cairo_qt_surface_get_qimage (cairo_surface_t *surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_QT_SURFACE */ - -# error Cairo was not compiled with support for the Qt backend - -#endif /* CAIRO_HAS_QT_SURFACE */ - -#endif /* CAIRO_QT_H */ diff --git a/libs/cairo/cairo/src/cairo-quartz-font.c b/libs/cairo/cairo/src/cairo-quartz-font.c deleted file mode 100644 index e58ae0559..000000000 --- a/libs/cairo/cairo/src/cairo-quartz-font.c +++ /dev/null @@ -1,811 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include - -#include "cairo-quartz.h" -#include "cairo-quartz-private.h" - -#include "cairo-error-private.h" - -/** - * SECTION:cairo-quartz-fonts - * @Title: Quartz (CGFont) Fonts - * @Short_Description: Font support via CGFont on OS X - * @See_Also: #cairo_font_face_t - * - * The Quartz font backend is primarily used to render text on Apple - * MacOS X systems. The CGFont API is used for the internal - * implementation of the font backend methods. - */ - -/** - * CAIRO_HAS_QUARTZ_FONT: - * - * Defined if the Quartz font backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */ -static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL; -static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL; - -/* These aren't public before 10.5, and some have different names in 10.4 */ -static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL; -static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL; -static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL; -static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL; - -/* Not public, but present */ -static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; -static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - -/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ -typedef struct { - int ascent; - int descent; - int leading; -} quartz_CGFontMetrics; -static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; - -/* CTFontCreateWithGraphicsFont is not public until 10.5. */ -typedef const struct __CTFontDescriptor *CTFontDescriptorRef; -static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL; - -static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; -static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; - -static void -quartz_font_ensure_symbols(void) -{ - if (_cairo_quartz_font_symbol_lookup_done) - return; - - /* Look for the 10.5 versions first */ - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); - if (!CGFontGetGlyphBBoxesPtr) - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); - - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); - if (!CGFontGetGlyphsForUnicharsPtr) - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); - - CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); - - /* We just need one of these two */ - CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); - CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); - - /* These have the same name in 10.4 and 10.5 */ - CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); - CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); - - CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); - CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); - CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); - CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); - - CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - - CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); - - if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && - CGFontGetGlyphBBoxesPtr && - CGFontGetGlyphsForUnicharsPtr && - CGFontGetUnitsPerEmPtr && - CGFontGetGlyphAdvancesPtr && - (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) - _cairo_quartz_font_symbols_present = TRUE; - - _cairo_quartz_font_symbol_lookup_done = TRUE; -} - -typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t; -typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; - -struct _cairo_quartz_scaled_font { - cairo_scaled_font_t base; -}; - -struct _cairo_quartz_font_face { - cairo_font_face_t base; - - CGFontRef cgFont; - CTFontRef ctFont; -}; - -/* - * font face backend - */ - -static cairo_status_t -_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - const char *family; - char *full_name; - CFStringRef cgFontName = NULL; - CGFontRef cgFont = NULL; - int loop; - - quartz_font_ensure_symbols(); - if (! _cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - family = toy_face->family; - full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. - /* handle CSS-ish faces */ - if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) - family = "Times"; - else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) - family = "Helvetica"; - else if (!strcmp(family, "cursive")) - family = "Apple Chancery"; - else if (!strcmp(family, "fantasy")) - family = "Papyrus"; - else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) - family = "Courier"; - - /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, - * then drop the bold, then drop the slant, then drop both.. finally - * just use "Helvetica". And if Helvetica doesn't exist, give up. - */ - for (loop = 0; loop < 5; loop++) { - if (loop == 4) - family = "Helvetica"; - - strcpy (full_name, family); - - if (loop < 3 && (loop & 1) == 0) { - if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) - strcat (full_name, " Bold"); - } - - if (loop < 3 && (loop & 2) == 0) { - if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) - strcat (full_name, " Italic"); - else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) - strcat (full_name, " Oblique"); - } - - if (CGFontCreateWithFontNamePtr) { - cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - cgFont = CGFontCreateWithFontNamePtr (cgFontName); - CFRelease (cgFontName); - } else { - cgFont = CGFontCreateWithNamePtr (full_name); - } - - if (cgFont) - break; - } - - if (!cgFont) { - /* Give up */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); - CGFontRelease (cgFont); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_quartz_font_face_destroy (void *abstract_face) -{ - cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; - - if (font_face->ctFont) { - CFRelease (font_face->ctFont); - } - - CGFontRelease (font_face->cgFont); -} - -static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; - -static cairo_status_t -_cairo_quartz_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font_out) -{ - cairo_quartz_font_face_t *font_face = abstract_face; - cairo_quartz_scaled_font_t *font = NULL; - cairo_status_t status; - cairo_font_extents_t fs_metrics; - double ems; - CGRect bbox; - - quartz_font_ensure_symbols(); - if (!_cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font = malloc(sizeof(cairo_quartz_scaled_font_t)); - if (font == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memset (font, 0, sizeof(cairo_quartz_scaled_font_t)); - - status = _cairo_scaled_font_init (&font->base, - &font_face->base, font_matrix, ctm, options, - &_cairo_quartz_scaled_font_backend); - if (status) - goto FINISH; - - ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); - - /* initialize metrics */ - if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { - fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); - fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + - (CGFontGetLeadingPtr (font_face->cgFont) / ems); - - bbox = CGFontGetFontBBoxPtr (font_face->cgFont); - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - CGGlyph wGlyph; - UniChar u; - - quartz_CGFontMetrics *m; - m = CGFontGetHMetricsPtr (font_face->cgFont); - - /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ - if (!m) { - status = _cairo_error(CAIRO_STATUS_NULL_POINTER); - goto FINISH; - } - - fs_metrics.ascent = (m->ascent / ems); - fs_metrics.descent = - (m->descent / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); - - /* We kind of have to guess here; W's big, right? */ - u = (UniChar) 'W'; - CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); - if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - fs_metrics.max_x_advance = 0.0; - fs_metrics.max_y_advance = 0.0; - } - } - - status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); - -FINISH: - if (status != CAIRO_STATUS_SUCCESS) { - free (font); - } else { - *font_out = (cairo_scaled_font_t*) font; - } - - return status; -} - -const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { - CAIRO_FONT_TYPE_QUARTZ, - _cairo_quartz_font_face_create_for_toy, - _cairo_quartz_font_face_destroy, - _cairo_quartz_font_face_scaled_font_create -}; - -/** - * cairo_quartz_font_face_create_for_cgfont - * @font: a #CGFontRef obtained through a method external to cairo. - * - * Creates a new font for the Quartz font backend based on a - * #CGFontRef. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.6 - */ -cairo_font_face_t * -cairo_quartz_font_face_create_for_cgfont (CGFontRef font) -{ - cairo_quartz_font_face_t *font_face; - - quartz_font_ensure_symbols(); - - font_face = malloc (sizeof (cairo_quartz_font_face_t)); - if (!font_face) { - cairo_status_t ignore_status; - ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } - - font_face->cgFont = CGFontRetain (font); - - if (CTFontCreateWithGraphicsFontPtr) { - font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL); - } else { - font_face->ctFont = NULL; - } - - _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); - - return &font_face->base; -} - -/* - * scaled font backend - */ - -static cairo_quartz_font_face_t * -_cairo_quartz_scaled_to_face (void *abstract_font) -{ - cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font; - cairo_font_face_t *font_face = sfont->base.font_face; - assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ); - return (cairo_quartz_font_face_t*) font_face; -} - -static void -_cairo_quartz_scaled_font_fini(void *abstract_font) -{ -} - -#define INVALID_GLYPH 0x00 - -static inline CGGlyph -_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) { - unsigned long index = _cairo_scaled_glyph_index (scaled_glyph); - if (index > 0xffff) - return INVALID_GLYPH; - return (CGGlyph) index; -} - -static cairo_int_status_t -_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); - cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; - CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - int advance; - CGRect bbox; - double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); - double xscale, yscale; - double xmin, ymin, xmax, ymax; - - if (glyph == INVALID_GLYPH) - goto FAIL; - - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) - goto FAIL; - - /* broken fonts like Al Bayan return incorrect bounds for some null characters, - see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ - if (unlikely (bbox.origin.x == -32767 && - bbox.origin.y == -32767 && - bbox.size.width == 65534 && - bbox.size.height == 65534)) { - bbox.origin.x = bbox.origin.y = 0; - bbox.size.width = bbox.size.height = 0; - } - - status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale, - &xscale, &yscale, 1); - if (status) - goto FAIL; - - bbox = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - /* Should we want to always integer-align glyph extents, we can do so in this way */ -#if 0 - { - CGAffineTransform textMatrix; - textMatrix = CGAffineTransformMake (font->base.scale.xx, - -font->base.scale.yx, - -font->base.scale.xy, - font->base.scale.yy, - 0.0f, 0.0f); - - bbox = CGRectApplyAffineTransform (bbox, textMatrix); - bbox = CGRectIntegral (bbox); - bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); - } -#endif - -#if 0 - fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph, - bbox.origin.x / emscale, bbox.origin.y / emscale, - bbox.size.width / emscale, bbox.size.height / emscale); -#endif - - xmin = CGRectGetMinX(bbox); - ymin = CGRectGetMinY(bbox); - xmax = CGRectGetMaxX(bbox); - ymax = CGRectGetMaxY(bbox); - - extents.x_bearing = xmin; - extents.y_bearing = - ymax; - extents.width = xmax - xmin; - extents.height = ymax - ymin; - - extents.x_advance = (double) advance / emscale; - extents.y_advance = 0.0; - -#if 0 - fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, - extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); -#endif - - FAIL: - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &font->base, - &extents); - - return status; -} - -static void -_cairo_quartz_path_apply_func (void *info, const CGPathElement *el) -{ - cairo_path_fixed_t *path = (cairo_path_fixed_t *) info; - cairo_status_t status; - - switch (el->type) { - case kCGPathElementMoveToPoint: - status = _cairo_path_fixed_move_to (path, - _cairo_fixed_from_double(el->points[0].x), - _cairo_fixed_from_double(el->points[0].y)); - assert(!status); - break; - case kCGPathElementAddLineToPoint: - status = _cairo_path_fixed_line_to (path, - _cairo_fixed_from_double(el->points[0].x), - _cairo_fixed_from_double(el->points[0].y)); - assert(!status); - break; - case kCGPathElementAddQuadCurveToPoint: { - cairo_fixed_t fx, fy; - double x, y; - if (!_cairo_path_fixed_get_current_point (path, &fx, &fy)) - fx = fy = 0; - x = _cairo_fixed_to_double (fx); - y = _cairo_fixed_to_double (fy); - - status = _cairo_path_fixed_curve_to (path, - _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0), - _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0), - _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0), - _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0), - _cairo_fixed_from_double(el->points[1].x), - _cairo_fixed_from_double(el->points[1].y)); - } - assert(!status); - break; - case kCGPathElementAddCurveToPoint: - status = _cairo_path_fixed_curve_to (path, - _cairo_fixed_from_double(el->points[0].x), - _cairo_fixed_from_double(el->points[0].y), - _cairo_fixed_from_double(el->points[1].x), - _cairo_fixed_from_double(el->points[1].y), - _cairo_fixed_from_double(el->points[2].x), - _cairo_fixed_from_double(el->points[2].y)); - assert(!status); - break; - case kCGPathElementCloseSubpath: - status = _cairo_path_fixed_close_path (path); - assert(!status); - break; - } -} - -static cairo_int_status_t -_cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); - CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - CGAffineTransform textMatrix; - CGPathRef glyphPath; - CTFontRef ctFont; - cairo_path_fixed_t *path; - - if (glyph == INVALID_GLYPH) { - _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); - return CAIRO_STATUS_SUCCESS; - } - - /* scale(1,-1) * font->base.scale */ - textMatrix = CGAffineTransformMake (font->base.scale.xx, - font->base.scale.yx, - -font->base.scale.xy, - -font->base.scale.yy, - 0, 0); - - ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); - CFRelease (ctFont); - if (!glyphPath) - return CAIRO_INT_STATUS_UNSUPPORTED; - - path = _cairo_path_fixed_create (); - if (!path) { - CGPathRelease (glyphPath); - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - } - - CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func); - - CGPathRelease (glyphPath); - - _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); - - cairo_image_surface_t *surface = NULL; - - CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - - int advance; - CGRect bbox; - double width, height; - double xscale, yscale; - double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); - - CGContextRef cgContext = NULL; - CGAffineTransform textMatrix; - CGRect glyphRect, glyphRectInt; - CGPoint glyphOrigin; - - //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); - - /* Create blank 2x2 image if we don't have this character. - * Maybe we should draw a better missing-glyph slug or something, - * but this is ok for now. - */ - if (glyph == INVALID_GLYPH) { - surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2); - status = cairo_surface_status ((cairo_surface_t *) surface); - if (status) - return status; - - _cairo_scaled_glyph_set_surface (scaled_glyph, - &font->base, - surface); - return CAIRO_STATUS_SUCCESS; - } - - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale, - &xscale, &yscale, 1); - if (status) - return status; - - /* scale(1,-1) * font->base.scale * scale(1,-1) */ - textMatrix = CGAffineTransformMake (font->base.scale.xx, - -font->base.scale.yx, - -font->base.scale.xy, - font->base.scale.yy, - 0, -0); - glyphRect = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); - - /* Round the rectangle outwards, so that we don't have to deal - * with non-integer-pixel origins or dimensions. - */ - glyphRectInt = CGRectIntegral (glyphRect); - -#if 0 - fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", - glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); - fprintf (stderr, "glyphRectInt: %f %f %f %f\n", - glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height); -#endif - - glyphOrigin = glyphRectInt.origin; - - //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); - - width = glyphRectInt.size.width; - height = glyphRectInt.size.height; - - //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); - - surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - if (surface->base.status) - return surface->base.status; - - if (surface->width != 0 && surface->height != 0) { - cgContext = CGBitmapContextCreate (surface->data, - surface->width, - surface->height, - 8, - surface->stride, - NULL, - kCGImageAlphaOnly); - - if (cgContext == NULL) { - cairo_surface_destroy (&surface->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - CGContextSetFont (cgContext, font_face->cgFont); - CGContextSetFontSize (cgContext, 1.0); - CGContextSetTextMatrix (cgContext, textMatrix); - - switch (font->base.options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, TRUE); - if (CGContextSetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (cgContext)) - CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE); - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - default: - /* Don't do anything */ - break; - } - - CGContextSetAlpha (cgContext, 1.0); - CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); - - CGContextRelease (cgContext); - } - - cairo_surface_set_device_offset (&surface->base, - - glyphOrigin.x, - height + glyphOrigin.y); - - _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); - - return status; -} - -static cairo_int_status_t -_cairo_quartz_scaled_glyph_init (void *abstract_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) -{ - cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font; - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS)) - status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph); - - if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) - status = _cairo_quartz_init_glyph_path (font, scaled_glyph); - - if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) - status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); - - return status; -} - -static unsigned long -_cairo_quartz_ucs4_to_index (void *abstract_font, - uint32_t ucs4) -{ - cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); - UniChar u = (UniChar) ucs4; - CGGlyph glyph; - - CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1); - - return glyph; -} - -static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { - CAIRO_FONT_TYPE_QUARTZ, - _cairo_quartz_scaled_font_fini, - _cairo_quartz_scaled_glyph_init, - NULL, /* text_to_glyphs */ - _cairo_quartz_ucs4_to_index, - NULL, /* show_glyphs */ - NULL, /* load_truetype_table */ - NULL, /* map_glyphs_to_unicode */ -}; - -/* - * private methods that the quartz surface uses - */ - -CGFontRef -_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) -{ - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); - - return ffont->cgFont; -} - -CTFontRef -_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font) -{ - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); - - return ffont->ctFont; -} - -#if !defined(__LP64__) && !TARGET_OS_IPHONE -/* - * compat with old ATSUI backend - */ - -/** - * cairo_quartz_font_face_create_for_atsu_font_id - * @font_id: an ATSUFontID for the font. - * - * Creates a new font for the Quartz font backend based on an - * #ATSUFontID. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.6 - **/ -cairo_font_face_t * -cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id) -{ - ATSFontRef atsFont = FMGetATSFontRefFromFont (font_id); - CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); - cairo_font_face_t *ff; - - ff = cairo_quartz_font_face_create_for_cgfont (cgFont); - - CGFontRelease (cgFont); - - return ff; -} - -/* This is the old name for the above function, exported for compat purposes */ -cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id); - -cairo_font_face_t * -cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id) -{ - return cairo_quartz_font_face_create_for_atsu_font_id (font_id); -} -#endif diff --git a/libs/cairo/cairo/src/cairo-quartz-image-surface.c b/libs/cairo/cairo/src/cairo-quartz-image-surface.c deleted file mode 100644 index 155ec494f..000000000 --- a/libs/cairo/cairo/src/cairo-quartz-image-surface.c +++ /dev/null @@ -1,258 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-quartz-image.h" -#include "cairo-quartz-private.h" - -#include "cairo-error-private.h" - -#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY))) -#define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH))) -#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE))) -#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) - -static void -DataProviderReleaseCallback (void *info, const void *data, size_t size) -{ - cairo_surface_t *surface = (cairo_surface_t *) info; - cairo_surface_destroy (surface); -} - -static cairo_surface_t * -_cairo_quartz_image_surface_create_similar (void *asurface, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *result; - cairo_surface_t *isurf = - _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status(isurf)) - return isurf; - - result = cairo_quartz_image_surface_create (isurf); - cairo_surface_destroy (isurf); - - return result; -} - -static cairo_status_t -_cairo_quartz_image_surface_finish (void *asurface) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - - /* the imageSurface will be destroyed by the data provider's release callback */ - CGImageRelease (surface->image); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_quartz_image_surface_acquire_source_image (void *asurface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - - *image_out = surface->imageSurface; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_quartz_image_surface_acquire_dest_image (void *asurface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - - *image_out = surface->imageSurface; - *image_rect = surface->extents; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_quartz_image_surface_get_extents (void *asurface, - cairo_rectangle_int_t *extents) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - - *extents = surface->extents; - return TRUE; -} - -/* we assume some drawing happened to the image buffer; make sure it's - * represented in the CGImage on flush() - */ - -static cairo_status_t -_cairo_quartz_image_surface_flush (void *asurface) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - CGImageRef oldImage = surface->image; - CGImageRef newImage = NULL; - - /* To be released by the ReleaseCallback */ - cairo_surface_reference ((cairo_surface_t*) surface->imageSurface); - - newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format, - surface->imageSurface->width, - surface->imageSurface->height, - surface->imageSurface->stride, - surface->imageSurface->data, - TRUE, - NULL, - DataProviderReleaseCallback, - surface->imageSurface); - - surface->image = newImage; - CGImageRelease (oldImage); - - surface->base.is_clear = surface->imageSurface->base.is_clear; - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { - CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, - _cairo_quartz_image_surface_create_similar, - _cairo_quartz_image_surface_finish, - _cairo_quartz_image_surface_acquire_source_image, - NULL, /* release_source_image */ - _cairo_quartz_image_surface_acquire_dest_image, - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_quartz_image_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - _cairo_quartz_image_surface_flush, - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* surface_show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL /* fill_stroke */ - -}; - -/** - * cairo_quartz_image_surface_create - * @surface: a cairo image surface to wrap with a quartz image surface - * - * Creates a Quartz surface backed by a CGImageRef that references the - * given image surface. The resulting surface can be rendered quickly - * when used as a source when rendering to a #cairo_quartz_surface. If - * the data in the image surface is ever updated, cairo_surface_flush() - * must be called on the #cairo_quartz_image_surface to ensure that the - * CGImageRef refers to the updated data. - * - * Return value: the newly created surface. - * - * Since: 1.6 - */ -cairo_surface_t * -cairo_quartz_image_surface_create (cairo_surface_t *surface) -{ - cairo_quartz_image_surface_t *qisurf; - - CGImageRef image; - - cairo_image_surface_t *image_surface; - int width, height, stride; - cairo_format_t format; - unsigned char *data; - - if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) - return SURFACE_ERROR_TYPE_MISMATCH; - - image_surface = (cairo_image_surface_t*) surface; - width = image_surface->width; - height = image_surface->height; - stride = image_surface->stride; - format = image_surface->format; - data = image_surface->data; - - if (!_cairo_quartz_verify_surface_size(width, height)) - return SURFACE_ERROR_INVALID_SIZE; - - if (width == 0 || height == 0) - return SURFACE_ERROR_INVALID_SIZE; - - if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) - return SURFACE_ERROR_INVALID_FORMAT; - - qisurf = malloc(sizeof(cairo_quartz_image_surface_t)); - if (qisurf == NULL) - return SURFACE_ERROR_NO_MEMORY; - - memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t)); - - /* In case the create_cgimage fails, this ref will - * be released via the callback (which will be called in - * case of failure.) - */ - cairo_surface_reference (surface); - - image = _cairo_quartz_create_cgimage (format, - width, height, - stride, - data, - TRUE, - NULL, - DataProviderReleaseCallback, - image_surface); - - if (!image) { - free (qisurf); - return SURFACE_ERROR_NO_MEMORY; - } - - _cairo_surface_init (&qisurf->base, - &cairo_quartz_image_surface_backend, - NULL, /* device */ - _cairo_content_from_format (format)); - - qisurf->extents.x = qisurf->extents.y = 0; - qisurf->extents.width = width; - qisurf->extents.height = height; - - qisurf->image = image; - qisurf->imageSurface = image_surface; - - qisurf->base.is_clear = image_surface->base.is_clear; - - return &qisurf->base; -} - - -cairo_surface_t * -cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; - - if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) - return NULL; - - return (cairo_surface_t*) surface->imageSurface; -} diff --git a/libs/cairo/cairo/src/cairo-quartz-image.h b/libs/cairo/cairo/src/cairo-quartz-image.h deleted file mode 100644 index fffc1ee86..000000000 --- a/libs/cairo/cairo/src/cairo-quartz-image.h +++ /dev/null @@ -1,33 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_QUARTZ_IMAGE_H -#define CAIRO_QUARTZ_IMAGE_H - -#include "cairo.h" - -#if CAIRO_HAS_QUARTZ_IMAGE_SURFACE -#include "TargetConditionals.h" - -#if !TARGET_OS_IPHONE -#include -#else -#include -#endif - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_quartz_image_surface_create (cairo_surface_t *image_surface); - -cairo_public cairo_surface_t * -cairo_quartz_image_surface_get_image (cairo_surface_t *surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ -# error Cairo was not compiled with support for the quartz-image backend -#endif /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ - -#endif /* CAIRO_QUARTZ_IMAGE_H */ diff --git a/libs/cairo/cairo/src/cairo-quartz-private.h b/libs/cairo/cairo/src/cairo-quartz-private.h deleted file mode 100644 index c0dcf3b91..000000000 --- a/libs/cairo/cairo/src/cairo-quartz-private.h +++ /dev/null @@ -1,86 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_QUARTZ_PRIVATE_H -#define CAIRO_QUARTZ_PRIVATE_H - -#include "cairoint.h" - -#if CAIRO_HAS_QUARTZ_SURFACE -#include "cairo-quartz.h" -#include "cairo-surface-clipper-private.h" - -#ifdef CGFLOAT_DEFINED -typedef CGFloat cairo_quartz_float_t; -#else -typedef float cairo_quartz_float_t; -#endif - -/* define CTFontRef for pre-10.5 SDKs */ -typedef const struct __CTFont *CTFontRef; - -typedef struct cairo_quartz_surface { - cairo_surface_t base; - - CGContextRef cgContext; - CGAffineTransform cgContextBaseCTM; - - void *imageData; - cairo_surface_t *imageSurfaceEquiv; - - cairo_surface_clipper_t clipper; - - /** - * If non-null, this is a CGImage representing the contents of the surface. - * We clear this out before any painting into the surface, so that we - * don't force a copy to be created. - */ - CGImageRef bitmapContextImage; - - /** - * If non-null, this is the CGLayer for the surface. - */ - CGLayerRef cgLayer; - - cairo_rectangle_int_t extents; - - cairo_bool_t ownsData; -} cairo_quartz_surface_t; - -typedef struct cairo_quartz_image_surface { - cairo_surface_t base; - - cairo_rectangle_int_t extents; - - CGImageRef image; - cairo_image_surface_t *imageSurface; -} cairo_quartz_image_surface_t; - -cairo_bool_t -_cairo_quartz_verify_surface_size(int width, int height); - -CGImageRef -_cairo_quartz_create_cgimage (cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride, - void *data, - cairo_bool_t interpolate, - CGColorSpaceRef colorSpaceOverride, - CGDataProviderReleaseDataCallback releaseCallback, - void *releaseInfo); - -CGFontRef -_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); - -CTFontRef -_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont); - -#else - -# error Cairo was not compiled with support for the quartz backend - -#endif /* CAIRO_HAS_QUARTZ_SURFACE */ - -#endif /* CAIRO_QUARTZ_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-quartz-surface.c b/libs/cairo/cairo/src/cairo-quartz-surface.c deleted file mode 100644 index d5a5ffd92..000000000 --- a/libs/cairo/cairo/src/cairo-quartz-surface.c +++ /dev/null @@ -1,3768 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* required for RTLD_DEFAULT */ -#endif -#include "cairoint.h" - -#include "cairo-quartz-private.h" - -#include "cairo-error-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-gstate-private.h" -#include "cairo-private.h" - -#include - -#ifndef RTLD_DEFAULT -#define RTLD_DEFAULT ((void *) 0) -#endif - -#include - -#undef QUARTZ_DEBUG - -#ifdef QUARTZ_DEBUG -#define ND(_x) fprintf _x -#else -#define ND(_x) do {} while(0) -#endif - -#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) - -/** - * SECTION:cairo-quartz - * @Title: Quartz Surfaces - * @Short_Description: Rendering to Quartz surfaces - * @See_Also: #cairo_surface_t - * - * The Quartz surface is used to render cairo graphics targeting the - * Apple OS X Quartz rendering system. - */ - -/** - * CAIRO_HAS_QUARTZ_SURFACE: - * - * Defined if the Quartz surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/* Here are some of the differences between cairo and CoreGraphics - - cairo has only a single source active at once vs. CoreGraphics having - separate sources for stroke and fill -*/ - -/* This method is private, but it exists. Its params are are exposed - * as args to the NS* method, but not as CG. - */ -enum PrivateCGCompositeMode { - kPrivateCGCompositeClear = 0, - kPrivateCGCompositeCopy = 1, - kPrivateCGCompositeSourceOver = 2, - kPrivateCGCompositeSourceIn = 3, - kPrivateCGCompositeSourceOut = 4, - kPrivateCGCompositeSourceAtop = 5, - kPrivateCGCompositeDestinationOver = 6, - kPrivateCGCompositeDestinationIn = 7, - kPrivateCGCompositeDestinationOut = 8, - kPrivateCGCompositeDestinationAtop = 9, - kPrivateCGCompositeXOR = 10, - kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) - kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) -}; -typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; -CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); -CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform); - -/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9 - * has all the stuff we care about, just some of it isn't exported in the SDK. - */ -#ifndef kCGBitmapByteOrder32Host -#define USE_10_3_WORKAROUNDS -#define kCGBitmapAlphaInfoMask 0x1F -#define kCGBitmapByteOrderMask 0x7000 -#define kCGBitmapByteOrder32Host 0 - -typedef uint32_t CGBitmapInfo; - -/* public in 10.4, present in 10.3.9 */ -CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef); -CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); -#endif - -/* Some of these are present in earlier versions of the OS than where - * they are public; others are not public at all (CGContextCopyPath, - * CGContextReplacePathWithClipPath, many of the getters, etc.) - */ -static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL; -static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; -static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; -static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; -static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; -static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; -static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; - -/* CTFontDrawGlyphs is not available until 10.7 */ -static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL; - -static SInt32 _cairo_quartz_osx_version = 0x0; - -static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; - -/* - * Utility functions - */ - -#ifdef QUARTZ_DEBUG -static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); -static void quartz_image_to_png (CGImageRef, char *dest); -#endif - -static cairo_quartz_surface_t * -_cairo_quartz_surface_create_internal (CGContextRef cgContext, - cairo_content_t content, - unsigned int width, - unsigned int height); - -static cairo_bool_t -_cairo_surface_is_quartz (const cairo_surface_t *surface); - -/* Load all extra symbols */ -static void quartz_ensure_symbols(void) -{ - if (_cairo_quartz_symbol_lookup_done) - return; - - CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask"); - CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); - CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); - CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); - CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); - CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); - - CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); - -#if !TARGET_OS_IPHONE - if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { - // assume 10.5 - _cairo_quartz_osx_version = 0x1050; - } -#else - //TODO: this is not great - _cairo_quartz_osx_version = 0x1050; -#endif - - _cairo_quartz_symbol_lookup_done = TRUE; -} - -CGImageRef -_cairo_quartz_create_cgimage (cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride, - void *data, - cairo_bool_t interpolate, - CGColorSpaceRef colorSpaceOverride, - CGDataProviderReleaseDataCallback releaseCallback, - void *releaseInfo) -{ - CGImageRef image = NULL; - CGDataProviderRef dataProvider = NULL; - CGColorSpaceRef colorSpace = colorSpaceOverride; - CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host; - int bitsPerComponent, bitsPerPixel; - - switch (format) { - case CAIRO_FORMAT_ARGB32: - if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB(); - bitinfo |= kCGImageAlphaPremultipliedFirst; - bitsPerComponent = 8; - bitsPerPixel = 32; - break; - - case CAIRO_FORMAT_RGB24: - if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB(); - bitinfo |= kCGImageAlphaNoneSkipFirst; - bitsPerComponent = 8; - bitsPerPixel = 32; - break; - - case CAIRO_FORMAT_A8: - bitsPerComponent = 8; - bitsPerPixel = 8; - break; - - case CAIRO_FORMAT_A1: -#ifdef WORDS_BIGENDIAN - bitsPerComponent = 1; - bitsPerPixel = 1; - break; -#endif - - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_INVALID: - default: - return NULL; - } - - dataProvider = CGDataProviderCreateWithData (releaseInfo, - data, - height * stride, - releaseCallback); - - if (!dataProvider) { - // manually release - if (releaseCallback) - releaseCallback (releaseInfo, data, height * stride); - goto FINISH; - } - - if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) { - cairo_quartz_float_t decode[] = {1.0, 0.0}; - image = CGImageMaskCreate (width, height, - bitsPerComponent, - bitsPerPixel, - stride, - dataProvider, - decode, - interpolate); - } else - image = CGImageCreate (width, height, - bitsPerComponent, - bitsPerPixel, - stride, - colorSpace, - bitinfo, - dataProvider, - NULL, - interpolate, - kCGRenderingIntentDefault); - -FINISH: - - CGDataProviderRelease (dataProvider); - - if (colorSpace != colorSpaceOverride) - CGColorSpaceRelease (colorSpace); - - return image; -} - -static inline cairo_bool_t -_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) { - if (cgc == NULL) - return FALSE; - - if (CGContextGetTypePtr) { - /* 4 is the type value of a bitmap context */ - if (CGContextGetTypePtr(cgc) == 4) - return TRUE; - return FALSE; - } - - /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */ - return CGBitmapContextGetBitsPerPixel(cgc) != 0; -} - -/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */ - -#define CG_MAX_HEIGHT SHRT_MAX -#define CG_MAX_WIDTH USHRT_MAX - -/* is the desired size of the surface within bounds? */ -cairo_bool_t -_cairo_quartz_verify_surface_size(int width, int height) -{ - /* hmmm, allow width, height == 0 ? */ - if (width < 0 || height < 0) { - return FALSE; - } - - if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) { - return FALSE; - } - - return TRUE; -} - -/* - * Cairo path -> Quartz path conversion helpers - */ - -/* cairo path -> execute in context */ -static cairo_status_t -_cairo_path_to_quartz_context_move_to (void *closure, - const cairo_point_t *point) -{ - //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - CGContextMoveToPoint (closure, x, y); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_line_to (void *closure, - const cairo_point_t *point) -{ - //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - CGContextAddLineToPoint (closure, x, y); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_curve_to (void *closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n", - // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), - // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), - // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y))); - double x0 = _cairo_fixed_to_double (p0->x); - double y0 = _cairo_fixed_to_double (p0->y); - double x1 = _cairo_fixed_to_double (p1->x); - double y1 = _cairo_fixed_to_double (p1->y); - double x2 = _cairo_fixed_to_double (p2->x); - double y2 = _cairo_fixed_to_double (p2->y); - - CGContextAddCurveToPoint (closure, - x0, y0, x1, y1, x2, y2); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_to_quartz_context_close_path (void *closure) -{ - //ND((stderr, "closepath\n")); - CGContextClosePath (closure); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, - CGContextRef closure) -{ - cairo_status_t status; - - CGContextBeginPath (closure); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_path_to_quartz_context_move_to, - _cairo_path_to_quartz_context_line_to, - _cairo_path_to_quartz_context_curve_to, - _cairo_path_to_quartz_context_close_path, - closure); - - assert (status == CAIRO_STATUS_SUCCESS); -} - -/* - * Misc helpers/callbacks - */ - -static PrivateCGCompositeMode -_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return kPrivateCGCompositeClear; - case CAIRO_OPERATOR_SOURCE: - return kPrivateCGCompositeCopy; - case CAIRO_OPERATOR_OVER: - return kPrivateCGCompositeSourceOver; - case CAIRO_OPERATOR_IN: - return kPrivateCGCompositeSourceIn; - case CAIRO_OPERATOR_OUT: - return kPrivateCGCompositeSourceOut; - case CAIRO_OPERATOR_ATOP: - return kPrivateCGCompositeSourceAtop; - case CAIRO_OPERATOR_DEST_OVER: - return kPrivateCGCompositeDestinationOver; - case CAIRO_OPERATOR_DEST_IN: - return kPrivateCGCompositeDestinationIn; - case CAIRO_OPERATOR_DEST_OUT: - return kPrivateCGCompositeDestinationOut; - case CAIRO_OPERATOR_DEST_ATOP: - return kPrivateCGCompositeDestinationAtop; - case CAIRO_OPERATOR_XOR: - return kPrivateCGCompositeXOR; - case CAIRO_OPERATOR_ADD: - return kPrivateCGCompositePlusLighter; - - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - assert (0); - return kPrivateCGCompositeClear; - } -} - -static cairo_int_status_t -_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op) -{ - ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op)); - - if (surface->base.content == CAIRO_CONTENT_ALPHA) { - /* For some weird reason, some compositing operators are - swapped when operating on masks */ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_ADD: - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_IN: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_DEST_ATOP: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_SATURATE: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter); - return CAIRO_STATUS_SUCCESS; - - - case CAIRO_OPERATOR_ATOP: - /* - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver); - return CAIRO_STATUS_SUCCESS; - */ - case CAIRO_OPERATOR_DEST: - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_XOR: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_DEST: - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - case CAIRO_OPERATOR_SATURATE: - /* TODO: the following are mostly supported by CGContextSetBlendMode*/ - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } -} - -static inline CGLineCap -_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) -{ - switch (ccap) { - case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break; - case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break; - case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break; - } - - return kCGLineCapButt; -} - -static inline CGLineJoin -_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) -{ - switch (cjoin) { - case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break; - case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break; - case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break; - } - - return kCGLineJoinMiter; -} - -static inline CGInterpolationQuality -_cairo_quartz_filter_to_quartz (cairo_filter_t filter) -{ - switch (filter) { - case CAIRO_FILTER_NEAREST: - return kCGInterpolationNone; - - case CAIRO_FILTER_FAST: - return kCGInterpolationLow; - - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BILINEAR: - case CAIRO_FILTER_GAUSSIAN: - return kCGInterpolationDefault; - } - - return kCGInterpolationDefault; -} - -static inline void -_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, - CGAffineTransform *dst) -{ - dst->a = src->xx; - dst->b = src->yx; - dst->c = src->xy; - dst->d = src->yy; - dst->tx = src->x0; - dst->ty = src->y0; -} - -typedef struct { - bool isClipping; - CGGlyph *cg_glyphs; - union { - CGSize *cg_advances; - CGPoint *cg_positions; - } u; - size_t nglyphs; - CGAffineTransform textTransform; - cairo_scaled_font_t *scaled_font; - CGPoint origin; -} unbounded_show_glyphs_t; - -typedef struct { - CGPathRef cgPath; - cairo_fill_rule_t fill_rule; -} unbounded_stroke_fill_t; - -typedef struct { - CGImageRef mask; - CGAffineTransform maskTransform; -} unbounded_mask_t; - -typedef enum { - UNBOUNDED_STROKE_FILL, - UNBOUNDED_SHOW_GLYPHS, - UNBOUNDED_MASK -} unbounded_op_t; - -typedef struct { - unbounded_op_t op; - union { - unbounded_stroke_fill_t stroke_fill; - unbounded_show_glyphs_t show_glyphs; - unbounded_mask_t mask; - } u; -} unbounded_op_data_t; - -static void -_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface, - unbounded_op_data_t *op, - cairo_antialias_t antialias) -{ - CGRect clipBox, clipBoxRound; - CGContextRef cgc; - CGImageRef maskImage; - - /* TODO: handle failure */ - if (!CGContextClipToMaskPtr) - return; - - clipBox = CGContextGetClipBoundingBox (surface->cgContext); - clipBoxRound = CGRectIntegral (clipBox); - - cgc = CGBitmapContextCreate (NULL, - clipBoxRound.size.width, - clipBoxRound.size.height, - 8, - (((size_t) clipBoxRound.size.width) + 15) & (~15), - NULL, - kCGImageAlphaOnly); - - if (!cgc) - return; - - CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy); - /* We want to mask out whatever we just rendered, so we fill the - * surface opaque, and then we'll render transparent. - */ - CGContextSetAlpha (cgc, 1.0f); - CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height)); - - CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear); - CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE)); - - CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y); - - /* We need to either render the path that was given to us, or the glyph op */ - if (op->op == UNBOUNDED_STROKE_FILL) { - CGContextBeginPath (cgc); - CGContextAddPath (cgc, op->u.stroke_fill.cgPath); - - if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (cgc); - else - CGContextEOFillPath (cgc); - } else if (op->op == UNBOUNDED_SHOW_GLYPHS) { - if (op->u.show_glyphs.isClipping) { - /* Note that the comment in show_glyphs about kCGTextClip - * and the text transform still applies here; however, the - * cg_advances we have were already transformed, so we - * don't have to do anything. */ - CGContextSetTextDrawingMode (cgc, kCGTextClip); - CGContextSaveGState (cgc); - } - CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y); - CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform); - if (CTFontDrawGlyphsPtr) { - CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font), - op->u.show_glyphs.cg_glyphs, - op->u.show_glyphs.u.cg_positions, - op->u.show_glyphs.nglyphs, - cgc); - } else { - CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font)); - CGContextSetFontSize (cgc, 1.0); - CGContextSetTextMatrix (cgc, CGAffineTransformIdentity); - - CGContextShowGlyphsWithAdvances (cgc, - op->u.show_glyphs.cg_glyphs, - op->u.show_glyphs.u.cg_advances, - op->u.show_glyphs.nglyphs); - - } - if (op->u.show_glyphs.isClipping) { - CGContextClearRect (cgc, clipBoxRound); - CGContextRestoreGState (cgc); - } - } else if (op->op == UNBOUNDED_MASK) { - CGAffineTransform ctm = CGContextGetCTM (cgc); - CGContextSaveGState (cgc); - CGContextConcatCTM (cgc, op->u.mask.maskTransform); - CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f, - CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)), - op->u.mask.mask); - CGContextSetCTM (cgc, ctm); - CGContextClearRect (cgc, clipBoxRound); - CGContextRestoreGState (cgc); - } - - /* Also mask out the portion of the clipbox that we rounded out, if any */ - if (!CGRectEqualToRect (clipBox, clipBoxRound)) { - CGContextBeginPath (cgc); - CGContextAddRect (cgc, clipBoxRound); - CGContextAddRect (cgc, clipBox); - CGContextEOFillPath (cgc); - } - - maskImage = CGBitmapContextCreateImage (cgc); - CGContextRelease (cgc); - - if (!maskImage) - return; - - /* Then render with the mask */ - CGContextSaveGState (surface->cgContext); - - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy); - CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage); - CGImageRelease (maskImage); - - /* Finally, clear out the entire clipping region through our mask */ - CGContextClearRect (surface->cgContext, clipBoxRound); - - CGContextRestoreGState (surface->cgContext); -} - -/* - * Source -> Quartz setup and finish functions - */ - -static void -ComputeGradientValue (void *info, - const cairo_quartz_float_t *in, - cairo_quartz_float_t *out) -{ - double fdist = *in; - const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; - unsigned int i; - - /* Put fdist back in the 0.0..1.0 range if we're doing - * REPEAT/REFLECT - */ - if (grad->base.extend == CAIRO_EXTEND_REPEAT) { - fdist = fdist - floor(fdist); - } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) { - fdist = fmod(fabs(fdist), 2.0); - if (fdist > 1.0) { - fdist = 2.0 - fdist; - } - } - - for (i = 0; i < grad->n_stops; i++) { - if (grad->stops[i].offset > fdist) - break; - } - - if (i == 0 || i == grad->n_stops) { - if (i == grad->n_stops) - --i; - out[0] = grad->stops[i].color.red; - out[1] = grad->stops[i].color.green; - out[2] = grad->stops[i].color.blue; - out[3] = grad->stops[i].color.alpha; - } else { - cairo_quartz_float_t ax = grad->stops[i-1].offset; - cairo_quartz_float_t bx = grad->stops[i].offset - ax; - cairo_quartz_float_t bp = (fdist - ax)/bx; - cairo_quartz_float_t ap = 1.0 - bp; - - out[0] = - grad->stops[i-1].color.red * ap + - grad->stops[i].color.red * bp; - out[1] = - grad->stops[i-1].color.green * ap + - grad->stops[i].color.green * bp; - out[2] = - grad->stops[i-1].color.blue * ap + - grad->stops[i].color.blue * bp; - out[3] = - grad->stops[i-1].color.alpha * ap + - grad->stops[i].color.alpha * bp; - } -} - -static const cairo_quartz_float_t gradient_output_value_ranges[8] = { - 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f -}; -static const CGFunctionCallbacks gradient_callbacks = { - 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy -}; -/* Quartz will clamp input values to the input range. - - Our stops are all in the range 0.0 to 1.0. However, the color before the - beginning of the gradient line is obtained by Quartz computing a negative - position on the gradient line, clamping it to the input range we specified - for our color function, and then calling our color function (actually it - pre-samples the color function into an array, but that doesn't matter just - here). Therefore if we set the lower bound to 0.0, a negative position - on the gradient line will pass 0.0 to ComputeGradientValue, which will - select the last color stop with position 0, although it should select - the first color stop (this matters when there are multiple color stops with - position 0). - - Therefore we pass a small negative number as the lower bound of the input - range, so this value gets passed into ComputeGradientValue, which will - return the color of the first stop. The number should be small because - as far as I can tell, Quartz pre-samples the entire input range of the color - function into an array of fixed size, so if the input range is larger - than needed, the resolution of the gradient will be unnecessarily low. -*/ -static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f }; - -static CGFunctionRef -CreateGradientFunction (const cairo_gradient_pattern_t *gpat) -{ - cairo_pattern_t *pat; - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, - nonrepeating_gradient_input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); -} - -static void -UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start, - double dx, double dy, - double x, double y) -{ - /* Compute a parameter t such that a line perpendicular to the (dx,dy) - vector, passing through (start->x + dx*t, start->y + dy*t), also - passes through (x,y). - - Let px = x - start->x, py = y - start->y. - t is given by - (px - dx*t)*dx + (py - dy*t)*dy = 0 - - Solving for t we get - numerator = dx*px + dy*py - denominator = dx^2 + dy^2 - t = numerator/denominator - - In CreateRepeatingLinearGradientFunction we know the length of (dx,dy) - is not zero. (This is checked in _cairo_quartz_setup_linear_source.) - */ - double px = x - start->x; - double py = y - start->y; - double numerator = dx*px + dy*py; - double denominator = dx*dx + dy*dy; - double t = numerator/denominator; - - if (*min_t > t) { - *min_t = t; - } - if (*max_t < t) { - *max_t = t; - } -} - -static CGFunctionRef -CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, CGPoint *end, - cairo_rectangle_int_t *extents) -{ - cairo_pattern_t *pat; - cairo_quartz_float_t input_value_range[2]; - double t_min = 0.; - double t_max = 0.; - double dx = end->x - start->x; - double dy = end->y - start->y; - double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - - if (!extents) { - extents = &surface->extents; - } - bounds_x1 = extents->x; - bounds_y1 = extents->y; - bounds_x2 = extents->x + extents->width; - bounds_y2 = extents->y + extents->height; - _cairo_matrix_transform_bounding_box (&gpat->base.matrix, - &bounds_x1, &bounds_y1, - &bounds_x2, &bounds_y2, - NULL); - - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x1, bounds_y1); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x2, bounds_y1); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x2, bounds_y2); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x1, bounds_y2); - - /* Move t_min and t_max to the nearest usable integer to try to avoid - subtle variations due to numerical instability, especially accidentally - cutting off a pixel. Extending the gradient repetitions is always safe. */ - t_min = floor (t_min); - t_max = ceil (t_max); - end->x = start->x + dx*t_max; - end->y = start->y + dy*t_max; - start->x = start->x + dx*t_min; - start->y = start->y + dy*t_min; - - // set the input range for the function -- the function knows how to - // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. - input_value_range[0] = t_min; - input_value_range[1] = t_max; - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, - input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); -} - -static void -UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center, - double dr, double dx, double dy, - double x, double y) -{ - /* Compute a parameter t such that a circle centered at - (center->x + dx*t, center->y + dy*t) with radius dr*t contains the - point (x,y). - - Let px = x - center->x, py = y - center->y. - Parameter values for which t is on the circle are given by - (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2 - - Solving for t using the quadratic formula, and simplifying, we get - numerator = dx*px + dy*py +- - sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 ) - denominator = dx^2 + dy^2 - dr^2 - t = numerator/denominator - - In CreateRepeatingRadialGradientFunction we know the outer circle - contains the inner circle. Therefore the distance between the circle - centers plus the radius of the inner circle is less than the radius of - the outer circle. (This is checked in _cairo_quartz_setup_radial_source.) - Therefore - dx^2 + dy^2 < dr^2 - So the denominator is negative and the larger solution for t is given by - numerator = dx*px + dy*py - - sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 ) - denominator = dx^2 + dy^2 - dr^2 - t = numerator/denominator - dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive. - */ - double px = x - center->x; - double py = y - center->y; - double dx_py_minus_dy_px = dx*py - dy*px; - double numerator = dx*px + dy*py - - sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px); - double denominator = dx*dx + dy*dy - dr*dr; - double t = numerator/denominator; - - if (*max_t < t) { - *max_t = t; - } -} - -/* This must only be called when one of the circles properly contains the other */ -static CGFunctionRef -CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, double *start_radius, - CGPoint *end, double *end_radius, - cairo_rectangle_int_t *extents) -{ - cairo_pattern_t *pat; - cairo_quartz_float_t input_value_range[2]; - CGPoint *inner; - double *inner_radius; - CGPoint *outer; - double *outer_radius; - /* minimum and maximum t-parameter values that will make our gradient - cover the clipBox */ - double t_min, t_max, t_temp; - /* outer minus inner */ - double dr, dx, dy; - double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - - if (!extents) { - extents = &surface->extents; - } - bounds_x1 = extents->x; - bounds_y1 = extents->y; - bounds_x2 = extents->x + extents->width; - bounds_y2 = extents->y + extents->height; - _cairo_matrix_transform_bounding_box (&gpat->base.matrix, - &bounds_x1, &bounds_y1, - &bounds_x2, &bounds_y2, - NULL); - - if (*start_radius < *end_radius) { - /* end circle contains start circle */ - inner = start; - outer = end; - inner_radius = start_radius; - outer_radius = end_radius; - } else { - /* start circle contains end circle */ - inner = end; - outer = start; - inner_radius = end_radius; - outer_radius = start_radius; - } - - dr = *outer_radius - *inner_radius; - dx = outer->x - inner->x; - dy = outer->y - inner->y; - - /* We can't round or fudge t_min here, it has to be as accurate as possible. */ - t_min = -(*inner_radius/dr); - inner->x += t_min*dx; - inner->y += t_min*dy; - *inner_radius = 0.; - - t_temp = 0.; - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x1, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x2, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x2, bounds_y2); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x1, bounds_y2); - /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0. - But for the parameter values we use with Quartz, t_min means radius 0. - Since the circles are alway expanding and contain the earlier circles, - it's safe to extend t_max/t_temp as much as we want, so round t_temp up - to the nearest integer. This may help us give stable results. */ - t_temp = ceil (t_temp); - t_max = t_min + t_temp; - outer->x = inner->x + t_temp*dx; - outer->y = inner->y + t_temp*dy; - *outer_radius = t_temp*dr; - - /* set the input range for the function -- the function knows how to - map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */ - if (*start_radius < *end_radius) { - input_value_range[0] = t_min; - input_value_range[1] = t_max; - } else { - input_value_range[0] = 1 - t_max; - input_value_range[1] = 1 - t_min; - } - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, - input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); -} - -/* Obtain a CGImageRef from a #cairo_surface_t * */ - -typedef struct { - cairo_surface_t *surface; - cairo_image_surface_t *image_out; - void *image_extra; -} quartz_source_image_t; - -static void -DataProviderReleaseCallback (void *info, const void *data, size_t size) -{ - quartz_source_image_t *source_img = info; - _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra); - cairo_surface_destroy (source_img->surface); - free (source_img); -} - -static cairo_status_t -_cairo_surface_to_cgimage (cairo_surface_t *source, - CGImageRef *image_out) -{ - cairo_status_t status; - quartz_source_image_t *source_img; - - if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) { - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source; - *image_out = CGImageRetain (surface->image); - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_surface_is_quartz (source)) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; - if (IS_EMPTY(surface)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { - if (!surface->bitmapContextImage) { - surface->bitmapContextImage = - CGBitmapContextCreateImage (surface->cgContext); - } - if (surface->bitmapContextImage) { - *image_out = CGImageRetain (surface->bitmapContextImage); - return CAIRO_STATUS_SUCCESS; - } - } - } - - source_img = malloc (sizeof (quartz_source_image_t)); - if (source_img == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - source_img->surface = cairo_surface_reference(source); - - status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra); - if (status) { - cairo_surface_destroy (source_img->surface); - free (source_img); - return status; - } - - if (source_img->image_out->width == 0 || source_img->image_out->height == 0) { - *image_out = NULL; - DataProviderReleaseCallback (source_img, - source_img->image_out->data, - source_img->image_out->height * source_img->image_out->stride); - } else { - *image_out = _cairo_quartz_create_cgimage (source_img->image_out->format, - source_img->image_out->width, - source_img->image_out->height, - source_img->image_out->stride, - source_img->image_out->data, - TRUE, - NULL, - DataProviderReleaseCallback, - source_img); - - /* TODO: differentiate memory error and unsupported surface type */ - if (*image_out == NULL) - status = CAIRO_INT_STATUS_UNSUPPORTED; - } - - return status; -} - -/* Generic #cairo_pattern_t -> CGPattern function */ - -typedef struct { - CGImageRef image; - CGRect imageBounds; - cairo_bool_t do_reflect; -} SurfacePatternDrawInfo; - -static void -SurfacePatternDrawFunc (void *ainfo, CGContextRef context) -{ - SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; - - CGContextTranslateCTM (context, 0, info->imageBounds.size.height); - CGContextScaleCTM (context, 1, -1); - - CGContextDrawImage (context, info->imageBounds, info->image); - if (info->do_reflect) { - /* draw 3 more copies of the image, flipped. - * DrawImage draws the image according to the current Y-direction into the rectangle given - * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left - * of the base image position, and the Y axis is extending upwards. - */ - - /* Make the y axis extend downwards, and draw a flipped image below */ - CGContextScaleCTM (context, 1, -1); - CGContextDrawImage (context, info->imageBounds, info->image); - - /* Shift over to the right, and flip vertically (translation is 2x, - * since we'll be flipping and thus rendering the rectangle "backwards" - */ - CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0); - CGContextScaleCTM (context, -1, 1); - CGContextDrawImage (context, info->imageBounds, info->image); - - /* Then unflip the Y-axis again, and draw the image above the point. */ - CGContextScaleCTM (context, 1, -1); - CGContextDrawImage (context, info->imageBounds, info->image); - } -} - -static void -SurfacePatternReleaseInfoFunc (void *ainfo) -{ - SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; - - CGImageRelease (info->image); - free (info); -} - -static cairo_int_status_t -_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest, - const cairo_pattern_t *apattern, - CGPatternRef *cgpat) -{ - cairo_surface_pattern_t *spattern; - cairo_surface_t *pat_surf; - cairo_rectangle_int_t extents; - - CGImageRef image; - CGRect pbounds; - CGAffineTransform ptransform, stransform; - CGPatternCallbacks cb = { 0, - SurfacePatternDrawFunc, - SurfacePatternReleaseInfoFunc }; - SurfacePatternDrawInfo *info; - cairo_quartz_float_t rw, rh; - cairo_status_t status; - cairo_bool_t is_bounded; - - cairo_matrix_t m; - - /* SURFACE is the only type we'll handle here */ - if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - spattern = (cairo_surface_pattern_t *) apattern; - pat_surf = spattern->surface; - - is_bounded = _cairo_surface_get_extents (pat_surf, &extents); - assert (is_bounded); - - status = _cairo_surface_to_cgimage (pat_surf, &image); - if (status) - return status; - if (image == NULL) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - info = malloc(sizeof(SurfacePatternDrawInfo)); - if (!info) - return CAIRO_STATUS_NO_MEMORY; - - /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure - * that the data will stick around for this image when the printer gets to it. - * Otherwise, the underlying data store may disappear from under us! - * - * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces, - * since the Quartz surfaces have a higher chance of sticking around. If the - * source is a quartz image surface, then it's set up to retain a ref to the - * image surface that it's backed by. - */ - info->image = image; - info->imageBounds = CGRectMake (0, 0, extents.width, extents.height); - info->do_reflect = FALSE; - - pbounds.origin.x = 0; - pbounds.origin.y = 0; - - if (spattern->base.extend == CAIRO_EXTEND_REFLECT) { - pbounds.size.width = 2.0 * extents.width; - pbounds.size.height = 2.0 * extents.height; - info->do_reflect = TRUE; - } else { - pbounds.size.width = extents.width; - pbounds.size.height = extents.height; - } - rw = pbounds.size.width; - rh = pbounds.size.height; - - m = spattern->base.matrix; - cairo_matrix_invert(&m); - _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform); - - /* The pattern matrix is relative to the bottom left, again; the - * incoming cairo pattern matrix is relative to the upper left. - * So we take the pattern matrix and the original context matrix, - * which gives us the correct base translation/y flip. - */ - ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM); - -#ifdef QUARTZ_DEBUG - ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); - ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); - CGAffineTransform xform = CGContextGetCTM(dest->cgContext); - ND((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d)); -#endif - - *cgpat = CGPatternCreate (info, - pbounds, - ptransform, - rw, rh, - kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ - TRUE, - &cb); - - return CAIRO_STATUS_SUCCESS; -} - -typedef enum { - DO_SOLID, - DO_SHADING, - DO_PATTERN, - DO_IMAGE, - DO_TILED_IMAGE, - DO_LAYER, - DO_UNSUPPORTED, - DO_NOTHING -} cairo_quartz_action_t; - -/* State used during a drawing operation. */ -typedef struct { - CGContextRef context; - cairo_quartz_action_t action; - - // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER - CGAffineTransform transform; - - // Used with DO_IMAGE and DO_TILED_IMAGE - CGImageRef image; - cairo_surface_t *imageSurface; - - // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER - CGRect imageRect; - - // Used with DO_LAYER - CGLayerRef layer; - - // Used with DO_SHADING - CGShadingRef shading; - - // Used with DO_PATTERN - CGPatternRef pattern; -} cairo_quartz_drawing_state_t; - -static void -_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, - const cairo_pattern_t *source, - cairo_quartz_drawing_state_t *state) -{ - CGRect clipBox = CGContextGetClipBoundingBox (state->context); - double x0, y0, w, h; - - cairo_surface_t *fallback; - CGImageRef img; - - cairo_status_t status; - - if (clipBox.size.width == 0.0f || - clipBox.size.height == 0.0f) { - state->action = DO_NOTHING; - return; - } - - x0 = floor(clipBox.origin.x); - y0 = floor(clipBox.origin.y); - w = ceil(clipBox.origin.x + clipBox.size.width) - x0; - h = ceil(clipBox.origin.y + clipBox.size.height) - y0; - - /* Create a temporary the size of the clip surface, and position - * it so that the device origin coincides with the original surface */ - fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h); - cairo_surface_set_device_offset (fallback, -x0, -y0); - -#if 0 - { - cairo_t *fallback_cr; - cairo_pattern_t *source_copy; - - /* Paint the source onto our temporary */ - fallback_cr = cairo_create (fallback); - cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE); - - /* Use a copy of the pattern because it is const and could be allocated - * on the stack */ - status = _cairo_pattern_create_copy (&source_copy, source); - cairo_set_source (fallback_cr, source_copy); - cairo_pattern_destroy (source_copy); - - cairo_paint (fallback_cr); - cairo_destroy (fallback_cr); - } -#else - { - cairo_pattern_union_t pattern; - - _cairo_pattern_init_static_copy (&pattern.base, source); - _cairo_pattern_transform (&pattern.base, - &fallback->device_transform_inverse); - status = _cairo_surface_paint (fallback, - CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); - } -#endif - - status = _cairo_surface_to_cgimage (fallback, &img); - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - if (img == NULL) { - state->action = DO_NOTHING; - return; - } - - state->imageRect = CGRectMake (0.0, 0.0, w, h); - state->image = img; - state->imageSurface = fallback; - state->transform = CGAffineTransformMakeTranslation (x0, y0); - state->action = DO_IMAGE; -} - -/* -Quartz does not support repeating radients. We handle repeating gradients -by manually extending the gradient and repeating color stops. We need to -minimize the number of repetitions since Quartz seems to sample our color -function across the entire range, even if part of that range is not needed -for the visible area of the gradient, and it samples with some fixed resolution, -so if the gradient range is too large it samples with very low resolution and -the gradient is very coarse. CreateRepeatingLinearGradientFunction and -CreateRepeatingRadialGradientFunction compute the number of repetitions needed -based on the extents of the object (the clip region cannot be used here since -we don't want the rasterization of the entire gradient to depend on the -clip region). -*/ -static void -_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, - const cairo_linear_pattern_t *lpat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) -{ - const cairo_pattern_t *abspat = &lpat->base.base; - cairo_matrix_t mat; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; - - if (lpat->base.n_stops == 0) { - CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); - CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); - state->action = DO_SOLID; - return; - } - - if (lpat->p1.x == lpat->p2.x && - lpat->p1.y == lpat->p2.y) { - /* Quartz handles cases where the vector has no length very - * differently from pixman. - * Whatever the correct behaviour is, let's at least have only pixman's - * implementation to worry about. - */ - _cairo_quartz_setup_fallback_source (surface, abspat, state); - return; - } - - mat = abspat->matrix; - cairo_matrix_invert (&mat); - _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); - - rgb = CGColorSpaceCreateDeviceRGB(); - - start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x), - _cairo_fixed_to_double (lpat->p1.y)); - end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x), - _cairo_fixed_to_double (lpat->p2.y)); - - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) - { - gradFunc = CreateGradientFunction (&lpat->base); - } else { - gradFunc = CreateRepeatingLinearGradientFunction (surface, - &lpat->base, - &start, &end, - extents); - } - - state->shading = CGShadingCreateAxial (rgb, - start, end, - gradFunc, - extend, extend); - - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - state->action = DO_SHADING; -} - -static void -_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, - const cairo_radial_pattern_t *rpat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) -{ - const cairo_pattern_t *abspat = &rpat->base.base; - cairo_matrix_t mat; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; - double c1x = _cairo_fixed_to_double (rpat->c1.x); - double c1y = _cairo_fixed_to_double (rpat->c1.y); - double c2x = _cairo_fixed_to_double (rpat->c2.x); - double c2y = _cairo_fixed_to_double (rpat->c2.y); - double r1 = _cairo_fixed_to_double (rpat->r1); - double r2 = _cairo_fixed_to_double (rpat->r2); - double dx = c1x - c2x; - double dy = c1y - c2y; - double centerDistance = sqrt (dx*dx + dy*dy); - - if (rpat->base.n_stops == 0) { - CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); - CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); - state->action = DO_SOLID; - return; - } - - if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */ - r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */ - /* Quartz handles cases where neither circle contains the other very - * differently from pixman. - * Whatever the correct behaviour is, let's at least have only pixman's - * implementation to worry about. - * Note that this also catches the cases where r1 == r2. - */ - _cairo_quartz_setup_fallback_source (surface, abspat, state); - return; - } - - mat = abspat->matrix; - cairo_matrix_invert (&mat); - _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); - - rgb = CGColorSpaceCreateDeviceRGB(); - - start = CGPointMake (c1x, c1y); - end = CGPointMake (c2x, c2y); - - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) - { - gradFunc = CreateGradientFunction (&rpat->base); - } else { - gradFunc = CreateRepeatingRadialGradientFunction (surface, - &rpat->base, - &start, &r1, - &end, &r2, - extents); - } - - state->shading = CGShadingCreateRadial (rgb, - start, - r1, - end, - r2, - gradFunc, - extend, extend); - - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - state->action = DO_SHADING; -} - -static void -_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface, - const cairo_surface_pattern_t *spat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) -{ - const cairo_pattern_t *source = &spat->base; - CGContextRef context = state->context; - - if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD || - (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)) - { - cairo_surface_t *pat_surf = spat->surface; - CGImageRef img; - cairo_matrix_t m = spat->base.matrix; - cairo_rectangle_int_t extents; - CGAffineTransform xform; - CGRect srcRect; - cairo_fixed_t fw, fh; - cairo_bool_t is_bounded; - cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT; - cairo_status_t status; - - cairo_matrix_invert(&m); - _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); - - /* Draw nonrepeating CGLayer surface using DO_LAYER */ - if (!repeat && cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { - cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf; - if (quartz_surf->cgLayer) { - state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height); - state->layer = quartz_surf->cgLayer; - state->action = DO_LAYER; - return; - } - } - - status = _cairo_surface_to_cgimage (pat_surf, &img); - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - if (img == NULL) { - state->action = DO_NOTHING; - return; - } - - /* XXXroc what is this for? */ - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); - - state->image = img; - - is_bounded = _cairo_surface_get_extents (pat_surf, &extents); - assert (is_bounded); - - if (!repeat) { - state->imageRect = CGRectMake (0, 0, extents.width, extents.height); - state->action = DO_IMAGE; - return; - } - - /* Quartz seems to tile images at pixel-aligned regions only -- this - * leads to seams if the image doesn't end up scaling to fill the - * space exactly. The CGPattern tiling approach doesn't have this - * problem. Check if we're going to fill up the space (within some - * epsilon), and if not, fall back to the CGPattern type. - */ - - xform = CGAffineTransformConcat (CGContextGetCTM (context), - state->transform); - - srcRect = CGRectMake (0, 0, extents.width, extents.height); - srcRect = CGRectApplyAffineTransform (srcRect, xform); - - fw = _cairo_fixed_from_double (srcRect.size.width); - fh = _cairo_fixed_from_double (srcRect.size.height); - - if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON && - (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON) - { - /* We're good to use DrawTiledImage, but ensure that - * the math works out */ - - srcRect.size.width = round(srcRect.size.width); - srcRect.size.height = round(srcRect.size.height); - - xform = CGAffineTransformInvert (xform); - - srcRect = CGRectApplyAffineTransform (srcRect, xform); - - state->imageRect = srcRect; - state->action = DO_TILED_IMAGE; - return; - } - - /* Fall through to generic SURFACE case */ - } - - CGFloat patternAlpha = 1.0f; - CGColorSpaceRef patternSpace; - CGPatternRef pattern; - cairo_int_status_t status; - - status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - state->action = DO_NOTHING; - return; - } - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - - patternSpace = CGColorSpaceCreatePattern (NULL); - CGContextSetFillColorSpace (context, patternSpace); - CGContextSetFillPattern (context, pattern, &patternAlpha); - CGContextSetStrokeColorSpace (context, patternSpace); - CGContextSetStrokePattern (context, pattern, &patternAlpha); - CGColorSpaceRelease (patternSpace); - - /* Quartz likes to munge the pattern phase (as yet unexplained - * why); force it to 0,0 as we've already baked in the correct - * pattern translation into the pattern matrix - */ - CGContextSetPatternPhase (context, CGSizeMake(0,0)); - - state->pattern = pattern; - state->action = DO_PATTERN; - return; -} - -/** - * Call this before any operation that can modify the contents of a - * cairo_quartz_surface_t. - */ -static void -_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface) -{ - if (surface->bitmapContextImage) { - CGImageRelease (surface->bitmapContextImage); - surface->bitmapContextImage = NULL; - } -} - -/** - * Sets up internal state to be used to draw the source mask, stored in - * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on - * surface->cgContext. - */ -static cairo_quartz_drawing_state_t -_cairo_quartz_setup_state (cairo_quartz_surface_t *surface, - const cairo_pattern_t *source, - cairo_operator_t op, - cairo_rectangle_int_t *extents) -{ - CGContextRef context = surface->cgContext; - cairo_quartz_drawing_state_t state; - cairo_status_t status; - - state.context = context; - state.image = NULL; - state.imageSurface = NULL; - state.layer = NULL; - state.shading = NULL; - state.pattern = NULL; - - _cairo_quartz_surface_will_change (surface); - - // Save before we change the pattern, colorspace, etc. so that - // we can restore and make sure that quartz releases our - // pattern (which may be stack allocated) - CGContextSaveGState(context); - - CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter)); - - status = _cairo_quartz_surface_set_cairo_operator (surface, op); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - state.action = DO_NOTHING; - return state; - } - if (status) { - state.action = DO_UNSUPPORTED; - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - - CGContextSetRGBStrokeColor (context, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - CGContextSetRGBFillColor (context, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - state.action = DO_SOLID; - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_LINEAR) { - const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source; - _cairo_quartz_setup_linear_source (surface, lpat, extents, &state); - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_RADIAL) { - const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source; - _cairo_quartz_setup_radial_source (surface, rpat, extents, &state); - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) && - CGContextGetAlphaPtr && - CGContextGetAlphaPtr (surface->cgContext) == 1.0) { - // Quartz won't touch pixels outside the bounds of the - // source surface, so we can just go ahead and use Copy here - // to accelerate things. - // Quartz won't necessarily be able to do this optimization internally; - // for CGLayer surfaces, we can know all the pixels are opaque - // (because it's CONTENT_COLOR), but Quartz won't know. - CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy); - } - - const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; - _cairo_quartz_setup_surface_source (surface, spat, extents, &state); - return state; - } - - state.action = DO_UNSUPPORTED; - return state; -} - -/** - * 1) Tears down internal state used to draw the source - * 2) Does CGContextRestoreGState(state->context) - */ -static void -_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state) -{ - if (state->image) { - CGImageRelease(state->image); - } - - if (state->imageSurface) { - cairo_surface_destroy(state->imageSurface); - } - - if (state->shading) { - CGShadingRelease(state->shading); - } - - if (state->pattern) { - CGPatternRelease(state->pattern); - } - - CGContextRestoreGState(state->context); -} - - -static void -_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op) -{ - assert (state && - ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) || - (state->layer && state->action == DO_LAYER))); - - CGContextConcatCTM (state->context, state->transform); - CGContextTranslateCTM (state->context, 0, state->imageRect.size.height); - CGContextScaleCTM (state->context, 1, -1); - - if (state->action == DO_TILED_IMAGE) { - CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image); - /* no need to worry about unbounded operators, since tiled images - fill the entire clip region */ - } else { - if (state->action == DO_LAYER) { - /* Note that according to Apple docs it's completely legal - * to draw a CGLayer to any CGContext, even one it wasn't - * created for. - */ - CGContextDrawLayerAtPoint (state->context, state->imageRect.origin, - state->layer); - } else { - CGContextDrawImage (state->context, state->imageRect, state->image); - } - - /* disable this EXTEND_NONE correctness code because we use this path - * for both EXTEND_NONE and EXTEND_PAD */ - if (0 && !_cairo_operator_bounded_by_source (op)) { - CGContextBeginPath (state->context); - CGContextAddRect (state->context, state->imageRect); - CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context)); - CGContextSetRGBFillColor (state->context, 0, 0, 0, 0); - CGContextEOFillPath (state->context); - } - } -} - - -/* - * get source/dest image implementation - */ - -/* Read the image from the surface's front buffer */ -static cairo_int_status_t -_cairo_quartz_get_image (cairo_quartz_surface_t *surface, - cairo_image_surface_t **image_out) -{ - unsigned char *imageData; - cairo_image_surface_t *isurf; - - if (IS_EMPTY(surface)) { - *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - return CAIRO_STATUS_SUCCESS; - } - - if (surface->imageSurfaceEquiv) { - CGContextFlush(surface->cgContext); - *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv); - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) { - unsigned int stride; - unsigned int bitinfo; - unsigned int bpc, bpp; - CGColorSpaceRef colorspace; - unsigned int color_comps; - - CGContextFlush(surface->cgContext); - imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext); - -#ifdef USE_10_3_WORKAROUNDS - bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext); -#else - bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); -#endif - stride = CGBitmapContextGetBytesPerRow (surface->cgContext); - bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); - bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext); - - // let's hope they don't add YUV under us - colorspace = CGBitmapContextGetColorSpace (surface->cgContext); - color_comps = CGColorSpaceGetNumberOfComponents(colorspace); - - // XXX TODO: We can handle all of these by converting to - // pixman masks, including non-native-endian masks - if (bpc != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp != 32 && bpp != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (color_comps != 3 && color_comps != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_ARGB32, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_RGB24, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 8 && color_comps == 1) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_A8, - surface->extents.width, - surface->extents.height, - stride); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *image_out = isurf; - return CAIRO_STATUS_SUCCESS; -} - -/* - * Cairo surface backend implementations - */ - -static cairo_status_t -_cairo_quartz_surface_finish (void *abstract_surface) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - - ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - /* Restore our saved gstate that we use to reset clipping */ - CGContextRestoreGState (surface->cgContext); - _cairo_surface_clipper_reset (&surface->clipper); - - CGContextRelease (surface->cgContext); - - surface->cgContext = NULL; - - if (surface->bitmapContextImage) { - CGImageRelease (surface->bitmapContextImage); - surface->bitmapContextImage = NULL; - } - - if (surface->imageSurfaceEquiv) { - if (surface->ownsData) - _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv); - cairo_surface_destroy (surface->imageSurfaceEquiv); - surface->imageSurfaceEquiv = NULL; - } else if (surface->imageData && surface->ownsData) { - free (surface->imageData); - } - - surface->imageData = NULL; - - if (surface->cgLayer) { - CGLayerRelease (surface->cgLayer); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_quartz_surface_acquire_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_int_status_t status; - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - - *image_extra = NULL; - - /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */ - - status = _cairo_quartz_get_image (surface, image_out); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) { - /* copy the layer into a Quartz bitmap context so we can get the data */ - cairo_surface_t *tmp = - cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, - surface->extents.width, - surface->extents.height); - cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp; - - /* if surface creation failed, we won't have a Quartz surface here */ - if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ && - tmp_surface->imageSurfaceEquiv) { - CGContextSaveGState (tmp_surface->cgContext); - CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height); - CGContextScaleCTM (tmp_surface->cgContext, 1, -1); - /* Note that according to Apple docs it's completely legal - * to draw a CGLayer to any CGContext, even one it wasn't - * created for. - */ - CGContextDrawLayerAtPoint (tmp_surface->cgContext, - CGPointMake (0.0, 0.0), - surface->cgLayer); - CGContextRestoreGState (tmp_surface->cgContext); - - *image_out = (cairo_image_surface_t*) - cairo_surface_reference(tmp_surface->imageSurfaceEquiv); - *image_extra = tmp; - status = CAIRO_STATUS_SUCCESS; - } else { - cairo_surface_destroy (tmp); - } - } - - if (status) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_quartz_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy ((cairo_surface_t *) image); - - if (image_extra) { - cairo_surface_destroy ((cairo_surface_t *) image_extra); - } -} - - -static cairo_status_t -_cairo_quartz_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - - ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface)); - - *image_rect = surface->extents; - *image_extra = NULL; - - _cairo_quartz_surface_will_change (surface); - - return _cairo_quartz_surface_acquire_image (abstract_surface, - image_out, image_extra); -} - -static void -_cairo_quartz_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */ - - cairo_surface_destroy ((cairo_surface_t *) image); - - if (image_extra) { - /* we need to write the data from the temp surface back to the layer */ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra; - CGImageRef img; - cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img); - if (status) { - cairo_surface_destroy (&tmp_surface->base); - return; - } - - CGContextSaveGState (surface->cgContext); - CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - CGContextDrawImage (surface->cgContext, - CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height), - img); - CGContextRestoreGState (surface->cgContext); - - cairo_surface_destroy (&tmp_surface->base); - } -} - -static cairo_surface_t * -_cairo_quartz_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_format_t format; - - if (surface->cgLayer) - return cairo_quartz_surface_create_cg_layer (abstract_surface, content, - width, height); - - if (content == CAIRO_CONTENT_COLOR_ALPHA) - format = CAIRO_FORMAT_ARGB32; - else if (content == CAIRO_CONTENT_COLOR) - format = CAIRO_FORMAT_RGB24; - else if (content == CAIRO_CONTENT_ALPHA) - format = CAIRO_FORMAT_A8; - else - return NULL; - - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) { - return _cairo_surface_create_in_error (_cairo_error - (CAIRO_STATUS_INVALID_SIZE)); - } - - return cairo_quartz_surface_create (format, width, height); -} - -static cairo_status_t -_cairo_quartz_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_quartz_surface_t *new_surface = NULL; - cairo_format_t new_format; - CGImageRef quartz_image = NULL; - cairo_status_t status; - - *clone_out = NULL; - - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (width == 0 || height == 0) { - *clone_out = (cairo_surface_t*) - _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, - width, height); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_surface_is_quartz (src)) { - cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src; - - if (IS_EMPTY(qsurf)) { - *clone_out = (cairo_surface_t*) - _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, - qsurf->extents.width, qsurf->extents.height); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - } - - status = _cairo_surface_to_cgimage (src, &quartz_image); - if (status) - return CAIRO_INT_STATUS_UNSUPPORTED; - - new_format = CAIRO_FORMAT_ARGB32; /* assumed */ - if (_cairo_surface_is_image (src)) { - new_format = ((cairo_image_surface_t *) src)->format; - } - - new_surface = (cairo_quartz_surface_t *) - cairo_quartz_surface_create (new_format, width, height); - - if (quartz_image == NULL) - goto FINISH; - - if (!new_surface || new_surface->base.status) { - CGImageRelease (quartz_image); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - CGContextSaveGState (new_surface->cgContext); - - CGContextSetCompositeOperation (new_surface->cgContext, - kPrivateCGCompositeCopy); - - CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y); - CGContextDrawImage (new_surface->cgContext, - CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)), - quartz_image); - - CGContextRestoreGState (new_surface->cgContext); - - CGImageRelease (quartz_image); - -FINISH: - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = (cairo_surface_t*) new_surface; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_quartz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - - *extents = surface->extents; - return TRUE; -} - -static cairo_int_status_t -_cairo_quartz_surface_paint_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_quartz_drawing_state_t state; - - ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - state = _cairo_quartz_setup_state (surface, source, op, NULL); - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextFillRect (state.context, CGRectMake(surface->extents.x, - surface->extents.y, - surface->extents.width, - surface->extents.height)); - } else if (state.action == DO_SHADING) { - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - _cairo_quartz_draw_image (&state, op); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_quartz_teardown_state (&state); - - ND((stderr, "-- paint\n")); - return rv; -} - -static cairo_bool_t -_cairo_quartz_source_needs_extents (const cairo_pattern_t *source) -{ - /* For repeating gradients we need to manually extend the gradient and - repeat stops, since Quartz doesn't support repeating gradients natively. - We need to minimze the number of repeated stops, and since rasterization - depends on the number of repetitions we use (even if some of the - repetitions go beyond the extents of the object or outside the clip - region), it's important to use the same number of repetitions when - rendering an object no matter what the clip region is. So the - computation of the repetition count cannot depended on the clip region, - and should only depend on the object extents, so we need to compute - the object extents for repeating gradients. */ - return (source->type == CAIRO_PATTERN_TYPE_LINEAR || - source->type == CAIRO_PATTERN_TYPE_RADIAL) && - (source->extend == CAIRO_EXTEND_REPEAT || - source->extend == CAIRO_EXTEND_REFLECT); -} - -static cairo_int_status_t -_cairo_quartz_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_paint_cg (abstract_surface, - op, - source, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_paint (&image->base, op, source, clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_fill_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_quartz_drawing_state_t state; - CGPathRef path_for_unbounded = NULL; - - ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - if (_cairo_quartz_source_needs_extents (source)) - { - /* We don't need precise extents since these are only used to - compute the number of gradient reptitions needed to cover the - object. */ - cairo_rectangle_int_t path_extents; - _cairo_path_fixed_approximate_fill_extents (path, &path_extents); - state = _cairo_quartz_setup_state (surface, source, op, &path_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); - } - - CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); - - _cairo_quartz_cairo_path_to_quartz_context (path, state.context); - - if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr) - path_for_unbounded = CGContextCopyPathPtr (state.context); - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (state.context); - else - CGContextEOFillPath (state.context); - } else if (state.action == DO_SHADING) { - - // we have to clip and then paint the shading; we can't fill - // with the shading - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (state.context); - else - CGContextEOClip (state.context); - - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (state.context); - else - CGContextEOClip (state.context); - - _cairo_quartz_draw_image (&state, op); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_quartz_teardown_state (&state); - - if (path_for_unbounded) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_STROKE_FILL; - ub.u.stroke_fill.cgPath = path_for_unbounded; - ub.u.stroke_fill.fill_rule = fill_rule; - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); - CGPathRelease (path_for_unbounded); - } - - ND((stderr, "-- fill\n")); - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_fill_cg (abstract_surface, - op, - source, - path, - fill_rule, - tolerance, - antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_fill (&image->base, op, source, - path, fill_rule, tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_stroke_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_quartz_drawing_state_t state; - CGAffineTransform origCTM, strokeTransform; - CGPathRef path_for_unbounded = NULL; - - ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); - if (unlikely (rv)) - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; - - if (_cairo_quartz_source_needs_extents (source)) - { - cairo_rectangle_int_t path_extents; - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents); - state = _cairo_quartz_setup_state (surface, source, op, &path_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); - } - - // Turning antialiasing off used to cause misrendering with - // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). - // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases. - CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetLineWidth (state.context, style->line_width); - CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); - CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); - CGContextSetMiterLimit (state.context, style->miter_limit); - - origCTM = CGContextGetCTM (state.context); - - if (style->dash && style->num_dashes) { -#define STATIC_DASH 32 - cairo_quartz_float_t sdash[STATIC_DASH]; - cairo_quartz_float_t *fdash = sdash; - unsigned int max_dashes = style->num_dashes; - unsigned int k; - - bool set_line_dash = false; - if (style->num_dashes % 2 == 0) { - for (k = 1; k < max_dashes; k++) { - if (style->dash[k]) { - set_line_dash = true; - break; - } - } - } else - set_line_dash = true; - - if (set_line_dash) { - if (style->num_dashes%2) - max_dashes *= 2; - if (max_dashes > STATIC_DASH) - fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t)); - if (fdash == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (k = 0; k < max_dashes; k++) - fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes]; - - CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes); - if (fdash != sdash) - free (fdash); - } else - CGContextSetLineDash (state.context, 0, NULL, 0); - } else - CGContextSetLineDash (state.context, 0, NULL, 0); - - - _cairo_quartz_cairo_path_to_quartz_context (path, state.context); - - _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); - CGContextConcatCTM (state.context, strokeTransform); - - if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr) - path_for_unbounded = CGContextCopyPathPtr (state.context); - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextStrokePath (state.context); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - CGContextReplacePathWithStrokedPath (state.context); - CGContextClip (state.context); - - CGContextSetCTM (state.context, origCTM); - _cairo_quartz_draw_image (&state, op); - } else if (state.action == DO_SHADING) { - CGContextReplacePathWithStrokedPath (state.context); - CGContextClip (state.context); - - CGContextSetCTM (state.context, origCTM); - - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - - if (path_for_unbounded) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_STROKE_FILL; - ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING; - - CGContextBeginPath (state.context); - CGContextAddPath (state.context, path_for_unbounded); - CGPathRelease (path_for_unbounded); - - CGContextSaveGState (state.context); - CGContextConcatCTM (state.context, strokeTransform); - CGContextReplacePathWithStrokedPath (state.context); - CGContextRestoreGState (state.context); - - ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context); - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); - CGPathRelease (ub.u.stroke_fill.cgPath); - } - - BAIL: - _cairo_quartz_teardown_state (&state); - - ND((stderr, "-- stroke\n")); - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_stroke_cg (abstract_surface, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_stroke (&image->base, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -#if CAIRO_HAS_QUARTZ_FONT -static cairo_int_status_t -_cairo_quartz_surface_show_glyphs_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - CGAffineTransform textTransform, ctm, invTextTransform; -#define STATIC_BUF_SIZE 64 - CGGlyph glyphs_static[STATIC_BUF_SIZE]; - CGSize cg_advances_static[STATIC_BUF_SIZE]; - CGGlyph *cg_glyphs = &glyphs_static[0]; - /* We'll use the cg_advances array for either advances or positions, - depending which API we're using to actually draw. The types involved - have the same size, so this is safe. */ - CGSize *cg_advances = &cg_advances_static[0]; - - cairo_rectangle_int_t glyph_extents; - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - cairo_quartz_drawing_state_t state; - cairo_quartz_float_t xprev, yprev; - int i; - CGFontRef cgfref = NULL; - - cairo_bool_t isClipping = FALSE; - cairo_bool_t didForceFontSmoothing = FALSE; - cairo_antialias_t effective_antialiasing; - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; - - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) - return CAIRO_INT_STATUS_UNSUPPORTED; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - if (_cairo_quartz_source_needs_extents (source) && - !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents, NULL)) - { - state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); - } - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextSetTextDrawingMode (state.context, kCGTextFill); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_SHADING || state.action == DO_LAYER) { - CGContextSetTextDrawingMode (state.context, kCGTextClip); - isClipping = TRUE; - } else { - if (state.action != DO_NOTHING) - rv = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; - } - - /* this doesn't addref */ - cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); - CGContextSetFont (state.context, cgfref); - CGContextSetFontSize (state.context, 1.0); - - effective_antialiasing = scaled_font->options.antialias; - if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && - !surface->base.permit_subpixel_antialiasing) { - effective_antialiasing = CAIRO_ANTIALIAS_GRAY; - } - - switch (scaled_font->options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - CGContextSetShouldAntialias (state.context, TRUE); - CGContextSetShouldSmoothFonts (state.context, TRUE); - if (CGContextSetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (state.context)) - { - didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothingPtr (state.context, TRUE); - } - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (state.context, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - CGContextSetShouldAntialias (state.context, TRUE); - CGContextSetShouldSmoothFonts (state.context, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - /* Don't do anything */ - break; - } - - if (num_glyphs > STATIC_BUF_SIZE) { - cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph)); - if (cg_glyphs == NULL) { - rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize)); - if (cg_advances == NULL) { - rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - /* scale(1,-1) * scaled_font->scale */ - textTransform = CGAffineTransformMake (scaled_font->scale.xx, - scaled_font->scale.yx, - -scaled_font->scale.xy, - -scaled_font->scale.yy, - 0, 0); - - /* scaled_font->scale_inverse * scale(1,-1) */ - invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx, - -scaled_font->scale_inverse.yx, - scaled_font->scale_inverse.xy, - -scaled_font->scale_inverse.yy, - 0.0, 0.0); - - CGContextSetTextMatrix (state.context, CGAffineTransformIdentity); - - /* Translate to the first glyph's position before drawing */ - ctm = CGContextGetCTM (state.context); - CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y); - CGContextConcatCTM (state.context, textTransform); - - if (CTFontDrawGlyphsPtr) { - /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use - * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap - * fonts like Apple Color Emoji will render properly. - * For this, we need to convert our glyph positions to Core Graphics's CGPoint. - * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */ - - CGPoint *cg_positions = (CGPoint*) cg_advances; - cairo_quartz_float_t origin_x = glyphs[0].x; - cairo_quartz_float_t origin_y = glyphs[0].y; - - for (i = 0; i < num_glyphs; i++) { - CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y); - cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform); - cg_glyphs[i] = glyphs[i].index; - } - - CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font), - cg_glyphs, cg_positions, num_glyphs, state.context); - } else { - /* Convert our glyph positions to glyph advances. We need n-1 advances, - * since the advance at index 0 is applied after glyph 0. */ - xprev = glyphs[0].x; - yprev = glyphs[0].y; - - cg_glyphs[0] = glyphs[0].index; - - for (i = 1; i < num_glyphs; i++) { - cairo_quartz_float_t xf = glyphs[i].x; - cairo_quartz_float_t yf = glyphs[i].y; - cg_glyphs[i] = glyphs[i].index; - cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform); - xprev = xf; - yprev = yf; - } - - CGContextShowGlyphsWithAdvances (state.context, - cg_glyphs, - cg_advances, - num_glyphs); - } - - CGContextSetCTM (state.context, ctm); - - if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - _cairo_quartz_draw_image (&state, op); - } else if (state.action == DO_SHADING) { - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } - -BAIL: - if (didForceFontSmoothing) - CGContextSetAllowsFontSmoothingPtr (state.context, FALSE); - - _cairo_quartz_teardown_state (&state); - - if (rv == CAIRO_STATUS_SUCCESS && - cgfref && - !_cairo_operator_bounded_by_mask (op)) - { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_SHOW_GLYPHS; - - ub.u.show_glyphs.isClipping = isClipping; - ub.u.show_glyphs.cg_glyphs = cg_glyphs; - if (CTFontDrawGlyphsPtr) { - /* we're using Core Text API: the cg_advances array was - reused (above) for glyph positions */ - CGPoint *cg_positions = (CGPoint*) cg_advances; - ub.u.show_glyphs.u.cg_positions = cg_positions; - } else { - ub.u.show_glyphs.u.cg_advances = cg_advances; - } - ub.u.show_glyphs.nglyphs = num_glyphs; - ub.u.show_glyphs.textTransform = textTransform; - ub.u.show_glyphs.scaled_font = scaled_font; - ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y); - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias); - } - - - if (cg_advances != &cg_advances_static[0]) { - free (cg_advances); - } - - if (cg_glyphs != &glyphs_static[0]) { - free (cg_glyphs); - } - - return rv; -} -#endif /* CAIRO_HAS_QUARTZ_FONT */ - -static cairo_int_status_t -_cairo_quartz_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_image_surface_t *image; - -#if CAIRO_HAS_QUARTZ_FONT - rv = _cairo_quartz_surface_show_glyphs_cg (abstract_surface, op, source, - glyphs, num_glyphs, - scaled_font, clip, remaining_glyphs); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - -#endif - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_show_text_glyphs (&image->base, op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_surface_pattern_t *mask, - cairo_clip_t *clip) -{ - CGRect rect; - CGImageRef img; - cairo_surface_t *pat_surf = mask->surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - CGAffineTransform ctm, mask_matrix; - - status = _cairo_surface_to_cgimage (pat_surf, &img); - if (status) - return status; - if (img == NULL) { - if (!_cairo_operator_bounded_by_mask (op)) - CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); - return CAIRO_STATUS_SUCCESS; - } - - rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img)); - - CGContextSaveGState (surface->cgContext); - - /* ClipToMask is essentially drawing an image, so we need to flip the CTM - * to get the image to appear oriented the right way */ - ctm = CGContextGetCTM (surface->cgContext); - - _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix); - mask_matrix = CGAffineTransformInvert(mask_matrix); - mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img)); - mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0); - - CGContextConcatCTM (surface->cgContext, mask_matrix); - CGContextClipToMaskPtr (surface->cgContext, rect, img); - - CGContextSetCTM (surface->cgContext, ctm); - - status = _cairo_quartz_surface_paint_cg (surface, op, source, clip); - - CGContextRestoreGState (surface->cgContext); - - if (!_cairo_operator_bounded_by_mask (op)) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_MASK; - ub.u.mask.mask = img; - ub.u.mask.maskTransform = mask_matrix; - _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE); - } - - CGImageRelease (img); - - return status; -} - -/* This is somewhat less than ideal, but it gets the job done; - * it would be better to avoid calling back into cairo. This - * creates a temporary surface to use as the mask. - */ -static cairo_int_status_t -_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - int width = surface->extents.width; - int height = surface->extents.height; - - cairo_surface_t *gradient_surf = NULL; - cairo_surface_pattern_t surface_pattern; - cairo_int_status_t status; - - /* Render the gradient to a surface */ - gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8, - width, - height); - - status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL); - if (status) - goto BAIL; - - _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf); - - status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip); - - _cairo_pattern_fini (&surface_pattern.base); - - BAIL: - if (gradient_surf) - cairo_surface_destroy (gradient_surf); - - return status; -} - -static cairo_int_status_t -_cairo_quartz_surface_mask_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - - ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */ - if (mask->type == CAIRO_PATTERN_TYPE_SOLID && - op == CAIRO_OPERATOR_OVER) { - /* This is easy; we just need to paint with the alpha. */ - cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; - - CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip); - CGContextSetAlpha (surface->cgContext, 1.0); - - return rv; - } - - /* If we have CGContextClipToMask, we can do more complex masks */ - if (CGContextClipToMaskPtr) { - /* For these, we can skip creating a temporary surface, since we already have one */ - /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ - if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && - mask->extend == CAIRO_EXTEND_NONE) { - return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); - } - - return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); - } - - /* So, CGContextClipToMask is not present in 10.3.9, so we're - * doomed; if we have imageData, we can do fallback, otherwise - * just pretend success. - */ - if (surface->imageData) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_quartz_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_mask_cg (abstract_surface, - op, - source, - mask, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_mask (&image->base, op, source, mask, clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_status_t -_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_quartz_surface_t *surface = - cairo_container_of (clipper, cairo_quartz_surface_t, clipper); - - ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - if (path == NULL) { - /* If we're being asked to reset the clip, we can only do it - * by restoring the gstate to our previous saved one, and - * saving it again. - * - * Note that this assumes that ALL quartz surface creation - * functions will do a SaveGState first; we do this in create_internal. - */ - CGContextRestoreGState (surface->cgContext); - CGContextSaveGState (surface->cgContext); - } else { - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - - _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext); - - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (surface->cgContext); - else - CGContextEOClip (surface->cgContext); - } - - ND((stderr, "-- intersect_clip_path\n")); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface, - int x, int y, - int width, int height) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - _cairo_quartz_surface_will_change (surface); - return CAIRO_STATUS_SUCCESS; -} - - -// XXXtodo implement show_page; need to figure out how to handle begin/end - -static const struct _cairo_surface_backend cairo_quartz_surface_backend = { - CAIRO_SURFACE_TYPE_QUARTZ, - _cairo_quartz_surface_create_similar, - _cairo_quartz_surface_finish, - _cairo_quartz_surface_acquire_image, - _cairo_quartz_surface_release_source_image, - _cairo_quartz_surface_acquire_dest_image, - _cairo_quartz_surface_release_dest_image, - _cairo_quartz_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_quartz_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - _cairo_quartz_surface_mark_dirty_rectangle, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_quartz_surface_paint, - _cairo_quartz_surface_mask, - _cairo_quartz_surface_stroke, - _cairo_quartz_surface_fill, - _cairo_quartz_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL /* fill_stroke */ -}; - -cairo_quartz_surface_t * -_cairo_quartz_surface_create_internal (CGContextRef cgContext, - cairo_content_t content, - unsigned int width, - unsigned int height) -{ - cairo_quartz_surface_t *surface; - - quartz_ensure_symbols(); - - /* Init the base surface */ - surface = malloc(sizeof(cairo_quartz_surface_t)); - if (surface == NULL) - return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - memset(surface, 0, sizeof(cairo_quartz_surface_t)); - - _cairo_surface_init (&surface->base, - &cairo_quartz_surface_backend, - NULL, /* device */ - content); - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_quartz_surface_clipper_intersect_clip_path); - - /* Save our extents */ - surface->extents.x = surface->extents.y = 0; - surface->extents.width = width; - surface->extents.height = height; - - if (IS_EMPTY(surface)) { - surface->cgContext = NULL; - surface->cgContextBaseCTM = CGAffineTransformIdentity; - surface->imageData = NULL; - return surface; - } - - /* Save so we can always get back to a known-good CGContext -- this is - * required for proper behaviour of intersect_clip_path(NULL) - */ - CGContextSaveGState (cgContext); - - surface->cgContext = cgContext; - surface->cgContextBaseCTM = CGContextGetCTM (cgContext); - - surface->imageData = NULL; - surface->imageSurfaceEquiv = NULL; - surface->bitmapContextImage = NULL; - surface->cgLayer = NULL; - surface->ownsData = TRUE; - - return surface; -} - -/** - * cairo_quartz_surface_create_for_cg_context - * @cgContext: the existing CGContext for which to create the surface - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a Quartz surface that wraps the given CGContext. The - * CGContext is assumed to be in the standard Cairo coordinate space - * (that is, with the origin at the upper left and the Y axis - * increasing downward). If the CGContext is in the Quartz coordinate - * space (with the origin at the bottom left), then it should be - * flipped before this function is called. The flip can be accomplished - * using a translate and a scale; for example: - * - * - * CGContextTranslateCTM (cgContext, 0.0, height); - * CGContextScaleCTM (cgContext, 1.0, -1.0); - * - * - * All Cairo operations are implemented in terms of Quartz operations, - * as long as Quartz-compatible elements are used (such as Quartz fonts). - * - * Return value: the newly created Cairo surface. - * - * Since: 1.4 - **/ - -cairo_surface_t * -cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, - unsigned int width, - unsigned int height) -{ - cairo_quartz_surface_t *surf; - - CGContextRetain (cgContext); - - surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, - width, height); - if (surf->base.status) { - CGContextRelease (cgContext); - // create_internal will have set an error - return (cairo_surface_t*) surf; - } - - return (cairo_surface_t *) surf; -} - -/** - * cairo_quartz_cglayer_surface_create_similar - * @surface: The returned surface can be efficiently drawn into this - * destination surface (if tiling is not used)." - * @content: the content type of the surface - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a Quartz surface backed by a CGLayer, if the given surface - * is a Quartz surface; the CGLayer is created to match the surface's - * Quartz context. Otherwise just calls cairo_surface_create_similar. - * The returned surface can be efficiently blitted to the given surface, - * but tiling and 'extend' modes other than NONE are not so efficient. - * - * Return value: the newly created surface. - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, - cairo_content_t content, - unsigned int width, - unsigned int height) -{ - cairo_quartz_surface_t *surf; - CGLayerRef layer; - CGContextRef ctx; - CGContextRef cgContext; - - cgContext = cairo_quartz_surface_get_cg_context (surface); - if (!cgContext) - return cairo_surface_create_similar (surface, content, - width, height); - - if (!_cairo_quartz_verify_surface_size(width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - /* If we pass zero width or height into CGLayerCreateWithContext below, - * it will fail. - */ - if (width == 0 || height == 0) { - return (cairo_surface_t*) - _cairo_quartz_surface_create_internal (NULL, content, - width, height); - } - - layer = CGLayerCreateWithContext (cgContext, - CGSizeMake (width, height), - NULL); - if (!layer) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - ctx = CGLayerGetContext (layer); - /* Flip it when we draw into it, so that when we finally composite it - * to a flipped target, the directions match and Quartz will optimize - * the composition properly - */ - CGContextTranslateCTM (ctx, 0, height); - CGContextScaleCTM (ctx, 1, -1); - - CGContextRetain (ctx); - surf = _cairo_quartz_surface_create_internal (ctx, content, - width, height); - if (surf->base.status) { - CGLayerRelease (layer); - // create_internal will have set an error - return (cairo_surface_t*) surf; - } - surf->cgLayer = layer; - - return (cairo_surface_t *) surf; -} - -/** - * cairo_quartz_surface_create - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a Quartz surface backed by a CGBitmap. The surface is - * created using the Device RGB (or Device Gray, for A8) color space. - * All Cairo operations, including those that require software - * rendering, will succeed on this surface. - * - * Return value: the newly created surface. - * - * Since: 1.4 - **/ -cairo_surface_t * -cairo_quartz_surface_create (cairo_format_t format, - unsigned int width, - unsigned int height) -{ - int stride; - unsigned char *data; - - if (!_cairo_quartz_verify_surface_size(width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - if (width == 0 || height == 0) { - return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), - width, height); - } - - if (format == CAIRO_FORMAT_ARGB32 || - format == CAIRO_FORMAT_RGB24) - { - stride = width * 4; - } else if (format == CAIRO_FORMAT_A8) { - stride = width; - } else if (format == CAIRO_FORMAT_A1) { - /* I don't think we can usefully support this, as defined by - * cairo_format_t -- these are 1-bit pixels stored in 32-bit - * quantities. - */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } else { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } - - /* The Apple docs say that for best performance, the stride and the data - * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, - * so we don't have to anything special on allocation. - */ - stride = (stride + 15) & ~15; - - data = _cairo_malloc_ab (height, stride); - if (!data) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* zero the memory to match the image surface behaviour */ - memset (data, 0, height * stride); - - cairo_quartz_surface_t *surf; - surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data - (data, format, width, height, stride); - if (surf->base.status) { - free (data); - return (cairo_surface_t *) surf; - } - - // We created this data, so we can delete it. - surf->ownsData = TRUE; - - return (cairo_surface_t *) surf; -} - -/** - * cairo_quartz_surface_create_for_data - * @data: a pointer to a buffer supplied by the application in which - * to write contents. This pointer must be suitably aligned for any - * kind of variable, (for example, a pointer returned by malloc). - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a Quartz surface backed by a CGBitmap. The surface is - * created using the Device RGB (or Device Gray, for A8) color space. - * All Cairo operations, including those that require software - * rendering, will succeed on this surface. - * - * Return value: the newly created surface. - * - * Since: 1.12 - **/ -cairo_surface_t * -cairo_quartz_surface_create_for_data (unsigned char *data, - cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride) -{ - cairo_quartz_surface_t *surf; - CGContextRef cgc; - CGColorSpaceRef cgColorspace; - CGBitmapInfo bitinfo; - void *imageData = data; - int bitsPerComponent; - unsigned int i; - - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - if (width == 0 || height == 0) { - return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), - width, height); - } - - if (format == CAIRO_FORMAT_ARGB32 || - format == CAIRO_FORMAT_RGB24) - { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - bitinfo = kCGBitmapByteOrder32Host; - if (format == CAIRO_FORMAT_ARGB32) - bitinfo |= kCGImageAlphaPremultipliedFirst; - else - bitinfo |= kCGImageAlphaNoneSkipFirst; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A8) { - cgColorspace = NULL; - bitinfo = kCGImageAlphaOnly; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A1) { - /* I don't think we can usefully support this, as defined by - * cairo_format_t -- these are 1-bit pixels stored in 32-bit - * quantities. - */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } else { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } - - cgc = CGBitmapContextCreate (imageData, - width, - height, - bitsPerComponent, - stride, - cgColorspace, - bitinfo); - CGColorSpaceRelease (cgColorspace); - - if (!cgc) { - free (imageData); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* flip the Y axis */ - CGContextTranslateCTM (cgc, 0.0, height); - CGContextScaleCTM (cgc, 1.0, -1.0); - - surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), - width, height); - if (surf->base.status) { - CGContextRelease (cgc); - free (imageData); - // create_internal will have set an error - return (cairo_surface_t*) surf; - } - - surf->imageData = imageData; - - cairo_surface_t* tmpImageSurfaceEquiv = - cairo_image_surface_create_for_data (imageData, format, - width, height, stride); - - if (cairo_surface_status (tmpImageSurfaceEquiv)) { - // Tried & failed to create an imageSurfaceEquiv! - cairo_surface_destroy (tmpImageSurfaceEquiv); - surf->imageSurfaceEquiv = NULL; - } else { - surf->imageSurfaceEquiv = tmpImageSurfaceEquiv; - surf->ownsData = FALSE; - } - - return (cairo_surface_t *) surf; -} - -/** - * cairo_quartz_surface_get_cg_context - * @surface: the Cairo Quartz surface - * - * Returns the CGContextRef that the given Quartz surface is backed - * by. - * - * Return value: the CGContextRef for the given surface. - * - * Since: 1.4 - **/ -CGContextRef -cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) -{ - if (surface && _cairo_surface_is_quartz (surface)) { - cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; - return quartz->cgContext; - } else - return NULL; -} - -static cairo_bool_t -_cairo_surface_is_quartz (const cairo_surface_t *surface) -{ - return surface->backend == &cairo_quartz_surface_backend; -} - -CGContextRef -cairo_quartz_get_cg_context_with_clip (cairo_t *cr) -{ - - cairo_surface_t *surface = cr->gstate->target; - cairo_clip_t *clip = &cr->gstate->clip; - cairo_status_t status; - - cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface; - - if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ) - return NULL; - - if (!clip->path) { - if (clip->all_clipped) { - /* Save the state before we set an empty clip rect so that - * our previous clip will be restored */ - - /* _cairo_surface_clipper_set_clip doesn't deal with - * clip->all_clipped because drawing is normally discarded earlier */ - CGRect empty = {{0,0}, {0,0}}; - CGContextClipToRect (quartz->cgContext, empty); - CGContextSaveGState (quartz->cgContext); - - return quartz->cgContext; - } - - /* an empty clip is represented by NULL */ - clip = NULL; - } - - status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip); - - /* Save the state after we set the clip so that it persists - * after we restore */ - CGContextSaveGState (quartz->cgContext); - - if (unlikely (status)) - return NULL; - - return quartz->cgContext; -} - -void -cairo_quartz_finish_cg_context_with_clip (cairo_t *cr) -{ - cairo_surface_t *surface = cr->gstate->target; - - cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface; - - if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ) - return; - - CGContextRestoreGState (quartz->cgContext); -} - -cairo_surface_t * -cairo_quartz_surface_get_image (cairo_surface_t *surface) -{ - cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface; - cairo_image_surface_t *image; - - if (_cairo_quartz_get_image(quartz, &image)) - return NULL; - - return (cairo_surface_t *)image; -} - -/* Debug stuff */ - -#ifdef QUARTZ_DEBUG - -#include - -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/libs/cairo/cairo/src/cairo-quartz.h b/libs/cairo/cairo/src/cairo-quartz.h deleted file mode 100644 index 699a4e7ce..000000000 --- a/libs/cairo/cairo/src/cairo-quartz.h +++ /dev/null @@ -1,81 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_QUARTZ_H -#define CAIRO_QUARTZ_H - -#include "cairo.h" - -#if CAIRO_HAS_QUARTZ_SURFACE -#include "TargetConditionals.h" - -#if !TARGET_OS_IPHONE -#include -#else -#include -#include -#endif - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_quartz_surface_create (cairo_format_t format, - unsigned int width, - unsigned int height); - -cairo_public cairo_surface_t * -cairo_quartz_surface_create_for_data (unsigned char *data, - cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride); - -cairo_public cairo_surface_t * -cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, - cairo_content_t content, - unsigned int width, - unsigned int height); - -cairo_public cairo_surface_t * -cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, - unsigned int width, - unsigned int height); - -cairo_public CGContextRef -cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); - -cairo_public CGContextRef -cairo_quartz_get_cg_context_with_clip (cairo_t *cr); - -cairo_public void -cairo_quartz_finish_cg_context_with_clip (cairo_t *cr); - -cairo_public cairo_surface_t * -cairo_quartz_surface_get_image (cairo_surface_t *surface); - -#if CAIRO_HAS_QUARTZ_FONT - -/* - * Quartz font support - */ - -cairo_public cairo_font_face_t * -cairo_quartz_font_face_create_for_cgfont (CGFontRef font); - -#if !defined(__LP64__) && !TARGET_OS_IPHONE -cairo_public cairo_font_face_t * -cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id); -#endif - -#endif /* CAIRO_HAS_QUARTZ_FONT */ - -CAIRO_END_DECLS - -#else - -# error Cairo was not compiled with support for the quartz backend - -#endif /* CAIRO_HAS_QUARTZ_SURFACE */ - -#endif /* CAIRO_QUARTZ_H */ diff --git a/libs/cairo/cairo/src/cairo-recording-surface-private.h b/libs/cairo/cairo/src/cairo-recording-surface-private.h deleted file mode 100644 index c21a93205..000000000 --- a/libs/cairo/cairo/src/cairo-recording-surface-private.h +++ /dev/null @@ -1,139 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_RECORDING_SURFACE_H -#define CAIRO_RECORDING_SURFACE_H - -#include "cairoint.h" -#include "cairo-path-fixed-private.h" -#include "cairo-clip-private.h" - -typedef enum { - /* The 5 basic drawing operations. */ - CAIRO_COMMAND_PAINT, - CAIRO_COMMAND_MASK, - CAIRO_COMMAND_STROKE, - CAIRO_COMMAND_FILL, - CAIRO_COMMAND_SHOW_TEXT_GLYPHS, -} cairo_command_type_t; - -typedef enum { - CAIRO_RECORDING_REGION_ALL, - CAIRO_RECORDING_REGION_NATIVE, - CAIRO_RECORDING_REGION_IMAGE_FALLBACK -} cairo_recording_region_type_t; - -typedef struct _cairo_command_header { - cairo_command_type_t type; - cairo_recording_region_type_t region; - cairo_operator_t op; - cairo_clip_t clip; -} cairo_command_header_t; - -typedef struct _cairo_command_paint { - cairo_command_header_t header; - cairo_pattern_union_t source; -} cairo_command_paint_t; - -typedef struct _cairo_command_mask { - cairo_command_header_t header; - cairo_pattern_union_t source; - cairo_pattern_union_t mask; -} cairo_command_mask_t; - -typedef struct _cairo_command_stroke { - cairo_command_header_t header; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_stroke_style_t style; - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_stroke_t; - -typedef struct _cairo_command_fill { - cairo_command_header_t header; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_fill_t; - -typedef struct _cairo_command_show_text_glyphs { - cairo_command_header_t header; - cairo_pattern_union_t source; - char *utf8; - int utf8_len; - cairo_glyph_t *glyphs; - unsigned int num_glyphs; - cairo_text_cluster_t *clusters; - int num_clusters; - cairo_text_cluster_flags_t cluster_flags; - cairo_scaled_font_t *scaled_font; -} cairo_command_show_text_glyphs_t; - -typedef union _cairo_command { - cairo_command_header_t header; - - cairo_command_paint_t paint; - cairo_command_mask_t mask; - cairo_command_stroke_t stroke; - cairo_command_fill_t fill; - cairo_command_show_text_glyphs_t show_text_glyphs; -} cairo_command_t; - -typedef struct _cairo_recording_surface { - cairo_surface_t base; - - cairo_content_t content; - - /* A recording-surface is logically unbounded, but when used as a - * source we need to render it to an image, so we need a size at - * which to create that image. */ - cairo_rectangle_t extents_pixels; - cairo_rectangle_int_t extents; - cairo_bool_t unbounded; - - cairo_clip_t clip; - - cairo_array_t commands; - - int replay_start_idx; -} cairo_recording_surface_t; - -slim_hidden_proto (cairo_recording_surface_create); - -cairo_private cairo_int_status_t -_cairo_recording_surface_get_path (cairo_surface_t *surface, - cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_recording_surface_replay (cairo_surface_t *surface, - cairo_surface_t *target); - - -cairo_private cairo_status_t -_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface, - cairo_surface_t *target); - -cairo_private cairo_status_t -_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target); -cairo_private cairo_status_t -_cairo_recording_surface_replay_region (cairo_surface_t *surface, - const cairo_rectangle_int_t *surface_extents, - cairo_surface_t *target, - cairo_recording_region_type_t region); - -cairo_private cairo_status_t -_cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, - cairo_box_t *bbox, - const cairo_matrix_t *transform); - -cairo_private cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface); - -#endif /* CAIRO_RECORDING_SURFACE_H */ diff --git a/libs/cairo/cairo/src/cairo-recording-surface.c b/libs/cairo/cairo/src/cairo-recording-surface.c deleted file mode 100644 index 8230ac375..000000000 --- a/libs/cairo/cairo/src/cairo-recording-surface.c +++ /dev/null @@ -1,1099 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * SECTION:cairo-recording - * @Title: Recording Surfaces - * @Short_Description: Records all drawing operations - * @See_Also: #cairo_surface_t - * - * A recording surface is a surface that records all drawing operations at - * the highest level of the surface backend interface, (that is, the - * level of paint, mask, stroke, fill, and show_text_glyphs). The recording - * surface can then be "replayed" against any target surface by using it - * as a source surface. - * - * If you want to replay a surface so that the results in target will be - * identical to the results that would have been obtained if the original - * operations applied to the recording surface had instead been applied to the - * target surface, you can use code like this: - * - * cairo_t *cr; - * - * cr = cairo_create (target); - * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); - * cairo_paint (cr); - * cairo_destroy (cr); - * - * - * A recording surface is logically unbounded, i.e. it has no implicit constraint - * on the size of the drawing surface. However, in practice this is rarely - * useful as you wish to replay against a particular target surface with - * known bounds. For this case, it is more efficient to specify the target - * extents to the recording surface upon creation. - * - * The recording phase of the recording surface is careful to snapshot all - * necessary objects (paths, patterns, etc.), in order to achieve - * accurate replay. The efficiency of the recording surface could be - * improved by improving the implementation of snapshot for the - * various objects. For example, it would be nice to have a - * copy-on-write implementation for _cairo_surface_snapshot. - */ - -#include "cairoint.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-wrapper-private.h" - -typedef enum { - CAIRO_RECORDING_REPLAY, - CAIRO_RECORDING_CREATE_REGIONS -} cairo_recording_replay_type_t; - -static const cairo_surface_backend_t cairo_recording_surface_backend; - -/** - * CAIRO_HAS_RECORDING_SURFACE: - * - * Defined if the recording surface backend is available. - * The recording surface backend is always built in. - * This macro was added for completeness in cairo 1.10. - * - * Since: 1.10 - */ - -/* Currently all recording surfaces do have a size which should be passed - * in as the maximum size of any target surface against which the - * recording-surface will ever be replayed. - * - * XXX: The naming of "pixels" in the size here is a misnomer. It's - * actually a size in whatever device-space units are desired (again, - * according to the intended replay target). - */ - -/** - * cairo_recording_surface_create: - * @content: the content of the recording surface - * @extents: the extents to record in pixels, can be %NULL to record - * unbounded operations. - * - * Creates a recording-surface which can be used to record all drawing operations - * at the highest level (that is, the level of paint, mask, stroke, fill - * and show_text_glyphs). The recording surface can then be "replayed" against - * any target surface by using it as a source to drawing operations. - * - * The recording phase of the recording surface is careful to snapshot all - * necessary objects (paths, patterns, etc.), in order to achieve - * accurate replay. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_recording_surface_create (cairo_content_t content, - const cairo_rectangle_t *extents) -{ - cairo_recording_surface_t *recording_surface; - cairo_status_t status; - - recording_surface = malloc (sizeof (cairo_recording_surface_t)); - if (unlikely (recording_surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&recording_surface->base, - &cairo_recording_surface_backend, - NULL, /* device */ - content); - - recording_surface->content = content; - - recording_surface->unbounded = TRUE; - _cairo_clip_init (&recording_surface->clip); - - /* unbounded -> 'infinite' extents */ - if (extents != NULL) { - recording_surface->extents_pixels = *extents; - - /* XXX check for overflow */ - recording_surface->extents.x = floor (extents->x); - recording_surface->extents.y = floor (extents->y); - recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x; - recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y; - - status = _cairo_clip_rectangle (&recording_surface->clip, - &recording_surface->extents); - if (unlikely (status)) { - free (recording_surface); - return _cairo_surface_create_in_error (status); - } - - recording_surface->unbounded = FALSE; - } - - _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); - - recording_surface->replay_start_idx = 0; - recording_surface->base.is_clear = TRUE; - - return &recording_surface->base; -} -slim_hidden_def (cairo_recording_surface_create); - -static cairo_surface_t * -_cairo_recording_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_rectangle_t extents; - extents.x = extents.y = 0; - extents.width = width; - extents.height = height; - return cairo_recording_surface_create (content, &extents); -} - -static cairo_status_t -_cairo_recording_surface_finish (void *abstract_surface) -{ - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_t **elements; - int i, num_elements; - - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); - for (i = 0; i < num_elements; i++) { - cairo_command_t *command = elements[i]; - - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - _cairo_pattern_fini (&command->paint.source.base); - break; - - case CAIRO_COMMAND_MASK: - _cairo_pattern_fini (&command->mask.source.base); - _cairo_pattern_fini (&command->mask.mask.base); - break; - - case CAIRO_COMMAND_STROKE: - _cairo_pattern_fini (&command->stroke.source.base); - _cairo_path_fixed_fini (&command->stroke.path); - _cairo_stroke_style_fini (&command->stroke.style); - break; - - case CAIRO_COMMAND_FILL: - _cairo_pattern_fini (&command->fill.source.base); - _cairo_path_fixed_fini (&command->fill.path); - break; - - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - _cairo_pattern_fini (&command->show_text_glyphs.source.base); - free (command->show_text_glyphs.utf8); - free (command->show_text_glyphs.glyphs); - free (command->show_text_glyphs.clusters); - cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); - break; - - default: - ASSERT_NOT_REACHED; - } - - _cairo_clip_fini (&command->header.clip); - free (command); - } - - _cairo_array_fini (&recording_surface->commands); - _cairo_clip_fini (&recording_surface->clip); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_recording_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_status_t status; - cairo_recording_surface_t *surface = abstract_surface; - cairo_surface_t *image; - - image = _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); - if (image != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - image = _cairo_image_surface_create_with_content (surface->content, - surface->extents.width, - surface->extents.height); - if (unlikely (image->status)) - return image->status; - - cairo_surface_set_device_offset (image, - -surface->extents.x, - -surface->extents.y); - - status = _cairo_recording_surface_replay (&surface->base, image); - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - cairo_surface_attach_snapshot (&surface->base, image, NULL); - - *image_out = (cairo_image_surface_t *) image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_recording_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_command_init (cairo_recording_surface_t *recording_surface, - cairo_command_header_t *command, - cairo_command_type_t type, - cairo_operator_t op, - cairo_clip_t *clip) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - command->type = type; - command->op = op; - command->region = CAIRO_RECORDING_REGION_ALL; - _cairo_clip_init_copy (&command->clip, clip); - if (recording_surface->clip.path != NULL) - status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip); - - return status; -} - -static cairo_int_status_t -_cairo_recording_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_paint_t *command; - - command = malloc (sizeof (cairo_command_paint_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_PAINT, op, clip); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_array_append (&recording_surface->commands, &command); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - /* An optimisation that takes care to not replay what was done - * before surface is cleared. We don't erase recorded commands - * since we may have earlier snapshots of this surface. */ - if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) - recording_surface->replay_start_idx = recording_surface->commands.num_elements; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_SOURCE: - _cairo_pattern_fini (&command->source.base); - CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); - free (command); - return status; -} - -static cairo_int_status_t -_cairo_recording_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_mask_t *command; - - command = malloc (sizeof (cairo_command_mask_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_MASK, op, clip); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->mask.base, mask); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - status = _cairo_array_append (&recording_surface->commands, &command); - if (unlikely (status)) - goto CLEANUP_MASK; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_MASK: - _cairo_pattern_fini (&command->mask.base); - CLEANUP_SOURCE: - _cairo_pattern_fini (&command->source.base); - CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); - free (command); - return status; -} - -static cairo_int_status_t -_cairo_recording_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_stroke_t *command; - - command = malloc (sizeof (cairo_command_stroke_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_STROKE, op, clip); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_path_fixed_init_copy (&command->path, path); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - status = _cairo_stroke_style_init_copy (&command->style, style); - if (unlikely (status)) - goto CLEANUP_PATH; - - command->ctm = *ctm; - command->ctm_inverse = *ctm_inverse; - command->tolerance = tolerance; - command->antialias = antialias; - - status = _cairo_array_append (&recording_surface->commands, &command); - if (unlikely (status)) - goto CLEANUP_STYLE; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_STYLE: - _cairo_stroke_style_fini (&command->style); - CLEANUP_PATH: - _cairo_path_fixed_fini (&command->path); - CLEANUP_SOURCE: - _cairo_pattern_fini (&command->source.base); - CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); - free (command); - return status; -} - -static cairo_int_status_t -_cairo_recording_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_fill_t *command; - - command = malloc (sizeof (cairo_command_fill_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status =_command_init (recording_surface, - &command->header, CAIRO_COMMAND_FILL, op, clip); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_path_fixed_init_copy (&command->path, path); - if (unlikely (status)) - goto CLEANUP_SOURCE; - - command->fill_rule = fill_rule; - command->tolerance = tolerance; - command->antialias = antialias; - - status = _cairo_array_append (&recording_surface->commands, &command); - if (unlikely (status)) - goto CLEANUP_PATH; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_PATH: - _cairo_path_fixed_fini (&command->path); - CLEANUP_SOURCE: - _cairo_pattern_fini (&command->source.base); - CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); - free (command); - return status; -} - -static cairo_bool_t -_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static cairo_int_status_t -_cairo_recording_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; - cairo_command_show_text_glyphs_t *command; - - command = malloc (sizeof (cairo_command_show_text_glyphs_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, - op, clip); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (unlikely (status)) - goto CLEANUP_COMMAND; - - command->utf8 = NULL; - command->utf8_len = utf8_len; - command->glyphs = NULL; - command->num_glyphs = num_glyphs; - command->clusters = NULL; - command->num_clusters = num_clusters; - - if (utf8_len) { - command->utf8 = malloc (utf8_len); - if (unlikely (command->utf8 == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->utf8, utf8, utf8_len); - } - if (num_glyphs) { - command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); - if (unlikely (command->glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); - } - if (num_clusters) { - command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); - if (unlikely (command->clusters == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_ARRAYS; - } - memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); - } - - command->cluster_flags = cluster_flags; - - command->scaled_font = cairo_scaled_font_reference (scaled_font); - - status = _cairo_array_append (&recording_surface->commands, &command); - if (unlikely (status)) - goto CLEANUP_SCALED_FONT; - - return CAIRO_STATUS_SUCCESS; - - CLEANUP_SCALED_FONT: - cairo_scaled_font_destroy (command->scaled_font); - CLEANUP_ARRAYS: - free (command->utf8); - free (command->glyphs); - free (command->clusters); - - _cairo_pattern_fini (&command->source.base); - CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); - free (command); - return status; -} - -/** - * _cairo_recording_surface_snapshot - * @surface: a #cairo_surface_t which must be a recording surface - * - * Make an immutable copy of @surface. It is an error to call a - * surface-modifying function on the result of this function. - * - * The caller owns the return value and should call - * cairo_surface_destroy() when finished with it. This function will not - * return %NULL, but will return a nil surface instead. - * - * Return value: The snapshot surface. - **/ -static cairo_surface_t * -_cairo_recording_surface_snapshot (void *abstract_other) -{ - cairo_recording_surface_t *other = abstract_other; - cairo_recording_surface_t *recording_surface; - cairo_status_t status; - - recording_surface = malloc (sizeof (cairo_recording_surface_t)); - if (unlikely (recording_surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&recording_surface->base, - &cairo_recording_surface_backend, - NULL, /* device */ - other->base.content); - - recording_surface->extents_pixels = other->extents_pixels; - recording_surface->extents = other->extents; - recording_surface->unbounded = other->unbounded; - recording_surface->content = other->content; - - _cairo_clip_init_copy (&recording_surface->clip, &other->clip); - - /* XXX We should in theory be able to reuse the original array, but we - * need to handle reference cycles during subsurface and self-copy. - */ - recording_surface->replay_start_idx = 0; - recording_surface->base.is_clear = TRUE; - - _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); - status = _cairo_recording_surface_replay (&other->base, &recording_surface->base); - if (unlikely (status)) { - cairo_surface_destroy (&recording_surface->base); - return _cairo_surface_create_in_error (status); - } - - return &recording_surface->base; -} - -static cairo_bool_t -_cairo_recording_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_recording_surface_t *surface = abstract_surface; - - if (surface->unbounded) - return FALSE; - - *rectangle = surface->extents; - return TRUE; -} - -/** - * _cairo_surface_is_recording: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_recording_surface_t - * - * Return value: %TRUE if the surface is a recording surface - **/ -cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface) -{ - return surface->backend == &cairo_recording_surface_backend; -} - -static const cairo_surface_backend_t cairo_recording_surface_backend = { - CAIRO_SURFACE_TYPE_RECORDING, - _cairo_recording_surface_create_similar, - _cairo_recording_surface_finish, - _cairo_recording_surface_acquire_source_image, - _cairo_recording_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_recording_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here are the 5 basic drawing operations, (which are in some - * sense the only things that cairo_recording_surface should need to - * implement). However, we implement the more generic show_text_glyphs - * instead of show_glyphs. One or the other is eough. */ - - _cairo_recording_surface_paint, - _cairo_recording_surface_mask, - _cairo_recording_surface_stroke, - _cairo_recording_surface_fill, - NULL, - - _cairo_recording_surface_snapshot, - - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - _cairo_recording_surface_has_show_text_glyphs, - _cairo_recording_surface_show_text_glyphs -}; - -cairo_int_status_t -_cairo_recording_surface_get_path (cairo_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_recording_surface_t *recording_surface; - cairo_command_t **elements; - int i, num_elements; - cairo_int_status_t status; - - if (surface->status) - return surface->status; - - recording_surface = (cairo_recording_surface_t *) surface; - status = CAIRO_STATUS_SUCCESS; - - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { - cairo_command_t *command = elements[i]; - - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - case CAIRO_COMMAND_MASK: - status = CAIRO_INT_STATUS_UNSUPPORTED; - break; - - case CAIRO_COMMAND_STROKE: - { - cairo_traps_t traps; - - _cairo_traps_init (&traps); - - /* XXX call cairo_stroke_to_path() when that is implemented */ - status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, - &command->stroke.style, - &command->stroke.ctm, - &command->stroke.ctm_inverse, - command->stroke.tolerance, - &traps); - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_traps_path (&traps, path); - - _cairo_traps_fini (&traps); - break; - } - case CAIRO_COMMAND_FILL: - { - status = _cairo_path_fixed_append (path, - &command->fill.path, CAIRO_DIRECTION_FORWARD, - 0, 0); - break; - } - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - { - status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, - command->show_text_glyphs.glyphs, - command->show_text_glyphs.num_glyphs, - path); - break; - } - - default: - ASSERT_NOT_REACHED; - } - - if (unlikely (status)) - break; - } - - return _cairo_surface_set_error (surface, status); -} - -#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL) -static cairo_status_t -_cairo_recording_surface_replay_internal (cairo_surface_t *surface, - const cairo_rectangle_int_t *surface_extents, - cairo_surface_t *target, - cairo_recording_replay_type_t type, - cairo_recording_region_type_t region) -{ - cairo_recording_surface_t *recording_surface; - cairo_command_t **elements; - int i, num_elements; - cairo_int_status_t status; - cairo_surface_wrapper_t wrapper; - - if (unlikely (surface->status)) - return surface->status; - - if (unlikely (target->status)) - return target->status; - - if (unlikely (surface->finished)) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - - if (surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - assert (_cairo_surface_is_recording (surface)); - - _cairo_surface_wrapper_init (&wrapper, target); - _cairo_surface_wrapper_set_extents (&wrapper, surface_extents); - - recording_surface = (cairo_recording_surface_t *) surface; - status = CAIRO_STATUS_SUCCESS; - - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); - - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { - cairo_command_t *command = elements[i]; - - if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) { - if (command->header.region != region) - continue; - } - - switch (command->header.type) { - case CAIRO_COMMAND_PAINT: - status = _cairo_surface_wrapper_paint (&wrapper, - command->header.op, - &command->paint.source.base, - _clip (command)); - break; - - case CAIRO_COMMAND_MASK: - status = _cairo_surface_wrapper_mask (&wrapper, - command->header.op, - &command->mask.source.base, - &command->mask.mask.base, - _clip (command)); - break; - - case CAIRO_COMMAND_STROKE: - { - status = _cairo_surface_wrapper_stroke (&wrapper, - command->header.op, - &command->stroke.source.base, - &command->stroke.path, - &command->stroke.style, - &command->stroke.ctm, - &command->stroke.ctm_inverse, - command->stroke.tolerance, - command->stroke.antialias, - _clip (command)); - break; - } - case CAIRO_COMMAND_FILL: - { - cairo_command_t *stroke_command; - - stroke_command = NULL; - if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) - stroke_command = elements[i + 1]; - - if (stroke_command != NULL && - type == CAIRO_RECORDING_REPLAY && - region != CAIRO_RECORDING_REGION_ALL) - { - if (stroke_command->header.region != region) - stroke_command = NULL; - } - - if (stroke_command != NULL && - stroke_command->header.type == CAIRO_COMMAND_STROKE && - _cairo_path_fixed_is_equal (&command->fill.path, - &stroke_command->stroke.path)) - { - status = _cairo_surface_wrapper_fill_stroke (&wrapper, - command->header.op, - &command->fill.source.base, - command->fill.fill_rule, - command->fill.tolerance, - command->fill.antialias, - &command->fill.path, - stroke_command->header.op, - &stroke_command->stroke.source.base, - &stroke_command->stroke.style, - &stroke_command->stroke.ctm, - &stroke_command->stroke.ctm_inverse, - stroke_command->stroke.tolerance, - stroke_command->stroke.antialias, - _clip (command)); - i++; - } - else - { - status = _cairo_surface_wrapper_fill (&wrapper, - command->header.op, - &command->fill.source.base, - &command->fill.path, - command->fill.fill_rule, - command->fill.tolerance, - command->fill.antialias, - _clip (command)); - } - break; - } - case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - { - cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; - cairo_glyph_t *glyphs_copy; - int num_glyphs = command->show_text_glyphs.num_glyphs; - - /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed - * to modify the glyph array that's passed in. We must always - * copy the array before handing it to the backend. - */ - glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (glyphs_copy == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - } - - memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - - status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, - command->header.op, - &command->show_text_glyphs.source.base, - command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, - glyphs_copy, num_glyphs, - command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, - command->show_text_glyphs.cluster_flags, - command->show_text_glyphs.scaled_font, - _clip (command)); - free (glyphs_copy); - break; - } - default: - ASSERT_NOT_REACHED; - } - - if (type == CAIRO_RECORDING_CREATE_REGIONS) { - if (status == CAIRO_STATUS_SUCCESS) { - command->header.region = CAIRO_RECORDING_REGION_NATIVE; - } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { - command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; - status = CAIRO_STATUS_SUCCESS; - } else { - assert (_cairo_status_is_error (status)); - } - } - - if (unlikely (status)) - break; - } - - /* free up any caches */ - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { - cairo_command_t *command = elements[i]; - - _cairo_clip_drop_cache (&command->header.clip); - } - - _cairo_surface_wrapper_fini (&wrapper); - - return _cairo_surface_set_error (surface, status); -} - -/** - * _cairo_recording_surface_replay: - * @surface: the #cairo_recording_surface_t - * @target: a target #cairo_surface_t onto which to replay the operations - * @width_pixels: width of the surface, in pixels - * @height_pixels: height of the surface, in pixels - * - * A recording surface can be "replayed" against any target surface, - * after which the results in target will be identical to the results - * that would have been obtained if the original operations applied to - * the recording surface had instead been applied to the target surface. - **/ -cairo_status_t -_cairo_recording_surface_replay (cairo_surface_t *surface, - cairo_surface_t *target) -{ - return _cairo_recording_surface_replay_internal (surface, NULL, - target, - CAIRO_RECORDING_REPLAY, - CAIRO_RECORDING_REGION_ALL); -} - -/* Replay recording to surface. When the return status of each operation is - * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or - * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation - * will be stored in the recording surface. Any other status will abort the - * replay and return the status. - */ -cairo_status_t -_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target) -{ - return _cairo_recording_surface_replay_internal (surface, NULL, - target, - CAIRO_RECORDING_CREATE_REGIONS, - CAIRO_RECORDING_REGION_ALL); -} - -cairo_status_t -_cairo_recording_surface_replay_region (cairo_surface_t *surface, - const cairo_rectangle_int_t *surface_extents, - cairo_surface_t *target, - cairo_recording_region_type_t region) -{ - return _cairo_recording_surface_replay_internal (surface, surface_extents, - target, - CAIRO_RECORDING_REPLAY, - region); -} - -static cairo_status_t -_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, - cairo_box_t *bbox, - const cairo_matrix_t *transform) -{ - cairo_surface_t *null_surface; - cairo_surface_t *analysis_surface; - cairo_status_t status; - - null_surface = cairo_null_surface_create (surface->content); - analysis_surface = _cairo_analysis_surface_create (null_surface); - cairo_surface_destroy (null_surface); - - status = analysis_surface->status; - if (unlikely (status)) - return status; - - if (transform != NULL) - _cairo_analysis_surface_set_ctm (analysis_surface, transform); - - status = _cairo_recording_surface_replay (&surface->base, analysis_surface); - _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); - cairo_surface_destroy (analysis_surface); - - return status; -} - -/** - * cairo_recording_surface_ink_extents: - * @surface: a #cairo_recording_surface_t - * @x0: the x-coordinate of the top-left of the ink bounding box - * @y0: the y-coordinate of the top-left of the ink bounding box - * @width: the width of the ink bounding box - * @height: the height of the ink bounding box - * - * Measures the extents of the operations stored within the recording-surface. - * This is useful to compute the required size of an image surface (or - * equivalent) into which to replay the full sequence of drawing operations. - * - * Since: 1.10 - **/ -void -cairo_recording_surface_ink_extents (cairo_surface_t *surface, - double *x0, - double *y0, - double *width, - double *height) -{ - cairo_status_t status; - cairo_box_t bbox; - - memset (&bbox, 0, sizeof (bbox)); - - if (! _cairo_surface_is_recording (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - goto DONE; - } - - status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface, - &bbox, - NULL); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - -DONE: - if (x0) - *x0 = _cairo_fixed_to_double (bbox.p1.x); - if (y0) - *y0 = _cairo_fixed_to_double (bbox.p1.y); - if (width) - *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - if (height) - *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); -} - -cairo_status_t -_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, - cairo_box_t *bbox, - const cairo_matrix_t *transform) -{ - if (! surface->unbounded) { - _cairo_box_from_rectangle (bbox, &surface->extents); - if (transform != NULL) - _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); - - return CAIRO_STATUS_SUCCESS; - } - - return _recording_surface_get_ink_bbox (surface, bbox, transform); -} diff --git a/libs/cairo/cairo/src/cairo-rectangle.c b/libs/cairo/cairo/src/cairo-rectangle.c deleted file mode 100644 index ab043c1bc..000000000 --- a/libs/cairo/cairo/src/cairo-rectangle.c +++ /dev/null @@ -1,231 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -cairo_private void -_cairo_box_from_doubles (cairo_box_t *box, - double *x1, double *y1, - double *x2, double *y2) -{ - box->p1.x = _cairo_fixed_from_double (*x1); - box->p1.y = _cairo_fixed_from_double (*y1); - box->p2.x = _cairo_fixed_from_double (*x2); - box->p2.y = _cairo_fixed_from_double (*y2); -} - -cairo_private void -_cairo_box_to_doubles (const cairo_box_t *box, - double *x1, double *y1, - double *x2, double *y2) -{ - *x1 = _cairo_fixed_to_double (box->p1.x); - *y1 = _cairo_fixed_to_double (box->p1.y); - *x2 = _cairo_fixed_to_double (box->p2.x); - *y2 = _cairo_fixed_to_double (box->p2.y); -} - -void -_cairo_box_from_rectangle (cairo_box_t *box, - const cairo_rectangle_int_t *rect) -{ - box->p1.x = _cairo_fixed_from_int (rect->x); - box->p1.y = _cairo_fixed_from_int (rect->y); - box->p2.x = _cairo_fixed_from_int (rect->x + rect->width); - box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); -} - -void -_cairo_boxes_get_extents (const cairo_box_t *boxes, - int num_boxes, - cairo_box_t *extents) -{ - int n; - - assert (num_boxes > 0); - *extents = *boxes; - - for (n = 1; n < num_boxes; n++) { - if (boxes[n].p1.x < extents->p1.x) - extents->p1.x = boxes[n].p1.x; - if (boxes[n].p2.x > extents->p2.x) - extents->p2.x = boxes[n].p2.x; - - if (boxes[n].p1.y < extents->p1.y) - extents->p1.y = boxes[n].p1.y; - if (boxes[n].p2.y > extents->p2.y) - extents->p2.y = boxes[n].p2.y; - } -} - -/* This function will return 'true' if the containing_rectangle contains the - * contained_rectangle, and false otherwise. - */ -cairo_bool_t -_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, - const cairo_rectangle_int_t *contained_rectangle) -{ - if (containing_rectangle->x > contained_rectangle->x || - containing_rectangle->y > contained_rectangle->y) - return FALSE; - - if (containing_rectangle->x + containing_rectangle->width < - contained_rectangle->x + contained_rectangle->width || - containing_rectangle->y + containing_rectangle->height < - contained_rectangle->y + contained_rectangle->height) - return FALSE; - - return TRUE; -} - -/* XXX We currently have a confusing mix of boxes and rectangles as - * exemplified by this function. A #cairo_box_t is a rectangular area - * represented by the coordinates of the upper left and lower right - * corners, expressed in fixed point numbers. A #cairo_rectangle_int_t is - * also a rectangular area, but represented by the upper left corner - * and the width and the height, as integer numbers. - * - * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by - * increasing the area to the nearest integer coordinates. We should - * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and - * this function could be renamed to the more reasonable - * _cairo_rectangle_fixed_round. - */ - -void -_cairo_box_round_to_rectangle (const cairo_box_t *box, - cairo_rectangle_int_t *rectangle) -{ - rectangle->x = _cairo_fixed_integer_floor (box->p1.x); - rectangle->y = _cairo_fixed_integer_floor (box->p1.y); - rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; - rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; -} - -cairo_bool_t -_cairo_rectangle_intersect (cairo_rectangle_int_t *dst, - const cairo_rectangle_int_t *src) -{ - int x1, y1, x2, y2; - - x1 = MAX (dst->x, src->x); - y1 = MAX (dst->y, src->y); - /* Beware the unsigned promotion, fortunately we have bits to spare - * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX - */ - x2 = MIN (dst->x + (int) dst->width, src->x + (int) src->width); - y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height); - - if (x1 >= x2 || y1 >= y2) { - dst->x = 0; - dst->y = 0; - dst->width = 0; - dst->height = 0; - - return FALSE; - } else { - dst->x = x1; - dst->y = y1; - dst->width = x2 - x1; - dst->height = y2 - y1; - - return TRUE; - } -} - -#define P1x (line->p1.x) -#define P1y (line->p1.y) -#define P2x (line->p2.x) -#define P2y (line->p2.y) -#define B1x (box->p1.x) -#define B1y (box->p1.y) -#define B2x (box->p2.x) -#define B2y (box->p2.y) - -/* - * Check whether any part of line intersects box. This function essentially - * computes whether the ray starting at line->p1 in the direction of line->p2 - * intersects the box before it reaches p2. Normally, this is done - * by dividing by the lengths of the line projected onto each axis. Because - * we're in fixed point, this function does a bit more work to avoid having to - * do the division -- we don't care about the actual intersection point, so - * it's of no interest to us. - */ - -cairo_bool_t -_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) -{ - cairo_fixed_t t1=0, t2=0, t3=0, t4=0; - cairo_int64_t t1y, t2y, t3x, t4x; - - cairo_fixed_t xlen, ylen; - - if (_cairo_box_contains_point (box, &line->p1) || - _cairo_box_contains_point (box, &line->p2)) - return TRUE; - - xlen = P2x - P1x; - ylen = P2y - P1y; - - if (xlen) { - if (xlen > 0) { - t1 = B1x - P1x; - t2 = B2x - P1x; - } else { - t1 = P1x - B2x; - t2 = P1x - B1x; - xlen = - xlen; - } - - if (t1 > xlen || t2 < 0) - return FALSE; - } else { - /* Fully vertical line -- check that X is in bounds */ - if (P1x < B1x || P1x > B2x) - return FALSE; - } - - if (ylen) { - if (ylen > 0) { - t3 = B1y - P1y; - t4 = B2y - P1y; - } else { - t3 = P1y - B2y; - t4 = P1y - B1y; - ylen = - ylen; - } - - if (t3 > ylen || t4 < 0) - return FALSE; - } else { - /* Fully horizontal line -- check Y */ - if (P1y < B1y || P1y > B2y) - return FALSE; - } - - /* If we had a horizontal or vertical line, then it's already been checked */ - if (P1x == P2x || P1y == P2y) - return TRUE; - - /* Check overlap. Note that t1 < t2 and t3 < t4 here. */ - t1y = _cairo_int32x32_64_mul (t1, ylen); - t2y = _cairo_int32x32_64_mul (t2, ylen); - t3x = _cairo_int32x32_64_mul (t3, xlen); - t4x = _cairo_int32x32_64_mul (t4, xlen); - - if (_cairo_int64_lt(t1y, t4x) && - _cairo_int64_lt(t3x, t2y)) - return TRUE; - - return FALSE; -} - -cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point) -{ - if (point->x < box->p1.x || point->x > box->p2.x || - point->y < box->p1.y || point->y > box->p2.y) - return FALSE; - return TRUE; -} diff --git a/libs/cairo/cairo/src/cairo-rectangular-scan-converter.c b/libs/cairo/cairo/src/cairo-rectangular-scan-converter.c deleted file mode 100644 index 6c21f5fd5..000000000 --- a/libs/cairo/cairo/src/cairo-rectangular-scan-converter.c +++ /dev/null @@ -1,694 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-combsort-private.h" -#include "cairo-error-private.h" -#include "cairo-freelist-private.h" -#include "cairo-list-private.h" -#include "cairo-spans-private.h" - -#include - -typedef struct _rectangle { - struct _rectangle *next, *prev; - cairo_fixed_t left, right; - cairo_fixed_t top, bottom; - int32_t top_y, bottom_y; - int dir; -} rectangle_t; - -#define UNROLL3(x) x x x - -/* the parent is always given by index/2 */ -#define PQ_PARENT_INDEX(i) ((i) >> 1) -#define PQ_FIRST_ENTRY 1 - -/* left and right children are index * 2 and (index * 2) +1 respectively */ -#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) - -typedef struct _pqueue { - int size, max_size; - - rectangle_t **elements; - rectangle_t *elements_embedded[1024]; -} pqueue_t; - -typedef struct { - rectangle_t **start; - pqueue_t stop; - rectangle_t head, tail; - rectangle_t *insert_cursor; - int32_t current_y; - int32_t xmin, xmax; - - struct coverage { - struct cell { - struct cell *prev, *next; - int x, covered, uncovered; - } head, tail, *cursor; - unsigned int count; - cairo_freepool_t pool; - } coverage; - - cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; - cairo_half_open_span_t *spans; - unsigned int num_spans; - unsigned int size_spans; - - jmp_buf jmpbuf; -} sweep_line_t; - -static inline int -rectangle_compare_start (const rectangle_t *a, - const rectangle_t *b) -{ - int cmp; - - cmp = a->top_y - b->top_y; - if (cmp) - return cmp; - - return a->left - b->left; -} - -static inline int -rectangle_compare_stop (const rectangle_t *a, - const rectangle_t *b) -{ - return a->bottom_y - b->bottom_y; -} - -static inline void -pqueue_init (pqueue_t *pq) -{ - pq->max_size = ARRAY_LENGTH (pq->elements_embedded); - pq->size = 0; - - pq->elements = pq->elements_embedded; - pq->elements[PQ_FIRST_ENTRY] = NULL; -} - -static inline void -pqueue_fini (pqueue_t *pq) -{ - if (pq->elements != pq->elements_embedded) - free (pq->elements); -} - -static cairo_bool_t -pqueue_grow (pqueue_t *pq) -{ - rectangle_t **new_elements; - pq->max_size *= 2; - - if (pq->elements == pq->elements_embedded) { - new_elements = _cairo_malloc_ab (pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - - memcpy (new_elements, pq->elements_embedded, - sizeof (pq->elements_embedded)); - } else { - new_elements = _cairo_realloc_ab (pq->elements, - pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - } - - pq->elements = new_elements; - return TRUE; -} - -static inline void -pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) -{ - rectangle_t **elements; - int i, parent; - - if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) { - if (unlikely (! pqueue_grow (&sweep->stop))) - longjmp (sweep->jmpbuf, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - elements = sweep->stop.elements; - for (i = ++sweep->stop.size; - i != PQ_FIRST_ENTRY && - rectangle_compare_stop (rectangle, - elements[parent = PQ_PARENT_INDEX (i)]) < 0; - i = parent) - { - elements[i] = elements[parent]; - } - - elements[i] = rectangle; -} - -static inline void -pqueue_pop (pqueue_t *pq) -{ - rectangle_t **elements = pq->elements; - rectangle_t *tail; - int child, i; - - tail = elements[pq->size--]; - if (pq->size == 0) { - elements[PQ_FIRST_ENTRY] = NULL; - return; - } - - for (i = PQ_FIRST_ENTRY; - (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; - i = child) - { - if (child != pq->size && - rectangle_compare_stop (elements[child+1], - elements[child]) < 0) - { - child++; - } - - if (rectangle_compare_stop (elements[child], tail) >= 0) - break; - - elements[i] = elements[child]; - } - elements[i] = tail; -} - -static inline rectangle_t * -peek_stop (sweep_line_t *sweep) -{ - return sweep->stop.elements[PQ_FIRST_ENTRY]; -} - -CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start) - -static void -sweep_line_init (sweep_line_t *sweep) -{ - sweep->head.left = INT_MIN; - sweep->head.next = &sweep->tail; - sweep->tail.left = INT_MAX; - sweep->tail.prev = &sweep->head; - sweep->insert_cursor = &sweep->tail; - - _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell)); - - sweep->spans = sweep->spans_stack; - sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack); - - sweep->coverage.head.prev = NULL; - sweep->coverage.head.x = INT_MIN; - sweep->coverage.tail.next = NULL; - sweep->coverage.tail.x = INT_MAX; - - pqueue_init (&sweep->stop); -} - -static void -sweep_line_fini (sweep_line_t *sweep) -{ - _cairo_freepool_fini (&sweep->coverage.pool); - pqueue_fini (&sweep->stop); - - if (sweep->spans != sweep->spans_stack) - free (sweep->spans); -} - -static inline void -add_cell (sweep_line_t *sweep, int x, int covered, int uncovered) -{ - struct cell *cell; - - cell = sweep->coverage.cursor; - if (cell->x > x) { - do { - UNROLL3({ - if (cell->prev->x < x) - break; - cell = cell->prev; - }) - } while (TRUE); - } else { - if (cell->x == x) - goto found; - - do { - UNROLL3({ - cell = cell->next; - if (cell->x >= x) - break; - }) - } while (TRUE); - } - - if (x != cell->x) { - struct cell *c; - - sweep->coverage.count++; - - c = _cairo_freepool_alloc (&sweep->coverage.pool); - if (unlikely (c == NULL)) { - longjmp (sweep->jmpbuf, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - cell->prev->next = c; - c->prev = cell->prev; - c->next = cell; - cell->prev = c; - - c->x = x; - c->covered = 0; - c->uncovered = 0; - - cell = c; - } - -found: - cell->covered += covered; - cell->uncovered += uncovered; - sweep->coverage.cursor = cell; -} - -static inline void -_active_edges_to_spans (sweep_line_t *sweep) -{ - int32_t y = sweep->current_y; - rectangle_t *rectangle; - int coverage, prev_coverage; - int prev_x; - struct cell *cell; - - sweep->num_spans = 0; - if (sweep->head.next == &sweep->tail) - return; - - sweep->coverage.head.next = &sweep->coverage.tail; - sweep->coverage.tail.prev = &sweep->coverage.head; - sweep->coverage.cursor = &sweep->coverage.tail; - sweep->coverage.count = 0; - - /* XXX cell coverage only changes when a rectangle appears or - * disappears. Try only modifying coverage at such times. - */ - for (rectangle = sweep->head.next; - rectangle != &sweep->tail; - rectangle = rectangle->next) - { - int height; - int frac, i; - - if (y == rectangle->bottom_y) { - height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK; - if (height == 0) - continue; - } else - height = CAIRO_FIXED_ONE; - if (y == rectangle->top_y) - height -= rectangle->top & CAIRO_FIXED_FRAC_MASK; - height *= rectangle->dir; - - i = _cairo_fixed_integer_part (rectangle->left), - frac = _cairo_fixed_fractional_part (rectangle->left); - add_cell (sweep, i, - (CAIRO_FIXED_ONE-frac) * height, - frac * height); - - i = _cairo_fixed_integer_part (rectangle->right), - frac = _cairo_fixed_fractional_part (rectangle->right); - add_cell (sweep, i, - -(CAIRO_FIXED_ONE-frac) * height, - -frac * height); - } - - if (2*sweep->coverage.count >= sweep->size_spans) { - unsigned size; - - size = sweep->size_spans; - while (size <= 2*sweep->coverage.count) - size <<= 1; - - if (sweep->spans != sweep->spans_stack) - free (sweep->spans); - - sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t)); - if (unlikely (sweep->spans == NULL)) - longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); - - sweep->size_spans = size; - } - - prev_coverage = coverage = 0; - prev_x = INT_MIN; - for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) { - if (cell->x != prev_x && coverage != prev_coverage) { - int n = sweep->num_spans++; - sweep->spans[n].x = prev_x; - sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); - sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; - prev_coverage = coverage; - } - - coverage += cell->covered; - if (coverage != prev_coverage) { - int n = sweep->num_spans++; - sweep->spans[n].x = cell->x; - sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); - sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; - prev_coverage = coverage; - } - coverage += cell->uncovered; - prev_x = cell->x + 1; - } - _cairo_freepool_reset (&sweep->coverage.pool); - - if (sweep->num_spans) { - if (prev_x <= sweep->xmax) { - int n = sweep->num_spans++; - sweep->spans[n].x = prev_x; - sweep->spans[n].coverage = coverage; - } - - if (coverage && prev_x < sweep->xmax) { - int n = sweep->num_spans++; - sweep->spans[n].x = sweep->xmax; - sweep->spans[n].coverage = 0; - } - } -} - -static inline void -sweep_line_delete (sweep_line_t *sweep, - rectangle_t *rectangle) -{ - if (sweep->insert_cursor == rectangle) - sweep->insert_cursor = rectangle->next; - - rectangle->prev->next = rectangle->next; - rectangle->next->prev = rectangle->prev; - - pqueue_pop (&sweep->stop); -} - -static inline void -sweep_line_insert (sweep_line_t *sweep, - rectangle_t *rectangle) -{ - rectangle_t *pos; - - pos = sweep->insert_cursor; - if (pos->left != rectangle->left) { - if (pos->left > rectangle->left) { - do { - UNROLL3({ - if (pos->prev->left < rectangle->left) - break; - pos = pos->prev; - }) - } while (TRUE); - } else { - do { - UNROLL3({ - pos = pos->next; - if (pos->left >= rectangle->left) - break; - }); - } while (TRUE); - } - } - - pos->prev->next = rectangle; - rectangle->prev = pos->prev; - rectangle->next = pos; - pos->prev = rectangle; - sweep->insert_cursor = rectangle; - - pqueue_push (sweep, rectangle); -} - -static void -render_rows (sweep_line_t *sweep_line, - cairo_span_renderer_t *renderer, - int height) -{ - cairo_status_t status; - - _active_edges_to_spans (sweep_line); - - status = renderer->render_rows (renderer, - sweep_line->current_y, height, - sweep_line->spans, - sweep_line->num_spans); - if (unlikely (status)) - longjmp (sweep_line->jmpbuf, status); -} - -static cairo_status_t -generate (cairo_rectangular_scan_converter_t *self, - cairo_span_renderer_t *renderer, - rectangle_t **rectangles) -{ - sweep_line_t sweep_line; - rectangle_t *start, *stop; - cairo_status_t status; - - sweep_line_init (&sweep_line); - sweep_line.xmin = self->xmin; - sweep_line.xmax = self->xmax; - sweep_line.start = rectangles; - if ((status = setjmp (sweep_line.jmpbuf))) - goto BAIL; - - sweep_line.current_y = self->ymin; - start = *sweep_line.start++; - do { - if (start->top_y != sweep_line.current_y) { - render_rows (&sweep_line, renderer, - start->top_y - sweep_line.current_y); - sweep_line.current_y = start->top_y; - } - - do { - sweep_line_insert (&sweep_line, start); - start = *sweep_line.start++; - if (start == NULL) - goto end; - if (start->top_y != sweep_line.current_y) - break; - } while (TRUE); - - render_rows (&sweep_line, renderer, 1); - - stop = peek_stop (&sweep_line); - while (stop->bottom_y == sweep_line.current_y) { - sweep_line_delete (&sweep_line, stop); - stop = peek_stop (&sweep_line); - if (stop == NULL) - break; - } - - sweep_line.current_y++; - - while (stop != NULL && stop->bottom_y < start->top_y) { - if (stop->bottom_y != sweep_line.current_y) { - render_rows (&sweep_line, renderer, - stop->bottom_y - sweep_line.current_y); - sweep_line.current_y = stop->bottom_y; - } - - render_rows (&sweep_line, renderer, 1); - - do { - sweep_line_delete (&sweep_line, stop); - stop = peek_stop (&sweep_line); - } while (stop != NULL && stop->bottom_y == sweep_line.current_y); - - sweep_line.current_y++; - } - } while (TRUE); - - end: - render_rows (&sweep_line, renderer, 1); - - stop = peek_stop (&sweep_line); - while (stop->bottom_y == sweep_line.current_y) { - sweep_line_delete (&sweep_line, stop); - stop = peek_stop (&sweep_line); - if (stop == NULL) - goto out; - } - - sweep_line.current_y++; - - do { - if (stop->bottom_y != sweep_line.current_y) { - render_rows (&sweep_line, renderer, - stop->bottom_y - sweep_line.current_y); - sweep_line.current_y = stop->bottom_y; - } - - render_rows (&sweep_line, renderer, 1); - - do { - sweep_line_delete (&sweep_line, stop); - stop = peek_stop (&sweep_line); - if (stop == NULL) - goto out; - } while (stop->bottom_y == sweep_line.current_y); - - sweep_line.current_y++; - } while (TRUE); - - out: - status = renderer->render_rows (renderer, - sweep_line.current_y, - self->ymax - sweep_line.current_y, - NULL, 0); - - BAIL: - sweep_line_fini (&sweep_line); - - return status; -} - -static cairo_status_t -_cairo_rectangular_scan_converter_generate (void *converter, - cairo_span_renderer_t *renderer) -{ - cairo_rectangular_scan_converter_t *self = converter; - rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)]; - rectangle_t **rectangles; - struct _cairo_rectangular_scan_converter_chunk *chunk; - cairo_status_t status; - int i, j; - - if (unlikely (self->num_rectangles == 0)) { - return renderer->render_rows (renderer, - self->ymin, self->ymax - self->ymin, - NULL, 0); - } - - rectangles = rectangles_stack; - if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) { - rectangles = _cairo_malloc_ab (self->num_rectangles + 1, - sizeof (rectangle_t *)); - if (unlikely (rectangles == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - j = 0; - for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { - rectangle_t *rectangle; - - rectangle = chunk->base; - for (i = 0; i < chunk->count; i++) - rectangles[j++] = &rectangle[i]; - } - rectangle_sort (rectangles, j); - rectangles[j] = NULL; - - status = generate (self, renderer, rectangles); - - if (rectangles != rectangles_stack) - free (rectangles); - - return status; -} - -static rectangle_t * -_allocate_rectangle (cairo_rectangular_scan_converter_t *self) -{ - rectangle_t *rectangle; - struct _cairo_rectangular_scan_converter_chunk *chunk; - - chunk = self->tail; - if (chunk->count == chunk->size) { - int size; - - size = chunk->size * 2; - chunk->next = _cairo_malloc_ab_plus_c (size, - sizeof (rectangle_t), - sizeof (struct _cairo_rectangular_scan_converter_chunk)); - - if (unlikely (chunk->next == NULL)) - return NULL; - - chunk = chunk->next; - chunk->next = NULL; - chunk->count = 0; - chunk->size = size; - chunk->base = chunk + 1; - self->tail = chunk; - } - - rectangle = chunk->base; - return rectangle + chunk->count++; -} - -cairo_status_t -_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, - const cairo_box_t *box, - int dir) -{ - rectangle_t *rectangle; - - rectangle = _allocate_rectangle (self); - if (unlikely (rectangle == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - rectangle->left = box->p1.x; - rectangle->right = box->p2.x; - rectangle->dir = dir; - - rectangle->top = box->p1.y; - rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y); - rectangle->bottom = box->p2.y; - rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y); - assert (rectangle->bottom_y >= rectangle->top_y); - - self->num_rectangles++; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_rectangular_scan_converter_destroy (void *converter) -{ - cairo_rectangular_scan_converter_t *self = converter; - struct _cairo_rectangular_scan_converter_chunk *chunk, *next; - - for (chunk = self->chunks.next; chunk != NULL; chunk = next) { - next = chunk->next; - free (chunk); - } -} - -void -_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, - const cairo_rectangle_int_t *extents) -{ - self->base.destroy = _cairo_rectangular_scan_converter_destroy; - self->base.add_edge = NULL; - self->base.add_polygon = NULL; - self->base.generate = _cairo_rectangular_scan_converter_generate; - - self->xmin = extents->x; - self->xmax = extents->x + extents->width; - self->ymin = extents->y; - self->ymax = extents->y + extents->height; - - self->chunks.base = self->buf; - self->chunks.next = NULL; - self->chunks.count = 0; - self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t); - self->tail = &self->chunks; - - self->num_rectangles = 0; -} diff --git a/libs/cairo/cairo/src/cairo-reference-count-private.h b/libs/cairo/cairo/src/cairo-reference-count-private.h deleted file mode 100644 index c05d4c910..000000000 --- a/libs/cairo/cairo/src/cairo-reference-count-private.h +++ /dev/null @@ -1,29 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H -#define CAIRO_REFRENCE_COUNT_PRIVATE_H - -#include "cairo-atomic-private.h" - -/* Encapsulate operations on the object's reference count */ -typedef struct { - cairo_atomic_int_t ref_count; -} cairo_reference_count_t; - -#define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count) -#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count) - -#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE)) - -#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count) - -#define CAIRO_REFERENCE_COUNT_INVALID_VALUE (-1) -#define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE} - -#define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE) - -#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0) - -#endif diff --git a/libs/cairo/cairo/src/cairo-region-private.h b/libs/cairo/cairo/src/cairo-region-private.h deleted file mode 100644 index 32c1e82ab..000000000 --- a/libs/cairo/cairo/src/cairo-region-private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_REGION_PRIVATE_H -#define CAIRO_REGION_PRIVATE_H - -#include "cairo-types-private.h" -#include "cairo-reference-count-private.h" - -#include - -CAIRO_BEGIN_DECLS - -struct _cairo_region { - cairo_reference_count_t ref_count; - cairo_status_t status; - - pixman_region32_t rgn; -}; - -cairo_private cairo_region_t * -_cairo_region_create_in_error (cairo_status_t status); - -cairo_private void -_cairo_region_init (cairo_region_t *region); - -cairo_private void -_cairo_region_init_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *rectangle); - -cairo_private void -_cairo_region_fini (cairo_region_t *region); - -CAIRO_END_DECLS - -#endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-region.c b/libs/cairo/cairo/src/cairo-region.c deleted file mode 100644 index 3e8ac1248..000000000 --- a/libs/cairo/cairo/src/cairo-region.c +++ /dev/null @@ -1,871 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-region-private.h" - -/* XXX need to update pixman headers to be const as appropriate */ -#define CONST_CAST (pixman_region32_t *) - -/** - * SECTION:cairo-region - * @Title: Regions - * @Short_Description: Representing a pixel-aligned area - * - * Regions are a simple graphical data type representing an area of - * integer-aligned rectangles. They are often used on raster surfaces - * to track areas of interest, such as change or clip areas. - */ - -static const cairo_region_t _cairo_region_nil = { - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NO_MEMORY, /* status */ -}; - -cairo_region_t * -_cairo_region_create_in_error (cairo_status_t status) -{ - switch (status) { - case CAIRO_STATUS_NO_MEMORY: - return (cairo_region_t *) &_cairo_region_nil; - - case CAIRO_STATUS_SUCCESS: - case CAIRO_STATUS_LAST_STATUS: - ASSERT_NOT_REACHED; - /* fall-through */ - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: - case CAIRO_STATUS_INVALID_STATUS: - case CAIRO_STATUS_INVALID_CONTENT: - case CAIRO_STATUS_INVALID_FORMAT: - case CAIRO_STATUS_INVALID_VISUAL: - case CAIRO_STATUS_READ_ERROR: - case CAIRO_STATUS_WRITE_ERROR: - case CAIRO_STATUS_FILE_NOT_FOUND: - case CAIRO_STATUS_TEMP_FILE_ERROR: - case CAIRO_STATUS_INVALID_STRIDE: - case CAIRO_STATUS_INVALID_SIZE: - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: - case CAIRO_STATUS_DEVICE_ERROR: - case CAIRO_STATUS_INVALID_RESTORE: - case CAIRO_STATUS_INVALID_POP_GROUP: - case CAIRO_STATUS_NO_CURRENT_POINT: - case CAIRO_STATUS_INVALID_MATRIX: - case CAIRO_STATUS_NULL_POINTER: - case CAIRO_STATUS_INVALID_STRING: - case CAIRO_STATUS_INVALID_PATH_DATA: - case CAIRO_STATUS_SURFACE_FINISHED: - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: - case CAIRO_STATUS_INVALID_DASH: - case CAIRO_STATUS_INVALID_DSC_COMMENT: - case CAIRO_STATUS_INVALID_INDEX: - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: - case CAIRO_STATUS_FONT_TYPE_MISMATCH: - case CAIRO_STATUS_USER_FONT_IMMUTABLE: - case CAIRO_STATUS_USER_FONT_ERROR: - case CAIRO_STATUS_NEGATIVE_COUNT: - case CAIRO_STATUS_INVALID_CLUSTERS: - case CAIRO_STATUS_INVALID_SLANT: - case CAIRO_STATUS_INVALID_WEIGHT: - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: - default: - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_region_t *) &_cairo_region_nil; - } -} - -/** - * _cairo_region_set_error: - * @region: a region - * @status: a status value indicating an error - * - * Atomically sets region->status to @status and calls _cairo_error; - * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal - * status values. - * - * All assignments of an error status to region->status should happen - * through _cairo_region_set_error(). Note that due to the nature of - * the atomic operation, it is not safe to call this function on the - * nil objects. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - * - * Return value: the error status. - **/ -static cairo_status_t -_cairo_region_set_error (cairo_region_t *region, - cairo_status_t status) -{ - if (! _cairo_status_is_error (status)) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (®ion->status, status); - - return _cairo_error (status); -} - -void -_cairo_region_init (cairo_region_t *region) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); - - region->status = CAIRO_STATUS_SUCCESS; - CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); - pixman_region32_init (®ion->rgn); -} - -void -_cairo_region_init_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *rectangle) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); - - region->status = CAIRO_STATUS_SUCCESS; - CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); - pixman_region32_init_rect (®ion->rgn, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); -} - -void -_cairo_region_fini (cairo_region_t *region) -{ - assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); - pixman_region32_fini (®ion->rgn); - VG (VALGRIND_MAKE_MEM_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 (®ion->ref_count, 1); - - pixman_region32_init (®ion->rgn); - - return region; -} -slim_hidden_def (cairo_region_create); - -/** - * cairo_region_create_rectangles: - * @rects: an array of @count rectangles - * @count: number of rectangles - * - * Allocates a new region object containing the union of all given @rects. - * - * Return value: A newly allocated #cairo_region_t. Free with - * cairo_region_destroy(). This function always returns a - * valid pointer; if memory cannot be allocated, then a special - * error object is returned where all operations on the object do nothing. - * You can check for this with cairo_region_status(). - * - * Since: 1.10 - **/ -cairo_region_t * -cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, - int count) -{ - pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; - pixman_box32_t *pboxes = stack_pboxes; - cairo_region_t *region; - int i; - - region = _cairo_malloc (sizeof (cairo_region_t)); - if (unlikely (region == NULL)) - return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - if (count > ARRAY_LENGTH (stack_pboxes)) { - pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); - if (unlikely (pboxes == NULL)) { - free (region); - return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - for (i = 0; i < count; i++) { - pboxes[i].x1 = rects[i].x; - pboxes[i].y1 = rects[i].y; - pboxes[i].x2 = rects[i].x + rects[i].width; - pboxes[i].y2 = rects[i].y + rects[i].height; - } - - i = pixman_region32_init_rects (®ion->rgn, pboxes, count); - - if (pboxes != stack_pboxes) - free (pboxes); - - if (unlikely (i == 0)) { - free (region); - return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); - region->status = CAIRO_STATUS_SUCCESS; - return region; -} -slim_hidden_def (cairo_region_create_rectangles); - -/** - * cairo_region_create_rectangle: - * @rectangle: a #cairo_rectangle_int_t - * - * Allocates a new region object containing @rectangle. - * - * Return value: A newly allocated #cairo_region_t. Free with - * cairo_region_destroy(). This function always returns a - * valid pointer; if memory cannot be allocated, then a special - * error object is returned where all operations on the object do nothing. - * You can check for this with cairo_region_status(). - * - * Since: 1.10 - **/ -cairo_region_t * -cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) -{ - cairo_region_t *region; - - region = _cairo_malloc (sizeof (cairo_region_t)); - if (unlikely (region == NULL)) - return (cairo_region_t *) &_cairo_region_nil; - - region->status = CAIRO_STATUS_SUCCESS; - CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); - - pixman_region32_init_rect (®ion->rgn, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); - - return region; -} -slim_hidden_def (cairo_region_create_rectangle); - -/** - * cairo_region_copy: - * @original: a #cairo_region_t - * - * Allocates a new region object copying the area from @original. - * - * Return value: A newly allocated #cairo_region_t. Free with - * cairo_region_destroy(). This function always returns a - * valid pointer; if memory cannot be allocated, then a special - * error object is returned where all operations on the object do nothing. - * You can check for this with cairo_region_status(). - * - * Since: 1.10 - **/ -cairo_region_t * -cairo_region_copy (const cairo_region_t *original) -{ - cairo_region_t *copy; - - if (original != NULL && original->status) - return (cairo_region_t *) &_cairo_region_nil; - - copy = cairo_region_create (); - if (unlikely (copy->status)) - return copy; - - if (original != NULL && - ! pixman_region32_copy (©->rgn, CONST_CAST &original->rgn)) - { - cairo_region_destroy (copy); - return (cairo_region_t *) &_cairo_region_nil; - } - - return copy; -} -slim_hidden_def (cairo_region_copy); - -/** - * cairo_region_reference: - * @region: a #cairo_region_t - * - * Increases the reference count on @region by one. This prevents - * @region from being destroyed until a matching call to - * cairo_region_destroy() is made. - * - * Return value: the referenced #cairo_region_t. - * - * Since: 1.10 - **/ -cairo_region_t * -cairo_region_reference (cairo_region_t *region) -{ - if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) - return NULL; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); - - _cairo_reference_count_inc (®ion->ref_count); - return region; -} -slim_hidden_def (cairo_region_reference); - -/** - * cairo_region_destroy: - * @region: a #cairo_region_t - * - * Destroys a #cairo_region_t object created with - * cairo_region_create(), cairo_region_copy(), or - * or cairo_region_create_rectangle(). - * - * Since: 1.10 - **/ -void -cairo_region_destroy (cairo_region_t *region) -{ - if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); - - if (! _cairo_reference_count_dec_and_test (®ion->ref_count)) - return; - - _cairo_region_fini (region); - free (region); -} -slim_hidden_def (cairo_region_destroy); - -/** - * cairo_region_num_rectangles: - * @region: a #cairo_region_t - * - * Returns the number of rectangles contained in @region. - * - * Return value: The number of rectangles contained in @region. - * - * Since: 1.10 - **/ -int -cairo_region_num_rectangles (const cairo_region_t *region) -{ - if (region->status) - return 0; - - return pixman_region32_n_rects (CONST_CAST ®ion->rgn); -} -slim_hidden_def (cairo_region_num_rectangles); - -/** - * cairo_region_get_rectangle: - * @region: a #cairo_region_t - * @nth: a number indicating which rectangle should be returned - * @rectangle: return location for a #cairo_rectangle_int_t - * - * Stores the @nth rectangle from the region in @rectangle. - * - * Since: 1.10 - **/ -void -cairo_region_get_rectangle (const cairo_region_t *region, - int nth, - cairo_rectangle_int_t *rectangle) -{ - pixman_box32_t *pbox; - - if (region->status) { - rectangle->x = rectangle->y = 0; - rectangle->width = rectangle->height = 0; - return; - } - - pbox = pixman_region32_rectangles (CONST_CAST ®ion->rgn, NULL) + nth; - - rectangle->x = pbox->x1; - rectangle->y = pbox->y1; - rectangle->width = pbox->x2 - pbox->x1; - rectangle->height = pbox->y2 - pbox->y1; -} -slim_hidden_def (cairo_region_get_rectangle); - -/** - * cairo_region_get_extents: - * @region: a #cairo_region_t - * @extents: rectangle into which to store the extents - * - * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t - * - * Since: 1.10 - **/ -void -cairo_region_get_extents (const cairo_region_t *region, - cairo_rectangle_int_t *extents) -{ - pixman_box32_t *pextents; - - if (region->status) { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - return; - } - - pextents = pixman_region32_extents (CONST_CAST ®ion->rgn); - - extents->x = pextents->x1; - extents->y = pextents->y1; - extents->width = pextents->x2 - pextents->x1; - extents->height = pextents->y2 - pextents->y1; -} -slim_hidden_def (cairo_region_get_extents); - -/** - * cairo_region_status: - * @region: a #cairo_region_t - * - * Checks whether an error has previous occured for this - * region object. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_status (const cairo_region_t *region) -{ - return region->status; -} -slim_hidden_def (cairo_region_status); - -/** - * cairo_region_subtract: - * @dst: a #cairo_region_t - * @other: another #cairo_region_t - * - * Subtracts @other from @dst and places the result in @dst - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other) -{ - if (dst->status) - return dst->status; - - if (other->status) - return _cairo_region_set_error (dst, other->status); - - if (! pixman_region32_subtract (&dst->rgn, - &dst->rgn, - CONST_CAST &other->rgn)) - { - return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_region_subtract); - -/** - * cairo_region_subtract_rectangle: - * @dst: a #cairo_region_t - * @rectangle: a #cairo_rectangle_int_t - * - * Subtracts @rectangle from @dst and places the result in @dst - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_subtract_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - pixman_region32_t region; - - if (dst->status) - return dst->status; - - pixman_region32_init_rect (®ion, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); - - if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) - status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - pixman_region32_fini (®ion); - - return status; -} -slim_hidden_def (cairo_region_subtract_rectangle); - -/** - * cairo_region_intersect: - * @dst: a #cairo_region_t - * @other: another #cairo_region_t - * - * Computes the intersection of @dst with @other and places the result in @dst - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other) -{ - if (dst->status) - return dst->status; - - if (other->status) - return _cairo_region_set_error (dst, other->status); - - if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) - return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_region_intersect); - -/** - * cairo_region_intersect_rectangle: - * @dst: a #cairo_region_t - * @rectangle: a #cairo_rectangle_int_t - * - * Computes the intersection of @dst with @rectangle and places the - * result in @dst - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_intersect_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - pixman_region32_t region; - - if (dst->status) - return dst->status; - - pixman_region32_init_rect (®ion, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); - - if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) - status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - pixman_region32_fini (®ion); - - return status; -} -slim_hidden_def (cairo_region_intersect_rectangle); - -/** - * cairo_region_union: - * @dst: a #cairo_region_t - * @other: another #cairo_region_t - * - * Computes the union of @dst with @other and places the result in @dst - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_union (cairo_region_t *dst, - const cairo_region_t *other) -{ - if (dst->status) - return dst->status; - - if (other->status) - return _cairo_region_set_error (dst, other->status); - - if (! pixman_region32_union (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) - return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_region_union); - -/** - * cairo_region_union_rectangle: - * @dst: a #cairo_region_t - * @rectangle: a #cairo_rectangle_int_t - * - * Computes the union of @dst with @rectangle and places the result in @dst. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_union_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - pixman_region32_t region; - - if (dst->status) - return dst->status; - - pixman_region32_init_rect (®ion, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); - - if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) - status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - pixman_region32_fini (®ion); - - return status; -} -slim_hidden_def (cairo_region_union_rectangle); - -/** - * cairo_region_xor: - * @dst: a #cairo_region_t - * @other: another #cairo_region_t - * - * Computes the exclusive difference of @dst with @other and places the - * result in @dst. That is, @dst will be set to contain all areas that - * are either in @dst or in @other, but not in both. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - pixman_region32_t tmp; - - if (dst->status) - return dst->status; - - if (other->status) - return _cairo_region_set_error (dst, other->status); - - pixman_region32_init (&tmp); - - /* XXX: get an xor function into pixman */ - if (! pixman_region32_subtract (&tmp, CONST_CAST &other->rgn, &dst->rgn) || - ! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn) || - ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) - status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - pixman_region32_fini (&tmp); - - return status; -} -slim_hidden_def (cairo_region_xor); - -/** - * cairo_region_xor_rectangle: - * @dst: a #cairo_region_t - * @rectangle: a #cairo_rectangle_int_t - * - * Computes the exclusive difference of @dst with @rectangle and places the - * result in @dst. That is, @dst will be set to contain all areas that are - * either in @dst or in @rectangle, but not in both. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY - * - * Since: 1.10 - **/ -cairo_status_t -cairo_region_xor_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - pixman_region32_t region, tmp; - - if (dst->status) - return dst->status; - - pixman_region32_init_rect (®ion, - rectangle->x, rectangle->y, - rectangle->width, rectangle->height); - pixman_region32_init (&tmp); - - /* XXX: get an xor function into pixman */ - if (! pixman_region32_subtract (&tmp, ®ion, &dst->rgn) || - ! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion) || - ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) - status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); - - pixman_region32_fini (&tmp); - pixman_region32_fini (®ion); - - return status; -} -slim_hidden_def (cairo_region_xor_rectangle); - -/** - * cairo_region_is_empty: - * @region: a #cairo_region_t - * - * Checks whether @region is empty. - * - * Return value: %TRUE if @region is empty, %FALSE if it isn't. - * - * Since: 1.10 - **/ -cairo_bool_t -cairo_region_is_empty (const cairo_region_t *region) -{ - if (region->status) - return TRUE; - - return ! pixman_region32_not_empty (CONST_CAST ®ion->rgn); -} -slim_hidden_def (cairo_region_is_empty); - -/** - * cairo_region_translate: - * @region: a #cairo_region_t - * @dx: Amount to translate in the x direction - * @dy: Amount to translate in the y direction - * - * Translates @region by (@dx, @dy). - * - * Since: 1.10 - **/ -void -cairo_region_translate (cairo_region_t *region, - int dx, int dy) -{ - if (region->status) - return; - - pixman_region32_translate (®ion->rgn, dx, dy); -} -slim_hidden_def (cairo_region_translate); - -/** - * cairo_region_overlap_t: - * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region - * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region - * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and - * partially outside the region. - * - * Used as the return value for cairo_region_contains_rectangle(). - */ - -/** - * cairo_region_contains_rectangle: - * @region: a #cairo_region_t - * @rectangle: a #cairo_rectangle_int_t - * - * Checks whether @rectangle is inside, outside or partially contained - * in @region - * - * Return value: - * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, - * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or - * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. - * - * Since: 1.10 - **/ -cairo_region_overlap_t -cairo_region_contains_rectangle (const cairo_region_t *region, - const cairo_rectangle_int_t *rectangle) -{ - pixman_box32_t pbox; - pixman_region_overlap_t poverlap; - - if (region->status) - return CAIRO_REGION_OVERLAP_OUT; - - pbox.x1 = rectangle->x; - pbox.y1 = rectangle->y; - pbox.x2 = rectangle->x + rectangle->width; - pbox.y2 = rectangle->y + rectangle->height; - - poverlap = pixman_region32_contains_rectangle (CONST_CAST ®ion->rgn, - &pbox); - switch (poverlap) { - default: - case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; - case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; - case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; - } -} -slim_hidden_def (cairo_region_contains_rectangle); - -/** - * cairo_region_contains_point: - * @region: a #cairo_region_t - * @x: the x coordinate of a point - * @y: the y coordinate of a point - * - * Checks whether (@x, @y) is contained in @region. - * - * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. - * - * Since: 1.10 - **/ -cairo_bool_t -cairo_region_contains_point (const cairo_region_t *region, - int x, int y) -{ - pixman_box32_t box; - - if (region->status) - return FALSE; - - return pixman_region32_contains_point (CONST_CAST ®ion->rgn, x, y, &box); -} -slim_hidden_def (cairo_region_contains_point); - -/** - * cairo_region_equal: - * @a: a #cairo_region_t or %NULL - * @b: a #cairo_region_t or %NULL - * - * Compares whether region_a is equivalent to region_b. %NULL as an argument - * is equal to itself, but not to any non-%NULL region. - * - * Return value: %TRUE if both regions contained the same coverage, - * %FALSE if it is not or any region is in an error status. - * - * Since: 1.10 - **/ -cairo_bool_t -cairo_region_equal (const cairo_region_t *a, - const cairo_region_t *b) -{ - /* error objects are never equal */ - if ((a != NULL && a->status) || (b != NULL && b->status)) - return FALSE; - - if (a == b) - return TRUE; - - if (a == NULL || b == NULL) - return FALSE; - - return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn); -} -slim_hidden_def (cairo_region_equal); diff --git a/libs/cairo/cairo/src/cairo-rename.h b/libs/cairo/cairo/src/cairo-rename.h deleted file mode 100644 index db20352ea..000000000 --- a/libs/cairo/cairo/src/cairo-rename.h +++ /dev/null @@ -1,411 +0,0 @@ -#define cairo_append_path _moz_cairo_append_path -#define cairo_arc _moz_cairo_arc -#define cairo_arc_negative _moz_cairo_arc_negative -#define cairo_arc_to _moz_cairo_arc_to -#define cairo_beos_surface_create _moz_cairo_beos_surface_create -#define cairo_beos_surface_create_for_bitmap _moz_cairo_beos_surface_create_for_bitmap -#define cairo_clip _moz_cairo_clip -#define cairo_clip_extents _moz_cairo_clip_extents -#define cairo_clip_preserve _moz_cairo_clip_preserve -#define cairo_close_path _moz_cairo_close_path -#define cairo_copy_clip_rectangle_list _moz_cairo_copy_clip_rectangle_list -#define cairo_copy_page _moz_cairo_copy_page -#define cairo_copy_path _moz_cairo_copy_path -#define cairo_copy_path_flat _moz_cairo_copy_path_flat -#define cairo_create _moz_cairo_create -#define cairo_curve_to _moz_cairo_curve_to -#define cairo_d2d_create_device _moz_cairo_d2d_create_device -#define cairo_d2d_create_device_from_d3d10device _moz_cairo_d2d_create_device_from_d3d10device -#define cairo_d2d_device_get_device _moz_cairo_d2d_device_get_device -#define cairo_d2d_get_dc _moz_cairo_d2d_get_dc -#define cairo_d2d_get_image_surface_cache_usage _moz_cairo_d2d_get_image_surface_cache_usage -#define cairo_d2d_get_surface_vram_usage _moz_cairo_d2d_get_surface_vram_usage -#define cairo_d2d_present_backbuffer _moz_cairo_d2d_present_backbuffer -#define cairo_d2d_release_dc _moz_cairo_d2d_release_dc -#define cairo_d2d_scroll _moz_cairo_d2d_scroll -#define cairo_d2d_surface_create _moz_cairo_d2d_surface_create -#define cairo_d2d_surface_create_for_handle _moz_cairo_d2d_surface_create_for_handle -#define cairo_d2d_surface_create_for_hwnd _moz_cairo_d2d_surface_create_for_hwnd -#define cairo_d2d_surface_create_for_texture _moz_cairo_d2d_surface_create_for_texture -#define cairo_d2d_surface_get_height _moz_cairo_d2d_surface_get_height -#define cairo_d2d_surface_get_texture _moz_cairo_d2d_surface_get_texture -#define cairo_d2d_surface_get_width _moz_cairo_d2d_surface_get_width -#define cairo_debug_reset_static_data _moz_cairo_debug_reset_static_data -#define cairo_destroy _moz_cairo_destroy -#define cairo_device_acquire _moz_cairo_device_acquire -#define cairo_device_destroy _moz_cairo_device_destroy -#define cairo_device_finish _moz_cairo_device_finish -#define cairo_device_flush _moz_cairo_device_flush -#define cairo_device_get_reference_count _moz_cairo_device_get_reference_count -#define cairo_device_get_type _moz_cairo_device_get_type -#define cairo_device_get_user_data _moz_cairo_device_get_user_data -#define cairo_device_release _moz_cairo_device_release -#define cairo_device_set_user_data _moz_cairo_device_set_user_data -#define cairo_device_status _moz_cairo_device_status -#define cairo_device_reference _moz_cairo_device_reference -#define cairo_device_to_user _moz_cairo_device_to_user -#define cairo_device_to_user_distance _moz_cairo_device_to_user_distance -#define cairo_directfb_surface_create _moz_cairo_directfb_surface_create -#define cairo_dwrite_font_face_create_for_dwrite_fontface _moz_cairo_dwrite_font_face_create_for_dwrite_fontface -#define cairo_dwrite_get_cleartype_rendering_mode _moz_cairo_dwrite_get_cleartype_rendering_mode -#define cairo_dwrite_scaled_font_allow_manual_show_glyphs _moz_cairo_dwrite_scaled_font_allow_manual_show_glyphs -#define cairo_dwrite_scaled_font_get_force_GDI_classic _moz_cairo_dwrite_scaled_font_get_force_GDI_classic -#define cairo_dwrite_scaled_font_set_force_GDI_classic _moz_cairo_dwrite_scaled_font_set_force_GDI_classic -#define cairo_dwrite_set_cleartype_params _moz_cairo_dwrite_set_cleartype_params -#define cairo_fill _moz_cairo_fill -#define cairo_fill_extents _moz_cairo_fill_extents -#define cairo_fill_preserve _moz_cairo_fill_preserve -#define cairo_font_extents _moz_cairo_font_extents -#define cairo_font_face_destroy _moz_cairo_font_face_destroy -#define cairo_font_face_get_reference_count _moz_cairo_font_face_get_reference_count -#define cairo_font_face_get_type _moz_cairo_font_face_get_type -#define cairo_font_face_get_user_data _moz_cairo_font_face_get_user_data -#define cairo_font_face_reference _moz_cairo_font_face_reference -#define cairo_font_face_set_user_data _moz_cairo_font_face_set_user_data -#define cairo_font_face_status _moz_cairo_font_face_status -#define cairo_font_options_copy _moz_cairo_font_options_copy -#define cairo_font_options_create _moz_cairo_font_options_create -#define cairo_font_options_destroy _moz_cairo_font_options_destroy -#define cairo_font_options_equal _moz_cairo_font_options_equal -#define cairo_font_options_get_antialias _moz_cairo_font_options_get_antialias -#define cairo_font_options_get_hint_metrics _moz_cairo_font_options_get_hint_metrics -#define cairo_font_options_get_hint_style _moz_cairo_font_options_get_hint_style -#define cairo_font_options_get_lcd_filter _moz_cairo_font_options_get_lcd_filter -#define cairo_font_options_get_subpixel_order _moz_cairo_font_options_get_subpixel_order -#define cairo_font_options_hash _moz_cairo_font_options_hash -#define cairo_font_options_merge _moz_cairo_font_options_merge -#define cairo_font_options_set_antialias _moz_cairo_font_options_set_antialias -#define cairo_font_options_set_hint_metrics _moz_cairo_font_options_set_hint_metrics -#define cairo_font_options_set_hint_style _moz_cairo_font_options_set_hint_style -#define cairo_font_options_set_lcd_filter _moz_cairo_font_options_set_lcd_filter -#define cairo_font_options_set_subpixel_order _moz_cairo_font_options_set_subpixel_order -#define cairo_font_options_status _moz_cairo_font_options_status -#define cairo_format_stride_for_width _moz_cairo_format_stride_for_width -#define cairo_ft_font_face_create_for_ft_face _moz_cairo_ft_font_face_create_for_ft_face -#define cairo_ft_font_face_create_for_pattern _moz_cairo_ft_font_face_create_for_pattern -#define cairo_ft_font_options_substitute _moz_cairo_ft_font_options_substitute -#define cairo_ft_scaled_font_lock_face _moz_cairo_ft_scaled_font_lock_face -#define cairo_ft_scaled_font_unlock_face _moz_cairo_ft_scaled_font_unlock_face -#define cairo_get_antialias _moz_cairo_get_antialias -#define cairo_get_current_point _moz_cairo_get_current_point -#define cairo_get_dash _moz_cairo_get_dash -#define cairo_get_dash_count _moz_cairo_get_dash_count -#define cairo_get_fill_rule _moz_cairo_get_fill_rule -#define cairo_get_font_face _moz_cairo_get_font_face -#define cairo_get_font_matrix _moz_cairo_get_font_matrix -#define cairo_get_font_options _moz_cairo_get_font_options -#define cairo_get_group_target _moz_cairo_get_group_target -#define cairo_get_line_cap _moz_cairo_get_line_cap -#define cairo_get_line_join _moz_cairo_get_line_join -#define cairo_get_line_width _moz_cairo_get_line_width -#define cairo_get_matrix _moz_cairo_get_matrix -#define cairo_get_miter_limit _moz_cairo_get_miter_limit -#define cairo_get_operator _moz_cairo_get_operator -#define cairo_get_reference_count _moz_cairo_get_reference_count -#define cairo_get_scaled_font _moz_cairo_get_scaled_font -#define cairo_get_source _moz_cairo_get_source -#define cairo_get_target _moz_cairo_get_target -#define cairo_get_tolerance _moz_cairo_get_tolerance -#define cairo_get_user_data _moz_cairo_get_user_data -#define cairo_glitz_surface_create _moz_cairo_glitz_surface_create -#define cairo_glyph_allocate _moz_cairo_glyph_allocate -#define cairo_glyph_extents _moz_cairo_glyph_extents -#define cairo_glyph_free _moz_cairo_glyph_free -#define cairo_glyph_path _moz_cairo_glyph_path -#define cairo_has_current_point _moz_cairo_has_current_point -#define cairo_has_show_text_glyphs _moz_cairo_has_show_text_glyphs -#define cairo_identity_matrix _moz_cairo_identity_matrix -#define cairo_image_surface_create _moz_cairo_image_surface_create -#define cairo_image_surface_create_for_data _moz_cairo_image_surface_create_for_data -#define cairo_image_surface_create_from_png _moz_cairo_image_surface_create_from_png -#define cairo_image_surface_create_from_png_stream _moz_cairo_image_surface_create_from_png_stream -#define cairo_image_surface_get_data _moz_cairo_image_surface_get_data -#define cairo_image_surface_get_format _moz_cairo_image_surface_get_format -#define cairo_image_surface_get_height _moz_cairo_image_surface_get_height -#define cairo_image_surface_get_stride _moz_cairo_image_surface_get_stride -#define cairo_image_surface_get_width _moz_cairo_image_surface_get_width -#define cairo_in_clip _moz_cairo_in_clip -#define cairo_in_fill _moz_cairo_in_fill -#define cairo_in_stroke _moz_cairo_in_stroke -#define cairo_line_to _moz_cairo_line_to -#define cairo_mask _moz_cairo_mask -#define cairo_mask_surface _moz_cairo_mask_surface -#define cairo_matrix_init _moz_cairo_matrix_init -#define cairo_matrix_init_identity _moz_cairo_matrix_init_identity -#define cairo_matrix_init_rotate _moz_cairo_matrix_init_rotate -#define cairo_matrix_init_scale _moz_cairo_matrix_init_scale -#define cairo_matrix_init_translate _moz_cairo_matrix_init_translate -#define cairo_matrix_invert _moz_cairo_matrix_invert -#define cairo_matrix_multiply _moz_cairo_matrix_multiply -#define cairo_matrix_rotate _moz_cairo_matrix_rotate -#define cairo_matrix_scale _moz_cairo_matrix_scale -#define cairo_matrix_transform_distance _moz_cairo_matrix_transform_distance -#define cairo_matrix_transform_point _moz_cairo_matrix_transform_point -#define cairo_matrix_translate _moz_cairo_matrix_translate -#define cairo_move_to _moz_cairo_move_to -#define cairo_new_path _moz_cairo_new_path -#define cairo_new_sub_path _moz_cairo_new_sub_path -#define cairo_null_surface_create _moz_cairo_null_surface_create -#define cairo_os2_fini _moz_cairo_os2_fini -#define cairo_os2_init _moz_cairo_os2_init -#define cairo_os2_surface_create _moz_cairo_os2_surface_create -#define cairo_os2_surface_create_for_window _moz_cairo_os2_surface_create_for_window -#define cairo_os2_surface_get_hps _moz_cairo_os2_surface_get_hps -#define cairo_os2_surface_get_manual_window_refresh _moz_cairo_os2_surface_get_manual_window_refresh -#define cairo_os2_surface_refresh_window _moz_cairo_os2_surface_refresh_window -#define cairo_os2_surface_set_hps _moz_cairo_os2_surface_set_hps -#define cairo_os2_surface_set_hwnd _moz_cairo_os2_surface_set_hwnd -#define cairo_os2_surface_set_manual_window_refresh _moz_cairo_os2_surface_set_manual_window_refresh -#define cairo_os2_surface_set_size _moz_cairo_os2_surface_set_size -#define cairo_paint _moz_cairo_paint -#define cairo_paint_with_alpha _moz_cairo_paint_with_alpha -#define cairo_path_destroy _moz_cairo_path_destroy -#define cairo_path_extents _moz_cairo_path_extents -#define cairo_pattern_add_color_stop_rgb _moz_cairo_pattern_add_color_stop_rgb -#define cairo_pattern_add_color_stop_rgba _moz_cairo_pattern_add_color_stop_rgba -#define cairo_pattern_create_for_surface _moz_cairo_pattern_create_for_surface -#define cairo_pattern_create_linear _moz_cairo_pattern_create_linear -#define cairo_pattern_create_radial _moz_cairo_pattern_create_radial -#define cairo_pattern_create_rgb _moz_cairo_pattern_create_rgb -#define cairo_pattern_create_rgba _moz_cairo_pattern_create_rgba -#define cairo_pattern_destroy _moz_cairo_pattern_destroy -#define cairo_pattern_get_color_stop_count _moz_cairo_pattern_get_color_stop_count -#define cairo_pattern_get_color_stop_rgba _moz_cairo_pattern_get_color_stop_rgba -#define cairo_pattern_get_extend _moz_cairo_pattern_get_extend -#define cairo_pattern_get_filter _moz_cairo_pattern_get_filter -#define cairo_pattern_get_linear_points _moz_cairo_pattern_get_linear_points -#define cairo_pattern_get_matrix _moz_cairo_pattern_get_matrix -#define cairo_pattern_get_radial_circles _moz_cairo_pattern_get_radial_circles -#define cairo_pattern_get_reference_count _moz_cairo_pattern_get_reference_count -#define cairo_pattern_get_rgba _moz_cairo_pattern_get_rgba -#define cairo_pattern_get_surface _moz_cairo_pattern_get_surface -#define cairo_pattern_get_type _moz_cairo_pattern_get_type -#define cairo_pattern_get_user_data _moz_cairo_pattern_get_user_data -#define cairo_pattern_reference _moz_cairo_pattern_reference -#define cairo_pattern_set_extend _moz_cairo_pattern_set_extend -#define cairo_pattern_set_filter _moz_cairo_pattern_set_filter -#define cairo_pattern_set_matrix _moz_cairo_pattern_set_matrix -#define cairo_pattern_set_user_data _moz_cairo_pattern_set_user_data -#define cairo_pattern_status _moz_cairo_pattern_status -#define cairo_pdf_get_versions _moz_cairo_pdf_get_versions -#define cairo_pdf_surface_create _moz_cairo_pdf_surface_create -#define cairo_pdf_surface_create_for_stream _moz_cairo_pdf_surface_create_for_stream -#define cairo_pdf_surface_restrict_to_version _moz_cairo_pdf_surface_restrict_to_version -#define cairo_pdf_surface_set_size _moz_cairo_pdf_surface_set_size -#define cairo_pdf_version_to_string _moz_cairo_pdf_version_to_string -#define cairo_pop_group _moz_cairo_pop_group -#define cairo_pop_group_to_source _moz_cairo_pop_group_to_source -#define cairo_ps_get_levels _moz_cairo_ps_get_levels -#define cairo_ps_level_to_string _moz_cairo_ps_level_to_string -#define cairo_ps_surface_create _moz_cairo_ps_surface_create -#define cairo_ps_surface_create_for_stream _moz_cairo_ps_surface_create_for_stream -#define cairo_ps_surface_dsc_begin_page_setup _moz_cairo_ps_surface_dsc_begin_page_setup -#define cairo_ps_surface_dsc_begin_setup _moz_cairo_ps_surface_dsc_begin_setup -#define cairo_ps_surface_dsc_comment _moz_cairo_ps_surface_dsc_comment -#define cairo_ps_surface_get_eps _moz_cairo_ps_surface_get_eps -#define cairo_ps_surface_restrict_to_level _moz_cairo_ps_surface_restrict_to_level -#define cairo_ps_surface_set_eps _moz_cairo_ps_surface_set_eps -#define cairo_ps_surface_set_size _moz_cairo_ps_surface_set_size -#define cairo_push_group _moz_cairo_push_group -#define cairo_push_group_with_content _moz_cairo_push_group_with_content -#define cairo_qpainter_surface_create _moz_cairo_qpainter_surface_create -#define cairo_qpainter_surface_create_with_qimage _moz_cairo_qpainter_surface_create_with_qimage -#define cairo_qpainter_surface_create_with_qpixmap _moz_cairo_qpainter_surface_create_with_qpixmap -#define cairo_qpainter_surface_get_image _moz_cairo_qpainter_surface_get_image -#define cairo_qpainter_surface_get_qimage _moz_cairo_qpainter_surface_get_qimage -#define cairo_qpainter_surface_get_qpainter _moz_cairo_qpainter_surface_get_qpainter -#define cairo_quartz_font_face_create_for_atsu_font_id _moz_cairo_quartz_font_face_create_for_atsu_font_id -#define cairo_quartz_font_face_create_for_cgfont _moz_cairo_quartz_font_face_create_for_cgfont -#define cairo_quartz_image_surface_create _moz_cairo_quartz_image_surface_create -#define cairo_quartz_image_surface_get_image _moz_cairo_quartz_image_surface_get_image -#define cairo_quartz_surface_create _moz_cairo_quartz_surface_create -#define cairo_quartz_surface_create_for_cg_context _moz_cairo_quartz_surface_create_for_cg_context -#define cairo_quartz_surface_create_for_data _moz_cairo_quartz_surface_create_for_data -#define cairo_quartz_surface_get_cg_context _moz_cairo_quartz_surface_get_cg_context -#define cairo_quartz_surface_get_image _moz_cairo_quartz_surface_get_image -#define cairo_recording_surface_create _moz_cairo_recording_surface_create -#define cairo_recording_surface_ink_extents _moz_cairo_recording_surface_ink_extents -#define cairo_rectangle _moz_cairo_rectangle -#define cairo_rectangle_list_destroy _moz_cairo_rectangle_list_destroy -#define cairo_reference _moz_cairo_reference -#define cairo_region_contains_point _moz_cairo_region_contains_point -#define cairo_region_contains_rectangle _moz_cairo_region_contains_rectangle -#define cairo_region_copy _moz_cairo_region_copy -#define cairo_region_create _moz_cairo_region_create -#define cairo_region_create_rectangle _moz_cairo_region_create_rectangle -#define cairo_region_create_rectangles _moz_cairo_region_create_rectangles -#define cairo_region_destroy _moz_cairo_region_destroy -#define cairo_region_equal _moz_cairo_region_equal -#define cairo_region_get_extents _moz_cairo_region_get_extents -#define cairo_region_get_rectangle _moz_cairo_region_get_rectangle -#define cairo_region_intersect _moz_cairo_region_intersect -#define cairo_region_intersect_rectangle _moz_cairo_region_intersect_rectangle -#define cairo_region_is_empty _moz_cairo_region_is_empty -#define cairo_region_num_rectangles _moz_cairo_region_num_rectangles -#define cairo_region_reference _moz_cairo_region_reference -#define cairo_region_status _moz_cairo_region_status -#define cairo_region_subtract _moz_cairo_region_subtract -#define cairo_region_subtract_rectangle _moz_cairo_region_subtract_rectangle -#define cairo_region_translate _moz_cairo_region_translate -#define cairo_region_union _moz_cairo_region_union -#define cairo_region_union_rectangle _moz_cairo_region_union_rectangle -#define cairo_region_xor _moz_cairo_region_xor -#define cairo_region_xor_rectangle _moz_cairo_region_xor_rectangle -#define cairo_rel_curve_to _moz_cairo_rel_curve_to -#define cairo_rel_line_to _moz_cairo_rel_line_to -#define cairo_rel_move_to _moz_cairo_rel_move_to -#define cairo_release_device _moz_cairo_release_device -#define cairo_reset_clip _moz_cairo_reset_clip -#define cairo_restore _moz_cairo_restore -#define cairo_rotate _moz_cairo_rotate -#define cairo_save _moz_cairo_save -#define cairo_scale _moz_cairo_scale -#define cairo_scaled_font_create _moz_cairo_scaled_font_create -#define cairo_scaled_font_destroy _moz_cairo_scaled_font_destroy -#define cairo_scaled_font_extents _moz_cairo_scaled_font_extents -#define cairo_scaled_font_get_ctm _moz_cairo_scaled_font_get_ctm -#define cairo_scaled_font_get_font_face _moz_cairo_scaled_font_get_font_face -#define cairo_scaled_font_get_font_matrix _moz_cairo_scaled_font_get_font_matrix -#define cairo_scaled_font_get_font_options _moz_cairo_scaled_font_get_font_options -#define cairo_scaled_font_get_reference_count _moz_cairo_scaled_font_get_reference_count -#define cairo_scaled_font_get_scale_matrix _moz_cairo_scaled_font_get_scale_matrix -#define cairo_scaled_font_get_type _moz_cairo_scaled_font_get_type -#define cairo_scaled_font_get_user_data _moz_cairo_scaled_font_get_user_data -#define cairo_scaled_font_glyph_extents _moz_cairo_scaled_font_glyph_extents -#define cairo_scaled_font_reference _moz_cairo_scaled_font_reference -#define cairo_scaled_font_set_user_data _moz_cairo_scaled_font_set_user_data -#define cairo_scaled_font_status _moz_cairo_scaled_font_status -#define cairo_scaled_font_text_extents _moz_cairo_scaled_font_text_extents -#define cairo_scaled_font_text_to_glyphs _moz_cairo_scaled_font_text_to_glyphs -#define cairo_select_font_face _moz_cairo_select_font_face -#define cairo_set_antialias _moz_cairo_set_antialias -#define cairo_set_dash _moz_cairo_set_dash -#define cairo_set_fill_rule _moz_cairo_set_fill_rule -#define cairo_set_font_face _moz_cairo_set_font_face -#define cairo_set_font_matrix _moz_cairo_set_font_matrix -#define cairo_set_font_options _moz_cairo_set_font_options -#define cairo_set_font_size _moz_cairo_set_font_size -#define cairo_set_line_cap _moz_cairo_set_line_cap -#define cairo_set_line_join _moz_cairo_set_line_join -#define cairo_set_line_width _moz_cairo_set_line_width -#define cairo_set_matrix _moz_cairo_set_matrix -#define cairo_set_miter_limit _moz_cairo_set_miter_limit -#define cairo_set_operator _moz_cairo_set_operator -#define cairo_set_scaled_font _moz_cairo_set_scaled_font -#define cairo_set_source _moz_cairo_set_source -#define cairo_set_source_rgb _moz_cairo_set_source_rgb -#define cairo_set_source_rgba _moz_cairo_set_source_rgba -#define cairo_set_source_surface _moz_cairo_set_source_surface -#define cairo_set_tolerance _moz_cairo_set_tolerance -#define cairo_set_user_data _moz_cairo_set_user_data -#define cairo_show_glyphs _moz_cairo_show_glyphs -#define cairo_show_page _moz_cairo_show_page -#define cairo_show_text _moz_cairo_show_text -#define cairo_show_text_glyphs _moz_cairo_show_text_glyphs -#define cairo_status _moz_cairo_status -#define cairo_status_to_string _moz_cairo_status_to_string -#define cairo_stroke _moz_cairo_stroke -#define cairo_stroke_extents _moz_cairo_stroke_extents -#define cairo_stroke_preserve _moz_cairo_stroke_preserve -#define cairo_stroke_to_path _moz_cairo_stroke_to_path -#define cairo_surface_attach_snapshot _moz_cairo_surface_attach_snapshot -#define cairo_surface_copy_page _moz_cairo_surface_copy_page -#define cairo_surface_create_for_rectangle _moz_cairo_surface_create_for_rectangle -#define cairo_surface_create_similar _moz_cairo_surface_create_similar -#define cairo_surface_detach_snapshot _moz_cairo_surface_detach_snapshot -#define cairo_surface_destroy _moz_cairo_surface_destroy -#define cairo_surface_finish _moz_cairo_surface_finish -#define cairo_surface_flush _moz_cairo_surface_flush -#define cairo_surface_get_content _moz_cairo_surface_get_content -#define cairo_surface_get_device _moz_cairo_surface_get_device -#define cairo_surface_get_device_offset _moz_cairo_surface_get_device_offset -#define cairo_surface_get_fallback_resolution _moz_cairo_surface_get_fallback_resolution -#define cairo_surface_get_font_options _moz_cairo_surface_get_font_options -#define cairo_surface_get_mime_data _moz_cairo_surface_get_mime_data -#define cairo_surface_get_reference_count _moz_cairo_surface_get_reference_count -#define cairo_surface_get_subpixel_antialiasing _moz_cairo_surface_get_subpixel_antialiasing -#define cairo_surface_get_type _moz_cairo_surface_get_type -#define cairo_surface_get_user_data _moz_cairo_surface_get_user_data -#define cairo_surface_has_show_text_glyphs _moz_cairo_surface_has_show_text_glyphs -#define cairo_surface_mark_dirty _moz_cairo_surface_mark_dirty -#define cairo_surface_mark_dirty_rectangle _moz_cairo_surface_mark_dirty_rectangle -#define cairo_surface_reference _moz_cairo_surface_reference -#define cairo_surface_set_device_offset _moz_cairo_surface_set_device_offset -#define cairo_surface_set_fallback_resolution _moz_cairo_surface_set_fallback_resolution -#define cairo_surface_set_mime_data _moz_cairo_surface_set_mime_data -#define cairo_surface_set_subpixel_antialiasing _moz_cairo_surface_set_subpixel_antialiasing -#define cairo_surface_set_user_data _moz_cairo_surface_set_user_data -#define cairo_surface_show_page _moz_cairo_surface_show_page -#define cairo_surface_status _moz_cairo_surface_status -#define cairo_surface_write_to_png _moz_cairo_surface_write_to_png -#define cairo_surface_write_to_png_stream _moz_cairo_surface_write_to_png_stream -#define cairo_svg_get_versions _moz_cairo_svg_get_versions -#define cairo_svg_surface_create _moz_cairo_svg_surface_create -#define cairo_svg_surface_create_for_stream _moz_cairo_svg_surface_create_for_stream -#define cairo_svg_surface_restrict_to_version _moz_cairo_svg_surface_restrict_to_version -#define cairo_svg_version_to_string _moz_cairo_svg_version_to_string -#define cairo_tee_surface_add _moz_cairo_tee_surface_add -#define cairo_tee_surface_create _moz_cairo_tee_surface_create -#define cairo_tee_surface_index _moz_cairo_tee_surface_index -#define cairo_tee_surface_remove _moz_cairo_tee_surface_remove -#define cairo_text_cluster_allocate _moz_cairo_text_cluster_allocate -#define cairo_text_cluster_free _moz_cairo_text_cluster_free -#define cairo_text_extents _moz_cairo_text_extents -#define cairo_text_path _moz_cairo_text_path -#define cairo_toy_font_face_create _moz_cairo_toy_font_face_create -#define cairo_toy_font_face_get_family _moz_cairo_toy_font_face_get_family -#define cairo_toy_font_face_get_slant _moz_cairo_toy_font_face_get_slant -#define cairo_toy_font_face_get_weight _moz_cairo_toy_font_face_get_weight -#define cairo_transform _moz_cairo_transform -#define cairo_translate _moz_cairo_translate -#define cairo_user_font_face_create _moz_cairo_user_font_face_create -#define cairo_user_font_face_get_init_func _moz_cairo_user_font_face_get_init_func -#define cairo_user_font_face_get_render_glyph_func _moz_cairo_user_font_face_get_render_glyph_func -#define cairo_user_font_face_get_text_to_glyphs_func _moz_cairo_user_font_face_get_text_to_glyphs_func -#define cairo_user_font_face_get_unicode_to_glyph_func _moz_cairo_user_font_face_get_unicode_to_glyph_func -#define cairo_user_font_face_set_init_func _moz_cairo_user_font_face_set_init_func -#define cairo_user_font_face_set_render_glyph_func _moz_cairo_user_font_face_set_render_glyph_func -#define cairo_user_font_face_set_text_to_glyphs_func _moz_cairo_user_font_face_set_text_to_glyphs_func -#define cairo_user_font_face_set_unicode_to_glyph_func _moz_cairo_user_font_face_set_unicode_to_glyph_func -#define cairo_user_to_device _moz_cairo_user_to_device -#define cairo_user_to_device_distance _moz_cairo_user_to_device_distance -#define cairo_version _moz_cairo_version -#define cairo_version_string _moz_cairo_version_string -#define cairo_win32_get_dc_with_clip _moz_cairo_win32_get_dc_with_clip -#define cairo_win32_get_system_text_quality _moz_cairo_win32_get_system_text_quality -#define cairo_win32_font_face_create_for_hfont _moz_cairo_win32_font_face_create_for_hfont -#define cairo_win32_font_face_create_for_logfontw _moz_cairo_win32_font_face_create_for_logfontw -#define cairo_win32_font_face_create_for_logfontw_hfont _moz_cairo_win32_font_face_create_for_logfontw_hfont -#define cairo_win32_printing_surface_create _moz_cairo_win32_printing_surface_create -#define cairo_win32_scaled_font_done_font _moz_cairo_win32_scaled_font_done_font -#define cairo_win32_scaled_font_get_device_to_logical _moz_cairo_win32_scaled_font_get_device_to_logical -#define cairo_win32_scaled_font_get_logical_to_device _moz_cairo_win32_scaled_font_get_logical_to_device -#define cairo_win32_scaled_font_get_metrics_factor _moz_cairo_win32_scaled_font_get_metrics_factor -#define cairo_win32_scaled_font_select_font _moz_cairo_win32_scaled_font_select_font -#define cairo_win32_surface_create _moz_cairo_win32_surface_create -#define cairo_win32_surface_create_with_alpha _moz_cairo_win32_surface_create_with_alpha -#define cairo_win32_surface_create_with_d3dsurface9 _moz_cairo_win32_surface_create_with_d3dsurface9 -#define cairo_win32_surface_create_with_ddb _moz_cairo_win32_surface_create_with_ddb -#define cairo_win32_surface_create_with_dib _moz_cairo_win32_surface_create_with_dib -#define cairo_win32_surface_get_dc _moz_cairo_win32_surface_get_dc -#define cairo_win32_surface_get_height _moz_cairo_win32_surface_get_height -#define cairo_win32_surface_get_image _moz_cairo_win32_surface_get_image -#define cairo_win32_surface_get_width _moz_cairo_win32_surface_get_width -#define cairo_win32_surface_set_can_convert_to_dib _moz_cairo_win32_surface_set_can_convert_to_dib -#define cairo_xcb_surface_create _moz_cairo_xcb_surface_create -#define cairo_xcb_surface_create_for_bitmap _moz_cairo_xcb_surface_create_for_bitmap -#define cairo_xcb_surface_create_with_xrender_format _moz_cairo_xcb_surface_create_with_xrender_format -#define cairo_xcb_surface_set_size _moz_cairo_xcb_surface_set_size -#define cairo_xlib_surface_create _moz_cairo_xlib_surface_create -#define cairo_xlib_surface_create_for_bitmap _moz_cairo_xlib_surface_create_for_bitmap -#define cairo_xlib_surface_create_with_xrender_format _moz_cairo_xlib_surface_create_with_xrender_format -#define cairo_xlib_surface_get_depth _moz_cairo_xlib_surface_get_depth -#define cairo_xlib_surface_get_display _moz_cairo_xlib_surface_get_display -#define cairo_xlib_surface_get_drawable _moz_cairo_xlib_surface_get_drawable -#define cairo_xlib_surface_get_height _moz_cairo_xlib_surface_get_height -#define cairo_xlib_surface_get_screen _moz_cairo_xlib_surface_get_screen -#define cairo_xlib_surface_get_visual _moz_cairo_xlib_surface_get_visual -#define cairo_xlib_surface_get_width _moz_cairo_xlib_surface_get_width -#define cairo_xlib_surface_get_xrender_format _moz_cairo_xlib_surface_get_xrender_format -#define cairo_xlib_surface_set_drawable _moz_cairo_xlib_surface_set_drawable -#define cairo_xlib_surface_set_size _moz_cairo_xlib_surface_set_size diff --git a/libs/cairo/cairo/src/cairo-rtree-private.h b/libs/cairo/cairo/src/cairo-rtree-private.h deleted file mode 100644 index 295f48ced..000000000 --- a/libs/cairo/cairo/src/cairo-rtree-private.h +++ /dev/null @@ -1,102 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_RTREE_PRIVATE_H -#define CAIRO_RTREE_PRIVATE_H - -#include "cairo-compiler-private.h" -#include "cairo-types-private.h" - -#include "cairo-freelist-private.h" -#include "cairo-list-private.h" - -enum { - CAIRO_RTREE_NODE_AVAILABLE, - CAIRO_RTREE_NODE_DIVIDED, - CAIRO_RTREE_NODE_OCCUPIED, -}; - -typedef struct _cairo_rtree_node { - struct _cairo_rtree_node *children[4], *parent; - void **owner; - cairo_list_t link; - uint16_t pinned; - uint16_t state; - uint16_t x, y; - uint16_t width, height; -} cairo_rtree_node_t; - -typedef struct _cairo_rtree { - cairo_rtree_node_t root; - int min_size; - cairo_list_t pinned; - cairo_list_t available; - cairo_list_t evictable; - cairo_freepool_t node_freepool; -} cairo_rtree_t; - -cairo_private cairo_rtree_node_t * -_cairo_rtree_node_create (cairo_rtree_t *rtree, - cairo_rtree_node_t *parent, - int x, - int y, - int width, - int height); - -cairo_private cairo_status_t -_cairo_rtree_node_insert (cairo_rtree_t *rtree, - cairo_rtree_node_t *node, - int width, - int height, - cairo_rtree_node_t **out); - -cairo_private void -_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node); - -cairo_private void -_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node); - -cairo_private void -_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node); - -cairo_private void -_cairo_rtree_init (cairo_rtree_t *rtree, - int width, - int height, - int min_size, - int node_size); - -cairo_private cairo_int_status_t -_cairo_rtree_insert (cairo_rtree_t *rtree, - int width, - int height, - cairo_rtree_node_t **out); - -cairo_private cairo_int_status_t -_cairo_rtree_evict_random (cairo_rtree_t *rtree, - int width, - int height, - cairo_rtree_node_t **out); - -static inline void * -_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node) -{ - if (! node->pinned) { - cairo_list_move (&node->link, &rtree->pinned); - node->pinned = 1; - } - - return node; -} - -cairo_private void -_cairo_rtree_unpin (cairo_rtree_t *rtree); - -cairo_private void -_cairo_rtree_reset (cairo_rtree_t *rtree); - -cairo_private void -_cairo_rtree_fini (cairo_rtree_t *rtree); - -#endif /* CAIRO_RTREE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-rtree.c b/libs/cairo/cairo/src/cairo-rtree.c deleted file mode 100644 index 059edff75..000000000 --- a/libs/cairo/cairo/src/cairo-rtree.c +++ /dev/null @@ -1,353 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-rtree-private.h" - -cairo_rtree_node_t * -_cairo_rtree_node_create (cairo_rtree_t *rtree, - cairo_rtree_node_t *parent, - int x, - int y, - int width, - int height) -{ - cairo_rtree_node_t *node; - - node = _cairo_freepool_alloc (&rtree->node_freepool); - if (node == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - node->children[0] = NULL; - node->parent = parent; - node->owner = NULL; - node->state = CAIRO_RTREE_NODE_AVAILABLE; - node->pinned = FALSE; - node->x = x; - node->y = y; - node->width = width; - node->height = height; - - cairo_list_add (&node->link, &rtree->available); - - return node; -} - -void -_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) -{ - int i; - - cairo_list_del (&node->link); - - if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { - if (node->owner != NULL) - *node->owner = NULL; - } else { - for (i = 0; i < 4 && node->children[i] != NULL; i++) - _cairo_rtree_node_destroy (rtree, node->children[i]); - } - - _cairo_freepool_free (&rtree->node_freepool, node); -} - -void -_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node) -{ - int i; - - do { - assert (node->state == CAIRO_RTREE_NODE_DIVIDED); - - for (i = 0; i < 4 && node->children[i] != NULL; i++) - if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE) - return; - - for (i = 0; i < 4 && node->children[i] != NULL; i++) - _cairo_rtree_node_destroy (rtree, node->children[i]); - - node->children[0] = NULL; - node->state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_move (&node->link, &rtree->available); - } while ((node = node->parent) != NULL); -} - -cairo_status_t -_cairo_rtree_node_insert (cairo_rtree_t *rtree, - cairo_rtree_node_t *node, - int width, - int height, - cairo_rtree_node_t **out) -{ - int w, h, i; - - assert (node->state == CAIRO_RTREE_NODE_AVAILABLE); - assert (node->pinned == FALSE); - - if (node->width - width > rtree->min_size || - node->height - height > rtree->min_size) - { - w = node->width - width; - h = node->height - height; - - i = 0; - node->children[i] = _cairo_rtree_node_create (rtree, node, - node->x, node->y, - width, height); - if (unlikely (node->children[i] == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - i++; - - if (w > rtree->min_size) { - node->children[i] = _cairo_rtree_node_create (rtree, node, - node->x + width, - node->y, - w, height); - if (unlikely (node->children[i] == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - i++; - } - - if (h > rtree->min_size) { - node->children[i] = _cairo_rtree_node_create (rtree, node, - node->x, - node->y + height, - width, h); - if (unlikely (node->children[i] == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - i++; - - if (w > rtree->min_size) { - node->children[i] = _cairo_rtree_node_create (rtree, node, - node->x + width, - node->y + height, - w, h); - if (unlikely (node->children[i] == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - i++; - } - } - - if (i < 4) - node->children[i] = NULL; - - node->state = CAIRO_RTREE_NODE_DIVIDED; - cairo_list_move (&node->link, &rtree->evictable); - node = node->children[0]; - } - - node->state = CAIRO_RTREE_NODE_OCCUPIED; - cairo_list_move (&node->link, &rtree->evictable); - *out = node; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node) -{ - assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); - assert (node->pinned == FALSE); - - node->state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_move (&node->link, &rtree->available); - - _cairo_rtree_node_collapse (rtree, node->parent); -} - -cairo_int_status_t -_cairo_rtree_insert (cairo_rtree_t *rtree, - int width, - int height, - cairo_rtree_node_t **out) -{ - cairo_rtree_node_t *node; - - cairo_list_foreach_entry (node, cairo_rtree_node_t, - &rtree->available, link) - { - if (node->width >= width && node->height >= height) - return _cairo_rtree_node_insert (rtree, node, width, height, out); - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static uint32_t -hars_petruska_f54_1_random (void) -{ -#define rol(x,k) ((x << k) | (x >> (32-k))) - static uint32_t x; - return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; -#undef rol -} - -cairo_int_status_t -_cairo_rtree_evict_random (cairo_rtree_t *rtree, - int width, - int height, - cairo_rtree_node_t **out) -{ - cairo_rtree_node_t *node, *next; - int i, cnt; - - /* propagate pinned from children to root */ - cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t, - &rtree->pinned, link) - { - if (node->parent != NULL) - _cairo_rtree_pin (rtree, node->parent); - } - - cnt = 0; - cairo_list_foreach_entry (node, cairo_rtree_node_t, - &rtree->evictable, link) - { - if (node->width >= width && node->height >= height) - cnt++; - } - - if (cnt == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cnt = hars_petruska_f54_1_random () % cnt; - cairo_list_foreach_entry (node, cairo_rtree_node_t, - &rtree->evictable, link) - { - if (node->width >= width && node->height >= height && cnt-- == 0) { - if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { - if (node->owner != NULL) - *node->owner = NULL; - } else { - for (i = 0; i < 4 && node->children[i] != NULL; i++) - _cairo_rtree_node_destroy (rtree, node->children[i]); - node->children[0] = NULL; - } - - node->state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_move (&node->link, &rtree->available); - - *out = node; - return CAIRO_STATUS_SUCCESS; - } - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -void -_cairo_rtree_unpin (cairo_rtree_t *rtree) -{ - cairo_rtree_node_t *node, *next; - cairo_list_t can_collapse; - - if (cairo_list_is_empty (&rtree->pinned)) - return; - - cairo_list_init (&can_collapse); - - cairo_list_foreach_entry_safe (node, next, - cairo_rtree_node_t, - &rtree->pinned, - link) - { - node->pinned = FALSE; - if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) { - cairo_bool_t all_available; - int i; - - node->state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_move (&node->link, &rtree->available); - - all_available = TRUE; - node = node->parent; - for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++) - all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE; - - if (all_available) { - cairo_list_move (&node->link, &can_collapse); - for (i = 0; i < 4 && node->children[i] != NULL; i++) - cairo_list_del (&node->children[i]->link); - } - } - else - { - cairo_list_move (&node->link, &rtree->evictable); - } - } - - cairo_list_foreach_entry_safe (node, next, - cairo_rtree_node_t, - &can_collapse, - link) - { - _cairo_rtree_node_collapse (rtree, node); - } -} - -void -_cairo_rtree_init (cairo_rtree_t *rtree, - int width, - int height, - int min_size, - int node_size) -{ - assert (node_size >= (int) sizeof (cairo_rtree_node_t)); - _cairo_freepool_init (&rtree->node_freepool, node_size); - - cairo_list_init (&rtree->available); - cairo_list_init (&rtree->pinned); - cairo_list_init (&rtree->evictable); - - rtree->min_size = min_size; - - memset (&rtree->root, 0, sizeof (rtree->root)); - rtree->root.width = width; - rtree->root.height = height; - rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_add (&rtree->root.link, &rtree->available); -} - -void -_cairo_rtree_reset (cairo_rtree_t *rtree) -{ - int i; - - if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { - if (rtree->root.owner != NULL) - *rtree->root.owner = NULL; - } else { - for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) - _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); - rtree->root.children[0] = NULL; - } - - cairo_list_init (&rtree->available); - cairo_list_init (&rtree->evictable); - cairo_list_init (&rtree->pinned); - - rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; - rtree->root.pinned = FALSE; - cairo_list_add (&rtree->root.link, &rtree->available); -} - -void -_cairo_rtree_fini (cairo_rtree_t *rtree) -{ - int i; - - if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { - if (rtree->root.owner != NULL) - *rtree->root.owner = NULL; - } else { - for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) - _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); - } - - _cairo_freepool_fini (&rtree->node_freepool); -} diff --git a/libs/cairo/cairo/src/cairo-scaled-font-private.h b/libs/cairo/cairo/src/cairo-scaled-font-private.h deleted file mode 100644 index 5d426494a..000000000 --- a/libs/cairo/cairo/src/cairo-scaled-font-private.h +++ /dev/null @@ -1,98 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SCALED_FONT_PRIVATE_H -#define CAIRO_SCALED_FONT_PRIVATE_H - -#include "cairo.h" - -#include "cairo-types-private.h" -#include "cairo-list-private.h" -#include "cairo-mutex-type-private.h" -#include "cairo-reference-count-private.h" - -typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t; - -struct _cairo_scaled_font { - /* For most cairo objects, the rule for multiple threads is that - * the user is responsible for any locking if the same object is - * manipulated from multiple threads simultaneously. - * - * However, with the caching that cairo does for scaled fonts, a - * user can easily end up with the same cairo_scaled_font object - * being manipulated from multiple threads without the user ever - * being aware of this, (and in fact, unable to control it). - * - * So, as a special exception, the cairo implementation takes care - * of all locking needed for cairo_scaled_font_t. Most of what is - * in the scaled font is immutable, (which is what allows for the - * sharing in the first place). The things that are modified and - * the locks protecting them are as follows: - * - * 1. The reference count (scaled_font->ref_count) - * - * Modifications to the reference count are protected by the - * _cairo_scaled_font_map_mutex. This is because the reference - * count of a scaled font is intimately related with the font - * map itself, (and the magic holdovers array). - * - * 2. The cache of glyphs (scaled_font->glyphs) - * 3. The backend private data (scaled_font->surface_backend, - * scaled_font->surface_private) - * - * Modifications to these fields are protected with locks on - * scaled_font->mutex in the generic scaled_font code. - */ - - cairo_hash_entry_t hash_entry; - - /* useful bits for _cairo_scaled_font_nil */ - cairo_status_t status; - cairo_reference_count_t ref_count; - cairo_user_data_array_t user_data; - - cairo_font_face_t *original_font_face; /* may be NULL */ - - /* hash key members */ - cairo_font_face_t *font_face; /* may be NULL */ - cairo_matrix_t font_matrix; /* font space => user space */ - cairo_matrix_t ctm; /* user space => device space */ - cairo_font_options_t options; - - unsigned int placeholder : 1; /* protected by fontmap mutex */ - unsigned int holdover : 1; - unsigned int finished : 1; - - /* "live" scaled_font members */ - cairo_matrix_t scale; /* font space => device space */ - cairo_matrix_t scale_inverse; /* device space => font space */ - double max_scale; /* maximum x/y expansion of scale */ - cairo_font_extents_t extents; /* user space */ - cairo_font_extents_t fs_extents; /* font space */ - - /* The mutex protects modification to all subsequent fields. */ - cairo_mutex_t mutex; - - cairo_hash_table_t *glyphs; - cairo_list_t glyph_pages; - cairo_bool_t cache_frozen; - cairo_bool_t global_cache_frozen; - - /* - * One surface backend may store data in each glyph. - * Whichever surface manages to store its pointer here - * first gets to store data in each glyph - */ - const cairo_surface_backend_t *surface_backend; - void *surface_private; - - /* font backend managing this scaled font */ - const cairo_scaled_font_backend_t *backend; - cairo_list_t link; -}; - -cairo_private void -_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font); - -#endif /* CAIRO_SCALED_FONT_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-scaled-font-subsets-private.h b/libs/cairo/cairo/src/cairo-scaled-font-subsets-private.h deleted file mode 100644 index 246dbdcfd..000000000 --- a/libs/cairo/cairo/src/cairo-scaled-font-subsets-private.h +++ /dev/null @@ -1,636 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H -#define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H - -#include "cairoint.h" - -#if CAIRO_HAS_FONT_SUBSET - -typedef struct _cairo_scaled_font_subsets_glyph { - unsigned int font_id; - unsigned int subset_id; - unsigned int subset_glyph_index; - cairo_bool_t is_scaled; - cairo_bool_t is_composite; - double x_advance; - double y_advance; - cairo_bool_t utf8_is_mapped; - uint32_t unicode; -} cairo_scaled_font_subsets_glyph_t; - -/** - * _cairo_scaled_font_subsets_create_scaled: - * - * Create a new #cairo_scaled_font_subsets_t object which can be used - * to create subsets of any number of #cairo_scaled_font_t - * objects. This allows the (arbitrarily large and sparse) glyph - * indices of a #cairo_scaled_font_t to be mapped to one or more font - * subsets with glyph indices packed into the range - * [0 .. max_glyphs_per_subset). - * - * Return value: a pointer to the newly creates font subsets. The - * caller owns this object and should call - * _cairo_scaled_font_subsets_destroy() when done with it. - **/ -cairo_private cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_scaled (void); - -/** - * _cairo_scaled_font_subsets_create_simple: - * - * Create a new #cairo_scaled_font_subsets_t object which can be used - * to create font subsets suitable for embedding as Postscript or PDF - * simple fonts. - * - * Glyphs with an outline path available will be mapped to one font - * subset for each font face. Glyphs from bitmap fonts will mapped to - * separate font subsets for each #cairo_scaled_font_t object. - * - * The maximum number of glyphs per subset is 256. Each subset - * reserves the first glyph for the .notdef glyph. - * - * Return value: a pointer to the newly creates font subsets. The - * caller owns this object and should call - * _cairo_scaled_font_subsets_destroy() when done with it. - **/ -cairo_private cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_simple (void); - -/** - * _cairo_scaled_font_subsets_create_composite: - * - * Create a new #cairo_scaled_font_subsets_t object which can be used - * to create font subsets suitable for embedding as Postscript or PDF - * composite fonts. - * - * Glyphs with an outline path available will be mapped to one font - * subset for each font face. Each unscaled subset has a maximum of - * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs. - * - * Glyphs from bitmap fonts will mapped to separate font subsets for - * each #cairo_scaled_font_t object. Each unscaled subset has a maximum - * of 256 glyphs. - * - * Each subset reserves the first glyph for the .notdef glyph. - * - * Return value: a pointer to the newly creates font subsets. The - * caller owns this object and should call - * _cairo_scaled_font_subsets_destroy() when done with it. - **/ -cairo_private cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_composite (void); - -/** - * _cairo_scaled_font_subsets_destroy: - * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed - * - * Destroys @font_subsets and all resources associated with it. - **/ -cairo_private void -_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets); - -/** - * _cairo_scaled_font_subsets_map_glyph: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @scaled_font: the font of the glyph to be mapped - * @scaled_font_glyph_index: the index of the glyph to be mapped - * @utf8: a string of text encoded in UTF-8 - * @utf8_len: length of @utf8 in bytes - * @subset_glyph_ret: return structure containing subset font and glyph id - * - * Map a glyph from a #cairo_scaled_font to a new index within a - * subset of that font. The mapping performed is from the tuple: - * - * (scaled_font, scaled_font_glyph_index) - * - * to the tuple: - * - * (font_id, subset_id, subset_glyph_index) - * - * This mapping is 1:1. If the input tuple has previously mapped, the - * the output tuple previously returned will be returned again. - * - * Otherwise, the return tuple will be constructed as follows: - * - * 1) There is a 1:1 correspondence between the input scaled_font - * value and the output font_id value. If no mapping has been - * previously performed with the scaled_font value then the - * smallest unused font_id value will be returned. - * - * 2) Within the set of output tuples of the same font_id value the - * smallest value of subset_id will be returned such that - * subset_glyph_index does not exceed max_glyphs_per_subset (as - * passed to _cairo_scaled_font_subsets_create()) and that the - * resulting tuple is unique. - * - * 3) The smallest value of subset_glyph_index is returned such that - * the resulting tuple is unique. - * - * The net result is that any #cairo_scaled_font_t will be represented - * by one or more font subsets. Each subset is effectively a tuple of - * (scaled_font, font_id, subset_id) and within each subset there - * exists a mapping of scaled_glyph_font_index to subset_glyph_index. - * - * This final description of a font subset is the same representation - * used by #cairo_scaled_font_subset_t as provided by - * _cairo_scaled_font_subsets_foreach. - * - * @utf8 and @utf8_len specify a string of unicode characters that the - * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in - * @subset_glyph_ret is %TRUE, the font subsetting will (where index to - * unicode mapping is supported) ensure that @scaled_font_glyph_index - * maps to @utf8. If @utf8_is_mapped is %FALSE, - * @scaled_font_glyph_index has already been mapped to a different - * unicode string. - * - * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are: - * - * @font_id: The font ID of the mapped glyph - * @subset_id : The subset ID of the mapped glyph within the @font_id - * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset - * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font - * subset is created for each font scale used. If false, the outline of the mapped glyph - * is available. One font subset for each font face is created. - * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain - * the x and y advance for the mapped glyph in device space. - * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for - * the the mapped glyph from an unhinted 1 point font. - * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph() - * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already - * mapped to a different utf8 string. - * @unicode: the unicode character mapped to this glyph by the font backend. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_t *scaled_font, - unsigned long scaled_font_glyph_index, - const char * utf8, - int utf8_len, - cairo_scaled_font_subsets_glyph_t *subset_glyph_ret); - -typedef cairo_status_t -(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset, - void *closure); - -/** - * _cairo_scaled_font_subsets_foreach: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @font_subset_callback: a function to be called for each font subset - * @closure: closure data for the callback function - * - * Iterate over each unique scaled font subset as created by calls to - * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by - * unique pairs of (font_id, subset_id) as returned by - * _cairo_scaled_font_subsets_map_glyph(). - * - * For each subset, @font_subset_callback will be called and will be - * provided with both a #cairo_scaled_font_subset_t object containing - * all the glyphs in the subset as well as the value of @closure. - * - * The #cairo_scaled_font_subset_t object contains the scaled_font, - * the font_id, and the subset_id corresponding to all glyphs - * belonging to the subset. In addition, it contains an array providing - * a mapping between subset glyph indices and the original scaled font - * glyph indices. - * - * The index of the array corresponds to subset_glyph_index values - * returned by _cairo_scaled_font_subsets_map_glyph() while the - * values of the array correspond to the scaled_font_glyph_index - * values passed as input to the same function. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure); - -/** - * _cairo_scaled_font_subsets_foreach_unscaled: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @font_subset_callback: a function to be called for each font subset - * @closure: closure data for the callback function - * - * Iterate over each unique unscaled font subset as created by calls to - * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by - * unique pairs of (font_id, subset_id) as returned by - * _cairo_scaled_font_subsets_map_glyph(). - * - * For each subset, @font_subset_callback will be called and will be - * provided with both a #cairo_scaled_font_subset_t object containing - * all the glyphs in the subset as well as the value of @closure. - * - * The #cairo_scaled_font_subset_t object contains the scaled_font, - * the font_id, and the subset_id corresponding to all glyphs - * belonging to the subset. In addition, it contains an array providing - * a mapping between subset glyph indices and the original scaled font - * glyph indices. - * - * The index of the array corresponds to subset_glyph_index values - * returned by _cairo_scaled_font_subsets_map_glyph() while the - * values of the array correspond to the scaled_font_glyph_index - * values passed as input to the same function. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure); - -/** - * _cairo_scaled_font_subsets_foreach_user: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @font_subset_callback: a function to be called for each font subset - * @closure: closure data for the callback function - * - * Iterate over each unique scaled font subset as created by calls to - * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by - * unique pairs of (font_id, subset_id) as returned by - * _cairo_scaled_font_subsets_map_glyph(). - * - * For each subset, @font_subset_callback will be called and will be - * provided with both a #cairo_scaled_font_subset_t object containing - * all the glyphs in the subset as well as the value of @closure. - * - * The #cairo_scaled_font_subset_t object contains the scaled_font, - * the font_id, and the subset_id corresponding to all glyphs - * belonging to the subset. In addition, it contains an array providing - * a mapping between subset glyph indices and the original scaled font - * glyph indices. - * - * The index of the array corresponds to subset_glyph_index values - * returned by _cairo_scaled_font_subsets_map_glyph() while the - * values of the array correspond to the scaled_font_glyph_index - * values passed as input to the same function. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure); - -/** - * _cairo_scaled_font_subset_create_glyph_names: - * @font_subsets: a #cairo_scaled_font_subsets_t - * - * Create an array of strings containing the glyph name for each glyph - * in @font_subsets. The array as store in font_subsets->glyph_names. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support - * mapping the glyph indices to unicode characters. Possible errors - * include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_int_status_t -_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset); - -typedef struct _cairo_cff_subset { - char *font_name; - char *ps_name; - double *widths; - double x_min, y_min, x_max, y_max; - double ascent, descent; - char *data; - unsigned long data_length; -} cairo_cff_subset_t; - -/** - * _cairo_cff_subset_init: - * @cff_subset: a #cairo_cff_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a - * cff file corresponding to @font_subset and initialize - * @cff_subset with information about the subset and the cff - * data. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a - * cff file, or an non-zero value indicating an error. Possible - * errors include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, - const char *name, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_cff_subset_fini: - * @cff_subset: a #cairo_cff_subset_t - * - * Free all resources associated with @cff_subset. After this - * call, @cff_subset should not be used again without a - * subsequent call to _cairo_cff_subset_init() again first. - **/ -cairo_private void -_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset); - -/** - * _cairo_cff_fallback_init: - * @cff_subset: a #cairo_cff_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a cff - * file corresponding to @font_subset and initialize @cff_subset - * with information about the subset and the cff data. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a - * cff file, or an non-zero value indicating an error. Possible - * errors include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, - const char *name, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_cff_fallback_fini: - * @cff_subset: a #cairo_cff_subset_t - * - * Free all resources associated with @cff_subset. After this - * call, @cff_subset should not be used again without a - * subsequent call to _cairo_cff_subset_init() again first. - **/ -cairo_private void -_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); - -typedef struct _cairo_truetype_subset { - char *font_name; - char *ps_name; - double *widths; - double x_min, y_min, x_max, y_max; - double ascent, descent; - unsigned char *data; - unsigned long data_length; - unsigned long *string_offsets; - unsigned long num_string_offsets; -} cairo_truetype_subset_t; - -/** - * _cairo_truetype_subset_init: - * @truetype_subset: a #cairo_truetype_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a - * truetype file corresponding to @font_subset and initialize - * @truetype_subset with information about the subset and the truetype - * data. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a - * truetype file, or an non-zero value indicating an error. Possible - * errors include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_truetype_subset_fini: - * @truetype_subset: a #cairo_truetype_subset_t - * - * Free all resources associated with @truetype_subset. After this - * call, @truetype_subset should not be used again without a - * subsequent call to _cairo_truetype_subset_init() again first. - **/ -cairo_private void -_cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset); - - - -typedef struct _cairo_type1_subset { - char *base_font; - double *widths; - double x_min, y_min, x_max, y_max; - double ascent, descent; - char *data; - unsigned long header_length; - unsigned long data_length; - unsigned long trailer_length; -} cairo_type1_subset_t; - - -#if CAIRO_HAS_FT_FONT - -/** - * _cairo_type1_subset_init: - * @type1_subset: a #cairo_type1_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * @hex_encode: if true the encrypted portion of the font is hex encoded - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a type1 - * file corresponding to @font_subset and initialize @type1_subset - * with information about the subset and the type1 data. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 - * file, or an non-zero value indicating an error. Possible errors - * include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_type1_subset_init (cairo_type1_subset_t *type_subset, - const char *name, - cairo_scaled_font_subset_t *font_subset, - cairo_bool_t hex_encode); - -/** - * _cairo_type1_subset_fini: - * @type1_subset: a #cairo_type1_subset_t - * - * Free all resources associated with @type1_subset. After this call, - * @type1_subset should not be used again without a subsequent call to - * _cairo_truetype_type1_init() again first. - **/ -cairo_private void -_cairo_type1_subset_fini (cairo_type1_subset_t *subset); - -#endif /* CAIRO_HAS_FT_FONT */ - - -/** - * _cairo_type1_scaled_font_is_type1: - * @scaled_font: a #cairo_scaled_font_t - * - * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE. - **/ -cairo_private cairo_bool_t -_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); - -/** - * _cairo_type1_fallback_init_binary: - * @type1_subset: a #cairo_type1_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a type1 - * file corresponding to @font_subset and initialize @type1_subset - * with information about the subset and the type1 data. The encrypted - * part of the font is binary encoded. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 - * file, or an non-zero value indicating an error. Possible errors - * include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset, - const char *name, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_type1_fallback_init_hexencode: - * @type1_subset: a #cairo_type1_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate a type1 - * file corresponding to @font_subset and initialize @type1_subset - * with information about the subset and the type1 data. The encrypted - * part of the font is hex encoded. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 - * file, or an non-zero value indicating an error. Possible errors - * include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type_subset, - const char *name, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_type1_fallback_fini: - * @type1_subset: a #cairo_type1_subset_t - * - * Free all resources associated with @type1_subset. After this call, - * @type1_subset should not be used again without a subsequent call to - * _cairo_truetype_type1_init() again first. - **/ -cairo_private void -_cairo_type1_fallback_fini (cairo_type1_subset_t *subset); - -typedef struct _cairo_type2_charstrings { - int *widths; - long x_min, y_min, x_max, y_max; - long ascent, descent; - cairo_array_t charstrings; -} cairo_type2_charstrings_t; - -/** - * _cairo_type2_charstrings_init: - * @type2_subset: a #cairo_type2_subset_t to initialize - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) generate type2 - * charstrings to @font_subset and initialize @type2_subset - * with information about the subset. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2 - * charstrings, or an non-zero value indicating an error. Possible errors - * include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings, - cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_type2_charstrings_fini: - * @subset: a #cairo_type2_charstrings_t - * - * Free all resources associated with @type2_charstring. After this call, - * @type2_charstring should not be used again without a subsequent call to - * _cairo_type2_charstring_init() again first. - **/ -cairo_private void -_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings); - -/** - * _cairo_truetype_create_glyph_to_unicode_map: - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) assign - * the unicode character of each glyph in font_subset to - * fontsubset->to_unicode. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the unicode encoding of - * the glyphs is not available. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_int_status_t -_cairo_truetype_create_glyph_to_unicode_map (cairo_scaled_font_subset_t *font_subset); - -/** - * _cairo_truetype_index_to_ucs4: - * @scaled_font: the #cairo_scaled_font_t - * @index: the glyph index - * @ucs4: return value for the unicode value of the glyph - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) assign - * the unicode character of the glyph to @ucs4. - * - * If mapping glyph indices to unicode is supported but the unicode - * value of the specified glyph is not available, @ucs4 is set to -1. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode - * is not supported. Possible errors include %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_int_status_t -_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, - unsigned long index, - uint32_t *ucs4); - -/** - * _cairo_truetype_read_font_name: - * @scaled_font: the #cairo_scaled_font_t - * @ps_name: returns the PostScript name of the font - * or %NULL if the name could not be found. - * @font_name: returns the font name or %NULL if the name could not be found. - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) read the - * PostScript and Font names from a TrueType/OpenType font. - * - * The font name is the full name of the font eg "DejaVu Sans Bold". - * The PostScript name is a shortened name with spaces removed - * suitable for use as the font name in a PS or PDF file eg - * "DejaVuSans-Bold". - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType - * or the name table is not present. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_int_status_t -_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, - char **ps_name, - char **font_name); - -#endif /* CAIRO_HAS_FONT_SUBSET */ - -#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-scaled-font-subsets.c b/libs/cairo/cairo/src/cairo-scaled-font-subsets.c deleted file mode 100644 index ba769d509..000000000 --- a/libs/cairo/cairo/src/cairo-scaled-font-subsets.c +++ /dev/null @@ -1,1053 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-user-font-private.h" - -#define MAX_GLYPHS_PER_SIMPLE_FONT 256 -#define MAX_GLYPHS_PER_COMPOSITE_FONT 65536 - -typedef enum { - CAIRO_SUBSETS_SCALED, - CAIRO_SUBSETS_SIMPLE, - CAIRO_SUBSETS_COMPOSITE -} cairo_subsets_type_t; - -typedef enum { - CAIRO_SUBSETS_FOREACH_UNSCALED, - CAIRO_SUBSETS_FOREACH_SCALED, - CAIRO_SUBSETS_FOREACH_USER -} cairo_subsets_foreach_type_t; - -typedef struct _cairo_sub_font { - cairo_hash_entry_t base; - - cairo_bool_t is_scaled; - cairo_bool_t is_composite; - cairo_bool_t is_user; - cairo_scaled_font_subsets_t *parent; - cairo_scaled_font_t *scaled_font; - unsigned int font_id; - - int current_subset; - int num_glyphs_in_current_subset; - int max_glyphs_per_subset; - - cairo_hash_table_t *sub_font_glyphs; - struct _cairo_sub_font *next; -} cairo_sub_font_t; - -struct _cairo_scaled_font_subsets { - cairo_subsets_type_t type; - - int max_glyphs_per_unscaled_subset_used; - cairo_hash_table_t *unscaled_sub_fonts; - cairo_sub_font_t *unscaled_sub_fonts_list; - cairo_sub_font_t *unscaled_sub_fonts_list_end; - - int max_glyphs_per_scaled_subset_used; - cairo_hash_table_t *scaled_sub_fonts; - cairo_sub_font_t *scaled_sub_fonts_list; - cairo_sub_font_t *scaled_sub_fonts_list_end; - - int num_sub_fonts; -}; - -typedef struct _cairo_sub_font_glyph { - cairo_hash_entry_t base; - - unsigned int subset_id; - unsigned int subset_glyph_index; - double x_advance; - double y_advance; - - cairo_bool_t is_mapped; - uint32_t unicode; - char *utf8; - int utf8_len; -} cairo_sub_font_glyph_t; - -typedef struct _cairo_sub_font_collection { - unsigned long *glyphs; /* scaled_font_glyph_index */ - char **utf8; - unsigned int glyphs_size; - unsigned int max_glyph; - unsigned int num_glyphs; - - unsigned int subset_id; - - cairo_status_t status; - cairo_scaled_font_subset_callback_func_t font_subset_callback; - void *font_subset_callback_closure; -} cairo_sub_font_collection_t; - -typedef struct _cairo_string_entry { - cairo_hash_entry_t base; - char *string; -} cairo_string_entry_t; - -static cairo_status_t -_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, - unsigned long scaled_font_glyph_index, - const char * utf8, - int utf8_len, - cairo_scaled_font_subsets_glyph_t *subset_glyph); - -static void -_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, - unsigned long scaled_font_glyph_index) -{ - sub_font_glyph->base.hash = scaled_font_glyph_index; -} - -static cairo_bool_t -_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b) -{ - const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a; - const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b; - - return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash; -} - -static cairo_sub_font_glyph_t * -_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, - unsigned int subset_id, - unsigned int subset_glyph_index, - double x_advance, - double y_advance) -{ - cairo_sub_font_glyph_t *sub_font_glyph; - - sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); - if (unlikely (sub_font_glyph == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index); - sub_font_glyph->subset_id = subset_id; - sub_font_glyph->subset_glyph_index = subset_glyph_index; - sub_font_glyph->x_advance = x_advance; - sub_font_glyph->y_advance = y_advance; - sub_font_glyph->is_mapped = FALSE; - sub_font_glyph->unicode = -1; - sub_font_glyph->utf8 = NULL; - sub_font_glyph->utf8_len = 0; - - return sub_font_glyph; -} - -static void -_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) -{ - if (sub_font_glyph->utf8 != NULL) - free (sub_font_glyph->utf8); - - free (sub_font_glyph); -} - -static void -_cairo_sub_font_glyph_pluck (void *entry, void *closure) -{ - cairo_sub_font_glyph_t *sub_font_glyph = entry; - cairo_hash_table_t *sub_font_glyphs = closure; - - _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base); - _cairo_sub_font_glyph_destroy (sub_font_glyph); -} - -static void -_cairo_sub_font_glyph_collect (void *entry, void *closure) -{ - cairo_sub_font_glyph_t *sub_font_glyph = entry; - cairo_sub_font_collection_t *collection = closure; - unsigned long scaled_font_glyph_index; - unsigned int subset_glyph_index; - - if (sub_font_glyph->subset_id != collection->subset_id) - return; - - scaled_font_glyph_index = sub_font_glyph->base.hash; - subset_glyph_index = sub_font_glyph->subset_glyph_index; - - /* Ensure we don't exceed the allocated bounds. */ - assert (subset_glyph_index < collection->glyphs_size); - - collection->glyphs[subset_glyph_index] = scaled_font_glyph_index; - collection->utf8[subset_glyph_index] = sub_font_glyph->utf8; - if (subset_glyph_index > collection->max_glyph) - collection->max_glyph = subset_glyph_index; - - collection->num_glyphs++; -} - -static cairo_bool_t -_cairo_sub_fonts_equal (const void *key_a, const void *key_b) -{ - const cairo_sub_font_t *sub_font_a = key_a; - const cairo_sub_font_t *sub_font_b = key_b; - cairo_scaled_font_t *a = sub_font_a->scaled_font; - cairo_scaled_font_t *b = sub_font_b->scaled_font; - - if (sub_font_a->is_scaled) - return a == b; - else - return a->font_face == b->font_face || a->original_font_face == b->original_font_face; -} - -static void -_cairo_sub_font_init_key (cairo_sub_font_t *sub_font, - cairo_scaled_font_t *scaled_font) -{ - if (sub_font->is_scaled) - { - sub_font->base.hash = (unsigned long) scaled_font; - sub_font->scaled_font = scaled_font; - } - else - { - sub_font->base.hash = (unsigned long) scaled_font->font_face; - sub_font->scaled_font = scaled_font; - } -} - -static cairo_status_t -_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, - cairo_scaled_font_t *scaled_font, - unsigned int font_id, - int max_glyphs_per_subset, - cairo_bool_t is_scaled, - cairo_bool_t is_composite, - cairo_sub_font_t **sub_font_out) -{ - cairo_sub_font_t *sub_font; - cairo_status_t status; - cairo_scaled_font_subsets_glyph_t subset_glyph; - - sub_font = malloc (sizeof (cairo_sub_font_t)); - if (unlikely (sub_font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - sub_font->is_scaled = is_scaled; - sub_font->is_composite = is_composite; - sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face); - _cairo_sub_font_init_key (sub_font, scaled_font); - - sub_font->parent = parent; - sub_font->scaled_font = scaled_font; - sub_font->font_id = font_id; - - sub_font->current_subset = 0; - sub_font->num_glyphs_in_current_subset = 0; - sub_font->max_glyphs_per_subset = max_glyphs_per_subset; - - sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal); - if (unlikely (sub_font->sub_font_glyphs == NULL)) { - free (sub_font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - sub_font->next = NULL; - - /* Reserve first glyph in subset for the .notdef glyph except for - * Type 3 fonts */ - if (! is_scaled) { - status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph); - if (unlikely (status)) { - _cairo_hash_table_destroy (sub_font->sub_font_glyphs); - free (sub_font); - return status; - } - } - - *sub_font_out = sub_font; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_sub_font_destroy (cairo_sub_font_t *sub_font) -{ - _cairo_hash_table_foreach (sub_font->sub_font_glyphs, - _cairo_sub_font_glyph_pluck, - sub_font->sub_font_glyphs); - _cairo_hash_table_destroy (sub_font->sub_font_glyphs); - cairo_scaled_font_destroy (sub_font->scaled_font); - free (sub_font); -} - -static void -_cairo_sub_font_pluck (void *entry, void *closure) -{ - cairo_sub_font_t *sub_font = entry; - cairo_hash_table_t *sub_fonts = closure; - - _cairo_hash_table_remove (sub_fonts, &sub_font->base); - _cairo_sub_font_destroy (sub_font); -} - -static cairo_status_t -_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, - cairo_scaled_font_t *scaled_font, - unsigned long scaled_font_glyph_index) -{ - uint32_t unicode; - char buf[8]; - int len; - cairo_status_t status; - - /* Do a reverse lookup on the glyph index. unicode is -1 if the - * index could not be mapped to a unicode character. */ - unicode = -1; - status = _cairo_truetype_index_to_ucs4 (scaled_font, - scaled_font_glyph_index, - &unicode); - if (_cairo_status_is_error (status)) - return status; - - if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) { - status = scaled_font->backend->index_to_ucs4 (scaled_font, - scaled_font_glyph_index, - &unicode); - if (unlikely (status)) - return status; - } - - sub_font_glyph->unicode = unicode; - sub_font_glyph->utf8 = NULL; - sub_font_glyph->utf8_len = 0; - if (unicode != (uint32_t) -1) { - len = _cairo_ucs4_to_utf8 (unicode, buf); - if (len > 0) { - sub_font_glyph->utf8 = malloc (len + 1); - if (unlikely (sub_font_glyph->utf8 == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (sub_font_glyph->utf8, buf, len); - sub_font_glyph->utf8[len] = 0; - sub_font_glyph->utf8_len = len; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, - const char *utf8, - int utf8_len, - cairo_bool_t *is_mapped) -{ - *is_mapped = FALSE; - - if (utf8_len < 0) - return CAIRO_STATUS_SUCCESS; - - if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') - utf8_len--; - - if (utf8 != NULL && utf8_len != 0) { - if (sub_font_glyph->utf8 != NULL) { - if (utf8_len == sub_font_glyph->utf8_len && - memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) - { - /* Requested utf8 mapping matches the existing mapping */ - *is_mapped = TRUE; - } - } else { - /* No existing mapping. Use the requested mapping */ - sub_font_glyph->utf8 = malloc (utf8_len + 1); - if (unlikely (sub_font_glyph->utf8 == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (sub_font_glyph->utf8, utf8, utf8_len); - sub_font_glyph->utf8[utf8_len] = 0; - sub_font_glyph->utf8_len = utf8_len; - *is_mapped = TRUE; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, - unsigned long scaled_font_glyph_index, - const char *utf8, - int utf8_len, - cairo_scaled_font_subsets_glyph_t *subset_glyph) -{ - cairo_sub_font_glyph_t key, *sub_font_glyph; - cairo_int_status_t status; - - _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); - sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, - &key.base); - if (sub_font_glyph != NULL) { - subset_glyph->font_id = sub_font->font_id; - subset_glyph->subset_id = sub_font_glyph->subset_id; - subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; - subset_glyph->is_scaled = sub_font->is_scaled; - subset_glyph->is_composite = sub_font->is_composite; - subset_glyph->x_advance = sub_font_glyph->x_advance; - subset_glyph->y_advance = sub_font_glyph->y_advance; - status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, - utf8, utf8_len, - &subset_glyph->utf8_is_mapped); - subset_glyph->unicode = sub_font_glyph->unicode; - - return status; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, - unsigned long scaled_font_glyph_index, - const char *utf8, - int utf8_len, - cairo_scaled_font_subsets_glyph_t *subset_glyph) -{ - cairo_sub_font_glyph_t key, *sub_font_glyph; - cairo_status_t status; - - _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); - sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, - &key.base); - if (sub_font_glyph == NULL) { - cairo_scaled_glyph_t *scaled_glyph; - - if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) - { - cairo_scaled_font_subsets_glyph_t tmp_subset_glyph; - - sub_font->current_subset++; - sub_font->num_glyphs_in_current_subset = 0; - - /* Reserve first glyph in subset for the .notdef glyph - * except for Type 3 fonts */ - if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) { - status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph); - if (unlikely (status)) - return status; - } - } - - _cairo_scaled_font_freeze_cache (sub_font->scaled_font); - status = _cairo_scaled_glyph_lookup (sub_font->scaled_font, - scaled_font_glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) { - _cairo_scaled_font_thaw_cache (sub_font->scaled_font); - return status; - } - - sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, - sub_font->current_subset, - sub_font->num_glyphs_in_current_subset, - scaled_glyph->metrics.x_advance, - scaled_glyph->metrics.y_advance); - _cairo_scaled_font_thaw_cache (sub_font->scaled_font); - - if (unlikely (sub_font_glyph == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph, - sub_font->scaled_font, - scaled_font_glyph_index); - if (unlikely (status)) { - _cairo_sub_font_glyph_destroy (sub_font_glyph); - return status; - } - - status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); - if (unlikely (status)) { - _cairo_sub_font_glyph_destroy (sub_font_glyph); - return status; - } - - sub_font->num_glyphs_in_current_subset++; - - if (sub_font->is_scaled) { - if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used) - sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset; - } else { - if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used) - sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset; - } - } - - subset_glyph->font_id = sub_font->font_id; - subset_glyph->subset_id = sub_font_glyph->subset_id; - subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; - subset_glyph->is_scaled = sub_font->is_scaled; - subset_glyph->is_composite = sub_font->is_composite; - subset_glyph->x_advance = sub_font_glyph->x_advance; - subset_glyph->y_advance = sub_font_glyph->y_advance; - status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, - utf8, utf8_len, - &subset_glyph->utf8_is_mapped); - subset_glyph->unicode = sub_font_glyph->unicode; - - return status; -} - -static void -_cairo_sub_font_collect (void *entry, void *closure) -{ - cairo_sub_font_t *sub_font = entry; - cairo_sub_font_collection_t *collection = closure; - cairo_scaled_font_subset_t subset; - int i; - unsigned int j; - - if (collection->status) - return; - - collection->status = sub_font->scaled_font->status; - if (collection->status) - return; - - for (i = 0; i <= sub_font->current_subset; i++) { - collection->subset_id = i; - collection->num_glyphs = 0; - collection->max_glyph = 0; - - _cairo_hash_table_foreach (sub_font->sub_font_glyphs, - _cairo_sub_font_glyph_collect, collection); - if (collection->status) - break; - if (collection->num_glyphs == 0) - continue; - - /* Ensure the resulting array has no uninitialized holes */ - assert (collection->num_glyphs == collection->max_glyph + 1); - - subset.scaled_font = sub_font->scaled_font; - subset.is_composite = sub_font->is_composite; - subset.is_scaled = sub_font->is_scaled; - subset.font_id = sub_font->font_id; - subset.subset_id = i; - subset.glyphs = collection->glyphs; - subset.utf8 = collection->utf8; - subset.num_glyphs = collection->num_glyphs; - subset.glyph_names = NULL; - /* No need to check for out of memory here. If to_unicode is NULL, the PDF - * surface does not emit an ToUnicode stream */ - subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long)); - if (subset.to_unicode) { - for (j = 0; j < collection->num_glyphs; j++) { - /* default unicode character required when mapping fails */ - subset.to_unicode[j] = 0xfffd; - } - } - collection->status = (collection->font_subset_callback) (&subset, - collection->font_subset_callback_closure); - - if (subset.to_unicode != NULL) - free (subset.to_unicode); - - if (subset.glyph_names != NULL) { - for (j = 0; j < collection->num_glyphs; j++) - free (subset.glyph_names[j]); - free (subset.glyph_names); - } - - if (collection->status) - break; - } -} - -static cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) -{ - cairo_scaled_font_subsets_t *subsets; - - subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); - if (unlikely (subsets == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - subsets->type = type; - subsets->max_glyphs_per_unscaled_subset_used = 0; - subsets->max_glyphs_per_scaled_subset_used = 0; - subsets->num_sub_fonts = 0; - - subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); - if (! subsets->unscaled_sub_fonts) { - free (subsets); - return NULL; - } - subsets->unscaled_sub_fonts_list = NULL; - subsets->unscaled_sub_fonts_list_end = NULL; - - subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); - if (! subsets->scaled_sub_fonts) { - _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); - free (subsets); - return NULL; - } - subsets->scaled_sub_fonts_list = NULL; - subsets->scaled_sub_fonts_list_end = NULL; - - return subsets; -} - -cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_scaled (void) -{ - return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED); -} - -cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_simple (void) -{ - return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE); -} - -cairo_scaled_font_subsets_t * -_cairo_scaled_font_subsets_create_composite (void) -{ - return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE); -} - -void -_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) -{ - _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts); - _cairo_hash_table_destroy (subsets->scaled_sub_fonts); - - _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts); - _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); - - free (subsets); -} - -cairo_status_t -_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, - cairo_scaled_font_t *scaled_font, - unsigned long scaled_font_glyph_index, - const char * utf8, - int utf8_len, - cairo_scaled_font_subsets_glyph_t *subset_glyph) -{ - cairo_sub_font_t key, *sub_font; - cairo_scaled_glyph_t *scaled_glyph; - cairo_font_face_t *font_face; - cairo_matrix_t identity; - cairo_font_options_t font_options; - cairo_scaled_font_t *unscaled_font; - cairo_status_t status; - int max_glyphs; - cairo_bool_t type1_font; - - /* Lookup glyph in unscaled subsets */ - if (subsets->type != CAIRO_SUBSETS_SCALED) { - key.is_scaled = FALSE; - _cairo_sub_font_init_key (&key, scaled_font); - sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, - &key.base); - if (sub_font != NULL) { - status = _cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - } - - /* Lookup glyph in scaled subsets */ - key.is_scaled = TRUE; - _cairo_sub_font_init_key (&key, scaled_font); - sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, - &key.base); - if (sub_font != NULL) { - status = _cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - /* Glyph not found. Determine whether the glyph is outline or - * bitmap and add to the appropriate subset. - * - * glyph_index 0 (the .notdef glyph) is a special case. Some fonts - * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a - * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates - * empty glyphs in this case so we can put the glyph in a unscaled - * subset. */ - if (scaled_font_glyph_index == 0 || - _cairo_font_face_is_user (scaled_font->font_face)) { - status = CAIRO_STATUS_SUCCESS; - } else { - _cairo_scaled_font_freeze_cache (scaled_font); - status = _cairo_scaled_glyph_lookup (scaled_font, - scaled_font_glyph_index, - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - _cairo_scaled_font_thaw_cache (scaled_font); - } - if (_cairo_status_is_error (status)) - return status; - - if (status == CAIRO_STATUS_SUCCESS && - subsets->type != CAIRO_SUBSETS_SCALED && - ! _cairo_font_face_is_user (scaled_font->font_face)) - { - /* Path available. Add to unscaled subset. */ - key.is_scaled = FALSE; - _cairo_sub_font_init_key (&key, scaled_font); - sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, - &key.base); - if (sub_font == NULL) { - font_face = cairo_scaled_font_get_font_face (scaled_font); - cairo_matrix_init_identity (&identity); - _cairo_font_options_init_default (&font_options); - cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); - unscaled_font = cairo_scaled_font_create (font_face, - &identity, - &identity, - &font_options); - if (unlikely (unscaled_font->status)) - return unscaled_font->status; - - subset_glyph->is_scaled = FALSE; - type1_font = FALSE; -#if CAIRO_HAS_FT_FONT - type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font); -#endif - if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) { - max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT; - subset_glyph->is_composite = TRUE; - } else { - max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; - subset_glyph->is_composite = FALSE; - } - - status = _cairo_sub_font_create (subsets, - unscaled_font, - subsets->num_sub_fonts, - max_glyphs, - subset_glyph->is_scaled, - subset_glyph->is_composite, - &sub_font); - - if (unlikely (status)) { - cairo_scaled_font_destroy (unscaled_font); - return status; - } - - status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts, - &sub_font->base); - - if (unlikely (status)) { - _cairo_sub_font_destroy (sub_font); - return status; - } - if (!subsets->unscaled_sub_fonts_list) - subsets->unscaled_sub_fonts_list = sub_font; - else - subsets->unscaled_sub_fonts_list_end->next = sub_font; - subsets->unscaled_sub_fonts_list_end = sub_font; - subsets->num_sub_fonts++; - } - } else { - /* No path available. Add to scaled subset. */ - key.is_scaled = TRUE; - _cairo_sub_font_init_key (&key, scaled_font); - sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, - &key.base); - if (sub_font == NULL) { - subset_glyph->is_scaled = TRUE; - subset_glyph->is_composite = FALSE; - if (subsets->type == CAIRO_SUBSETS_SCALED) - max_glyphs = INT_MAX; - else - max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; - - status = _cairo_sub_font_create (subsets, - cairo_scaled_font_reference (scaled_font), - subsets->num_sub_fonts, - max_glyphs, - subset_glyph->is_scaled, - subset_glyph->is_composite, - &sub_font); - if (unlikely (status)) { - cairo_scaled_font_destroy (scaled_font); - return status; - } - - status = _cairo_hash_table_insert (subsets->scaled_sub_fonts, - &sub_font->base); - if (unlikely (status)) { - _cairo_sub_font_destroy (sub_font); - return status; - } - if (!subsets->scaled_sub_fonts_list) - subsets->scaled_sub_fonts_list = sub_font; - else - subsets->scaled_sub_fonts_list_end->next = sub_font; - subsets->scaled_sub_fonts_list_end = sub_font; - subsets->num_sub_fonts++; - } - } - - return _cairo_sub_font_map_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph); -} - -static cairo_status_t -_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure, - cairo_subsets_foreach_type_t type) -{ - cairo_sub_font_collection_t collection; - cairo_sub_font_t *sub_font; - cairo_bool_t is_scaled, is_user; - - is_scaled = FALSE; - is_user = FALSE; - - if (type == CAIRO_SUBSETS_FOREACH_USER) - is_user = TRUE; - - if (type == CAIRO_SUBSETS_FOREACH_SCALED || - type == CAIRO_SUBSETS_FOREACH_USER) - { - is_scaled = TRUE; - } - - if (is_scaled) - collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; - else - collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used; - - if (! collection.glyphs_size) - return CAIRO_STATUS_SUCCESS; - - collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); - collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); - if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) { - if (collection.glyphs != NULL) - free (collection.glyphs); - if (collection.utf8 != NULL) - free (collection.utf8); - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - collection.font_subset_callback = font_subset_callback; - collection.font_subset_callback_closure = closure; - collection.status = CAIRO_STATUS_SUCCESS; - - if (is_scaled) - sub_font = font_subsets->scaled_sub_fonts_list; - else - sub_font = font_subsets->unscaled_sub_fonts_list; - - while (sub_font) { - if (sub_font->is_user == is_user) - _cairo_sub_font_collect (sub_font, &collection); - - sub_font = sub_font->next; - } - free (collection.utf8); - free (collection.glyphs); - - return collection.status; -} - -cairo_status_t -_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure) -{ - return _cairo_scaled_font_subsets_foreach_internal (font_subsets, - font_subset_callback, - closure, - CAIRO_SUBSETS_FOREACH_SCALED); -} - -cairo_status_t -_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure) -{ - return _cairo_scaled_font_subsets_foreach_internal (font_subsets, - font_subset_callback, - closure, - CAIRO_SUBSETS_FOREACH_UNSCALED); -} - -cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure) -{ - return _cairo_scaled_font_subsets_foreach_internal (font_subsets, - font_subset_callback, - closure, - CAIRO_SUBSETS_FOREACH_USER); -} - -static cairo_bool_t -_cairo_string_equal (const void *key_a, const void *key_b) -{ - const cairo_string_entry_t *a = key_a; - const cairo_string_entry_t *b = key_b; - - if (strcmp (a->string, b->string) == 0) - return TRUE; - else - return FALSE; -} - -static void -_cairo_string_init_key (cairo_string_entry_t *key, char *s) -{ - unsigned long sum = 0; - unsigned int i; - - for (i = 0; i < strlen(s); i++) - sum += s[i]; - key->base.hash = sum; - key->string = s; -} - -static cairo_status_t -create_string_entry (char *s, cairo_string_entry_t **entry) -{ - *entry = malloc (sizeof (cairo_string_entry_t)); - if (unlikely (*entry == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_string_init_key (*entry, s); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_pluck_entry (void *entry, void *closure) -{ - _cairo_hash_table_remove (closure, entry); - free (entry); -} - -cairo_int_status_t -_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset) -{ - unsigned int i; - cairo_hash_table_t *names; - cairo_string_entry_t key, *entry; - char buf[30]; - char *utf8; - uint16_t *utf16; - int utf16_len; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - names = _cairo_hash_table_create (_cairo_string_equal); - if (unlikely (names == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *)); - if (unlikely (subset->glyph_names == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_HASH; - } - - i = 0; - if (! subset->is_scaled) { - subset->glyph_names[0] = strdup (".notdef"); - if (unlikely (subset->glyph_names[0] == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_HASH; - } - - status = create_string_entry (subset->glyph_names[0], &entry); - if (unlikely (status)) - goto CLEANUP_HASH; - - status = _cairo_hash_table_insert (names, &entry->base); - if (unlikely (status)) { - free (entry); - goto CLEANUP_HASH; - } - i++; - } - - for (; i < subset->num_glyphs; i++) { - utf8 = subset->utf8[i]; - utf16 = NULL; - utf16_len = 0; - if (utf8 && *utf8) { - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) - goto CLEANUP_HASH; - } - - if (utf16_len == 1) { - snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); - _cairo_string_init_key (&key, buf); - entry = _cairo_hash_table_lookup (names, &key.base); - if (entry != NULL) - snprintf (buf, sizeof (buf), "g%d", i); - } else { - snprintf (buf, sizeof (buf), "g%d", i); - } - if (utf16) - free (utf16); - - subset->glyph_names[i] = strdup (buf); - if (unlikely (subset->glyph_names[i] == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_HASH; - } - - status = create_string_entry (subset->glyph_names[i], &entry); - if (unlikely (status)) - goto CLEANUP_HASH; - - status = _cairo_hash_table_insert (names, &entry->base); - if (unlikely (status)) { - free (entry); - goto CLEANUP_HASH; - } - } - -CLEANUP_HASH: - _cairo_hash_table_foreach (names, _pluck_entry, names); - _cairo_hash_table_destroy (names); - - if (likely (status == CAIRO_STATUS_SUCCESS)) - return CAIRO_STATUS_SUCCESS; - - if (subset->glyph_names != NULL) { - for (i = 0; i < subset->num_glyphs; i++) { - if (subset->glyph_names[i] != NULL) - free (subset->glyph_names[i]); - } - - free (subset->glyph_names); - subset->glyph_names = NULL; - } - - return status; -} - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-scaled-font.c b/libs/cairo/cairo/src/cairo-scaled-font.c deleted file mode 100644 index 37806bc63..000000000 --- a/libs/cairo/cairo/src/cairo-scaled-font.c +++ /dev/null @@ -1,2954 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" -#include "cairo-scaled-font-private.h" - -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif - -/** - * SECTION:cairo-scaled-font - * @Title: cairo_scaled_font_t - * @Short_Description: Font face at particular size and options - * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t - * - * #cairo_scaled_font_t represents a realization of a font face at a particular - * size and transformation and a certain set of font options. - */ - -/* Global Glyph Cache - * - * We maintain a global pool of glyphs split between all active fonts. This - * allows a heavily used individual font to cache more glyphs than we could - * manage if we used per-font glyph caches, but at the same time maintains - * fairness across all fonts and provides a cap on the maximum number of - * global glyphs. - * - * The glyphs are allocated in pages, which are capped in the global pool. - * Using pages means we can reduce the frequency at which we have to probe the - * global pool and ameliorates the memory allocation pressure. - */ - -/* XXX: This number is arbitrary---we've never done any measurement of this. */ -#define MAX_GLYPH_PAGES_CACHED 256 -static cairo_cache_t cairo_scaled_glyph_page_cache; - -#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 -struct _cairo_scaled_glyph_page { - cairo_cache_entry_t cache_entry; - - cairo_list_t link; - - unsigned int num_glyphs; - cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE]; -}; - -/* - * Notes: - * - * To store rasterizations of glyphs, we use an image surface and the - * device offset to represent the glyph origin. - * - * A device_transform converts from device space (a conceptual space) to - * surface space. For simple cases of translation only, it's called a - * device_offset and is public API (cairo_surface_[gs]et_device_offset()). - * A possibly better name for those functions could have been - * cairo_surface_[gs]et_origin(). So, that's what they do: they set where - * the device-space origin (0,0) is in the surface. If the origin is inside - * the surface, device_offset values are positive. It may look like this: - * - * Device space: - * (-x,-y) <-- negative numbers - * +----------------+ - * | . | - * | . | - * |......(0,0) <---|-- device-space origin - * | | - * | | - * +----------------+ - * (width-x,height-y) - * - * Surface space: - * (0,0) <-- surface-space origin - * +---------------+ - * | . | - * | . | - * |......(x,y) <--|-- device_offset - * | | - * | | - * +---------------+ - * (width,height) - * - * In other words: device_offset is the coordinates of the device-space - * origin relative to the top-left of the surface. - * - * We use device offsets in a couple of places: - * - * - Public API: To let toolkits like Gtk+ give user a surface that - * only represents part of the final destination (say, the expose - * area), but has the same device space as the destination. In these - * cases device_offset is typically negative. Example: - * - * application window - * +---------------+ - * | . | - * | (x,y). | - * |......+---+ | - * | | | <--|-- expose area - * | +---+ | - * +---------------+ - * - * In this case, the user of cairo API can set the device_space on - * the expose area to (-x,-y) to move the device space origin to that - * of the application window, such that drawing in the expose area - * surface and painting it in the application window has the same - * effect as drawing in the application window directly. Gtk+ has - * been using this feature. - * - * - Glyph surfaces: In most font rendering systems, glyph surfaces - * have an origin at (0,0) and a bounding box that is typically - * represented as (x_bearing,y_bearing,width,height). Depending on - * which way y progresses in the system, y_bearing may typically be - * negative (for systems similar to cairo, with origin at top left), - * or be positive (in systems like PDF with origin at bottom left). - * No matter which is the case, it is important to note that - * (x_bearing,y_bearing) is the coordinates of top-left of the glyph - * relative to the glyph origin. That is, for example: - * - * Scaled-glyph space: - * - * (x_bearing,y_bearing) <-- negative numbers - * +----------------+ - * | . | - * | . | - * |......(0,0) <---|-- glyph origin - * | | - * | | - * +----------------+ - * (width+x_bearing,height+y_bearing) - * - * Note the similarity of the origin to the device space. That is - * exactly how we use the device_offset to represent scaled glyphs: - * to use the device-space origin as the glyph origin. - * - * Now compare the scaled-glyph space to device-space and surface-space - * and convince yourself that: - * - * (x_bearing,y_bearing) = (-x,-y) = - device_offset - * - * That's right. If you are not convinced yet, contrast the definition - * of the two: - * - * "(x_bearing,y_bearing) is the coordinates of top-left of the - * glyph relative to the glyph origin." - * - * "In other words: device_offset is the coordinates of the - * device-space origin relative to the top-left of the surface." - * - * and note that glyph origin = device-space origin. - */ - -static void -_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font); - -static void -_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend; - - if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) - surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); - - if (scaled_glyph->surface != NULL) - cairo_surface_destroy (&scaled_glyph->surface->base); - - if (scaled_glyph->path != NULL) - _cairo_path_fixed_destroy (scaled_glyph->path); - - if (scaled_glyph->recording_surface != NULL) { - cairo_surface_finish (scaled_glyph->recording_surface); - cairo_surface_destroy (scaled_glyph->recording_surface); - } -} - -#define ZOMBIE 0 -static const cairo_scaled_font_t _cairo_scaled_font_nil = { - { ZOMBIE }, /* hash_entry */ - CAIRO_STATUS_NO_MEMORY, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL, /* original_font_face */ - NULL, /* font_face */ - { 1., 0., 0., 1., 0, 0}, /* font_matrix */ - { 1., 0., 0., 1., 0, 0}, /* ctm */ - { CAIRO_ANTIALIAS_DEFAULT, /* options */ - CAIRO_SUBPIXEL_ORDER_DEFAULT, - CAIRO_HINT_STYLE_DEFAULT, - CAIRO_HINT_METRICS_DEFAULT} , - FALSE, /* placeholder */ - FALSE, /* holdover */ - TRUE, /* finished */ - { 1., 0., 0., 1., 0, 0}, /* scale */ - { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ - 1., /* max_scale */ - { 0., 0., 0., 0., 0. }, /* extents */ - { 0., 0., 0., 0., 0. }, /* fs_extents */ - CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ - NULL, /* glyphs */ - { NULL, NULL }, /* pages */ - FALSE, /* cache_frozen */ - FALSE, /* global_cache_frozen */ - NULL, /* surface_backend */ - NULL, /* surface_private */ - NULL /* backend */ -}; - -/** - * _cairo_scaled_font_set_error: - * @scaled_font: a scaled_font - * @status: a status value indicating an error - * - * Atomically sets scaled_font->status to @status and calls _cairo_error; - * Does nothing if status is %CAIRO_STATUS_SUCCESS. - * - * All assignments of an error status to scaled_font->status should happen - * through _cairo_scaled_font_set_error(). Note that due to the nature of - * the atomic operation, it is not safe to call this function on the nil - * objects. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - * - * Return value: the error status. - **/ -cairo_status_t -_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, - cairo_status_t status) -{ - if (status == CAIRO_STATUS_SUCCESS) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&scaled_font->status, status); - - return _cairo_error (status); -} - -/** - * cairo_scaled_font_get_type: - * @scaled_font: a #cairo_scaled_font_t - * - * This function returns the type of the backend used to create - * a scaled font. See #cairo_font_type_t for available types. - * However, this function never returns %CAIRO_FONT_TYPE_TOY. - * - * Return value: The type of @scaled_font. - * - * Since: 1.2 - **/ -cairo_font_type_t -cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) - return CAIRO_FONT_TYPE_TOY; - - return scaled_font->backend->type; -} - -/** - * cairo_scaled_font_status: - * @scaled_font: a #cairo_scaled_font_t - * - * Checks whether an error has previously occurred for this - * scaled_font. - * - * Return value: %CAIRO_STATUS_SUCCESS or another error such as - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) -{ - return scaled_font->status; -} -slim_hidden_def (cairo_scaled_font_status); - -/* Here we keep a unique mapping from - * font_face/matrix/ctm/font_options => #cairo_scaled_font_t. - * - * Here are the things that we want to map: - * - * a) All otherwise referenced #cairo_scaled_font_t's - * b) Some number of not otherwise referenced #cairo_scaled_font_t's - * - * The implementation uses a hash table which covers (a) - * completely. Then, for (b) we have an array of otherwise - * unreferenced fonts (holdovers) which are expired in - * least-recently-used order. - * - * The cairo_scaled_font_create() code gets to treat this like a regular - * hash table. All of the magic for the little holdover cache is in - * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). - */ - -/* This defines the size of the holdover array ... that is, the number - * of scaled fonts we keep around even when not otherwise referenced - */ -#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256 - -typedef struct _cairo_scaled_font_map { - cairo_scaled_font_t *mru_scaled_font; - cairo_hash_table_t *hash_table; - cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; - int num_holdovers; -} cairo_scaled_font_map_t; - -static cairo_scaled_font_map_t *cairo_scaled_font_map; - -static int -_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); - -static cairo_scaled_font_map_t * -_cairo_scaled_font_map_lock (void) -{ - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); - - if (cairo_scaled_font_map == NULL) { - cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); - if (unlikely (cairo_scaled_font_map == NULL)) - goto CLEANUP_MUTEX_LOCK; - - cairo_scaled_font_map->mru_scaled_font = NULL; - cairo_scaled_font_map->hash_table = - _cairo_hash_table_create (_cairo_scaled_font_keys_equal); - - if (unlikely (cairo_scaled_font_map->hash_table == NULL)) - goto CLEANUP_SCALED_FONT_MAP; - - cairo_scaled_font_map->num_holdovers = 0; - } - - return cairo_scaled_font_map; - - CLEANUP_SCALED_FONT_MAP: - free (cairo_scaled_font_map); - cairo_scaled_font_map = NULL; - CLEANUP_MUTEX_LOCK: - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; -} - -static void -_cairo_scaled_font_map_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); -} - -void -_cairo_scaled_font_map_destroy (void) -{ - cairo_scaled_font_map_t *font_map; - cairo_scaled_font_t *scaled_font; - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); - - font_map = cairo_scaled_font_map; - if (unlikely (font_map == NULL)) { - goto CLEANUP_MUTEX_LOCK; - } - - scaled_font = font_map->mru_scaled_font; - if (scaled_font != NULL) { - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - cairo_scaled_font_destroy (scaled_font); - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); - } - - /* remove scaled_fonts starting from the end so that font_map->holdovers - * is always in a consistent state when we release the mutex. */ - while (font_map->num_holdovers) { - scaled_font = font_map->holdovers[font_map->num_holdovers-1]; - assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); - _cairo_hash_table_remove (font_map->hash_table, - &scaled_font->hash_entry); - - font_map->num_holdovers--; - - /* This releases the font_map lock to avoid the possibility of a - * recursive deadlock when the scaled font destroy closure gets - * called - */ - _cairo_scaled_font_fini (scaled_font); - - free (scaled_font); - } - - _cairo_hash_table_destroy (font_map->hash_table); - - free (cairo_scaled_font_map); - cairo_scaled_font_map = NULL; - - CLEANUP_MUTEX_LOCK: - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); -} -static void -_cairo_scaled_glyph_page_destroy (void *closure) -{ - cairo_scaled_glyph_page_t *page = closure; - cairo_scaled_font_t *scaled_font; - unsigned int n; - - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; - for (n = 0; n < page->num_glyphs; n++) { - _cairo_hash_table_remove (scaled_font->glyphs, - &page->glyphs[n].hash_entry); - _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]); - } - - cairo_list_del (&page->link); - - free (page); -} - -/* If a scaled font wants to unlock the font map while still being - * created (needed for user-fonts), we need to take extra care not - * ending up with multiple identical scaled fonts being created. - * - * What we do is, we create a fake identical scaled font, and mark - * it as placeholder, lock its mutex, and insert that in the fontmap - * hash table. This makes other code trying to create an identical - * scaled font to just wait and retry. - * - * The reason we have to create a fake scaled font instead of just using - * scaled_font is for lifecycle management: we need to (or rather, - * other code needs to) reference the scaled_font in the hash table. - * We can't do that on the input scaled_font as it may be freed by - * font backend upon error. - */ - -cairo_status_t -_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font) -{ - cairo_status_t status; - cairo_scaled_font_t *placeholder_scaled_font; - - assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex)); - - status = scaled_font->status; - if (unlikely (status)) - return status; - - placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t)); - if (unlikely (placeholder_scaled_font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* full initialization is wasteful, but who cares... */ - status = _cairo_scaled_font_init (placeholder_scaled_font, - scaled_font->font_face, - &scaled_font->font_matrix, - &scaled_font->ctm, - &scaled_font->options, - NULL); - if (unlikely (status)) - goto FREE_PLACEHOLDER; - - placeholder_scaled_font->placeholder = TRUE; - - status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table, - &placeholder_scaled_font->hash_entry); - if (unlikely (status)) - goto FINI_PLACEHOLDER; - - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); - - return CAIRO_STATUS_SUCCESS; - - FINI_PLACEHOLDER: - _cairo_scaled_font_fini_internal (placeholder_scaled_font); - FREE_PLACEHOLDER: - free (placeholder_scaled_font); - - return _cairo_scaled_font_set_error (scaled_font, status); -} - -void -_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font) -{ - cairo_scaled_font_t *placeholder_scaled_font; - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); - - placeholder_scaled_font = - _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, - &scaled_font->hash_entry); - assert (placeholder_scaled_font != NULL); - assert (placeholder_scaled_font->placeholder); - assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex)); - - _cairo_hash_table_remove (cairo_scaled_font_map->hash_table, - &placeholder_scaled_font->hash_entry); - - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - - CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); - cairo_scaled_font_destroy (placeholder_scaled_font); - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); -} - -static void -_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font) -{ - /* reference the place holder so it doesn't go away */ - cairo_scaled_font_reference (placeholder_scaled_font); - - /* now unlock the fontmap mutex so creation has a chance to finish */ - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - - /* wait on placeholder mutex until we are awaken */ - CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); - - /* ok, creation done. just clean up and back out */ - CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); - cairo_scaled_font_destroy (placeholder_scaled_font); - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); -} - -/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) - * - * Not necessarily better than a lot of other hashes, but should be OK, and - * well tested with binary data. - */ - -#define FNV_32_PRIME ((uint32_t)0x01000193) -#define FNV1_32_INIT ((uint32_t)0x811c9dc5) - -static uint32_t -_hash_matrix_fnv (const cairo_matrix_t *matrix, - uint32_t hval) -{ - const uint8_t *buffer = (const uint8_t *) matrix; - int len = sizeof (cairo_matrix_t); - do { - hval *= FNV_32_PRIME; - hval ^= *buffer++; - } while (--len); - - return hval; -} - -static uint32_t -_hash_mix_bits (uint32_t hash) -{ - hash += hash << 12; - hash ^= hash >> 7; - hash += hash << 3; - hash ^= hash >> 17; - hash += hash << 5; - return hash; -} - -static void -_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - uint32_t hash = FNV1_32_INIT; - - scaled_font->status = CAIRO_STATUS_SUCCESS; - scaled_font->placeholder = FALSE; - scaled_font->font_face = font_face; - scaled_font->font_matrix = *font_matrix; - scaled_font->ctm = *ctm; - /* ignore translation values in the ctm */ - scaled_font->ctm.x0 = 0.; - scaled_font->ctm.y0 = 0.; - _cairo_font_options_init_copy (&scaled_font->options, options); - - /* We do a bytewise hash on the font matrices */ - hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); - hash = _hash_matrix_fnv (&scaled_font->ctm, hash); - hash = _hash_mix_bits (hash); - - hash ^= (uintptr_t) scaled_font->font_face; - hash ^= cairo_font_options_hash (&scaled_font->options); - - /* final mixing of bits */ - hash = _hash_mix_bits (hash); - - assert (hash != ZOMBIE); - scaled_font->hash_entry.hash = hash; -} - -static cairo_bool_t -_cairo_scaled_font_keys_equal (const void *abstract_key_a, - const void *abstract_key_b) -{ - const cairo_scaled_font_t *key_a = abstract_key_a; - const cairo_scaled_font_t *key_b = abstract_key_b; - - if (key_a->hash_entry.hash != key_b->hash_entry.hash) - return FALSE; - - return key_a->font_face == key_b->font_face && - memcmp ((unsigned char *)(&key_a->font_matrix.xx), - (unsigned char *)(&key_b->font_matrix.xx), - sizeof(cairo_matrix_t)) == 0 && - memcmp ((unsigned char *)(&key_a->ctm.xx), - (unsigned char *)(&key_b->ctm.xx), - sizeof(cairo_matrix_t)) == 0 && - cairo_font_options_equal (&key_a->options, &key_b->options); -} - -static cairo_bool_t -_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, - const cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - return scaled_font->original_font_face == font_face && - memcmp ((unsigned char *)(&scaled_font->font_matrix.xx), - (unsigned char *)(&font_matrix->xx), - sizeof(cairo_matrix_t)) == 0 && - memcmp ((unsigned char *)(&scaled_font->ctm.xx), - (unsigned char *)(&ctm->xx), - sizeof(cairo_matrix_t)) == 0 && - cairo_font_options_equal (&scaled_font->options, options); -} - -static cairo_bool_t -_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b) -{ - const cairo_scaled_glyph_t *a = abstract_a; - const cairo_scaled_glyph_t *b = abstract_b; - - return a->hash_entry.hash == b->hash_entry.hash; -} - -/* - * Basic #cairo_scaled_font_t object management - */ - -cairo_status_t -_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - const cairo_scaled_font_backend_t *backend) -{ - cairo_status_t status; - - status = cairo_font_options_status ((cairo_font_options_t *) options); - if (unlikely (status)) - return status; - - _cairo_scaled_font_init_key (scaled_font, font_face, - font_matrix, ctm, options); - - cairo_matrix_multiply (&scaled_font->scale, - &scaled_font->font_matrix, - &scaled_font->ctm); - - scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy), - fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy)); - scaled_font->scale_inverse = scaled_font->scale; - status = cairo_matrix_invert (&scaled_font->scale_inverse); - if (unlikely (status)) { - /* If the font scale matrix is rank 0, just using an all-zero inverse matrix - * makes everything work correctly. This make font size 0 work without - * producing an error. - * - * FIXME: If the scale is rank 1, we still go into error mode. But then - * again, that's what we do everywhere in cairo. - * - * Also, the check for == 0. below may be too harsh... - */ - if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) { - cairo_matrix_init (&scaled_font->scale_inverse, - 0, 0, 0, 0, - -scaled_font->scale.x0, - -scaled_font->scale.y0); - } else - return status; - } - - scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal); - if (unlikely (scaled_font->glyphs == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cairo_list_init (&scaled_font->glyph_pages); - scaled_font->cache_frozen = FALSE; - scaled_font->global_cache_frozen = FALSE; - - scaled_font->holdover = FALSE; - scaled_font->finished = FALSE; - - CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1); - - _cairo_user_data_array_init (&scaled_font->user_data); - - cairo_font_face_reference (font_face); - scaled_font->original_font_face = NULL; - - CAIRO_MUTEX_INIT (scaled_font->mutex); - - scaled_font->surface_backend = NULL; - scaled_font->surface_private = NULL; - - scaled_font->backend = backend; - cairo_list_init (&scaled_font->link); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) -{ - /* ensure we do not modify an error object */ - assert (scaled_font->status == CAIRO_STATUS_SUCCESS); - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - scaled_font->cache_frozen = TRUE; -} - -void -_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) -{ - scaled_font->cache_frozen = FALSE; - - if (scaled_font->global_cache_frozen) { - CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - _cairo_cache_thaw (&cairo_scaled_glyph_page_cache); - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); - - scaled_font->global_cache_frozen = FALSE; - } - - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); -} - -void -_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) -{ - assert (! scaled_font->cache_frozen); - - CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { - _cairo_cache_remove (&cairo_scaled_glyph_page_cache, - &cairo_list_first_entry (&scaled_font->glyph_pages, - cairo_scaled_glyph_page_t, - link)->cache_entry); - } - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); -} - -cairo_status_t -_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *fs_metrics) -{ - cairo_status_t status; - double font_scale_x, font_scale_y; - - scaled_font->fs_extents = *fs_metrics; - - status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, - &font_scale_x, &font_scale_y, - 1); - if (unlikely (status)) - return status; - - /* - * The font responded in unscaled units, scale by the font - * matrix scale factors to get to user space - */ - - scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y; - scaled_font->extents.descent = fs_metrics->descent * font_scale_y; - scaled_font->extents.height = fs_metrics->height * font_scale_y; - scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x; - scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) -{ - scaled_font->finished = TRUE; - - _cairo_scaled_font_reset_cache (scaled_font); - _cairo_hash_table_destroy (scaled_font->glyphs); - - cairo_font_face_destroy (scaled_font->font_face); - cairo_font_face_destroy (scaled_font->original_font_face); - - CAIRO_MUTEX_FINI (scaled_font->mutex); - - if (scaled_font->surface_backend != NULL && - scaled_font->surface_backend->scaled_font_fini != NULL) - scaled_font->surface_backend->scaled_font_fini (scaled_font); - - if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL) - scaled_font->backend->fini (scaled_font); - - _cairo_user_data_array_fini (&scaled_font->user_data); -} - -/* XXX: allow multiple backends to share the font */ -void -_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font->surface_backend == NULL) - return; - - _cairo_scaled_font_reset_cache (scaled_font); - - if (scaled_font->surface_backend->scaled_font_fini != NULL) - scaled_font->surface_backend->scaled_font_fini (scaled_font); - - scaled_font->surface_backend = NULL; - scaled_font->surface_private = NULL; -} - -void -_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - /* Release the lock to avoid the possibility of a recursive - * deadlock when the scaled font destroy closure gets called. */ - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); - _cairo_scaled_font_fini_internal (scaled_font); - CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); -} - -/** - * cairo_scaled_font_create: - * @font_face: a #cairo_font_face_t - * @font_matrix: font space to user space transformation matrix for the - * font. In the simplest case of a N point font, this matrix is - * just a scale by N, but it can also be used to shear the font - * or stretch it unequally along the two axes. See - * cairo_set_font_matrix(). - * @ctm: user to device transformation matrix with which the font will - * be used. - * @options: options to use when getting metrics for the font and - * rendering with it. - * - * Creates a #cairo_scaled_font_t object from a font face and matrices that - * describe the size of the font and the environment in which it will - * be used. - * - * Return value: a newly created #cairo_scaled_font_t. Destroy with - * cairo_scaled_font_destroy() - **/ -cairo_scaled_font_t * -cairo_scaled_font_create (cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - cairo_status_t status; - cairo_scaled_font_map_t *font_map; - cairo_font_face_t *original_font_face = font_face; - cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL; - double det; - - status = font_face->status; - if (unlikely (status)) - return _cairo_scaled_font_create_in_error (status); - - det = _cairo_matrix_compute_determinant (font_matrix); - if (! ISFINITE (det)) - return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); - - det = _cairo_matrix_compute_determinant (ctm); - if (! ISFINITE (det)) - return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); - - status = cairo_font_options_status ((cairo_font_options_t *) options); - if (unlikely (status)) - return _cairo_scaled_font_create_in_error (status); - - /* Note that degenerate ctm or font_matrix *are* allowed. - * We want to support a font size of 0. */ - - font_map = _cairo_scaled_font_map_lock (); - if (unlikely (font_map == NULL)) - return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - scaled_font = font_map->mru_scaled_font; - if (scaled_font != NULL && - _cairo_scaled_font_matches (scaled_font, - font_face, font_matrix, ctm, options)) - { - assert (scaled_font->hash_entry.hash != ZOMBIE); - assert (! scaled_font->placeholder); - - if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { - /* We increment the reference count manually here, (rather - * than calling into cairo_scaled_font_reference), since we - * must modify the reference count while our lock is still - * held. */ - _cairo_reference_count_inc (&scaled_font->ref_count); - _cairo_scaled_font_map_unlock (); - return scaled_font; - } - - /* the font has been put into an error status - abandon the cache */ - _cairo_hash_table_remove (font_map->hash_table, - &scaled_font->hash_entry); - scaled_font->hash_entry.hash = ZOMBIE; - dead = scaled_font; - font_map->mru_scaled_font = NULL; - - if (font_face->backend->get_implementation != NULL) { - font_face = font_face->backend->get_implementation (font_face, - font_matrix, - ctm, - options); - if (unlikely (font_face->status)) { - _cairo_scaled_font_map_unlock (); - cairo_scaled_font_destroy (scaled_font); - return _cairo_scaled_font_create_in_error (font_face->status); - } - } - - _cairo_scaled_font_init_key (&key, font_face, - font_matrix, ctm, options); - } - else - { - if (font_face->backend->get_implementation != NULL) { - font_face = font_face->backend->get_implementation (font_face, - font_matrix, - ctm, - options); - if (unlikely (font_face->status)) { - _cairo_scaled_font_map_unlock (); - return _cairo_scaled_font_create_in_error (font_face->status); - } - } - - _cairo_scaled_font_init_key (&key, font_face, - font_matrix, ctm, options); - - while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, - &key.hash_entry))) - { - if (! scaled_font->placeholder) - break; - - /* If the scaled font is being created (happens for user-font), - * just wait until it's done, then retry */ - _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font); - } - - /* Return existing scaled_font if it exists in the hash table. */ - if (scaled_font != NULL) { - /* If the original reference count is 0, then this font must have - * been found in font_map->holdovers, (which means this caching is - * actually working). So now we remove it from the holdovers - * array, unless we caught the font in the middle of destruction. - */ - if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { - if (scaled_font->holdover) { - int i; - - for (i = 0; i < font_map->num_holdovers; i++) { - if (font_map->holdovers[i] == scaled_font) { - font_map->num_holdovers--; - memmove (&font_map->holdovers[i], - &font_map->holdovers[i+1], - (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); - break; - } - } - - scaled_font->holdover = FALSE; - } - - /* reset any error status */ - scaled_font->status = CAIRO_STATUS_SUCCESS; - } - - if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { - /* We increment the reference count manually here, (rather - * than calling into cairo_scaled_font_reference), since we - * must modify the reference count while our lock is still - * held. */ - - old = font_map->mru_scaled_font; - font_map->mru_scaled_font = scaled_font; - /* increment reference count for the mru cache */ - _cairo_reference_count_inc (&scaled_font->ref_count); - /* and increment for the returned reference */ - _cairo_reference_count_inc (&scaled_font->ref_count); - _cairo_scaled_font_map_unlock (); - - cairo_scaled_font_destroy (old); - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); - - return scaled_font; - } - - /* the font has been put into an error status - abandon the cache */ - _cairo_hash_table_remove (font_map->hash_table, - &scaled_font->hash_entry); - scaled_font->hash_entry.hash = ZOMBIE; - } - } - - /* Otherwise create it and insert it into the hash table. */ - status = font_face->backend->scaled_font_create (font_face, font_matrix, - ctm, options, &scaled_font); - /* Did we leave the backend in an error state? */ - if (unlikely (status)) { - _cairo_scaled_font_map_unlock (); - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); - - if (dead != NULL) - cairo_scaled_font_destroy (dead); - - status = _cairo_font_face_set_error (font_face, status); - return _cairo_scaled_font_create_in_error (status); - } - /* Or did we encounter an error whilst constructing the scaled font? */ - if (unlikely (scaled_font->status)) { - _cairo_scaled_font_map_unlock (); - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); - - if (dead != NULL) - cairo_scaled_font_destroy (dead); - - return scaled_font; - } - - /* Our caching above is defeated if the backend switches fonts on us - - * e.g. old incarnations of toy-font-face and lazily resolved - * ft-font-faces - */ - assert (scaled_font->font_face == font_face); - - scaled_font->original_font_face = - cairo_font_face_reference (original_font_face); - - status = _cairo_hash_table_insert (font_map->hash_table, - &scaled_font->hash_entry); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - old = font_map->mru_scaled_font; - font_map->mru_scaled_font = scaled_font; - _cairo_reference_count_inc (&scaled_font->ref_count); - } - - _cairo_scaled_font_map_unlock (); - - cairo_scaled_font_destroy (old); - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); - - if (dead != NULL) - cairo_scaled_font_destroy (dead); - - if (unlikely (status)) { - /* We can't call _cairo_scaled_font_destroy here since it expects - * that the font has already been successfully inserted into the - * hash table. */ - _cairo_scaled_font_fini_internal (scaled_font); - free (scaled_font); - return _cairo_scaled_font_create_in_error (status); - } - - return scaled_font; -} -slim_hidden_def (cairo_scaled_font_create); - -static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1]; - -/* XXX This should disappear in favour of a common pool of error objects. */ -cairo_scaled_font_t * -_cairo_scaled_font_create_in_error (cairo_status_t status) -{ - cairo_scaled_font_t *scaled_font; - - assert (status != CAIRO_STATUS_SUCCESS); - - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); - scaled_font = _cairo_scaled_font_nil_objects[status]; - if (unlikely (scaled_font == NULL)) { - scaled_font = malloc (sizeof (cairo_scaled_font_t)); - if (unlikely (scaled_font == NULL)) { - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; - } - - *scaled_font = _cairo_scaled_font_nil; - scaled_font->status = status; - _cairo_scaled_font_nil_objects[status] = scaled_font; - } - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); - - return scaled_font; -} - -void -_cairo_scaled_font_reset_static_data (void) -{ - int status; - - CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); - for (status = CAIRO_STATUS_SUCCESS; - status <= CAIRO_STATUS_LAST_STATUS; - status++) - { - if (_cairo_scaled_font_nil_objects[status] != NULL) { - free (_cairo_scaled_font_nil_objects[status]); - _cairo_scaled_font_nil_objects[status] = NULL; - } - } - CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); - - CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - if (cairo_scaled_glyph_page_cache.hash_table != NULL) { - _cairo_cache_fini (&cairo_scaled_glyph_page_cache); - cairo_scaled_glyph_page_cache.hash_table = NULL; - } - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); -} - -/** - * cairo_scaled_font_reference: - * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case - * this function does nothing) - * - * Increases the reference count on @scaled_font by one. This prevents - * @scaled_font from being destroyed until a matching call to - * cairo_scaled_font_destroy() is made. - * - * The number of references to a #cairo_scaled_font_t can be get using - * cairo_scaled_font_get_reference_count(). - * - * Returns: the referenced #cairo_scaled_font_t - **/ -cairo_scaled_font_t * -cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) - return scaled_font; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); - - _cairo_reference_count_inc (&scaled_font->ref_count); - - return scaled_font; -} -slim_hidden_def (cairo_scaled_font_reference); - -/** - * cairo_scaled_font_destroy: - * @scaled_font: a #cairo_scaled_font_t - * - * Decreases the reference count on @font by one. If the result - * is zero, then @font and all associated resources are freed. - * See cairo_scaled_font_reference(). - **/ -void -cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) -{ - cairo_scaled_font_t *lru = NULL; - cairo_scaled_font_map_t *font_map; - - assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex)); - - if (scaled_font == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) - return; - - font_map = _cairo_scaled_font_map_lock (); - assert (font_map != NULL); - - /* Another thread may have resurrected the font whilst we waited */ - if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { - if (! scaled_font->placeholder && - scaled_font->hash_entry.hash != ZOMBIE) - { - /* Another thread may have already inserted us into the holdovers */ - if (scaled_font->holdover) - goto unlock; - - /* Rather than immediately destroying this object, we put it into - * the font_map->holdovers array in case it will get used again - * soon (and is why we must hold the lock over the atomic op on - * the reference count). To make room for it, we do actually - * destroy the least-recently-used holdover. - */ - - if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { - lru = font_map->holdovers[0]; - assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count)); - - _cairo_hash_table_remove (font_map->hash_table, - &lru->hash_entry); - - font_map->num_holdovers--; - memmove (&font_map->holdovers[0], - &font_map->holdovers[1], - font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); - } - - font_map->holdovers[font_map->num_holdovers++] = scaled_font; - scaled_font->holdover = TRUE; - } else - lru = scaled_font; - } - - unlock: - _cairo_scaled_font_map_unlock (); - - /* If we pulled an item from the holdovers array, (while the font - * map lock was held, of course), then there is no way that anyone - * else could have acquired a reference to it. So we can now - * safely call fini on it without any lock held. This is desirable - * as we never want to call into any backend function with a lock - * held. */ - if (lru != NULL) { - _cairo_scaled_font_fini_internal (lru); - free (lru); - } -} -slim_hidden_def (cairo_scaled_font_destroy); - -/** - * cairo_scaled_font_get_reference_count: - * @scaled_font: a #cairo_scaled_font_t - * - * Returns the current reference count of @scaled_font. - * - * Return value: the current reference count of @scaled_font. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.4 - **/ -unsigned int -cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count); -} - -/** - * cairo_scaled_font_get_user_data: - * @scaled_font: a #cairo_scaled_font_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @scaled_font using the - * specified key. If no user data has been attached with the given - * key this function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - * - * Since: 1.4 - **/ -void * -cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&scaled_font->user_data, - key); -} -slim_hidden_def (cairo_scaled_font_get_user_data); - -/** - * cairo_scaled_font_set_user_data: - * @scaled_font: a #cairo_scaled_font_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the #cairo_scaled_font_t - * @destroy: a #cairo_destroy_func_t which will be called when the - * #cairo_t is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @scaled_font. To remove user data from a surface, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) - return scaled_font->status; - - return _cairo_user_data_array_set_data (&scaled_font->user_data, - key, user_data, destroy); -} -slim_hidden_def (cairo_scaled_font_set_user_data); - -/* Public font API follows. */ - -/** - * cairo_scaled_font_extents: - * @scaled_font: a #cairo_scaled_font_t - * @extents: a #cairo_font_extents_t which to store the retrieved extents. - * - * Gets the metrics for a #cairo_scaled_font_t. - **/ -void -cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents) -{ - if (scaled_font->status) { - extents->ascent = 0.0; - extents->descent = 0.0; - extents->height = 0.0; - extents->max_x_advance = 0.0; - extents->max_y_advance = 0.0; - return; - } - - *extents = scaled_font->extents; -} -slim_hidden_def (cairo_scaled_font_extents); - -/** - * cairo_scaled_font_text_extents: - * @scaled_font: a #cairo_scaled_font_t - * @utf8: a NUL-terminated string of text, encoded in UTF-8 - * @extents: a #cairo_text_extents_t which to store the retrieved extents. - * - * Gets the extents for a string of text. The extents describe a - * user-space rectangle that encloses the "inked" portion of the text - * drawn at the origin (0,0) (as it would be drawn by cairo_show_text() - * if the cairo graphics state were set to the same font_face, - * font_matrix, ctm, and font_options as @scaled_font). Additionally, - * the x_advance and y_advance values indicate the amount by which the - * current point would be advanced by cairo_show_text(). - * - * Note that whitespace characters do not directly contribute to the - * size of the rectangle (extents.width and extents.height). They do - * contribute indirectly by changing the position of non-whitespace - * characters. In particular, trailing whitespace characters are - * likely to not affect the size of the rectangle, though they will - * affect the x_advance and y_advance values. - * - * Since: 1.2 - **/ -void -cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, - const char *utf8, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - cairo_glyph_t *glyphs = NULL; - int num_glyphs; - - if (scaled_font->status) - goto ZERO_EXTENTS; - - if (utf8 == NULL) - goto ZERO_EXTENTS; - - status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., - utf8, -1, - &glyphs, &num_glyphs, - NULL, NULL, - NULL); - if (unlikely (status)) { - status = _cairo_scaled_font_set_error (scaled_font, status); - goto ZERO_EXTENTS; - } - - cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents); - free (glyphs); - - return; - -ZERO_EXTENTS: - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; -} - -/** - * cairo_scaled_font_glyph_extents: - * @scaled_font: a #cairo_scaled_font_t - * @glyphs: an array of glyph IDs with X and Y offsets. - * @num_glyphs: the number of glyphs in the @glyphs array - * @extents: a #cairo_text_extents_t which to store the retrieved extents. - * - * Gets the extents for an array of glyphs. The extents describe a - * user-space rectangle that encloses the "inked" portion of the - * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo - * graphics state were set to the same font_face, font_matrix, ctm, - * and font_options as @scaled_font). Additionally, the x_advance and - * y_advance values indicate the amount by which the current point - * would be advanced by cairo_show_glyphs(). - * - * Note that whitespace glyphs do not contribute to the size of the - * rectangle (extents.width and extents.height). - **/ -void -cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - int i; - double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; - cairo_bool_t visible = FALSE; - cairo_scaled_glyph_t *scaled_glyph = NULL; - - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - if (unlikely (scaled_font->status)) - goto ZERO_EXTENTS; - - if (num_glyphs == 0) - goto ZERO_EXTENTS; - - if (unlikely (num_glyphs < 0)) { - _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT); - /* XXX Can't propagate error */ - goto ZERO_EXTENTS; - } - - if (unlikely (glyphs == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); - /* XXX Can't propagate error */ - goto ZERO_EXTENTS; - } - - _cairo_scaled_font_freeze_cache (scaled_font); - - for (i = 0; i < num_glyphs; i++) { - double left, top, right, bottom; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) { - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_scaled_font_set_error (scaled_font, status); - } - goto UNLOCK; - } - - /* "Ink" extents should skip "invisible" glyphs */ - if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0) - continue; - - left = scaled_glyph->metrics.x_bearing + glyphs[i].x; - right = left + scaled_glyph->metrics.width; - top = scaled_glyph->metrics.y_bearing + glyphs[i].y; - bottom = top + scaled_glyph->metrics.height; - - if (!visible) { - visible = TRUE; - min_x = left; - max_x = right; - min_y = top; - max_y = bottom; - } else { - if (left < min_x) min_x = left; - if (right > max_x) max_x = right; - if (top < min_y) min_y = top; - if (bottom > max_y) max_y = bottom; - } - } - - if (visible) { - extents->x_bearing = min_x - glyphs[0].x; - extents->y_bearing = min_y - glyphs[0].y; - extents->width = max_x - min_x; - extents->height = max_y - min_y; - } else { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - } - - if (num_glyphs) { - double x0, y0, x1, y1; - - x0 = glyphs[0].x; - y0 = glyphs[0].y; - - /* scaled_glyph contains the glyph for num_glyphs - 1 already. */ - x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance; - y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance; - - extents->x_advance = x1 - x0; - extents->y_advance = y1 - y0; - } else { - extents->x_advance = 0.0; - extents->y_advance = 0.0; - } - - UNLOCK: - _cairo_scaled_font_thaw_cache (scaled_font); - return; - -ZERO_EXTENTS: - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; -} -slim_hidden_def (cairo_scaled_font_glyph_extents); - -#define GLYPH_LUT_SIZE 64 -static cairo_status_t -cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t *scaled_font, - double x, - double y, - const char *utf8, - cairo_glyph_t *glyphs, - cairo_text_cluster_t **clusters, - int num_chars) -{ - struct glyph_lut_elt { - unsigned long index; - double x_advance; - double y_advance; - } glyph_lut[GLYPH_LUT_SIZE]; - uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE]; - cairo_status_t status; - const char *p; - int i; - - for (i = 0; i < GLYPH_LUT_SIZE; i++) - glyph_lut_unicode[i] = ~0U; - - p = utf8; - for (i = 0; i < num_chars; i++) { - int idx, num_bytes; - uint32_t unicode; - cairo_scaled_glyph_t *scaled_glyph; - struct glyph_lut_elt *glyph_slot; - - num_bytes = _cairo_utf8_get_char_validated (p, &unicode); - p += num_bytes; - - glyphs[i].x = x; - glyphs[i].y = y; - - idx = unicode % ARRAY_LENGTH (glyph_lut); - glyph_slot = &glyph_lut[idx]; - if (glyph_lut_unicode[idx] == unicode) { - glyphs[i].index = glyph_slot->index; - x += glyph_slot->x_advance; - y += glyph_slot->y_advance; - } else { - unsigned long g; - - g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); - status = _cairo_scaled_glyph_lookup (scaled_font, - g, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - return status; - - x += scaled_glyph->metrics.x_advance; - y += scaled_glyph->metrics.y_advance; - - glyph_lut_unicode[idx] = unicode; - glyph_slot->index = g; - glyph_slot->x_advance = scaled_glyph->metrics.x_advance; - glyph_slot->y_advance = scaled_glyph->metrics.y_advance; - - glyphs[i].index = g; - } - - if (clusters) { - (*clusters)[i].num_bytes = num_bytes; - (*clusters)[i].num_glyphs = 1; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t *scaled_font, - double x, - double y, - const char *utf8, - cairo_glyph_t *glyphs, - cairo_text_cluster_t **clusters, - int num_chars) -{ - const char *p; - int i; - - p = utf8; - for (i = 0; i < num_chars; i++) { - unsigned long g; - int num_bytes; - uint32_t unicode; - cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status; - - num_bytes = _cairo_utf8_get_char_validated (p, &unicode); - p += num_bytes; - - glyphs[i].x = x; - glyphs[i].y = y; - - g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); - - /* - * No advance needed for a single character string. So, let's speed up - * one-character strings by skipping glyph lookup. - */ - if (num_chars > 1) { - status = _cairo_scaled_glyph_lookup (scaled_font, - g, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - return status; - - x += scaled_glyph->metrics.x_advance; - y += scaled_glyph->metrics.y_advance; - } - - glyphs[i].index = g; - - if (clusters) { - (*clusters)[i].num_bytes = num_bytes; - (*clusters)[i].num_glyphs = 1; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_scaled_font_text_to_glyphs: - * @x: X position to place first glyph - * @y: Y position to place first glyph - * @scaled_font: a #cairo_scaled_font_t - * @utf8: a string of text encoded in UTF-8 - * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated - * @glyphs: pointer to array of glyphs to fill - * @num_glyphs: pointer to number of glyphs - * @clusters: pointer to array of cluster mapping information to fill, or %NULL - * @num_clusters: pointer to number of clusters, or %NULL - * @cluster_flags: pointer to location to store cluster flags corresponding to the - * output @clusters, or %NULL - * - * Converts UTF-8 text to an array of glyphs, optionally with cluster - * mapping, that can be used to render later using @scaled_font. - * - * If @glyphs initially points to a non-%NULL value, that array is used - * as a glyph buffer, and @num_glyphs should point to the number of glyph - * entries available there. If the provided glyph array is too short for - * the conversion, a new glyph array is allocated using cairo_glyph_allocate() - * and placed in @glyphs. Upon return, @num_glyphs always contains the - * number of generated glyphs. If the value @glyphs points to has changed - * after the call, the user is responsible for freeing the allocated glyph - * array using cairo_glyph_free(). This may happen even if the provided - * array was large enough. - * - * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL, - * and cluster mapping will be computed. - * The semantics of how cluster array allocation works is similar to the glyph - * array. That is, - * if @clusters initially points to a non-%NULL value, that array is used - * as a cluster buffer, and @num_clusters should point to the number of cluster - * entries available there. If the provided cluster array is too short for - * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate() - * and placed in @clusters. Upon return, @num_clusters always contains the - * number of generated clusters. If the value @clusters points at has changed - * after the call, the user is responsible for freeing the allocated cluster - * array using cairo_text_cluster_free(). This may happen even if the provided - * array was large enough. - * - * In the simplest case, @glyphs and @clusters can point to %NULL initially - * and a suitable array will be allocated. In code: - * - * cairo_status_t status; - * - * cairo_glyph_t *glyphs = NULL; - * int num_glyphs; - * cairo_text_cluster_t *clusters = NULL; - * int num_clusters; - * cairo_text_cluster_flags_t cluster_flags; - * - * status = cairo_scaled_font_text_to_glyphs (scaled_font, - * x, y, - * utf8, utf8_len, - * &glyphs, &num_glyphs, - * &clusters, &num_clusters, &cluster_flags); - * - * if (status == CAIRO_STATUS_SUCCESS) { - * cairo_show_text_glyphs (cr, - * utf8, utf8_len, - * glyphs, num_glyphs, - * clusters, num_clusters, cluster_flags); - * - * cairo_glyph_free (glyphs); - * cairo_text_cluster_free (clusters); - * } - * - * - * If no cluster mapping is needed: - * - * cairo_status_t status; - * - * cairo_glyph_t *glyphs = NULL; - * int num_glyphs; - * - * status = cairo_scaled_font_text_to_glyphs (scaled_font, - * x, y, - * utf8, utf8_len, - * &glyphs, &num_glyphs, - * NULL, NULL, - * NULL); - * - * if (status == CAIRO_STATUS_SUCCESS) { - * cairo_show_glyphs (cr, glyphs, num_glyphs); - * cairo_glyph_free (glyphs); - * } - * - * - * If stack-based glyph and cluster arrays are to be used for small - * arrays: - * - * cairo_status_t status; - * - * cairo_glyph_t stack_glyphs[40]; - * cairo_glyph_t *glyphs = stack_glyphs; - * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]); - * cairo_text_cluster_t stack_clusters[40]; - * cairo_text_cluster_t *clusters = stack_clusters; - * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]); - * cairo_text_cluster_flags_t cluster_flags; - * - * status = cairo_scaled_font_text_to_glyphs (scaled_font, - * x, y, - * utf8, utf8_len, - * &glyphs, &num_glyphs, - * &clusters, &num_clusters, &cluster_flags); - * - * if (status == CAIRO_STATUS_SUCCESS) { - * cairo_show_text_glyphs (cr, - * utf8, utf8_len, - * glyphs, num_glyphs, - * clusters, num_clusters, cluster_flags); - * - * if (glyphs != stack_glyphs) - * cairo_glyph_free (glyphs); - * if (clusters != stack_clusters) - * cairo_text_cluster_free (clusters); - * } - * - * - * For details of how @clusters, @num_clusters, and @cluster_flags map input - * UTF-8 text to the output glyphs see cairo_show_text_glyphs(). - * - * The output values can be readily passed to cairo_show_text_glyphs() - * cairo_show_glyphs(), or related functions, assuming that the exact - * same @scaled_font is used for the operation. - * - * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status - * if the input values are wrong or if conversion failed. If the input - * values are correct but the conversion failed, the error status is also - * set on @scaled_font. - * - * Since: 1.8 - **/ -#define CACHING_THRESHOLD 16 -cairo_status_t -cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags) -{ - int num_chars = 0; - cairo_status_t status; - cairo_glyph_t *orig_glyphs; - cairo_text_cluster_t *orig_clusters; - - status = scaled_font->status; - if (unlikely (status)) - return status; - - /* A slew of sanity checks */ - - /* glyphs and num_glyphs can't be NULL */ - if (glyphs == NULL || - num_glyphs == NULL) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto BAIL; - } - - /* Special case for NULL and -1 */ - if (utf8 == NULL && utf8_len == -1) - utf8_len = 0; - - /* No NULLs for non-NULLs! */ - if ((utf8_len && utf8 == NULL) || - (clusters && num_clusters == NULL) || - (clusters && cluster_flags == NULL)) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto BAIL; - } - - /* A -1 for utf8_len means NUL-terminated */ - if (utf8_len == -1) - utf8_len = strlen (utf8); - - /* A NULL *glyphs means no prealloced glyphs array */ - if (glyphs && *glyphs == NULL) - *num_glyphs = 0; - - /* A NULL *clusters means no prealloced clusters array */ - if (clusters && *clusters == NULL) - *num_clusters = 0; - - if (!clusters && num_clusters) { - num_clusters = NULL; - } - - if (cluster_flags) { - *cluster_flags = FALSE; - } - - if (!clusters && cluster_flags) { - cluster_flags = NULL; - } - - /* Apart from that, no negatives */ - if (utf8_len < 0 || - *num_glyphs < 0 || - (num_clusters && *num_clusters < 0)) { - status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); - goto BAIL; - } - - if (utf8_len == 0) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL; - } - - /* validate input so backend does not have to */ - status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars); - if (unlikely (status)) - goto BAIL; - - _cairo_scaled_font_freeze_cache (scaled_font); - - orig_glyphs = *glyphs; - orig_clusters = clusters ? *clusters : NULL; - - if (scaled_font->backend->text_to_glyphs) { - status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (status == CAIRO_STATUS_SUCCESS) { - /* The checks here are crude; we only should do them in - * user-font backend, but they don't hurt here. This stuff - * can be hard to get right. */ - - if (*num_glyphs < 0) { - status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); - goto DONE; - } - if (num_glyphs && *glyphs == NULL) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto DONE; - } - - if (clusters) { - if (*num_clusters < 0) { - status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); - goto DONE; - } - if (num_clusters && *clusters == NULL) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto DONE; - } - - /* Don't trust the backend, validate clusters! */ - status = - _cairo_validate_text_clusters (utf8, utf8_len, - *glyphs, *num_glyphs, - *clusters, *num_clusters, - *cluster_flags); - } - } - - goto DONE; - } - } - - if (*num_glyphs < num_chars) { - *glyphs = cairo_glyph_allocate (num_chars); - if (unlikely (*glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; - } - } - *num_glyphs = num_chars; - - if (clusters) { - if (*num_clusters < num_chars) { - *clusters = cairo_text_cluster_allocate (num_chars); - if (unlikely (*clusters == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; - } - } - *num_clusters = num_chars; - } - - if (num_chars > CACHING_THRESHOLD) - status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font, - x, y, - utf8, - *glyphs, - clusters, - num_chars); - else - status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font, - x, y, - utf8, - *glyphs, - clusters, - num_chars); - - DONE: /* error that should be logged on scaled_font happened */ - _cairo_scaled_font_thaw_cache (scaled_font); - - if (unlikely (status)) { - *num_glyphs = 0; - if (*glyphs != orig_glyphs) { - cairo_glyph_free (*glyphs); - *glyphs = orig_glyphs; - } - - if (clusters) { - *num_clusters = 0; - if (*clusters != orig_clusters) { - cairo_text_cluster_free (*clusters); - *clusters = orig_clusters; - } - } - } - - return _cairo_scaled_font_set_error (scaled_font, status); - - BAIL: /* error with input arguments */ - - if (num_glyphs) - *num_glyphs = 0; - - if (num_clusters) - *num_clusters = 0; - - return status; -} -slim_hidden_def (cairo_scaled_font_text_to_glyphs); - -static inline cairo_bool_t -_range_contains_glyph (const cairo_box_t *extents, - cairo_fixed_t left, - cairo_fixed_t top, - cairo_fixed_t right, - cairo_fixed_t bottom) -{ - return right > extents->p1.x && - left < extents->p2.x && - bottom > extents->p1.y && - top < extents->p2.y; -} - -/* - * Compute a device-space bounding box for the glyphs. - */ -cairo_status_t -_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_rectangle_int_t *extents, - cairo_bool_t *overlap_out) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }}; - cairo_scaled_glyph_t *glyph_cache[64]; - cairo_bool_t overlap = overlap_out ? FALSE : TRUE; - cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options); - int i; - - if (unlikely (scaled_font->status)) - return scaled_font->status; - - _cairo_scaled_font_freeze_cache (scaled_font); - - memset (glyph_cache, 0, sizeof (glyph_cache)); - - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - cairo_fixed_t x, y, x1, y1, x2, y2; - int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); - - scaled_glyph = glyph_cache[cache_index]; - if (scaled_glyph == NULL || - _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) - { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - break; - - glyph_cache[cache_index] = scaled_glyph; - } - - if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) - x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x)); - else - x = _cairo_fixed_from_double (glyphs[i].x); - x1 = x + scaled_glyph->bbox.p1.x; - x2 = x + scaled_glyph->bbox.p2.x; - - if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) - y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y)); - else - y = _cairo_fixed_from_double (glyphs[i].y); - y1 = y + scaled_glyph->bbox.p1.y; - y2 = y + scaled_glyph->bbox.p2.y; - - if (overlap == FALSE) - overlap = _range_contains_glyph (&box, x1, y1, x2, y2); - - if (x1 < box.p1.x) box.p1.x = x1; - if (x2 > box.p2.x) box.p2.x = x2; - if (y1 < box.p1.y) box.p1.y = y1; - if (y2 > box.p2.y) box.p2.y = y2; - } - - _cairo_scaled_font_thaw_cache (scaled_font); - if (unlikely (status)) - return _cairo_scaled_font_set_error (scaled_font, status); - - if (box.p1.x < box.p2.x) { - _cairo_box_round_to_rectangle (&box, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } - - if (overlap_out != NULL) - *overlap_out = overlap; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_rectangle_int_t *extents) -{ - double x0 = HUGE_VAL, x1 = -HUGE_VAL; - double y0 = HUGE_VAL, y1 = -HUGE_VAL; - int i; - - for (i = 0; i < num_glyphs; i++) { - double g; - - g = glyphs[i].x; - if (g < x0) x0 = g; - if (g > x1) x1 = g; - - g = glyphs[i].y; - if (g < y0) y0 = g; - if (g > y1) y1 = g; - } - - if (x0 <= x1 && y0 <= y1) { - extents->x = floor (x0 - scaled_font->extents.max_x_advance); - extents->width = ceil (x1 + scaled_font->extents.max_x_advance); - extents->width -= extents->x; - - extents->y = floor (y0 - scaled_font->extents.ascent); - extents->height = ceil (y1 + scaled_font->extents.descent); - extents->height -= extents->y; - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -cairo_status_t -_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region) -{ - cairo_status_t status; - cairo_surface_t *mask = NULL; - cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */ - cairo_surface_pattern_t mask_pattern; - int i; - - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - - if (scaled_font->status) - return scaled_font->status; - - if (!num_glyphs) - return CAIRO_STATUS_SUCCESS; - - if (scaled_font->backend->show_glyphs != NULL) { - int remaining_glyphs = num_glyphs; - status = scaled_font->backend->show_glyphs (scaled_font, - op, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs, - clip_region, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_scaled_font_set_error (scaled_font, status); - } - - /* Font display routine either does not exist or failed. */ - - _cairo_scaled_font_freeze_cache (scaled_font); - - for (i = 0; i < num_glyphs; i++) { - int x, y; - cairo_image_surface_t *glyph_surface; - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - - if (unlikely (status)) - goto CLEANUP_MASK; - - glyph_surface = scaled_glyph->surface; - - /* To start, create the mask using the format from the first - * glyph. Later we'll deal with different formats. */ - if (mask == NULL) { - mask_format = glyph_surface->format; - mask = cairo_image_surface_create (mask_format, width, height); - status = mask->status; - if (unlikely (status)) - goto CLEANUP_MASK; - } - - /* If we have glyphs of different formats, we "upgrade" the mask - * to the wider of the formats. */ - if (glyph_surface->format != mask_format && - _cairo_format_bits_per_pixel (mask_format) < - _cairo_format_bits_per_pixel (glyph_surface->format) ) - { - cairo_surface_t *new_mask; - - switch (glyph_surface->format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_A1: - mask_format = glyph_surface->format; - break; - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_RGB24: - case CAIRO_FORMAT_INVALID: - default: - ASSERT_NOT_REACHED; - mask_format = CAIRO_FORMAT_ARGB32; - break; - } - - new_mask = cairo_image_surface_create (mask_format, width, height); - status = new_mask->status; - if (unlikely (status)) { - cairo_surface_destroy (new_mask); - goto CLEANUP_MASK; - } - - _cairo_pattern_init_for_surface (&mask_pattern, mask); - /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is - * never any component alpha here. - */ - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - &mask_pattern.base, - new_mask, - 0, 0, - 0, 0, - 0, 0, - width, height, - NULL); - - _cairo_pattern_fini (&mask_pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (new_mask); - goto CLEANUP_MASK; - } - - cairo_surface_destroy (mask); - mask = new_mask; - } - - if (glyph_surface->width && glyph_surface->height) { - cairo_surface_pattern_t glyph_pattern; - - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (glyphs[i].x - - glyph_surface->base.device_transform.x0); - y = _cairo_lround (glyphs[i].y - - glyph_surface->base.device_transform.y0); - - _cairo_pattern_init_for_surface (&glyph_pattern, - &glyph_surface->base); - if (mask_format == CAIRO_FORMAT_ARGB32) - glyph_pattern.base.has_component_alpha = TRUE; - - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - &glyph_pattern.base, - mask, - 0, 0, - 0, 0, - x - dest_x, y - dest_y, - glyph_surface->width, - glyph_surface->height, - NULL); - - _cairo_pattern_fini (&glyph_pattern.base); - - if (unlikely (status)) - goto CLEANUP_MASK; - } - } - - _cairo_pattern_init_for_surface (&mask_pattern, mask); - if (mask_format == CAIRO_FORMAT_ARGB32) - mask_pattern.base.has_component_alpha = TRUE; - - status = _cairo_surface_composite (op, pattern, &mask_pattern.base, - surface, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height, - clip_region); - - _cairo_pattern_fini (&mask_pattern.base); - -CLEANUP_MASK: - _cairo_scaled_font_thaw_cache (scaled_font); - - if (mask != NULL) - cairo_surface_destroy (mask); - return _cairo_scaled_font_set_error (scaled_font, status); -} - -/* Add a single-device-unit rectangle to a path. */ -static cairo_status_t -_add_unit_rectangle_to_path (cairo_path_fixed_t *path, - cairo_fixed_t x, - cairo_fixed_t y) -{ - cairo_status_t status; - - status = _cairo_path_fixed_move_to (path, x, y); - if (unlikely (status)) - return status; - - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (1), - _cairo_fixed_from_int (0)); - if (unlikely (status)) - return status; - - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (0), - _cairo_fixed_from_int (1)); - if (unlikely (status)) - return status; - - status = _cairo_path_fixed_rel_line_to (path, - _cairo_fixed_from_int (-1), - _cairo_fixed_from_int (0)); - if (unlikely (status)) - return status; - - return _cairo_path_fixed_close_path (path); -} - -/** - * _trace_mask_to_path: - * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8) - * @path: An initialized path to hold the result - * - * Given a mask surface, (an alpha image), fill out the provided path - * so that when filled it would result in something that approximates - * the mask. - * - * Note: The current tracing code here is extremely primitive. It - * operates only on an A1 surface, (converting an A8 surface to A1 if - * necessary), and performs the tracing by drawing a little square - * around each pixel that is on in the mask. We do not pretend that - * this is a high-quality result. But we are leaving it up to someone - * who cares enough about getting a better result to implement - * something more sophisticated. - **/ -static cairo_status_t -_trace_mask_to_path (cairo_image_surface_t *mask, - cairo_path_fixed_t *path, - double tx, double ty) -{ - const uint8_t *row; - int rows, cols, bytes_per_row; - int x, y, bit; - double xoff, yoff; - cairo_fixed_t x0, y0; - cairo_fixed_t px, py; - cairo_status_t status; - - mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1); - status = mask->base.status; - if (unlikely (status)) - return status; - - cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); - x0 = _cairo_fixed_from_double (tx - xoff); - y0 = _cairo_fixed_from_double (ty - yoff); - - bytes_per_row = (mask->width + 7) / 8; - row = mask->data; - for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { - const uint8_t *byte_ptr = row; - x = 0; - py = _cairo_fixed_from_int (y); - for (cols = bytes_per_row; cols--; ) { - uint8_t byte = *byte_ptr++; - if (byte == 0) { - x += 8; - continue; - } - - byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); - for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { - if (byte & bit) { - px = _cairo_fixed_from_int (x); - status = _add_unit_rectangle_to_path (path, - px + x0, - py + y0); - if (unlikely (status)) - goto BAIL; - } - } - } - } - -BAIL: - cairo_surface_destroy (&mask->base); - - return status; -} - -cairo_status_t -_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path) -{ - cairo_status_t status; - int i; - - status = scaled_font->status; - if (unlikely (status)) - return status; - - _cairo_scaled_font_freeze_cache (scaled_font); - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_path_fixed_append (path, - scaled_glyph->path, CAIRO_DIRECTION_FORWARD, - _cairo_fixed_from_double (glyphs[i].x), - _cairo_fixed_from_double (glyphs[i].y)); - - } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - /* If the font is incapable of providing a path, then we'll - * have to trace our own from a surface. - */ - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (unlikely (status)) - goto BAIL; - - status = _trace_mask_to_path (scaled_glyph->surface, path, - glyphs[i].x, glyphs[i].y); - } - - if (unlikely (status)) - goto BAIL; - } - BAIL: - _cairo_scaled_font_thaw_cache (scaled_font); - - return _cairo_scaled_font_set_error (scaled_font, status); -} - -/** - * _cairo_scaled_glyph_set_metrics: - * @scaled_glyph: a #cairo_scaled_glyph_t - * @scaled_font: a #cairo_scaled_font_t - * @fs_metrics: a #cairo_text_extents_t in font space - * - * _cairo_scaled_glyph_set_metrics() stores user space metrics - * for the specified glyph given font space metrics. It is - * called by the font backend when initializing a glyph with - * %CAIRO_SCALED_GLYPH_INFO_METRICS. - **/ -void -_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_text_extents_t *fs_metrics) -{ - cairo_bool_t first = TRUE; - double hm, wm; - double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0; - double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; - double device_x_advance, device_y_advance; - - scaled_glyph->fs_metrics = *fs_metrics; - - for (hm = 0.0; hm <= 1.0; hm += 1.0) - for (wm = 0.0; wm <= 1.0; wm += 1.0) { - double x, y; - - /* Transform this corner to user space */ - x = fs_metrics->x_bearing + fs_metrics->width * wm; - y = fs_metrics->y_bearing + fs_metrics->height * hm; - cairo_matrix_transform_point (&scaled_font->font_matrix, - &x, &y); - if (first) { - min_user_x = max_user_x = x; - min_user_y = max_user_y = y; - } else { - if (x < min_user_x) min_user_x = x; - if (x > max_user_x) max_user_x = x; - if (y < min_user_y) min_user_y = y; - if (y > max_user_y) max_user_y = y; - } - - /* Transform this corner to device space from glyph origin */ - x = fs_metrics->x_bearing + fs_metrics->width * wm; - y = fs_metrics->y_bearing + fs_metrics->height * hm; - cairo_matrix_transform_distance (&scaled_font->scale, - &x, &y); - - if (first) { - min_device_x = max_device_x = x; - min_device_y = max_device_y = y; - } else { - if (x < min_device_x) min_device_x = x; - if (x > max_device_x) max_device_x = x; - if (y < min_device_y) min_device_y = y; - if (y > max_device_y) max_device_y = y; - } - first = FALSE; - } - scaled_glyph->metrics.x_bearing = min_user_x; - scaled_glyph->metrics.y_bearing = min_user_y; - scaled_glyph->metrics.width = max_user_x - min_user_x; - scaled_glyph->metrics.height = max_user_y - min_user_y; - - scaled_glyph->metrics.x_advance = fs_metrics->x_advance; - scaled_glyph->metrics.y_advance = fs_metrics->y_advance; - cairo_matrix_transform_distance (&scaled_font->font_matrix, - &scaled_glyph->metrics.x_advance, - &scaled_glyph->metrics.y_advance); - - device_x_advance = fs_metrics->x_advance; - device_y_advance = fs_metrics->y_advance; - cairo_matrix_transform_distance (&scaled_font->scale, - &device_x_advance, - &device_y_advance); - - scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x); - scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y); - scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x); - scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y); - - scaled_glyph->x_advance = _cairo_lround (device_x_advance); - scaled_glyph->y_advance = _cairo_lround (device_y_advance); - - scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS; -} - -void -_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_image_surface_t *surface) -{ - if (scaled_glyph->surface != NULL) - cairo_surface_destroy (&scaled_glyph->surface->base); - - /* sanity check the backend glyph contents */ - _cairo_debug_check_image_surface_is_defined (&surface->base); - scaled_glyph->surface = surface; - - if (surface != NULL) - scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; - else - scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE; -} - -void -_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_path_fixed_t *path) -{ - if (scaled_glyph->path != NULL) - _cairo_path_fixed_destroy (scaled_glyph->path); - - scaled_glyph->path = path; - - if (path != NULL) - scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH; - else - scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH; -} - -void -_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface) -{ - if (scaled_glyph->recording_surface != NULL) { - cairo_surface_finish (scaled_glyph->recording_surface); - cairo_surface_destroy (scaled_glyph->recording_surface); - } - - scaled_glyph->recording_surface = recording_surface; - - if (recording_surface != NULL) - scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; - else - scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; -} - -static cairo_bool_t -_cairo_scaled_glyph_page_can_remove (const void *closure) -{ - const cairo_scaled_glyph_page_t *page = closure; - const cairo_scaled_font_t *scaled_font; - - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; - return scaled_font->cache_frozen == 0; -} - -static cairo_status_t -_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t **scaled_glyph) -{ - cairo_scaled_glyph_page_t *page; - cairo_status_t status; - - /* only the first page in the list may contain available slots */ - if (! cairo_list_is_empty (&scaled_font->glyph_pages)) { - page = cairo_list_last_entry (&scaled_font->glyph_pages, - cairo_scaled_glyph_page_t, - link); - if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) { - *scaled_glyph = &page->glyphs[page->num_glyphs++]; - return CAIRO_STATUS_SUCCESS; - } - } - - page = malloc (sizeof (cairo_scaled_glyph_page_t)); - if (unlikely (page == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - page->cache_entry.hash = (uintptr_t) scaled_font; - page->cache_entry.size = 1; /* XXX occupancy weighting? */ - page->num_glyphs = 0; - - CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - if (scaled_font->global_cache_frozen == FALSE) { - if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) { - status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, - NULL, - _cairo_scaled_glyph_page_can_remove, - _cairo_scaled_glyph_page_destroy, - MAX_GLYPH_PAGES_CACHED); - if (unlikely (status)) { - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); - free (page); - return status; - } - } - - _cairo_cache_freeze (&cairo_scaled_glyph_page_cache); - scaled_font->global_cache_frozen = TRUE; - } - - status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache, - &page->cache_entry); - CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); - if (unlikely (status)) { - free (page); - return status; - } - - cairo_list_add_tail (&page->link, &scaled_font->glyph_pages); - - *scaled_glyph = &page->glyphs[page->num_glyphs++]; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_scaled_glyph_page_t *page; - - assert (! cairo_list_is_empty (&scaled_font->glyph_pages)); - page = cairo_list_last_entry (&scaled_font->glyph_pages, - cairo_scaled_glyph_page_t, - link); - assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]); - - _cairo_scaled_glyph_fini (scaled_font, scaled_glyph); - - if (--page->num_glyphs == 0) { - _cairo_cache_remove (&cairo_scaled_glyph_page_cache, - &page->cache_entry); - } -} - -/** - * _cairo_scaled_glyph_lookup: - * @scaled_font: a #cairo_scaled_font_t - * @index: the glyph to create - * @info: a #cairo_scaled_glyph_info_t marking which portions of - * the glyph should be filled in. - * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph - * is returned. - * - * If the desired info is not available, (for example, when trying to - * get INFO_PATH with a bitmapped font), this function will return - * %CAIRO_INT_STATUS_UNSUPPORTED. - * - * Note: This function must be called with the scaled font frozen, and it must - * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled - * font was not frozen, then there is no guarantee that the glyph would not be - * evicted before you tried to access it.) See - * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache(). - * - * Returns: a glyph with the requested portions filled in. Glyph - * lookup is cached and glyph will be automatically freed along - * with the scaled_font so no explicit free is required. - * @info can be one or more of: - * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box - * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image - * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space - **/ -cairo_int_status_t -_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, - unsigned long index, - cairo_scaled_glyph_info_t info, - cairo_scaled_glyph_t **scaled_glyph_ret) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph; - cairo_scaled_glyph_info_t need_info; - - *scaled_glyph_ret = NULL; - - if (unlikely (scaled_font->status)) - return scaled_font->status; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* - * Check cache for glyph - */ - scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs, - (cairo_hash_entry_t *) &index); - if (scaled_glyph == NULL) { - status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph); - if (unlikely (status)) - goto err; - - memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t)); - _cairo_scaled_glyph_set_index (scaled_glyph, index); - - /* ask backend to initialize metrics and shape fields */ - status = - scaled_font->backend->scaled_glyph_init (scaled_font, - scaled_glyph, - info | CAIRO_SCALED_GLYPH_INFO_METRICS); - if (unlikely (status)) { - _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); - goto err; - } - - status = _cairo_hash_table_insert (scaled_font->glyphs, - &scaled_glyph->hash_entry); - if (unlikely (status)) { - _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); - goto err; - } - } - - /* - * Check and see if the glyph, as provided, - * already has the requested data and amend it if not - */ - need_info = info & ~scaled_glyph->has_info; - if (need_info) { - status = scaled_font->backend->scaled_glyph_init (scaled_font, - scaled_glyph, - need_info); - if (unlikely (status)) - goto err; - - /* Don't trust the scaled_glyph_init() return value, the font - * backend may not even know about some of the info. For example, - * no backend other than the user-fonts knows about recording-surface - * glyph info. */ - if (info & ~scaled_glyph->has_info) - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *scaled_glyph_ret = scaled_glyph; - return CAIRO_STATUS_SUCCESS; - -err: - /* It's not an error for the backend to not support the info we want. */ - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_scaled_font_set_error (scaled_font, status); - return status; -} - -double -_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) -{ - return scaled_font->max_scale; -} - - -/** - * cairo_scaled_font_get_font_face: - * @scaled_font: a #cairo_scaled_font_t - * - * Gets the font face that this scaled font uses. This is the - * font face passed to cairo_scaled_font_create(). - * - * Return value: The #cairo_font_face_t with which @scaled_font was - * created. - * - * Since: 1.2 - **/ -cairo_font_face_t * -cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font->status) - return (cairo_font_face_t*) &_cairo_font_face_nil; - - if (scaled_font->original_font_face != NULL) - return scaled_font->original_font_face; - - return scaled_font->font_face; -} -slim_hidden_def (cairo_scaled_font_get_font_face); - -/** - * cairo_scaled_font_get_font_matrix: - * @scaled_font: a #cairo_scaled_font_t - * @font_matrix: return value for the matrix - * - * Stores the font matrix with which @scaled_font was created into - * @matrix. - * - * Since: 1.2 - **/ -void -cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *font_matrix) -{ - if (scaled_font->status) { - cairo_matrix_init_identity (font_matrix); - return; - } - - *font_matrix = scaled_font->font_matrix; -} -slim_hidden_def (cairo_scaled_font_get_font_matrix); - -/** - * cairo_scaled_font_get_ctm: - * @scaled_font: a #cairo_scaled_font_t - * @ctm: return value for the CTM - * - * Stores the CTM with which @scaled_font was created into @ctm. - * Note that the translation offsets (x0, y0) of the CTM are ignored - * by cairo_scaled_font_create(). So, the matrix this - * function returns always has 0,0 as x0,y0. - * - * Since: 1.2 - **/ -void -cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *ctm) -{ - if (scaled_font->status) { - cairo_matrix_init_identity (ctm); - return; - } - - *ctm = scaled_font->ctm; -} -slim_hidden_def (cairo_scaled_font_get_ctm); - -/** - * cairo_scaled_font_get_scale_matrix: - * @scaled_font: a #cairo_scaled_font_t - * @scale_matrix: return value for the matrix - * - * Stores the scale matrix of @scaled_font into @matrix. - * The scale matrix is product of the font matrix and the ctm - * associated with the scaled font, and hence is the matrix mapping from - * font space to device space. - * - * Since: 1.8 - **/ -void -cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *scale_matrix) -{ - if (scaled_font->status) { - cairo_matrix_init_identity (scale_matrix); - return; - } - - *scale_matrix = scaled_font->scale; -} - -/** - * cairo_scaled_font_get_font_options: - * @scaled_font: a #cairo_scaled_font_t - * @options: return value for the font options - * - * Stores the font options with which @scaled_font was created into - * @options. - * - * Since: 1.2 - **/ -void -cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, - cairo_font_options_t *options) -{ - if (cairo_font_options_status (options)) - return; - - if (scaled_font->status) { - _cairo_font_options_init_default (options); - return; - } - - _cairo_font_options_init_copy (options, &scaled_font->options); -} -slim_hidden_def (cairo_scaled_font_get_font_options); diff --git a/libs/cairo/cairo/src/cairo-script-surface.c b/libs/cairo/cairo/src/cairo-script-surface.c deleted file mode 100644 index 50214aa19..000000000 --- a/libs/cairo/cairo/src/cairo-script-surface.c +++ /dev/null @@ -1,3590 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* The script surface is one that records all operations performed on - * it in the form of a procedural script, similar in fashion to - * PostScript but using Cairo's imaging model. In essence, this is - * equivalent to the recording-surface, but as there is no impedance mismatch - * between Cairo and CairoScript, we can generate output immediately - * without having to copy and hold the data in memory. - */ - -#include "cairoint.h" - -#include "cairo-script.h" - -#include "cairo-analysis-surface-private.h" -#include "cairo-device-private.h" -#include "cairo-error-private.h" -#include "cairo-list-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-scaled-font-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-surface-snapshot-private.h" -#include "cairo-surface-subsurface-private.h" -#include "cairo-surface-wrapper-private.h" - -#if CAIRO_HAS_FT_FONT -#include "cairo-ft-private.h" -#endif - -#include - -#ifdef WORDS_BIGENDIAN -#define to_be32(x) x -#else -#define to_be32(x) bswap_32(x) -#endif - -#define _cairo_output_stream_puts(S, STR) \ - _cairo_output_stream_write ((S), (STR), strlen (STR)) - -#define static cairo_warn static - -typedef struct _cairo_script_context cairo_script_context_t; -typedef struct _cairo_script_surface cairo_script_surface_t; -typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; -typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; - -typedef struct _operand { - enum { - SURFACE, - DEFERRED, - } type; - cairo_list_t link; -} operand_t; - - -struct deferred_finish { - cairo_list_t link; - operand_t operand; -}; - -struct _cairo_script_context { - cairo_device_t base; - - int active; - - cairo_output_stream_t *stream; - cairo_script_mode_t mode; - - struct _bitmap { - unsigned long min; - unsigned long count; - unsigned int map[64]; - struct _bitmap *next; - } surface_id, font_id; - - cairo_list_t operands; - cairo_list_t deferred; - - cairo_list_t fonts; - cairo_list_t defines; -}; - -struct _cairo_script_surface_font_private { - cairo_script_context_t *ctx; - cairo_bool_t has_sfnt; - unsigned long id; - unsigned long subset_glyph_index; - cairo_list_t link; - cairo_scaled_font_t *parent; -}; - -struct _cairo_script_implicit_context { - cairo_operator_t current_operator; - cairo_fill_rule_t current_fill_rule; - double current_tolerance; - cairo_antialias_t current_antialias; - cairo_stroke_style_t current_style; - cairo_pattern_union_t current_source; - cairo_matrix_t current_ctm; - cairo_matrix_t current_stroke_matrix; - cairo_matrix_t current_font_matrix; - cairo_font_options_t current_font_options; - cairo_scaled_font_t *current_scaled_font; - cairo_path_fixed_t current_path; - cairo_bool_t has_clip; -}; - -struct _cairo_script_surface { - cairo_surface_t base; - - cairo_surface_wrapper_t wrapper; - - cairo_surface_clipper_t clipper; - - operand_t operand; - cairo_bool_t emitted; - cairo_bool_t defined; - cairo_bool_t active; - - double width, height; - - /* implicit flattened context */ - cairo_script_implicit_context_t cr; -}; - -static const cairo_surface_backend_t _cairo_script_surface_backend; - -static cairo_script_surface_t * -_cairo_script_surface_create_internal (cairo_script_context_t *ctx, - cairo_content_t content, - double width, - double height, - cairo_surface_t *passthrough); - -static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -static void -_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); - -static void -_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); - -static void -_bitmap_release_id (struct _bitmap *b, unsigned long token) -{ - struct _bitmap **prev = NULL; - - do { - if (token < b->min + sizeof (b->map) * CHAR_BIT) { - unsigned int bit, elem; - - token -= b->min; - elem = token / (sizeof (b->map[0]) * CHAR_BIT); - bit = token % (sizeof (b->map[0]) * CHAR_BIT); - b->map[elem] &= ~(1 << bit); - if (! --b->count && prev) { - *prev = b->next; - free (b); - } - return; - } - prev = &b->next; - b = b->next; - } while (b != NULL); -} - -static cairo_status_t -_bitmap_next_id (struct _bitmap *b, - unsigned long *id) -{ - struct _bitmap *bb, **prev = NULL; - unsigned long min = 0; - - do { - if (b->min != min) - break; - - if (b->count < sizeof (b->map) * CHAR_BIT) { - unsigned int n, m, bit; - for (n = 0; n < ARRAY_LENGTH (b->map); n++) { - if (b->map[n] == (unsigned int) -1) - continue; - - for (m=0, bit=1; mmap[0])*CHAR_BIT; m++, bit<<=1) { - if ((b->map[n] & bit) == 0) { - b->map[n] |= bit; - b->count++; - *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; - return CAIRO_STATUS_SUCCESS; - } - } - } - } - min += sizeof (b->map) * CHAR_BIT; - - prev = &b->next; - b = b->next; - } while (b != NULL); - - bb = malloc (sizeof (struct _bitmap)); - if (unlikely (bb == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *prev = bb; - bb->next = b; - bb->min = min; - bb->count = 1; - bb->map[0] = 0x1; - memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); - *id = min; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_bitmap_fini (struct _bitmap *b) -{ - while (b != NULL) { - struct _bitmap *next = b->next; - free (b); - b = next; - } -} - -static const char * -_direction_to_string (cairo_bool_t backward) -{ - static const char *names[] = { - "FORWARD", - "BACKWARD" - }; - assert (backward < ARRAY_LENGTH (names)); - return names[backward]; -} - -static const char * -_operator_to_string (cairo_operator_t op) -{ - static const char *names[] = { - "CLEAR", /* CAIRO_OPERATOR_CLEAR */ - - "SOURCE", /* CAIRO_OPERATOR_SOURCE */ - "OVER", /* CAIRO_OPERATOR_OVER */ - "IN", /* CAIRO_OPERATOR_IN */ - "OUT", /* CAIRO_OPERATOR_OUT */ - "ATOP", /* CAIRO_OPERATOR_ATOP */ - - "DEST", /* CAIRO_OPERATOR_DEST */ - "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ - "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ - "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ - "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ - - "XOR", /* CAIRO_OPERATOR_XOR */ - "ADD", /* CAIRO_OPERATOR_ADD */ - "SATURATE", /* CAIRO_OPERATOR_SATURATE */ - - "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ - "SCREEN", /* CAIRO_OPERATOR_SCREEN */ - "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ - "DARKEN", /* CAIRO_OPERATOR_DARKEN */ - "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ - "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ - "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ - "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ - "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ - "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ - "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ - "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ - "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ - "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ - "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ - }; - assert (op < ARRAY_LENGTH (names)); - return names[op]; -} - -static const char * -_extend_to_string (cairo_extend_t extend) -{ - static const char *names[] = { - "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ - "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ - "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ - "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ - }; - assert (extend < ARRAY_LENGTH (names)); - return names[extend]; -} - -static const char * -_filter_to_string (cairo_filter_t filter) -{ - static const char *names[] = { - "FILTER_FAST", /* CAIRO_FILTER_FAST */ - "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ - "FILTER_BEST", /* CAIRO_FILTER_BEST */ - "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ - "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ - "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ - }; - assert (filter < ARRAY_LENGTH (names)); - return names[filter]; -} - -static const char * -_fill_rule_to_string (cairo_fill_rule_t rule) -{ - static const char *names[] = { - "WINDING", /* CAIRO_FILL_RULE_WINDING */ - "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ - }; - assert (rule < ARRAY_LENGTH (names)); - return names[rule]; -} - -static const char * -_antialias_to_string (cairo_antialias_t antialias) -{ - static const char *names[] = { - "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ - "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ - "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ - "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ - }; - assert (antialias < ARRAY_LENGTH (names)); - return names[antialias]; -} - -static const char * -_line_cap_to_string (cairo_line_cap_t line_cap) -{ - static const char *names[] = { - "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ - "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ - "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ - }; - assert (line_cap < ARRAY_LENGTH (names)); - return names[line_cap]; -} - -static const char * -_line_join_to_string (cairo_line_join_t line_join) -{ - static const char *names[] = { - "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ - "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ - "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ - }; - assert (line_join < ARRAY_LENGTH (names)); - return names[line_join]; -} - -static inline cairo_script_context_t * -to_context (cairo_script_surface_t *surface) -{ - return (cairo_script_context_t *) surface->base.device; -} - -static cairo_bool_t -target_is_active (cairo_script_surface_t *surface) -{ - return cairo_list_is_first (&surface->operand.link, - &to_context (surface)->operands); -} - -static void -target_push (cairo_script_surface_t *surface) -{ - cairo_list_move (&surface->operand.link, &to_context (surface)->operands); -} - -static int -target_depth (cairo_script_surface_t *surface) -{ - cairo_list_t *link; - int depth = 0; - - cairo_list_foreach (link, &to_context (surface)->operands) { - if (link == &surface->operand.link) - break; - depth++; - } - - return depth; -} - -static void -_get_target (cairo_script_surface_t *surface) -{ - cairo_script_context_t *ctx = to_context (surface); - - if (surface->defined) { - _cairo_output_stream_printf (ctx->stream, "s%u ", - surface->base.unique_id); - } else { - assert (! cairo_list_is_empty (&surface->operand.link)); - if (! target_is_active (surface)) { - int depth = target_depth (surface); - if (ctx->active) { - _cairo_output_stream_printf (ctx->stream, "%d index ", depth); - _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); - } else { - if (depth == 1) { - _cairo_output_stream_puts (ctx->stream, - "exch\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "%d -1 roll\n", - depth); - } - _cairo_output_stream_puts (ctx->stream, "/target get "); - } - } else { - _cairo_output_stream_puts (ctx->stream, "/target get "); - } - } -} - -static const char * -_content_to_string (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_ALPHA: return "ALPHA"; - case CAIRO_CONTENT_COLOR: return "COLOR"; - default: - case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; - } -} - -static cairo_status_t -_emit_surface (cairo_script_surface_t *surface) -{ - cairo_script_context_t *ctx = to_context (surface); - - _cairo_output_stream_printf (ctx->stream, - "<< /content //%s", - _content_to_string (surface->base.content)); - if (surface->width != -1 && surface->height != -1) { - _cairo_output_stream_printf (ctx->stream, - " /width %f /height %f", - surface->width, - surface->height); - } - - if (surface->base.x_fallback_resolution != - CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || - surface->base.y_fallback_resolution != - CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) - { - _cairo_output_stream_printf (ctx->stream, - " /fallback-resolution [%f %f]", - surface->base.x_fallback_resolution, - surface->base.y_fallback_resolution); - } - - if (surface->base.device_transform.x0 != 0. || - surface->base.device_transform.y0 != 0.) - { - /* XXX device offset is encoded into the pattern matrices etc. */ - if (0) { - _cairo_output_stream_printf (ctx->stream, - " /device-offset [%f %f]", - surface->base.device_transform.x0, - surface->base.device_transform.y0); - } - } - - _cairo_output_stream_puts (ctx->stream, " >> surface context\n"); - surface->emitted = TRUE; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_context (cairo_script_surface_t *surface) -{ - cairo_script_context_t *ctx = to_context (surface); - - if (target_is_active (surface)) - return CAIRO_STATUS_SUCCESS; - - while (! cairo_list_is_empty (&ctx->operands)) { - operand_t *op; - cairo_script_surface_t *old; - - op = cairo_list_first_entry (&ctx->operands, - operand_t, - link); - if (op->type == DEFERRED) - break; - - old = cairo_container_of (op, cairo_script_surface_t, operand); - if (old == surface) - break; - if (old->active) - break; - - if (! old->defined) { - assert (old->emitted); - _cairo_output_stream_printf (ctx->stream, - "/target get /s%u exch def pop\n", - old->base.unique_id); - old->defined = TRUE; - } else { - _cairo_output_stream_puts (ctx->stream, "pop\n"); - } - - cairo_list_del (&old->operand.link); - } - - if (target_is_active (surface)) - return CAIRO_STATUS_SUCCESS; - - if (! surface->emitted) { - cairo_status_t status; - - status = _emit_surface (surface); - if (unlikely (status)) - return status; - } else if (cairo_list_is_empty (&surface->operand.link)) { - assert (surface->defined); - _cairo_output_stream_printf (ctx->stream, - "s%u context\n", - surface->base.unique_id); - _cairo_script_implicit_context_reset (&surface->cr); - _cairo_surface_clipper_reset (&surface->clipper); - } else { - int depth = target_depth (surface); - if (depth == 1) { - _cairo_output_stream_puts (ctx->stream, "exch\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "%d -1 roll\n", - depth); - } - } - target_push (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_operator (cairo_script_surface_t *surface, - cairo_operator_t op) -{ - assert (target_is_active (surface)); - - if (surface->cr.current_operator == op) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_operator = op; - - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s set-operator\n", - _operator_to_string (op)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_fill_rule (cairo_script_surface_t *surface, - cairo_fill_rule_t fill_rule) -{ - assert (target_is_active (surface)); - - if (surface->cr.current_fill_rule == fill_rule) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_fill_rule = fill_rule; - - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s set-fill-rule\n", - _fill_rule_to_string (fill_rule)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_tolerance (cairo_script_surface_t *surface, - double tolerance, - cairo_bool_t force) -{ - assert (target_is_active (surface)); - - if ((! force || - fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && - surface->cr.current_tolerance == tolerance) - { - return CAIRO_STATUS_SUCCESS; - } - - surface->cr.current_tolerance = tolerance; - - _cairo_output_stream_printf (to_context (surface)->stream, - "%f set-tolerance\n", - tolerance); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_antialias (cairo_script_surface_t *surface, - cairo_antialias_t antialias) -{ - assert (target_is_active (surface)); - - if (surface->cr.current_antialias == antialias) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_antialias = antialias; - - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s set-antialias\n", - _antialias_to_string (antialias)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_line_width (cairo_script_surface_t *surface, - double line_width, - cairo_bool_t force) -{ - assert (target_is_active (surface)); - - if ((! force || - fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && - surface->cr.current_style.line_width == line_width) - { - return CAIRO_STATUS_SUCCESS; - } - - surface->cr.current_style.line_width = line_width; - - _cairo_output_stream_printf (to_context (surface)->stream, - "%f set-line-width\n", - line_width); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_line_cap (cairo_script_surface_t *surface, - cairo_line_cap_t line_cap) -{ - assert (target_is_active (surface)); - - if (surface->cr.current_style.line_cap == line_cap) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_style.line_cap = line_cap; - - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s set-line-cap\n", - _line_cap_to_string (line_cap)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_line_join (cairo_script_surface_t *surface, - cairo_line_join_t line_join) -{ - assert (target_is_active (surface)); - - if (surface->cr.current_style.line_join == line_join) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_style.line_join = line_join; - - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s set-line-join\n", - _line_join_to_string (line_join)); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_miter_limit (cairo_script_surface_t *surface, - double miter_limit, - cairo_bool_t force) -{ - assert (target_is_active (surface)); - - if ((! force || - fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && - surface->cr.current_style.miter_limit == miter_limit) - { - return CAIRO_STATUS_SUCCESS; - } - - surface->cr.current_style.miter_limit = miter_limit; - - _cairo_output_stream_printf (to_context (surface)->stream, - "%f set-miter-limit\n", - miter_limit); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_dashes_equal (const double *a, const double *b, int num_dashes) -{ - while (num_dashes--) { - if (fabs (*a - *b) > 1e-5) - return FALSE; - a++, b++; - } - - return TRUE; -} - -static cairo_status_t -_emit_dash (cairo_script_surface_t *surface, - const double *dash, - unsigned int num_dashes, - double offset, - cairo_bool_t force) -{ - unsigned int n; - - assert (target_is_active (surface)); - - if (force && - num_dashes == 0 && - surface->cr.current_style.num_dashes == 0) - { - return CAIRO_STATUS_SUCCESS; - } - - if (! force && - (surface->cr.current_style.num_dashes == num_dashes && - (num_dashes == 0 || - (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && - _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) - { - return CAIRO_STATUS_SUCCESS; - } - - - if (num_dashes) { - surface->cr.current_style.dash = _cairo_realloc_ab - (surface->cr.current_style.dash, num_dashes, sizeof (double)); - if (unlikely (surface->cr.current_style.dash == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (surface->cr.current_style.dash, dash, - sizeof (double) * num_dashes); - } else { - if (surface->cr.current_style.dash != NULL) { - free (surface->cr.current_style.dash); - surface->cr.current_style.dash = NULL; - } - } - - surface->cr.current_style.num_dashes = num_dashes; - surface->cr.current_style.dash_offset = offset; - - _cairo_output_stream_puts (to_context (surface)->stream, "["); - for (n = 0; n < num_dashes; n++) { - _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]); - if (n < num_dashes-1) - _cairo_output_stream_puts (to_context (surface)->stream, " "); - } - _cairo_output_stream_printf (to_context (surface)->stream, - "] %f set-dash\n", - offset); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_stroke_style (cairo_script_surface_t *surface, - const cairo_stroke_style_t *style, - cairo_bool_t force) -{ - cairo_status_t status; - - assert (target_is_active (surface)); - - status = _emit_line_width (surface, style->line_width, force); - if (unlikely (status)) - return status; - - status = _emit_line_cap (surface, style->line_cap); - if (unlikely (status)) - return status; - - status = _emit_line_join (surface, style->line_join); - if (unlikely (status)) - return status; - - status = _emit_miter_limit (surface, style->miter_limit, force); - if (unlikely (status)) - return status; - - status = _emit_dash (surface, - style->dash, style->num_dashes, style->dash_offset, - force); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static const char * -_format_to_string (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: return "ARGB32"; - case CAIRO_FORMAT_RGB24: return "RGB24"; - case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; - case CAIRO_FORMAT_A8: return "A8"; - case CAIRO_FORMAT_A1: return "A1"; - case CAIRO_FORMAT_INVALID: return "INVALID"; - } - ASSERT_NOT_REACHED; - return "INVALID"; -} - -static cairo_status_t -_emit_solid_pattern (cairo_script_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - cairo_script_context_t *ctx = to_context (surface); - - if (! CAIRO_COLOR_IS_OPAQUE (&solid->color)) - { - if (! (surface->base.content & CAIRO_CONTENT_COLOR) || - ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && - (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && - (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) - { - _cairo_output_stream_printf (ctx->stream, - "%f a", - solid->color.alpha); - } - else - { - _cairo_output_stream_printf (ctx->stream, - "%f %f %f %f rgba", - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - } - } - else - { - if (solid->color.red_short == solid->color.green_short && - solid->color.red_short == solid->color.blue_short) - { - _cairo_output_stream_printf (ctx->stream, - "%f g", - solid->color.red); - } - else - { - _cairo_output_stream_printf (ctx->stream, - "%f %f %f rgb", - solid->color.red, - solid->color.green, - solid->color.blue); - } - } - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, - cairo_output_stream_t *output) -{ - unsigned int n; - - for (n = 0; n < gradient->n_stops; n++) { - _cairo_output_stream_printf (output, - "\n %f %f %f %f %f add-color-stop", - gradient->stops[n].offset, - gradient->stops[n].color.red, - gradient->stops[n].color.green, - gradient->stops[n].color.blue, - gradient->stops[n].color.alpha); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_linear_pattern (cairo_script_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_linear_pattern_t *linear; - - linear = (cairo_linear_pattern_t *) pattern; - - _cairo_output_stream_printf (ctx->stream, - "%f %f %f %f linear", - _cairo_fixed_to_double (linear->p1.x), - _cairo_fixed_to_double (linear->p1.y), - _cairo_fixed_to_double (linear->p2.x), - _cairo_fixed_to_double (linear->p2.y)); - return _emit_gradient_color_stops (&linear->base, ctx->stream); -} - -static cairo_status_t -_emit_radial_pattern (cairo_script_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_radial_pattern_t *radial; - - radial = (cairo_radial_pattern_t *) pattern; - - _cairo_output_stream_printf (ctx->stream, - "%f %f %f %f %f %f radial", - _cairo_fixed_to_double (radial->c1.x), - _cairo_fixed_to_double (radial->c1.y), - _cairo_fixed_to_double (radial->r1), - _cairo_fixed_to_double (radial->c2.x), - _cairo_fixed_to_double (radial->c2.y), - _cairo_fixed_to_double (radial->r2)); - return _emit_gradient_color_stops (&radial->base, ctx->stream); -} - -static cairo_status_t -_emit_recording_surface_pattern (cairo_script_surface_t *surface, - cairo_recording_surface_t *source) -{ - cairo_script_implicit_context_t old_cr; - cairo_script_surface_t *similar; - cairo_status_t status; - cairo_box_t bbox; - cairo_rectangle_int_t rect; - - /* first measure the extents */ - status = _cairo_recording_surface_get_bbox (source, &bbox, NULL); - if (unlikely (status)) - return status; - - /* convert to extents so that it matches the public api */ - _cairo_box_round_to_rectangle (&bbox, &rect); - - similar = _cairo_script_surface_create_internal (to_context (surface), - source->content, - rect.width, - rect.height, - NULL); - if (unlikely (similar->base.status)) - return similar->base.status; - - cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y); - similar->base.is_clear = TRUE; - - _get_target (surface); - _cairo_output_stream_printf (to_context (surface)->stream, - "%d %d //%s similar dup context\n", - rect.width, rect.height, - _content_to_string (source->content)); - target_push (similar); - similar->emitted = TRUE; - - old_cr = surface->cr; - _cairo_script_implicit_context_init (&surface->cr); - status = _cairo_recording_surface_replay (&source->base, &similar->base); - surface->cr = old_cr; - - if (unlikely (status)) { - cairo_surface_destroy (&similar->base); - return status; - } - - cairo_list_del (&similar->operand.link); - assert (target_is_active (surface)); - - _cairo_output_stream_puts (to_context (surface)->stream, "pop "); - cairo_surface_destroy (&similar->base); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_script_surface_pattern (cairo_script_surface_t *surface, - cairo_script_surface_t *source) -{ - _get_target (source); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_write_image_surface (cairo_output_stream_t *output, - const cairo_image_surface_t *image) -{ - int stride, row, width; - uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; - uint8_t *rowdata; - uint8_t *data; - - stride = image->stride; - width = image->width; - data = image->data; -#if WORDS_BIGENDIAN - switch (image->format) { - case CAIRO_FORMAT_A1: - for (row = image->height; row--; ) { - _cairo_output_stream_write (output, data, (width+7)/8); - data += stride; - } - break; - case CAIRO_FORMAT_A8: - for (row = image->height; row--; ) { - _cairo_output_stream_write (output, data, width); - data += stride; - } - break; - case CAIRO_FORMAT_RGB16_565: - for (row = image->height; row--; ) { - _cairo_output_stream_write (output, data, 2*width); - data += stride; - } - break; - case CAIRO_FORMAT_RGB24: - for (row = image->height; row--; ) { - int col; - rowdata = data; - for (col = width; col--; ) { - _cairo_output_stream_write (output, rowdata, 3); - rowdata+=4; - } - data += stride; - } - break; - case CAIRO_FORMAT_ARGB32: - for (row = image->height; row--; ) { - _cairo_output_stream_write (output, data, 4*width); - data += stride; - } - break; - case CAIRO_FORMAT_INVALID: - default: - ASSERT_NOT_REACHED; - break; - } -#else - if (stride > ARRAY_LENGTH (row_stack)) { - rowdata = malloc (stride); - if (unlikely (rowdata == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else - rowdata = row_stack; - - switch (image->format) { - case CAIRO_FORMAT_A1: - for (row = image->height; row--; ) { - int col; - for (col = 0; col < (width + 7)/8; col++) - rowdata[col] = CAIRO_BITSWAP8 (data[col]); - _cairo_output_stream_write (output, rowdata, (width+7)/8); - data += stride; - } - break; - case CAIRO_FORMAT_A8: - for (row = image->height; row--; ) { - _cairo_output_stream_write (output, data, width); - data += stride; - } - break; - case CAIRO_FORMAT_RGB16_565: - for (row = image->height; row--; ) { - uint16_t *src = (uint16_t *) data; - uint16_t *dst = (uint16_t *) rowdata; - int col; - for (col = 0; col < width; col++) - dst[col] = bswap_16 (src[col]); - _cairo_output_stream_write (output, rowdata, 2*width); - data += stride; - } - break; - case CAIRO_FORMAT_RGB24: - for (row = image->height; row--; ) { - uint8_t *src = data; - int col; - for (col = 0; col < width; col++) { - rowdata[3*col+2] = *src++; - rowdata[3*col+1] = *src++; - rowdata[3*col+0] = *src++; - src++; - } - _cairo_output_stream_write (output, rowdata, 3*width); - data += stride; - } - break; - case CAIRO_FORMAT_ARGB32: - for (row = image->height; row--; ) { - uint32_t *src = (uint32_t *) data; - uint32_t *dst = (uint32_t *) rowdata; - int col; - for (col = 0; col < width; col++) - dst[col] = bswap_32 (src[col]); - _cairo_output_stream_write (output, rowdata, 4*width); - data += stride; - } - break; - case CAIRO_FORMAT_INVALID: - default: - ASSERT_NOT_REACHED; - break; - } - if (rowdata != row_stack) - free (rowdata); -#endif - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_emit_png_surface (cairo_script_surface_t *surface, - cairo_image_surface_t *image) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_output_stream_t *base85_stream; - cairo_status_t status; - const uint8_t *mime_data; - unsigned long mime_data_length; - - cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_output_stream_printf (ctx->stream, - "<< " - "/width %d " - "/height %d " - "/format //%s " - "/mime-type (image/png) " - "/source <~", - image->width, image->height, - _format_to_string (image->format)); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (ctx->stream, "~> >> image "); - return CAIRO_STATUS_SUCCESS; -} - -struct def { - cairo_script_context_t *ctx; - cairo_user_data_array_t *user_data; - unsigned int tag; - cairo_list_t link; -}; - -static void -_undef (void *data) -{ - struct def *def = data; - - cairo_list_del (&def->link); - _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag); - free (def); -} - -static cairo_status_t -_emit_image_surface (cairo_script_surface_t *surface, - cairo_image_surface_t *image) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_output_stream_t *base85_stream; - cairo_output_stream_t *zlib_stream; - cairo_status_t status, status2; - const uint8_t *mime_data; - unsigned long mime_data_length; - struct def *tag; - - if (_cairo_user_data_array_get_data (&image->base.user_data, - (cairo_user_data_key_t *) ctx)) - { - _cairo_output_stream_printf (ctx->stream, - "s%u ", - image->base.unique_id); - return CAIRO_STATUS_SUCCESS; - } - - status = _emit_png_surface (surface, image); - if (_cairo_status_is_error (status)) { - return status; - } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - cairo_image_surface_t *clone; - uint32_t len; - - if (image->format == CAIRO_FORMAT_INVALID) { - clone = _cairo_image_surface_coerce (image); - } else { - clone = (cairo_image_surface_t *) - cairo_surface_reference (&image->base); - } - - _cairo_output_stream_printf (ctx->stream, - "<< " - "/width %d " - "/height %d " - "/format //%s " - "/source ", - clone->width, clone->height, - _format_to_string (clone->format)); - - switch (clone->format) { - case CAIRO_FORMAT_A1: - len = (clone->width + 7)/8; - break; - case CAIRO_FORMAT_A8: - len = clone->width; - break; - case CAIRO_FORMAT_RGB16_565: - len = clone->width * 2; - break; - case CAIRO_FORMAT_RGB24: - len = clone->width * 3; - break; - case CAIRO_FORMAT_ARGB32: - len = clone->width * 4; - break; - case CAIRO_FORMAT_INVALID: - ASSERT_NOT_REACHED; - break; - } - len *= clone->height; - - if (len > 24) { - _cairo_output_stream_puts (ctx->stream, "<|"); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - - len = to_be32 (len); - _cairo_output_stream_write (base85_stream, &len, sizeof (len)); - - zlib_stream = _cairo_deflate_stream_create (base85_stream); - status = _write_image_surface (zlib_stream, clone); - - status2 = _cairo_output_stream_destroy (zlib_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - if (unlikely (status)) - return status; - } else { - _cairo_output_stream_puts (ctx->stream, "<~"); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - status = _write_image_surface (base85_stream, clone); - status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - if (unlikely (status)) - return status; - } - _cairo_output_stream_puts (ctx->stream, "~> >> image "); - - cairo_surface_destroy (&clone->base); - } - - tag = malloc (sizeof (*tag)); - if (unlikely (tag == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - tag->ctx = ctx; - tag->tag = image->base.unique_id; - tag->user_data = &image->base.user_data; - cairo_list_add (&tag->link, &ctx->defines); - status = _cairo_user_data_array_set_data (&image->base.user_data, - (cairo_user_data_key_t *) ctx, - tag, _undef); - if (unlikely (status)) { - free (tag); - return status; - } - - _cairo_output_stream_printf (ctx->stream, - "dup /s%u exch def ", - image->base.unique_id); - - cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (mime_data != NULL) { - _cairo_output_stream_printf (ctx->stream, - "\n (%s) <~", - CAIRO_MIME_TYPE_JPEG); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); - } - - cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, - &mime_data, &mime_data_length); - if (mime_data != NULL) { - _cairo_output_stream_printf (ctx->stream, - "\n (%s) <~", - CAIRO_MIME_TYPE_JP2); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_image_surface_pattern (cairo_script_surface_t *surface, - cairo_surface_t *source) -{ - cairo_surface_t *snapshot; - cairo_image_surface_t *image; - cairo_status_t status; - void *extra; - - /* XXX keeping a copy is nasty, but we want to hook into the surface's - * lifetime. Using a snapshot is a convenient method. - */ - snapshot = _cairo_surface_snapshot (source); - status = _cairo_surface_acquire_source_image (snapshot, &image, &extra); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _emit_image_surface (surface, image); - _cairo_surface_release_source_image (snapshot, image, extra); - } - cairo_surface_destroy (snapshot); - - return status; -} - -static cairo_status_t -_emit_subsurface_pattern (cairo_script_surface_t *surface, - cairo_surface_subsurface_t *sub) -{ - cairo_surface_t *source = sub->target; - cairo_status_t status; - - switch ((int) source->backend->type) { - case CAIRO_SURFACE_TYPE_RECORDING: - status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); - break; - case CAIRO_SURFACE_TYPE_SCRIPT: - status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); - break; - default: - status = _emit_image_surface_pattern (surface, source); - break; - } - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (to_context (surface)->stream, - "%d %d %d %d subsurface ", - sub->extents.x, - sub->extents.y, - sub->extents.width, - sub->extents.height); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_surface_pattern (cairo_script_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_surface_pattern_t *surface_pattern; - cairo_surface_t *source; - cairo_status_t status; - - surface_pattern = (cairo_surface_pattern_t *) pattern; - source = surface_pattern->surface; - - if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) - source = ((cairo_surface_snapshot_t *) source)->target; - - switch ((int) source->backend->type) { - case CAIRO_SURFACE_TYPE_RECORDING: - status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); - break; - case CAIRO_SURFACE_TYPE_SCRIPT: - status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); - break; - case CAIRO_SURFACE_TYPE_SUBSURFACE: - status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source); - break; - default: - status = _emit_image_surface_pattern (surface, source); - break; - } - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (to_context (surface)->stream, "pattern"); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_pattern (cairo_script_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_status_t status; - cairo_bool_t is_default_extend; - cairo_bool_t need_newline = TRUE; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - /* solid colors do not need filter/extend/matrix */ - return _emit_solid_pattern (surface, pattern); - - case CAIRO_PATTERN_TYPE_LINEAR: - status = _emit_linear_pattern (surface, pattern); - is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; - break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _emit_radial_pattern (surface, pattern); - is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; - break; - case CAIRO_PATTERN_TYPE_SURFACE: - status = _emit_surface_pattern (surface, pattern); - is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; - break; - - default: - ASSERT_NOT_REACHED; - status = CAIRO_INT_STATUS_UNSUPPORTED; - } - if (unlikely (status)) - return status; - - if (! _cairo_matrix_is_identity (&pattern->matrix)) { - if (need_newline) { - _cairo_output_stream_puts (ctx->stream, "\n "); - need_newline = FALSE; - } - - _cairo_output_stream_printf (ctx->stream, - " [%f %f %f %f %f %f] set-matrix\n ", - pattern->matrix.xx, pattern->matrix.yx, - pattern->matrix.xy, pattern->matrix.yy, - pattern->matrix.x0, pattern->matrix.y0); - } - - /* XXX need to discriminate the user explicitly setting the default */ - if (pattern->filter != CAIRO_FILTER_DEFAULT) { - if (need_newline) { - _cairo_output_stream_puts (ctx->stream, "\n "); - need_newline = FALSE; - } - - _cairo_output_stream_printf (ctx->stream, - " //%s set-filter\n ", - _filter_to_string (pattern->filter)); - } - if (! is_default_extend ){ - if (need_newline) { - _cairo_output_stream_puts (ctx->stream, "\n "); - need_newline = FALSE; - } - - _cairo_output_stream_printf (ctx->stream, - " //%s set-extend\n ", - _extend_to_string (pattern->extend)); - } - - if (need_newline) - _cairo_output_stream_puts (ctx->stream, "\n "); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_identity (cairo_script_surface_t *surface, - cairo_bool_t *matrix_updated) -{ - assert (target_is_active (surface)); - - if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) - return CAIRO_STATUS_SUCCESS; - - _cairo_output_stream_puts (to_context (surface)->stream, - "identity set-matrix\n"); - - *matrix_updated = TRUE; - cairo_matrix_init_identity (&surface->cr.current_ctm); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_source (cairo_script_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source) -{ - cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; - - assert (target_is_active (surface)); - - if (op == CAIRO_OPERATOR_CLEAR) { - /* the source is ignored, so don't change it */ - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) - return CAIRO_STATUS_SUCCESS; - - _cairo_pattern_fini (&surface->cr.current_source.base); - status = _cairo_pattern_init_copy (&surface->cr.current_source.base, - source); - if (unlikely (status)) - return status; - - status = _emit_identity (surface, &matrix_updated); - if (unlikely (status)) - return status; - - status = _emit_pattern (surface, source); - if (unlikely (status)) - return status; - - assert (target_is_active (surface)); - _cairo_output_stream_puts (to_context (surface)->stream, - " set-source\n"); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_path_move_to (void *closure, - const cairo_point_t *point) -{ - _cairo_output_stream_printf (closure, - " %f %f m", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_path_line_to (void *closure, - const cairo_point_t *point) -{ - _cairo_output_stream_printf (closure, - " %f %f l", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_path_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - _cairo_output_stream_printf (closure, - " %f %f %f %f %f %f c", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y), - _cairo_fixed_to_double (p2->x), - _cairo_fixed_to_double (p2->y), - _cairo_fixed_to_double (p3->x), - _cairo_fixed_to_double (p3->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_path_close (void *closure) -{ - _cairo_output_stream_printf (closure, - " h"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_path (cairo_script_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_box_t box; - cairo_status_t status; - - assert (target_is_active (surface)); - assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); - - if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) - return CAIRO_STATUS_SUCCESS; - - _cairo_path_fixed_fini (&surface->cr.current_path); - - _cairo_output_stream_puts (ctx->stream, "n"); - - if (path == NULL) { - _cairo_path_fixed_init (&surface->cr.current_path); - } else if (_cairo_path_fixed_is_box (path, &box)) { - double x1 = _cairo_fixed_to_double (box.p1.x); - double y1 = _cairo_fixed_to_double (box.p1.y); - double x2 = _cairo_fixed_to_double (box.p2.x); - double y2 = _cairo_fixed_to_double (box.p2.y); - - status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (ctx->stream, - " %f %f %f %f rectangle", - x1, y1, x2 - x1, y2 - y1); - } else { - cairo_status_t status; - - status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); - if (unlikely (status)) - return status; - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _path_move_to, - _path_line_to, - _path_curve_to, - _path_close, - ctx->stream); - if (unlikely (status)) - return status; - } - - _cairo_output_stream_puts (ctx->stream, "\n"); - - return CAIRO_STATUS_SUCCESS; -} -static cairo_bool_t -_scaling_matrix_equal (const cairo_matrix_t *a, - const cairo_matrix_t *b) -{ - return fabs (a->xx - b->xx) < 1e-5 && - fabs (a->xy - b->xy) < 1e-5 && - fabs (a->yx - b->yx) < 1e-5 && - fabs (a->yy - b->yy) < 1e-5; -} - -static cairo_status_t -_emit_scaling_matrix (cairo_script_surface_t *surface, - const cairo_matrix_t *ctm, - cairo_bool_t *matrix_updated) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_bool_t was_identity; - assert (target_is_active (surface)); - - if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) - return CAIRO_STATUS_SUCCESS; - - was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); - - *matrix_updated = TRUE; - surface->cr.current_ctm = *ctm; - surface->cr.current_ctm.x0 = 0.; - surface->cr.current_ctm.y0 = 0.; - - if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { - _cairo_output_stream_puts (ctx->stream, - "identity set-matrix\n"); - } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { - _cairo_output_stream_printf (ctx->stream, - "%f %f scale\n", - ctm->xx, ctm->yy); - } else { - _cairo_output_stream_printf (ctx->stream, - "[%f %f %f %f 0 0] set-matrix\n", - ctm->xx, ctm->yx, - ctm->xy, ctm->yy); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_font_matrix (cairo_script_surface_t *surface, - const cairo_matrix_t *font_matrix) -{ - cairo_script_context_t *ctx = to_context (surface); - assert (target_is_active (surface)); - - if (memcmp (&surface->cr.current_font_matrix, - font_matrix, - sizeof (cairo_matrix_t)) == 0) - { - return CAIRO_STATUS_SUCCESS; - } - - surface->cr.current_font_matrix = *font_matrix; - - if (_cairo_matrix_is_identity (font_matrix)) { - _cairo_output_stream_puts (ctx->stream, - "identity set-font-matrix\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "[%f %f %f %f %f %f] set-font-matrix\n", - font_matrix->xx, font_matrix->yx, - font_matrix->xy, font_matrix->yy, - font_matrix->x0, font_matrix->y0); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_script_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_script_surface_t *surface, *other = abstract_surface; - cairo_surface_t *passthrough = NULL; - cairo_script_context_t *ctx; - cairo_status_t status; - - ctx = to_context (other); - - status = cairo_device_acquire (&ctx->base); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - if (! other->emitted) { - status = _emit_surface (other); - if (unlikely (status)) { - cairo_device_release (&ctx->base); - return _cairo_surface_create_in_error (status); - } - - target_push (other); - } - - if (_cairo_surface_wrapper_is_active (&other->wrapper)) { - passthrough = - _cairo_surface_wrapper_create_similar (&other->wrapper, - content, width, height); - if (unlikely (passthrough->status)) { - cairo_device_release (&ctx->base); - return passthrough; - } - } - - surface = _cairo_script_surface_create_internal (ctx, - content, - width, height, - passthrough); - cairo_surface_destroy (passthrough); - - if (unlikely (surface->base.status)) { - cairo_device_release (&ctx->base); - return &surface->base; - } - - _get_target (other); - _cairo_output_stream_printf (ctx->stream, - "%u %u //%s similar dup /s%u exch def context\n", - width, height, - _content_to_string (content), - surface->base.unique_id); - surface->emitted = TRUE; - surface->defined = TRUE; - surface->base.is_clear = TRUE; - target_push (surface); - - cairo_device_release (&ctx->base); - return &surface->base; -} - -static void -_device_flush (void *abstract_device) -{ - cairo_script_context_t *ctx = abstract_device; - cairo_status_t status; - - status = _cairo_output_stream_flush (ctx->stream); -} - -static void -_device_destroy (void *abstract_device) -{ - cairo_script_context_t *ctx = abstract_device; - cairo_status_t status; - - while (! cairo_list_is_empty (&ctx->fonts)) { - cairo_script_surface_font_private_t *font; - - font = cairo_list_first_entry (&ctx->fonts, - cairo_script_surface_font_private_t, - link); - cairo_list_del (&font->link); - if (font->parent->surface_private == font) - font->parent->surface_private = NULL; - free (font); - } - - while (! cairo_list_is_empty (&ctx->defines)) { - struct def *def = cairo_list_first_entry (&ctx->defines, - struct def, link); - - status = _cairo_user_data_array_set_data (def->user_data, - (cairo_user_data_key_t *) ctx, - NULL, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - } - - _bitmap_fini (ctx->surface_id.next); - _bitmap_fini (ctx->font_id.next); - - status = _cairo_output_stream_destroy (ctx->stream); - - free (ctx); -} - -static cairo_status_t -_cairo_script_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_script_surface_t *surface = abstract_surface; - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, - image_out, - image_extra); - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_script_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_script_surface_t *surface = abstract_surface; - - assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); - _cairo_surface_wrapper_release_source_image (&surface->wrapper, - image, - image_extra); -} - -static cairo_status_t -_cairo_script_surface_finish (void *abstract_surface) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_script_context_t *ctx = to_context (surface); - cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; - - _cairo_surface_wrapper_fini (&surface->wrapper); - - if (surface->cr.current_style.dash != NULL) { - free (surface->cr.current_style.dash); - surface->cr.current_style.dash = NULL; - } - _cairo_pattern_fini (&surface->cr.current_source.base); - _cairo_path_fixed_fini (&surface->cr.current_path); - _cairo_surface_clipper_reset (&surface->clipper); - - status = cairo_device_acquire (&ctx->base); - if (unlikely (status)) - return status; - - if (surface->emitted) { - assert (! surface->active); - - if (! cairo_list_is_empty (&surface->operand.link)) { - if (! ctx->active) { - if (target_is_active (surface)) { - _cairo_output_stream_printf (ctx->stream, - "pop\n"); - } else { - int depth = target_depth (surface); - if (depth == 1) { - _cairo_output_stream_printf (ctx->stream, - "exch pop\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "%d -1 roll pop\n", - depth); - } - } - cairo_list_del (&surface->operand.link); - } else { - struct deferred_finish *link = malloc (sizeof (*link)); - if (link == NULL) { - status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - cairo_list_del (&surface->operand.link); - } else { - link->operand.type = DEFERRED; - cairo_list_swap (&link->operand.link, - &surface->operand.link); - cairo_list_add (&link->link, &ctx->deferred); - } - } - } - - if (surface->defined) { - _cairo_output_stream_printf (ctx->stream, - "/s%u undef\n", - surface->base.unique_id); - } - } - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_output_stream_flush (to_context (surface)->stream); - - cairo_device_release (&ctx->base); - - return status; -} - -static cairo_int_status_t -_cairo_script_surface_copy_page (void *abstract_surface) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n"); - -BAIL: - cairo_device_release (surface->base.device); - return status; -} - -static cairo_int_status_t -_cairo_script_surface_show_page (void *abstract_surface) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n"); - -BAIL: - cairo_device_release (surface->base.device); - return status; -} - -static cairo_status_t -_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_script_surface_t *surface = cairo_container_of (clipper, - cairo_script_surface_t, - clipper); - cairo_script_context_t *ctx = to_context (surface); - cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; - cairo_box_t box; - - status = _emit_context (surface); - if (unlikely (status)) - return status; - - if (path == NULL) { - if (surface->cr.has_clip) { - _cairo_output_stream_puts (ctx->stream, "reset-clip\n"); - surface->cr.has_clip = FALSE; - } - return CAIRO_STATUS_SUCCESS; - } - - /* skip the trivial clip covering the surface extents */ - if (surface->width >=0 && surface->height >= 0 && - _cairo_path_fixed_is_box (path, &box)) - { - if (box.p1.x <= 0 && box.p1.y <= 0 && - box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && - box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) - { - return CAIRO_STATUS_SUCCESS; - } - } - - status = _emit_identity (surface, &matrix_updated); - if (unlikely (status)) - return status; - - status = _emit_fill_rule (surface, fill_rule); - if (unlikely (status)) - return status; - - if (! path->is_rectilinear) { - status = _emit_tolerance (surface, tolerance, matrix_updated); - if (unlikely (status)) - return status; - } - - if (! path->maybe_fill_region) { - status = _emit_antialias (surface, antialias); - if (unlikely (status)) - return status; - } - - status = _emit_path (surface, path); - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (ctx->stream, "clip+\n"); - surface->cr.has_clip = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -active (cairo_script_surface_t *surface) -{ - cairo_status_t status; - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - if (surface->active++ == 0) - to_context (surface)->active++; - - return CAIRO_STATUS_SUCCESS; -} - -static void -inactive (cairo_script_surface_t *surface) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_list_t sorted; - - assert (surface->active > 0); - if (--surface->active) - goto DONE; - - assert (ctx->active > 0); - if (--ctx->active) - goto DONE; - - cairo_list_init (&sorted); - while (! cairo_list_is_empty (&ctx->deferred)) { - struct deferred_finish *df; - cairo_list_t *operand; - int depth; - - df = cairo_list_first_entry (&ctx->deferred, - struct deferred_finish, - link); - - depth = 0; - cairo_list_foreach (operand, &ctx->operands) { - if (operand == &df->operand.link) - break; - depth++; - } - - df->operand.type = depth; - - if (cairo_list_is_empty (&sorted)) { - cairo_list_move (&df->link, &sorted); - } else { - struct deferred_finish *pos; - - cairo_list_foreach_entry (pos, struct deferred_finish, - &sorted, - link) - { - if (df->operand.type < pos->operand.type) - break; - } - cairo_list_move_tail (&df->link, &pos->link); - } - } - - while (! cairo_list_is_empty (&sorted)) { - struct deferred_finish *df; - cairo_list_t *operand; - int depth; - - df = cairo_list_first_entry (&sorted, - struct deferred_finish, - link); - - depth = 0; - cairo_list_foreach (operand, &ctx->operands) { - if (operand == &df->operand.link) - break; - depth++; - } - - if (depth == 0) { - _cairo_output_stream_printf (ctx->stream, - "pop\n"); - } else if (depth == 1) { - _cairo_output_stream_printf (ctx->stream, - "exch pop\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "%d -1 roll pop\n", - depth); - } - - cairo_list_del (&df->operand.link); - cairo_list_del (&df->link); - free (df); - } - -DONE: - cairo_device_release (surface->base.device); -} - -static cairo_int_status_t -_cairo_script_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = active (surface); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - goto BAIL; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - status = _emit_source (surface, op, source); - if (unlikely (status)) - goto BAIL; - - status = _emit_operator (surface, op); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (to_context (surface)->stream, - "paint\n"); - - inactive (surface); - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_paint (&surface->wrapper, - op, source, clip); - } - - return CAIRO_STATUS_SUCCESS; - -BAIL: - inactive (surface); - return status; -} - -static cairo_int_status_t -_cairo_script_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = active (surface); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - goto BAIL; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - status = _emit_source (surface, op, source); - if (unlikely (status)) - goto BAIL; - - status = _emit_operator (surface, op); - if (unlikely (status)) - goto BAIL; - - if (_cairo_pattern_equal (source, mask)) { - _cairo_output_stream_puts (to_context (surface)->stream, "/source get"); - } else { - status = _emit_pattern (surface, mask); - if (unlikely (status)) - goto BAIL; - } - - assert (surface->cr.current_operator == op); - - _cairo_output_stream_puts (to_context (surface)->stream, - " mask\n"); - - inactive (surface); - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_mask (&surface->wrapper, - op, source, mask, clip); - } - - return CAIRO_STATUS_SUCCESS; - -BAIL: - inactive (surface); - return status; -} - -static cairo_int_status_t -_cairo_script_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; - - status = active (surface); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - goto BAIL; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - status = _emit_identity (surface, &matrix_updated); - if (unlikely (status)) - goto BAIL; - - status = _emit_path (surface, path); - if (unlikely (status)) - goto BAIL; - - status = _emit_source (surface, op, source); - if (unlikely (status)) - goto BAIL; - - status = _emit_scaling_matrix (surface, ctm, &matrix_updated); - if (unlikely (status)) - goto BAIL; - - status = _emit_operator (surface, op); - if (unlikely (status)) - goto BAIL; - - if (_scaling_matrix_equal (&surface->cr.current_ctm, - &surface->cr.current_stroke_matrix)) - { - matrix_updated = FALSE; - } - else - { - matrix_updated = TRUE; - surface->cr.current_stroke_matrix = surface->cr.current_ctm; - } - - status = _emit_stroke_style (surface, style, matrix_updated); - if (unlikely (status)) - goto BAIL; - - if (! path->is_rectilinear) { - status = _emit_tolerance (surface, tolerance, matrix_updated); - if (unlikely (status)) - goto BAIL; - } - - status = _emit_antialias (surface, antialias); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n"); - - inactive (surface); - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_stroke (&surface->wrapper, - op, source, path, - style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - } - - return CAIRO_STATUS_SUCCESS; - -BAIL: - inactive (surface); - return status; -} - -static cairo_int_status_t -_cairo_script_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; - cairo_box_t box; - - status = active (surface); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - goto BAIL; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - status = _emit_identity (surface, &matrix_updated); - if (unlikely (status)) - goto BAIL; - - status = _emit_source (surface, op, source); - if (unlikely (status)) - goto BAIL; - - if (! _cairo_path_fixed_is_box (path, &box)) { - status = _emit_fill_rule (surface, fill_rule); - if (unlikely (status)) - goto BAIL; - } - - if (! path->is_rectilinear) { - status = _emit_tolerance (surface, tolerance, matrix_updated); - if (unlikely (status)) - goto BAIL; - } - - if (! path->maybe_fill_region) { - status = _emit_antialias (surface, antialias); - if (unlikely (status)) - goto BAIL; - } - - status = _emit_path (surface, path); - if (unlikely (status)) - goto BAIL; - - status = _emit_operator (surface, op); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n"); - - inactive (surface); - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_fill (&surface->wrapper, - op, source, path, - fill_rule, - tolerance, - antialias, - clip); - } - - return CAIRO_STATUS_SUCCESS; - -BAIL: - inactive (surface); - return status; -} - -static cairo_surface_t * -_cairo_script_surface_snapshot (void *abstract_surface) -{ - cairo_script_surface_t *surface = abstract_surface; - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) - return _cairo_surface_wrapper_snapshot (&surface->wrapper); - - return NULL; -} - -static cairo_bool_t -_cairo_script_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static const char * -_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) -{ - static const char *names[] = { - "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ - "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ - "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ - "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ - "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ - }; - return names[subpixel_order]; -} -static const char * -_hint_style_to_string (cairo_hint_style_t hint_style) -{ - static const char *names[] = { - "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ - "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ - "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ - "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ - "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ - }; - return names[hint_style]; -} -static const char * -_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) -{ - static const char *names[] = { - "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ - "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ - "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ - }; - return names[hint_metrics]; -} - -static cairo_status_t -_emit_font_options (cairo_script_surface_t *surface, - cairo_font_options_t *font_options) -{ - cairo_script_context_t *ctx = to_context (surface); - - if (cairo_font_options_equal (&surface->cr.current_font_options, - font_options)) - { - return CAIRO_STATUS_SUCCESS; - } - - _cairo_output_stream_printf (ctx->stream, "<<"); - - if (font_options->antialias != surface->cr.current_font_options.antialias) { - _cairo_output_stream_printf (ctx->stream, - " /antialias //%s", - _antialias_to_string (font_options->antialias)); - } - - if (font_options->subpixel_order != - surface->cr.current_font_options.subpixel_order) - { - _cairo_output_stream_printf (ctx->stream, - " /subpixel-order //%s", - _subpixel_order_to_string (font_options->subpixel_order)); - } - - if (font_options->hint_style != - surface->cr.current_font_options.hint_style) - { - _cairo_output_stream_printf (ctx->stream, - " /hint-style //%s", - _hint_style_to_string (font_options->hint_style)); - } - - if (font_options->hint_metrics != - surface->cr.current_font_options.hint_metrics) - { - _cairo_output_stream_printf (ctx->stream, - " /hint-metrics //%s", - _hint_metrics_to_string (font_options->hint_metrics)); - } - - _cairo_output_stream_printf (ctx->stream, - " >> set-font-options\n"); - - surface->cr.current_font_options = *font_options; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_script_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_status_t status; - cairo_device_t *device; - - status = cairo_device_acquire (device = &font_private->ctx->base); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_output_stream_printf (font_private->ctx->stream, - "/f%lu undef /sf%lu undef\n", - font_private->id, - font_private->id); - - _bitmap_release_id (&font_private->ctx->font_id, font_private->id); - cairo_list_del (&font_private->link); - free (font_private); - - cairo_device_release (device); - } - - scaled_font->surface_private = NULL; - } -} - -static cairo_status_t -_emit_type42_font (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_script_context_t *ctx = to_context (surface); - const cairo_scaled_font_backend_t *backend; - cairo_script_surface_font_private_t *font_private; - cairo_output_stream_t *base85_stream; - cairo_output_stream_t *zlib_stream; - cairo_status_t status, status2; - unsigned long size; - unsigned int load_flags; - uint32_t len; - uint8_t *buf; - - backend = scaled_font->backend; - if (backend->load_truetype_table == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - size = 0; - status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); - if (unlikely (status)) - return status; - - buf = malloc (size); - if (unlikely (buf == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); - if (unlikely (status)) { - free (buf); - return status; - } - -#if CAIRO_HAS_FT_FONT - load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); -#else - load_flags = 0; -#endif - _cairo_output_stream_printf (ctx->stream, - "<< " - "/type 42 " - "/index 0 " - "/flags %d " - "/source <|", - load_flags); - - base85_stream = _cairo_base85_stream_create (ctx->stream); - len = to_be32 (size); - _cairo_output_stream_write (base85_stream, &len, sizeof (len)); - - zlib_stream = _cairo_deflate_stream_create (base85_stream); - - _cairo_output_stream_write (zlib_stream, buf, size); - free (buf); - - status2 = _cairo_output_stream_destroy (zlib_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - font_private = scaled_font->surface_private; - _cairo_output_stream_printf (ctx->stream, - "~> >> font dup /f%lu exch def set-font-face", - font_private->id); - - return status; -} - -static cairo_status_t -_emit_scaled_font_init (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; - cairo_status_t status; - - font_private = malloc (sizeof (cairo_script_surface_font_private_t)); - if (unlikely (font_private == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->ctx = ctx; - font_private->parent = scaled_font; - font_private->subset_glyph_index = 0; - font_private->has_sfnt = TRUE; - - cairo_list_add (&font_private->link, &ctx->fonts); - - status = _bitmap_next_id (&ctx->font_id, - &font_private->id); - if (unlikely (status)) { - free (font_private); - return status; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &_cairo_script_surface_backend; - - status = _emit_context (surface); - if (unlikely (status)) - return status; - - status = _emit_type42_font (surface, scaled_font); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - font_private->has_sfnt = FALSE; - _cairo_output_stream_printf (ctx->stream, - "dict\n" - " /type 3 set\n" - " /metrics [%f %f %f %f %f] set\n" - " /glyphs array set\n" - " font dup /f%lu exch def set-font-face", - scaled_font->fs_extents.ascent, - scaled_font->fs_extents.descent, - scaled_font->fs_extents.height, - scaled_font->fs_extents.max_x_advance, - scaled_font->fs_extents.max_y_advance, - font_private->id); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_scaled_font (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_matrix_t matrix; - cairo_font_options_t options; - cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; - cairo_script_surface_font_private_t *font_private; - - cairo_scaled_font_get_ctm (scaled_font, &matrix); - status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); - if (unlikely (status)) - return status; - - if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) - return CAIRO_STATUS_SUCCESS; - - surface->cr.current_scaled_font = scaled_font; - - if (! (scaled_font->surface_backend == NULL || - scaled_font->surface_backend == &_cairo_script_surface_backend)) - { - _cairo_scaled_font_revoke_ownership (scaled_font); - } - - font_private = scaled_font->surface_private; - if (font_private == NULL) { - cairo_scaled_font_get_font_matrix (scaled_font, &matrix); - status = _emit_font_matrix (surface, &matrix); - if (unlikely (status)) - return status; - - cairo_scaled_font_get_font_options (scaled_font, &options); - status = _emit_font_options (surface, &options); - if (unlikely (status)) - return status; - - status = _emit_scaled_font_init (surface, scaled_font); - if (unlikely (status)) - return status; - - font_private = scaled_font->surface_private; - assert (font_private != NULL); - - assert (target_is_active (surface)); - _cairo_output_stream_printf (ctx->stream, - " /scaled-font get /sf%lu exch def\n", - font_private->id); - } else { - _cairo_output_stream_printf (ctx->stream, - "sf%lu set-scaled-font\n", - font_private->id); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_scaled_glyph_vector (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; - cairo_script_implicit_context_t old_cr; - cairo_status_t status; - unsigned long index; - - font_private = scaled_font->surface_private; - index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; - - _cairo_output_stream_printf (ctx->stream, - "%lu <<\n" - " /metrics [%f %f %f %f %f %f]\n" - " /render {\n", - index, - scaled_glyph->fs_metrics.x_bearing, - scaled_glyph->fs_metrics.y_bearing, - scaled_glyph->fs_metrics.width, - scaled_glyph->fs_metrics.height, - scaled_glyph->fs_metrics.x_advance, - scaled_glyph->fs_metrics.y_advance); - - if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { - _cairo_output_stream_printf (ctx->stream, - "[%f %f %f %f %f %f] transform\n", - scaled_font->scale_inverse.xx, - scaled_font->scale_inverse.yx, - scaled_font->scale_inverse.xy, - scaled_font->scale_inverse.yy, - scaled_font->scale_inverse.x0, - scaled_font->scale_inverse.y0); - } - - old_cr = surface->cr; - _cairo_script_implicit_context_init (&surface->cr); - status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, - &surface->base); - surface->cr = old_cr; - - _cairo_output_stream_puts (ctx->stream, "} >> set\n"); - - return status; -} - -static cairo_status_t -_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; - cairo_status_t status; - unsigned long index; - - font_private = scaled_font->surface_private; - index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; - - _cairo_output_stream_printf (ctx->stream, - "%lu <<\n" - " /metrics [%f %f %f %f %f %f]\n" - " /render {\n" - "%f %f translate\n", - index, - scaled_glyph->fs_metrics.x_bearing, - scaled_glyph->fs_metrics.y_bearing, - scaled_glyph->fs_metrics.width, - scaled_glyph->fs_metrics.height, - scaled_glyph->fs_metrics.x_advance, - scaled_glyph->fs_metrics.y_advance, - scaled_glyph->fs_metrics.x_bearing, - scaled_glyph->fs_metrics.y_bearing); - - status = _emit_image_surface (surface, scaled_glyph->surface); - if (unlikely (status)) - return status; - - _cairo_output_stream_puts (ctx->stream, "pattern "); - - if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { - _cairo_output_stream_printf (ctx->stream, - "\n [%f %f %f %f %f %f] set-matrix\n", - scaled_font->font_matrix.xx, - scaled_font->font_matrix.yx, - scaled_font->font_matrix.xy, - scaled_font->font_matrix.yy, - scaled_font->font_matrix.x0, - scaled_font->font_matrix.y0); - } - _cairo_output_stream_puts (ctx->stream, - "mask\n} >> set\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_script_surface_font_private_t *font_private; - - assert (scaled_font->surface_backend == &_cairo_script_surface_backend); - - font_private = scaled_font->surface_private; - - _cairo_output_stream_printf (to_context (surface)->stream, - "f%lu /glyphs get\n", - font_private->id); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_emit_scaled_glyphs (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - unsigned int num_glyphs) -{ - cairo_script_surface_font_private_t *font_private; - cairo_status_t status; - unsigned int n; - cairo_bool_t have_glyph_prologue = FALSE; - - if (num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - - font_private = scaled_font->surface_private; - if (font_private->has_sfnt) - return CAIRO_STATUS_SUCCESS; - - _cairo_scaled_font_freeze_cache (scaled_font); - for (n = 0; n < num_glyphs; n++) { - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[n].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - break; - - if (scaled_glyph->surface_private != NULL) - continue; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[n].index, - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, - &scaled_glyph); - if (_cairo_status_is_error (status)) - break; - - if (status == CAIRO_STATUS_SUCCESS) { - if (! have_glyph_prologue) { - status = _emit_scaled_glyph_prologue (surface, scaled_font); - if (unlikely (status)) - break; - - have_glyph_prologue = TRUE; - } - - status = _emit_scaled_glyph_vector (surface, - scaled_font, - scaled_glyph); - if (unlikely (status)) - break; - - continue; - } - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[n].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (_cairo_status_is_error (status)) - break; - - if (status == CAIRO_STATUS_SUCCESS) { - if (! have_glyph_prologue) { - status = _emit_scaled_glyph_prologue (surface, scaled_font); - if (unlikely (status)) - break; - - have_glyph_prologue = TRUE; - } - - status = _emit_scaled_glyph_bitmap (surface, - scaled_font, - scaled_glyph); - if (unlikely (status)) - break; - - continue; - } - } - _cairo_scaled_font_thaw_cache (scaled_font); - - if (have_glyph_prologue) { - _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n"); - } - - return status; -} - -static void -to_octal (int value, char *buf, size_t size) -{ - do { - buf[--size] = '0' + (value & 7); - value >>= 3; - } while (size); -} - -static void -_emit_string_literal (cairo_script_surface_t *surface, - const char *utf8, int len) -{ - cairo_script_context_t *ctx = to_context (surface); - char c; - const char *end; - - _cairo_output_stream_puts (ctx->stream, "("); - - if (utf8 == NULL) { - end = utf8; - } else { - if (len < 0) - len = strlen (utf8); - end = utf8 + len; - } - - while (utf8 < end) { - switch ((c = *utf8++)) { - case '\n': - c = 'n'; - goto ESCAPED_CHAR; - case '\r': - c = 'r'; - goto ESCAPED_CHAR; - case '\t': - c = 't'; - goto ESCAPED_CHAR; - case '\b': - c = 'b'; - goto ESCAPED_CHAR; - case '\f': - c = 'f'; - goto ESCAPED_CHAR; - case '\\': - case '(': - case ')': -ESCAPED_CHAR: - _cairo_output_stream_printf (ctx->stream, "\\%c", c); - break; - default: - if (isprint (c) || isspace (c)) { - _cairo_output_stream_printf (ctx->stream, "%c", c); - } else { - char buf[4] = { '\\' }; - - to_octal (c, buf+1, 3); - _cairo_output_stream_write (ctx->stream, buf, 4); - } - break; - } - } - _cairo_output_stream_puts (ctx->stream, ")"); -} - -static cairo_int_status_t -_cairo_script_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t backward, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_script_surface_t *surface = abstract_surface; - cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; - cairo_scaled_glyph_t *scaled_glyph; - cairo_matrix_t matrix; - cairo_status_t status; - double x, y, ix, iy; - int n; - cairo_output_stream_t *base85_stream = NULL; - - status = active (surface); - if (unlikely (status)) - return status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - goto BAIL; - - status = _emit_context (surface); - if (unlikely (status)) - goto BAIL; - - status = _emit_source (surface, op, source); - if (unlikely (status)) - goto BAIL; - - status = _emit_scaled_font (surface, scaled_font); - if (unlikely (status)) - goto BAIL; - - status = _emit_operator (surface, op); - if (unlikely (status)) - goto BAIL; - - status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); - if (unlikely (status)) - goto BAIL; - - /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ - /* [cx cy [glyphs]] show_glyphs */ - - if (utf8 != NULL && clusters != NULL) { - _emit_string_literal (surface, utf8, utf8_len); - _cairo_output_stream_puts (ctx->stream, " "); - } - - matrix = surface->cr.current_ctm; - status = cairo_matrix_invert (&matrix); - assert (status == CAIRO_STATUS_SUCCESS); - - ix = x = glyphs[0].x; - iy = y = glyphs[0].y; - cairo_matrix_transform_point (&matrix, &ix, &iy); - ix -= scaled_font->font_matrix.x0; - iy -= scaled_font->font_matrix.y0; - - _cairo_scaled_font_freeze_cache (scaled_font); - font_private = scaled_font->surface_private; - - _cairo_output_stream_printf (ctx->stream, - "[%f %f ", - ix, iy); - - for (n = 0; n < num_glyphs; n++) { - if (font_private->has_sfnt) { - if (glyphs[n].index > 256) - break; - } else { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[n].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) { - _cairo_scaled_font_thaw_cache (scaled_font); - goto BAIL; - } - - if ((long unsigned) scaled_glyph->surface_private > 256) - break; - } - } - - if (n == num_glyphs) { - _cairo_output_stream_puts (ctx->stream, "<~"); - base85_stream = _cairo_base85_stream_create (ctx->stream); - } else - _cairo_output_stream_puts (ctx->stream, "["); - - for (n = 0; n < num_glyphs; n++) { - double dx, dy; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[n].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - goto BAIL; - - if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { - if (fabs (glyphs[n].y - y) < 1e-5) { - if (base85_stream != NULL) { - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) { - base85_stream = NULL; - break; - } - - _cairo_output_stream_printf (ctx->stream, - "~> %f <~", glyphs[n].x - x); - base85_stream = _cairo_base85_stream_create (ctx->stream); - } else { - _cairo_output_stream_printf (ctx->stream, - " ] %f [ ", glyphs[n].x - x); - } - - x = glyphs[n].x; - } else { - ix = x = glyphs[n].x; - iy = y = glyphs[n].y; - cairo_matrix_transform_point (&matrix, &ix, &iy); - ix -= scaled_font->font_matrix.x0; - iy -= scaled_font->font_matrix.y0; - if (base85_stream != NULL) { - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) { - base85_stream = NULL; - break; - } - - _cairo_output_stream_printf (ctx->stream, - "~> %f %f <~", - ix, iy); - base85_stream = _cairo_base85_stream_create (ctx->stream); - } else { - _cairo_output_stream_printf (ctx->stream, - " ] %f %f [ ", - ix, iy); - } - } - } - if (base85_stream != NULL) { - uint8_t c; - - if (font_private->has_sfnt) - c = glyphs[n].index; - else - c = (uint8_t) (long unsigned) scaled_glyph->surface_private; - - _cairo_output_stream_write (base85_stream, &c, 1); - } else { - if (font_private->has_sfnt) - _cairo_output_stream_printf (ctx->stream, " %lu", - glyphs[n].index); - else - _cairo_output_stream_printf (ctx->stream, " %lu", - (long unsigned) scaled_glyph->surface_private); - } - - dx = scaled_glyph->metrics.x_advance; - dy = scaled_glyph->metrics.y_advance; - cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); - x += dx; - y += dy; - } - _cairo_scaled_font_thaw_cache (scaled_font); - - if (base85_stream != NULL) { - cairo_status_t status2; - - status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_output_stream_printf (ctx->stream, "~>"); - } else { - _cairo_output_stream_puts (ctx->stream, " ]"); - } - if (unlikely (status)) - return status; - - if (utf8 != NULL && clusters != NULL) { - for (n = 0; n < num_clusters; n++) { - if (clusters[n].num_bytes > UCHAR_MAX || - clusters[n].num_glyphs > UCHAR_MAX) - { - break; - } - } - - if (n < num_clusters) { - _cairo_output_stream_puts (ctx->stream, "] [ "); - for (n = 0; n < num_clusters; n++) { - _cairo_output_stream_printf (ctx->stream, - "%d %d ", - clusters[n].num_bytes, - clusters[n].num_glyphs); - } - _cairo_output_stream_puts (ctx->stream, "]"); - } - else - { - _cairo_output_stream_puts (ctx->stream, "] <~"); - base85_stream = _cairo_base85_stream_create (ctx->stream); - for (n = 0; n < num_clusters; n++) { - uint8_t c[2]; - c[0] = clusters[n].num_bytes; - c[1] = clusters[n].num_glyphs; - _cairo_output_stream_write (base85_stream, c, 2); - } - status = _cairo_output_stream_destroy (base85_stream); - if (unlikely (status)) - goto BAIL; - - _cairo_output_stream_puts (ctx->stream, "~>"); - } - - _cairo_output_stream_printf (ctx->stream, - " //%s show-text-glyphs\n", - _direction_to_string (backward)); - } else { - _cairo_output_stream_puts (ctx->stream, - "] show-glyphs\n"); - } - - inactive (surface); - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ - return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, - op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - backward, - scaled_font, - clip); - } - - return CAIRO_STATUS_SUCCESS; - -BAIL: - inactive (surface); - return status; -} - -static cairo_bool_t -_cairo_script_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_script_surface_t *surface = abstract_surface; - - if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { - return _cairo_surface_wrapper_get_extents (&surface->wrapper, - rectangle); - } - - if (surface->width < 0 || surface->height < 0) - return FALSE; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static const cairo_surface_backend_t -_cairo_script_surface_backend = { - CAIRO_SURFACE_TYPE_SCRIPT, - _cairo_script_surface_create_similar, - _cairo_script_surface_finish, - _cairo_script_surface_acquire_source_image, - _cairo_script_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - _cairo_script_surface_copy_page, - _cairo_script_surface_show_page, - _cairo_script_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - _cairo_script_surface_scaled_font_fini, - NULL, /* scaled_glyph_fini */ - - /* The 5 high level operations */ - _cairo_script_surface_paint, - _cairo_script_surface_mask, - _cairo_script_surface_stroke, - _cairo_script_surface_fill, - NULL, - - _cairo_script_surface_snapshot, - - NULL, /* is_similar */ - /* XXX need fill-stroke for passthrough */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - /* The alternate high-level text operation */ - _cairo_script_surface_has_show_text_glyphs, - _cairo_script_surface_show_text_glyphs -}; - -static void -_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) -{ - cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; - cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; - cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; - cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; - _cairo_stroke_style_init (&cr->current_style); - _cairo_pattern_init_solid (&cr->current_source.solid, - CAIRO_COLOR_BLACK); - _cairo_path_fixed_init (&cr->current_path); - cairo_matrix_init_identity (&cr->current_ctm); - cairo_matrix_init_identity (&cr->current_stroke_matrix); - cairo_matrix_init_identity (&cr->current_font_matrix); - _cairo_font_options_init_default (&cr->current_font_options); - cr->current_scaled_font = NULL; - cr->has_clip = FALSE; -} - -static void -_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) -{ - if (cr->current_style.dash != NULL) { - free (cr->current_style.dash); - cr->current_style.dash = NULL; - } - _cairo_pattern_fini (&cr->current_source.base); - _cairo_path_fixed_fini (&cr->current_path); - - _cairo_script_implicit_context_init (cr); -} - -static cairo_script_surface_t * -_cairo_script_surface_create_internal (cairo_script_context_t *ctx, - cairo_content_t content, - double width, - double height, - cairo_surface_t *passthrough) -{ - cairo_script_surface_t *surface; - - if (unlikely (ctx == NULL)) - return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - - surface = malloc (sizeof (cairo_script_surface_t)); - if (unlikely (surface == NULL)) - return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_script_surface_backend, - &ctx->base, - content); - - _cairo_surface_wrapper_init (&surface->wrapper, passthrough); - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_script_surface_clipper_intersect_clip_path); - - surface->width = width; - surface->height = height; - - surface->emitted = FALSE; - surface->defined = FALSE; - surface->active = FALSE; - surface->operand.type = SURFACE; - cairo_list_init (&surface->operand.link); - - _cairo_script_implicit_context_init (&surface->cr); - - return surface; -} - -static const cairo_device_backend_t _cairo_script_device_backend = { - CAIRO_DEVICE_TYPE_SCRIPT, - - NULL, NULL, /* lock, unlock */ - - _device_flush, /* flush */ - NULL, /* finish */ - _device_destroy -}; - -static cairo_device_t * -_cairo_script_context_create_internal (cairo_output_stream_t *stream) -{ - cairo_script_context_t *ctx; - - ctx = malloc (sizeof (cairo_script_context_t)); - if (unlikely (ctx == NULL)) - return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - memset (ctx, 0, sizeof (cairo_script_context_t)); - - _cairo_device_init (&ctx->base, &_cairo_script_device_backend); - - cairo_list_init (&ctx->operands); - cairo_list_init (&ctx->deferred); - ctx->stream = stream; - ctx->mode = CAIRO_SCRIPT_MODE_ASCII; - - cairo_list_init (&ctx->fonts); - cairo_list_init (&ctx->defines); - - _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); - - return &ctx->base; -} - -cairo_device_t * -cairo_script_create (const char *filename) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create_for_filename (filename); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_script_context_create_internal (stream); -} - -cairo_device_t * -cairo_script_create_for_stream (cairo_write_func_t write_func, - void *closure) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create (write_func, NULL, closure); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_script_context_create_internal (stream); -} - -void -cairo_script_write_comment (cairo_device_t *device, - const char *comment, - int len) -{ - cairo_script_context_t *context = (cairo_script_context_t *) device; - - if (len < 0) - len = strlen (comment); - - _cairo_output_stream_puts (context->stream, "% "); - _cairo_output_stream_write (context->stream, comment, len); - _cairo_output_stream_puts (context->stream, "\n"); -} - -void -cairo_script_set_mode (cairo_device_t *device, - cairo_script_mode_t mode) -{ - cairo_script_context_t *context = (cairo_script_context_t *) device; - - context->mode = mode; -} - -cairo_script_mode_t -cairo_script_get_mode (cairo_device_t *device) -{ - cairo_script_context_t *context = (cairo_script_context_t *) device; - - return context->mode; -} - -cairo_surface_t * -cairo_script_surface_create (cairo_device_t *device, - cairo_content_t content, - double width, - double height) -{ - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) - return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, - content, - width, height, - NULL)->base; -} - -cairo_surface_t * -cairo_script_surface_create_for_target (cairo_device_t *device, - cairo_surface_t *target) -{ - cairo_rectangle_int_t extents; - - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) - return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - if (unlikely (target->status)) - return _cairo_surface_create_in_error (target->status); - - if (! _cairo_surface_get_extents (target, &extents)) - extents.width = extents.height = -1; - - return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, - target->content, - extents.width, - extents.height, - target)->base; -} - -cairo_status_t -cairo_script_from_recording_surface (cairo_device_t *device, - cairo_surface_t *recording_surface) -{ - cairo_box_t bbox; - cairo_rectangle_int_t extents; - cairo_surface_t *surface; - cairo_status_t status; - - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) - return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (device->status)) - return _cairo_error (device->status); - - if (unlikely (recording_surface->status)) - return recording_surface->status; - - if (unlikely (! _cairo_surface_is_recording (recording_surface))) - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - - status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - - surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, - recording_surface->content, - extents.width, - extents.height, - NULL)->base; - if (unlikely (surface->status)) - return surface->status; - - cairo_surface_set_device_offset (surface, -extents.x, -extents.y); - status = _cairo_recording_surface_replay (recording_surface, surface); - cairo_surface_destroy (surface); - - return status; -} diff --git a/libs/cairo/cairo/src/cairo-script.h b/libs/cairo/cairo/src/cairo-script.h deleted file mode 100644 index a9d1540cf..000000000 --- a/libs/cairo/cairo/src/cairo-script.h +++ /dev/null @@ -1,58 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SCRIPT_H -#define CAIRO_SCRIPT_H - -#include "cairo.h" - -#if CAIRO_HAS_SCRIPT_SURFACE - -CAIRO_BEGIN_DECLS - -typedef enum { - CAIRO_SCRIPT_MODE_BINARY, - CAIRO_SCRIPT_MODE_ASCII -} cairo_script_mode_t; - -cairo_public cairo_device_t * -cairo_script_create (const char *filename); - -cairo_public cairo_device_t * -cairo_script_create_for_stream (cairo_write_func_t write_func, - void *closure); - -cairo_public void -cairo_script_write_comment (cairo_device_t *script, - const char *comment, - int len); - -cairo_public void -cairo_script_set_mode (cairo_device_t *script, - cairo_script_mode_t mode); - -cairo_public cairo_script_mode_t -cairo_script_get_mode (cairo_device_t *script); - -cairo_public cairo_surface_t * -cairo_script_surface_create (cairo_device_t *script, - cairo_content_t content, - double width, - double height); - -cairo_public cairo_surface_t * -cairo_script_surface_create_for_target (cairo_device_t *script, - cairo_surface_t *target); - -cairo_public cairo_status_t -cairo_script_from_recording_surface (cairo_device_t *script, - cairo_surface_t *recording_surface); - -CAIRO_END_DECLS - -#else /*CAIRO_HAS_SCRIPT_SURFACE*/ -# error Cairo was not compiled with support for the CairoScript backend -#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ - -#endif /*CAIRO_SCRIPT_H*/ diff --git a/libs/cairo/cairo/src/cairo-skia.h b/libs/cairo/cairo/src/cairo-skia.h deleted file mode 100644 index 89bd2713e..000000000 --- a/libs/cairo/cairo/src/cairo-skia.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SKIA_H -#define CAIRO_SKIA_H - -#include "cairo.h" - -#if CAIRO_HAS_SKIA_SURFACE - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_skia_surface_create (cairo_format_t format, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_skia_surface_create_for_data (unsigned char *data, - cairo_format_t format, - int width, - int height, - int stride); - -cairo_public unsigned char * -cairo_skia_surface_get_data (cairo_surface_t *surface); - -cairo_public cairo_format_t -cairo_skia_surface_get_format (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_height (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_stride (cairo_surface_t *surface); - -cairo_public cairo_surface_t * -cairo_skia_surface_get_image (cairo_surface_t *surface); - -CAIRO_END_DECLS - -#else - -# error Cairo was not compiled with support for the Skia backend - -#endif - -#endif diff --git a/libs/cairo/cairo/src/cairo-slope-private.h b/libs/cairo/cairo/src/cairo-slope-private.h deleted file mode 100644 index bccc955d3..000000000 --- a/libs/cairo/cairo/src/cairo-slope-private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-slope.c b/libs/cairo/cairo/src/cairo-slope.c deleted file mode 100644 index fe93d66f6..000000000 --- a/libs/cairo/cairo/src/cairo-slope.c +++ /dev/null @@ -1,67 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-spans-private.h b/libs/cairo/cairo/src/cairo-spans-private.h deleted file mode 100644 index aecc9b976..000000000 --- a/libs/cairo/cairo/src/cairo-spans-private.h +++ /dev/null @@ -1,188 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright (c) 2008 M Joonas Pihlaja - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef CAIRO_SPANS_PRIVATE_H -#define CAIRO_SPANS_PRIVATE_H -#include "cairo-types-private.h" -#include "cairo-compiler-private.h" - -/* Number of bits of precision used for alpha. */ -#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8 -#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1) - -/* A structure representing an open-ended horizontal span of constant - * pixel coverage. */ -typedef struct _cairo_half_open_span { - /* The inclusive x-coordinate of the start of the span. */ - int x; - - /* The pixel coverage for the pixels to the right. */ - int coverage; -} cairo_half_open_span_t; - -/* Span renderer interface. Instances of renderers are provided by - * surfaces if they want to composite spans instead of trapezoids. */ -typedef struct _cairo_span_renderer cairo_span_renderer_t; -struct _cairo_span_renderer { - /* Private status variable. */ - cairo_status_t status; - - /* Called to destroy the renderer. */ - cairo_destroy_func_t destroy; - - /* Render the spans on row y of the destination by whatever compositing - * method is required. */ - cairo_warn cairo_status_t - (*render_rows) (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *coverages, - unsigned num_coverages); - - /* Called after all rows have been rendered to perform whatever - * final rendering step is required. This function is called just - * once before the renderer is destroyed. */ - cairo_status_t (*finish) (void *abstract_renderer); -}; - -/* Scan converter interface. */ -typedef struct _cairo_scan_converter cairo_scan_converter_t; -struct _cairo_scan_converter { - /* Destroy this scan converter. */ - cairo_destroy_func_t destroy; - - /* Add a single edge to the converter. */ - cairo_status_t (*add_edge) (void *abstract_converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir); - - /* Add a polygon (set of edges) to the converter. */ - cairo_status_t (*add_polygon) (void *abstract_converter, - const cairo_polygon_t *polygon); - - /* Generates coverage spans for rows for the added edges and calls - * the renderer function for each row. After generating spans the - * only valid thing to do with the converter is to destroy it. */ - cairo_status_t (*generate) (void *abstract_converter, - cairo_span_renderer_t *renderer); - - /* Private status. Read with _cairo_scan_converter_status(). */ - cairo_status_t status; -}; - -/* Scan converter constructors. */ - -cairo_private cairo_scan_converter_t * -_cairo_tor_scan_converter_create (int xmin, - int ymin, - int xmax, - int ymax, - cairo_fill_rule_t fill_rule); - -typedef struct _cairo_rectangular_scan_converter { - cairo_scan_converter_t base; - - int xmin, xmax; - int ymin, ymax; - - struct _cairo_rectangular_scan_converter_chunk { - struct _cairo_rectangular_scan_converter_chunk *next; - void *base; - int count; - int size; - } chunks, *tail; - char buf[CAIRO_STACK_BUFFER_SIZE]; - int num_rectangles; -} cairo_rectangular_scan_converter_t; - -cairo_private void -_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, - const cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, - const cairo_box_t *box, - int dir); - -typedef struct _cairo_botor_scan_converter { - cairo_scan_converter_t base; - - cairo_box_t extents; - cairo_fill_rule_t fill_rule; - - int xmin, xmax; - - struct _cairo_botor_scan_converter_chunk { - struct _cairo_botor_scan_converter_chunk *next; - void *base; - int count; - int size; - } chunks, *tail; - char buf[CAIRO_STACK_BUFFER_SIZE]; - int num_edges; -} cairo_botor_scan_converter_t; - -cairo_private void -_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, - const cairo_box_t *extents, - cairo_fill_rule_t fill_rule); - -/* cairo-spans.c: */ - -cairo_private cairo_scan_converter_t * -_cairo_scan_converter_create_in_error (cairo_status_t error); - -cairo_private cairo_status_t -_cairo_scan_converter_status (void *abstract_converter); - -cairo_private cairo_status_t -_cairo_scan_converter_set_error (void *abstract_converter, - cairo_status_t error); - -cairo_private cairo_span_renderer_t * -_cairo_span_renderer_create_in_error (cairo_status_t error); - -cairo_private cairo_status_t -_cairo_span_renderer_status (void *abstract_renderer); - -/* Set the renderer into an error state. This sets all the method - * pointers except ->destroy() of the renderer to no-op - * implementations that just return the error status. */ -cairo_private cairo_status_t -_cairo_span_renderer_set_error (void *abstract_renderer, - cairo_status_t error); - -cairo_private cairo_status_t -_cairo_surface_composite_polygon (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_polygon_t *polygon, - cairo_region_t *clip_region); - -#endif /* CAIRO_SPANS_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-spans.c b/libs/cairo/cairo/src/cairo-spans.c deleted file mode 100644 index f556d6b2a..000000000 --- a/libs/cairo/cairo/src/cairo-spans.c +++ /dev/null @@ -1,322 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright (c) 2008 M Joonas Pihlaja - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -#include "cairoint.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-fixed-private.h" - -static cairo_scan_converter_t * -_create_scan_converter (cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) -{ - if (antialias == CAIRO_ANTIALIAS_NONE) { - ASSERT_NOT_REACHED; - return NULL; - } - - return _cairo_tor_scan_converter_create (rects->bounded.x, - rects->bounded.y, - rects->bounded.x + rects->bounded.width, - rects->bounded.y + rects->bounded.height, - fill_rule); -} - -/* XXX Add me to the compositor interface. Ok, first create the compositor - * interface, and then add this with associated fallback! - */ -cairo_status_t -_cairo_surface_composite_polygon (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_polygon_t *polygon, - cairo_region_t *clip_region) -{ - cairo_span_renderer_t *renderer; - cairo_scan_converter_t *converter; - cairo_status_t status; - - converter = _create_scan_converter (fill_rule, antialias, rects); - status = converter->add_polygon (converter, polygon); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - renderer = _cairo_surface_create_span_renderer (op, pattern, surface, - antialias, rects, - clip_region); - status = converter->generate (converter, renderer); - if (unlikely (status)) - goto CLEANUP_RENDERER; - - status = renderer->finish (renderer); - - CLEANUP_RENDERER: - renderer->destroy (renderer); - CLEANUP_CONVERTER: - converter->destroy (converter); - return status; -} - -static void -_cairo_nil_destroy (void *abstract) -{ - (void) abstract; -} - -static cairo_status_t -_cairo_nil_scan_converter_add_polygon (void *abstract_converter, - const cairo_polygon_t *polygon) -{ - (void) abstract_converter; - (void) polygon; - return _cairo_scan_converter_status (abstract_converter); -} - -static cairo_status_t -_cairo_nil_scan_converter_add_edge (void *abstract_converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - (void) abstract_converter; - (void) p1; - (void) p2; - (void) top; - (void) bottom; - (void) dir; - return _cairo_scan_converter_status (abstract_converter); -} - -static cairo_status_t -_cairo_nil_scan_converter_generate (void *abstract_converter, - cairo_span_renderer_t *renderer) -{ - (void) abstract_converter; - (void) renderer; - return _cairo_scan_converter_status (abstract_converter); -} - -cairo_status_t -_cairo_scan_converter_status (void *abstract_converter) -{ - cairo_scan_converter_t *converter = abstract_converter; - return converter->status; -} - -cairo_status_t -_cairo_scan_converter_set_error (void *abstract_converter, - cairo_status_t error) -{ - cairo_scan_converter_t *converter = abstract_converter; - if (error == CAIRO_STATUS_SUCCESS) - ASSERT_NOT_REACHED; - if (converter->status == CAIRO_STATUS_SUCCESS) { - converter->add_polygon = _cairo_nil_scan_converter_add_polygon; - converter->add_edge = _cairo_nil_scan_converter_add_edge; - converter->generate = _cairo_nil_scan_converter_generate; - converter->status = error; - } - return converter->status; -} - -static void -_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter, - cairo_status_t status) -{ - converter->destroy = _cairo_nil_destroy; - converter->status = CAIRO_STATUS_SUCCESS; - status = _cairo_scan_converter_set_error (converter, status); -} - -cairo_scan_converter_t * -_cairo_scan_converter_create_in_error (cairo_status_t status) -{ -#define RETURN_NIL {\ - static cairo_scan_converter_t nil;\ - _cairo_nil_scan_converter_init (&nil, status);\ - return &nil;\ - } - switch (status) { - case CAIRO_STATUS_SUCCESS: - case CAIRO_STATUS_LAST_STATUS: - ASSERT_NOT_REACHED; - break; - case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; - case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; - case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; - case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; - case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; - case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; - case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; - case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; - case CAIRO_STATUS_READ_ERROR: RETURN_NIL; - case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; - case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; - case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; - case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; - case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; - case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; - case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; - case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; - case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; - case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; - case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; - case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; - case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; - case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; - case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; - case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; - case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; - default: - break; - } - status = CAIRO_STATUS_NO_MEMORY; - RETURN_NIL; -#undef RETURN_NIL -} - -static cairo_status_t -_cairo_nil_span_renderer_render_rows ( - void *abstract_renderer, - int y, - int height, - const cairo_half_open_span_t *coverages, - unsigned num_coverages) -{ - (void) y; - (void) height; - (void) coverages; - (void) num_coverages; - return _cairo_span_renderer_status (abstract_renderer); -} - -static cairo_status_t -_cairo_nil_span_renderer_finish (void *abstract_renderer) -{ - return _cairo_span_renderer_status (abstract_renderer); -} - -cairo_status_t -_cairo_span_renderer_status (void *abstract_renderer) -{ - cairo_span_renderer_t *renderer = abstract_renderer; - return renderer->status; -} - -cairo_status_t -_cairo_span_renderer_set_error ( - void *abstract_renderer, - cairo_status_t error) -{ - cairo_span_renderer_t *renderer = abstract_renderer; - if (error == CAIRO_STATUS_SUCCESS) { - ASSERT_NOT_REACHED; - } - if (renderer->status == CAIRO_STATUS_SUCCESS) { - renderer->render_rows = _cairo_nil_span_renderer_render_rows; - renderer->finish = _cairo_nil_span_renderer_finish; - renderer->status = error; - } - return renderer->status; -} - -static void -_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer, - cairo_status_t status) -{ - renderer->destroy = _cairo_nil_destroy; - renderer->status = CAIRO_STATUS_SUCCESS; - status = _cairo_span_renderer_set_error (renderer, status); -} - -cairo_span_renderer_t * -_cairo_span_renderer_create_in_error (cairo_status_t status) -{ -#define RETURN_NIL {\ - static cairo_span_renderer_t nil;\ - _cairo_nil_span_renderer_init (&nil, status);\ - return &nil;\ - } - switch (status) { - case CAIRO_STATUS_SUCCESS: - case CAIRO_STATUS_LAST_STATUS: - ASSERT_NOT_REACHED; - break; - case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; - case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; - case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; - case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; - case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; - case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; - case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; - case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; - case CAIRO_STATUS_READ_ERROR: RETURN_NIL; - case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; - case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; - case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; - case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; - case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; - case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; - case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; - case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; - case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; - case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; - case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; - case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; - case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; - case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; - case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; - case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; - case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; - case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; - default: - break; - } - status = CAIRO_STATUS_NO_MEMORY; - RETURN_NIL; -#undef RETURN_NIL -} diff --git a/libs/cairo/cairo/src/cairo-spline.c b/libs/cairo/cairo/src/cairo-spline.c deleted file mode 100644 index 2fc99dfcb..000000000 --- a/libs/cairo/cairo/src/cairo-spline.c +++ /dev/null @@ -1,339 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-slope-private.h" - -cairo_bool_t -_cairo_spline_init (cairo_spline_t *spline, - cairo_spline_add_point_func_t add_point_func, - void *closure, - const cairo_point_t *a, const cairo_point_t *b, - const cairo_point_t *c, const cairo_point_t *d) -{ - spline->add_point_func = add_point_func; - spline->closure = closure; - - spline->knots.a = *a; - spline->knots.b = *b; - spline->knots.c = *c; - spline->knots.d = *d; - - if (a->x != b->x || a->y != b->y) - _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); - else if (a->x != c->x || a->y != c->y) - _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); - else if (a->x != d->x || a->y != d->y) - _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); - else - return FALSE; - - if (c->x != d->x || c->y != d->y) - _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); - else if (b->x != d->x || b->y != d->y) - _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); - else - _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d); - - return TRUE; -} - -static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point) -{ - cairo_point_t *prev; - - prev = &spline->last_point; - if (prev->x == point->x && prev->y == point->y) - return CAIRO_STATUS_SUCCESS; - - spline->last_point = *point; - return spline->add_point_func (spline->closure, point); -} - -static void -_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result) -{ - result->x = a->x + ((b->x - a->x) >> 1); - result->y = a->y + ((b->y - a->y) >> 1); -} - -static void -_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2) -{ - cairo_point_t ab, bc, cd; - cairo_point_t abbc, bccd; - cairo_point_t final; - - _lerp_half (&s1->a, &s1->b, &ab); - _lerp_half (&s1->b, &s1->c, &bc); - _lerp_half (&s1->c, &s1->d, &cd); - _lerp_half (&ab, &bc, &abbc); - _lerp_half (&bc, &cd, &bccd); - _lerp_half (&abbc, &bccd, &final); - - s2->a = final; - s2->b = bccd; - s2->c = cd; - s2->d = s1->d; - - s1->b = ab; - s1->c = abbc; - s1->d = final; -} - -/* Return an upper bound on the error (squared) that could result from - * approximating a spline as a line segment connecting the two endpoints. */ -static double -_cairo_spline_error_squared (const cairo_spline_knots_t *knots) -{ - double bdx, bdy, berr; - double cdx, cdy, cerr; - - /* We are going to compute the distance (squared) between each of the the b - * and c control points and the segment a-b. The maximum of these two - * distances will be our approximation error. */ - - bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); - bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); - - cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x); - cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); - - if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { - /* Intersection point (px): - * px = p1 + u(p2 - p1) - * (p - px) ∙ (p2 - p1) = 0 - * Thus: - * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; - */ - - double dx, dy, u, v; - - dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); - dy = _cairo_fixed_to_double (knots->d.y - knots->a.y); - v = dx * dx + dy * dy; - - u = bdx * dx + bdy * dy; - if (u <= 0) { - /* bdx -= 0; - * bdy -= 0; - */ - } else if (u >= v) { - bdx -= dx; - bdy -= dy; - } else { - bdx -= u/v * dx; - bdy -= u/v * dy; - } - - u = cdx * dx + cdy * dy; - if (u <= 0) { - /* cdx -= 0; - * cdy -= 0; - */ - } else if (u >= v) { - cdx -= dx; - cdy -= dy; - } else { - cdx -= u/v * dx; - cdy -= u/v * dy; - } - } - - berr = bdx * bdx + bdy * bdy; - cerr = cdx * cdx + cdy * cdy; - if (berr > cerr) - return berr; - else - return cerr; -} - -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result) -{ - cairo_spline_knots_t s2; - cairo_status_t status; - - if (_cairo_spline_error_squared (s1) < tolerance_squared) { - return _cairo_spline_add_point (result, &s1->a); - } - - _de_casteljau (s1, &s2); - - status = _cairo_spline_decompose_into (s1, tolerance_squared, result); - if (unlikely (status)) { - return status; - } - - status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); - return status; -} - -cairo_status_t -_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) -{ - cairo_spline_knots_t s1; - cairo_status_t status; - - s1 = spline->knots; - spline->last_point = s1.a; - status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); - if (unlikely (status)) - return status; - - return _cairo_spline_add_point (spline, &spline->knots.d); -} - -/* Note: this function is only good for computing bounds in device space. */ -cairo_status_t -_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, - void *closure, - const cairo_point_t *p0, const cairo_point_t *p1, - const cairo_point_t *p2, const cairo_point_t *p3) -{ - double x0, x1, x2, x3; - double y0, y1, y2, y3; - double a, b, c; - double t[4]; - int t_num = 0, i; - cairo_status_t status; - - x0 = _cairo_fixed_to_double (p0->x); - y0 = _cairo_fixed_to_double (p0->y); - x1 = _cairo_fixed_to_double (p1->x); - y1 = _cairo_fixed_to_double (p1->y); - x2 = _cairo_fixed_to_double (p2->x); - y2 = _cairo_fixed_to_double (p2->y); - x3 = _cairo_fixed_to_double (p3->x); - y3 = _cairo_fixed_to_double (p3->y); - - /* The spline can be written as a polynomial of the four points: - * - * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 - * - * for 0≤t≤1. Now, the X and Y components of the spline follow the - * same polynomial but with x and y replaced for p. To find the - * bounds of the spline, we just need to find the X and Y bounds. - * To find the bound, we take the derivative and equal it to zero, - * and solve to find the t's that give the extreme points. - * - * Here is the derivative of the curve, sorted on t: - * - * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 - * - * Let: - * - * a = -p0+3p1-3p2+p3 - * b = p0-2p1+p2 - * c = -p0+p1 - * - * Gives: - * - * a.t² + 2b.t + c = 0 - * - * With: - * - * delta = b*b - a*c - * - * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if - * delta is positive, and at -b/a if delta is zero. - */ - -#define ADD(t0) \ - { \ - double _t0 = (t0); \ - if (0 < _t0 && _t0 < 1) \ - t[t_num++] = _t0; \ - } - -#define FIND_EXTREMES(a,b,c) \ - { \ - if (a == 0) { \ - if (b != 0) \ - ADD (-c / (2*b)); \ - } else { \ - double b2 = b * b; \ - double delta = b2 - a * c; \ - if (delta > 0) { \ - cairo_bool_t feasible; \ - double _2ab = 2 * a * b; \ - /* We are only interested in solutions t that satisfy 0= 0) \ - feasible = delta > b2 && delta < a*a + b2 + _2ab; \ - else if (-b / a >= 1) \ - feasible = delta < b2 && delta > a*a + b2 + _2ab; \ - else \ - feasible = delta < b2 || delta < a*a + b2 + _2ab; \ - \ - if (unlikely (feasible)) { \ - double sqrt_delta = sqrt (delta); \ - ADD ((-b - sqrt_delta) / a); \ - ADD ((-b + sqrt_delta) / a); \ - } \ - } else if (delta == 0) { \ - ADD (-b / a); \ - } \ - } \ - } - - /* Find X extremes */ - a = -x0 + 3*x1 - 3*x2 + x3; - b = x0 - 2*x1 + x2; - c = -x0 + x1; - FIND_EXTREMES (a, b, c); - - /* Find Y extremes */ - a = -y0 + 3*y1 - 3*y2 + y3; - b = y0 - 2*y1 + y2; - c = -y0 + y1; - FIND_EXTREMES (a, b, c); - - status = add_point_func (closure, p0); - if (unlikely (status)) - return status; - - for (i = 0; i < t_num; i++) { - cairo_point_t p; - double x, y; - double t_1_0, t_0_1; - double t_2_0, t_0_2; - double t_3_0, t_2_1_3, t_1_2_3, t_0_3; - - t_1_0 = t[i]; /* t */ - t_0_1 = 1 - t_1_0; /* (1 - t) */ - - t_2_0 = t_1_0 * t_1_0; /* t * t */ - t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ - - t_3_0 = t_2_0 * t_1_0; /* t * t * t */ - t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ - t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ - t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ - - /* Bezier polynomial */ - x = x0 * t_0_3 - + x1 * t_1_2_3 - + x2 * t_2_1_3 - + x3 * t_3_0; - y = y0 * t_0_3 - + y1 * t_1_2_3 - + y2 * t_2_1_3 - + y3 * t_3_0; - - p.x = _cairo_fixed_from_double (x); - p.y = _cairo_fixed_from_double (y); - status = add_point_func (closure, &p); - if (unlikely (status)) - return status; - } - - return add_point_func (closure, p3); -} diff --git a/libs/cairo/cairo/src/cairo-stroke-style.c b/libs/cairo/cairo/src/cairo-stroke-style.c deleted file mode 100644 index e068e9dec..000000000 --- a/libs/cairo/cairo/src/cairo-stroke-style.c +++ /dev/null @@ -1,279 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-error-private.h" - -void -_cairo_stroke_style_init (cairo_stroke_style_t *style) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); - - style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; - style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; - style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; - style->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; - - style->dash = NULL; - style->num_dashes = 0; - style->dash_offset = 0.0; -} - -cairo_status_t -_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, - const cairo_stroke_style_t *other) -{ - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); - - style->line_width = other->line_width; - style->line_cap = other->line_cap; - style->line_join = other->line_join; - style->miter_limit = other->miter_limit; - - style->num_dashes = other->num_dashes; - - if (other->dash == NULL) { - style->dash = NULL; - } else { - style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double)); - if (unlikely (style->dash == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (style->dash, other->dash, - style->num_dashes * sizeof (double)); - } - - style->dash_offset = other->dash_offset; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_stroke_style_fini (cairo_stroke_style_t *style) -{ - if (style->dash) { - free (style->dash); - style->dash = NULL; - } - style->num_dashes = 0; - - VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t))); -} - -/* - * For a stroke in the given style, compute the maximum distance - * from the path that vertices could be generated. In the case - * of rotation in the ctm, the distance will not be exact. - */ -void -_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double *dx, double *dy) -{ - double style_expansion = 0.5; - - if (style->line_cap == CAIRO_LINE_CAP_SQUARE) - style_expansion = M_SQRT1_2; - - if (style->line_join == CAIRO_LINE_JOIN_MITER && - style_expansion < M_SQRT2 * style->miter_limit) - { - style_expansion = M_SQRT2 * style->miter_limit; - } - - style_expansion *= style->line_width; - - *dx = style_expansion * hypot (ctm->xx, ctm->xy); - *dy = style_expansion * hypot (ctm->yy, ctm->yx); -} - -/* - * Computes the period of a dashed stroke style. - * Returns 0 for non-dashed styles. - */ -double -_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style) -{ - double period; - unsigned int i; - - period = 0.0; - for (i = 0; i < style->num_dashes; i++) - period += style->dash[i]; - - if (style->num_dashes & 1) - period *= 2.0; - - return period; -} - -/* - * Coefficient of the linear approximation (minimizing square difference) - * of the surface covered by round caps - * - * This can be computed in the following way: - * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is: - * f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2) - * The square difference to a generic linear approximation (c*d) in the range (0,w) would be: - * integrate ((f(w,d) - c*d)^2, d, 0, w) - * To minimize this difference it is sufficient to find a solution of the differential with - * respect to c: - * solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c) - * Which leads to c = 9/32*pi*w - * Since we're not interested in the true area, but just in a coverage extimate, - * we always divide the real area by the line width (w). - * The same computation for square caps would be - * f(w,d) = 2 * integrate(w/2, x, -d/2, d/2) - * c = 1*w - * but in this case it would not be an approximation, since f is already linear in d. - */ -#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32) - -/* - * Computes the length of the "on" part of a dashed stroke style, - * taking into account also line caps. - * Returns 0 for non-dashed styles. - */ -double -_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) -{ - double stroked, cap_scale; - unsigned int i; - - switch (style->line_cap) { - default: ASSERT_NOT_REACHED; - case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; - case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; - case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; - } - - stroked = 0.0; - if (style->num_dashes & 1) { - /* Each dash element is used both as on and as off. The order in which they are summed is - * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */ - for (i = 0; i < style->num_dashes; i++) - stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width); - } else { - /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus - * their coverage is approximated based on the area covered by the caps of adjacent on dases. */ - for (i = 0; i < style->num_dashes; i+=2) - stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); - } - - return stroked; -} - -/* - * Verifies if _cairo_stroke_style_dash_approximate should be used to generate - * an approximation of the dash pattern in the specified style, when used for - * stroking a path with the given CTM and tolerance. - * Always %FALSE for non-dashed styles. - */ -cairo_bool_t -_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double tolerance) -{ - double period; - - if (! style->num_dashes) - return FALSE; - - period = _cairo_stroke_style_dash_period (style); - return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance; -} - -/* - * Create a 2-dashes approximation of a dashed style, by making the "on" and "off" - * parts respect the original ratio. - */ -void -_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double tolerance, - double *dash_offset, - double *dashes, - unsigned int *num_dashes) -{ - double coverage, scale, offset; - cairo_bool_t on = TRUE; - unsigned int i = 0; - - coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style); - coverage = MIN (coverage, 1.0); - scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0); - - /* We stop searching for a starting point as soon as the - * offset reaches zero. Otherwise when an initial dash - * segment shrinks to zero it will be skipped over. */ - offset = style->dash_offset; - while (offset > 0.0 && offset >= style->dash[i]) { - offset -= style->dash[i]; - on = !on; - if (++i == style->num_dashes) - i = 0; - } - - *num_dashes = 2; - - /* - * We want to create a new dash pattern with the same relative coverage, - * but composed of just 2 elements with total length equal to scale. - * Based on the formula in _cairo_stroke_style_dash_stroked: - * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width) - * = MIN (dashes[0] + cap_scale * (scale - dashes[0]), - * dashes[0] + cap_scale * line_width) = - * = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale, - * dashes[0] + cap_scale * line_width) - * - * Solving both cases we get: - * dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale) - * when scale - dashes[0] <= line_width - * dashes[0] = scale * coverage - cap_scale * line_width - * when scale - dashes[0] > line_width. - * - * Comparing the two cases we get: - * second > first - * second > scale * (coverage - cap_scale) / (1 - cap_scale) - * second - cap_scale * second - scale * coverage + scale * cap_scale > 0 - * (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0 - * - line_width - second + scale > 0 - * scale - second > line_width - * which is the condition for the second solution to be the valid one. - * So when second > first, the second solution is the correct one (i.e. - * the solution is always MAX (first, second). - */ - switch (style->line_cap) { - default: - ASSERT_NOT_REACHED; - dashes[0] = 0.0; - break; - - case CAIRO_LINE_CAP_BUTT: - /* Simplified formula (substituting 0 for cap_scale): */ - dashes[0] = scale * coverage; - break; - - case CAIRO_LINE_CAP_ROUND: - dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION), - scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width); - break; - - case CAIRO_LINE_CAP_SQUARE: - /* - * Special attention is needed to handle the case cap_scale == 1 (since the first solution - * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using - * 0 as first solution always leads to the correct solution. - */ - dashes[0] = MAX(0.0, scale * coverage - style->line_width); - break; - } - - dashes[1] = scale - dashes[0]; - - *dash_offset = on ? 0.0 : dashes[0]; -} diff --git a/libs/cairo/cairo/src/cairo-supported-features.h b/libs/cairo/cairo/src/cairo-supported-features.h deleted file mode 100644 index aacbab781..000000000 --- a/libs/cairo/cairo/src/cairo-supported-features.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_SUPPORTED_FEATURES_H -#define CAIRO_SUPPORTED_FEATURES_H - -/* This is a dummy header, to trick gtk-doc only */ - -#define CAIRO_HAS_XLIB_SURFACE 1 -#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 -#define CAIRO_HAS_QUARTZ_SURFACE 1 -#define CAIRO_HAS_QUARTZ_FONT 1 -#define CAIRO_HAS_WIN32_SURFACE 1 -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_EGL_FUNCTIONS 1 -#define CAIRO_HAS_GLX_FUNCTIONS 1 -#define CAIRO_HAS_FT_FONT 1 -#define CAIRO_HAS_FC_FONT 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_META_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 - -#endif diff --git a/libs/cairo/cairo/src/cairo-surface-clipper-private.h b/libs/cairo/cairo/src/cairo-surface-clipper-private.h deleted file mode 100644 index d8750e642..000000000 --- a/libs/cairo/cairo/src/cairo-surface-clipper-private.h +++ /dev/null @@ -1,40 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H -#define CAIRO_SURFACE_CLIPPER_PRIVATE_H - -#include "cairo-types-private.h" -#include "cairo-clip-private.h" - -CAIRO_BEGIN_DECLS - -typedef struct _cairo_surface_clipper cairo_surface_clipper_t; - -typedef cairo_status_t -(*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *, - cairo_path_fixed_t *, - cairo_fill_rule_t, - double, - cairo_antialias_t); -struct _cairo_surface_clipper { - cairo_clip_t clip; - cairo_bool_t is_clipped; - cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path; -}; - -cairo_private cairo_status_t -_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, - cairo_clip_t *clip); - -cairo_private void -_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, - cairo_surface_clipper_intersect_clip_path_func_t intersect); - -cairo_private void -_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper); - -CAIRO_END_DECLS - -#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-clipper.c b/libs/cairo/cairo/src/cairo-surface-clipper.c deleted file mode 100644 index 21c6fb1d5..000000000 --- a/libs/cairo/cairo/src/cairo-surface-clipper.c +++ /dev/null @@ -1,104 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-surface-clipper-private.h" - -/* A collection of routines to facilitate vector surface clipping */ - -static cairo_status_t -_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, - cairo_clip_path_t *clip_path) -{ - cairo_status_t status; - - if (clip_path->prev != NULL) { - status = - _cairo_surface_clipper_intersect_clip_path_recursive (clipper, - clip_path->prev); - if (unlikely (status)) - return status; - } - - return clipper->intersect_clip_path (clipper, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias); -} - -cairo_status_t -_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_bool_t clear; - - /* XXX as we cache a reference to the path, and compare every time, - * we may in future need to install a notification if the clip->path - * is every modified (e.g. cairo_clip_translate). - */ - - if (clip == NULL && clipper->clip.path == NULL) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL && clipper->clip.path != NULL && - _cairo_clip_equal (clip, &clipper->clip)) - { - return CAIRO_STATUS_SUCCESS; - } - - /* all clipped out state should never propagate this far */ - assert (clip == NULL || clip->path != NULL); - - /* Check whether this clip is a continuation of the previous. - * If not, we have to remove the current clip and rebuild. - */ - clear = clip == NULL || clip->path->prev != clipper->clip.path; - - _cairo_clip_reset (&clipper->clip); - _cairo_clip_init_copy (&clipper->clip, clip); - - if (clear) { - clipper->is_clipped = FALSE; - status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); - if (unlikely (status)) - return status; - - if (clip != NULL && clip->path != NULL) { - status = - _cairo_surface_clipper_intersect_clip_path_recursive (clipper, - clip->path); - clipper->is_clipped = TRUE; - } - } else { - cairo_clip_path_t *path = clip->path; - - clipper->is_clipped = TRUE; - status = clipper->intersect_clip_path (clipper, - &path->path, - path->fill_rule, - path->tolerance, - path->antialias); - } - - return status; -} - -void -_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, - cairo_surface_clipper_intersect_clip_path_func_t func) -{ - _cairo_clip_init (&clipper->clip); - clipper->is_clipped = FALSE; - clipper->intersect_clip_path = func; -} - -void -_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) -{ - _cairo_clip_reset (&clipper->clip); - clipper->is_clipped = FALSE; -} diff --git a/libs/cairo/cairo/src/cairo-surface-fallback-private.h b/libs/cairo/cairo/src/cairo-surface-fallback-private.h deleted file mode 100644 index 3c6324815..000000000 --- a/libs/cairo/cairo/src/cairo-surface-fallback-private.h +++ /dev/null @@ -1,105 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H -#define CAIRO_SURFACE_FALLBACK_PRIVATE_H - -#include "cairoint.h" - -cairo_private cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fallback_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); - -cairo_private cairo_surface_t * -_cairo_surface_fallback_snapshot (cairo_surface_t *surface); - -cairo_private cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - -cairo_private cairo_status_t -_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_fallback_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out); - -#endif diff --git a/libs/cairo/cairo/src/cairo-surface-fallback.c b/libs/cairo/cairo/src/cairo-surface-fallback.c deleted file mode 100644 index 09ab644a7..000000000 --- a/libs/cairo/cairo/src/cairo-surface-fallback.c +++ /dev/null @@ -1,1602 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-spans-private.h" -#include "cairo-surface-fallback-private.h" - -typedef struct { - cairo_surface_t *dst; - cairo_rectangle_int_t extents; - cairo_image_surface_t *image; - cairo_rectangle_int_t image_rect; - void *image_extra; -} fallback_state_t; - -/** - * _fallback_init: - * - * Acquire destination image surface needed for an image-based - * fallback. - * - * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not - * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all - * went well, or some error status otherwise. - **/ -static cairo_int_status_t -_fallback_init (fallback_state_t *state, - cairo_surface_t *dst, - int x, - int y, - int width, - int height) -{ - cairo_status_t status; - - state->extents.x = x; - state->extents.y = y; - state->extents.width = width; - state->extents.height = height; - - state->dst = dst; - - status = _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, - &state->image_extra); - if (unlikely (status)) - return status; - - - /* XXX: This NULL value tucked away in state->image is a rather - * ugly interface. Cleaner would be to push the - * CAIRO_INT_STATUS_NOTHING_TO_DO value down into - * _cairo_surface_acquire_dest_image and its backend - * counterparts. */ - assert (state->image != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_fallback_fini (fallback_state_t *state) -{ - _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, - state->image_extra); -} - -typedef cairo_status_t -(*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static cairo_status_t -_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, - cairo_clip_t *clip, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *mask; - cairo_region_t *clip_region = NULL, *fallback_region = NULL; - cairo_status_t status; - cairo_bool_t clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with - * a mask (as called via _cairo_surface_mask) triggers assertion failures. - */ - mask = _cairo_surface_create_similar_solid (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - if (unlikely (mask->status)) - return mask->status; - - if (clip_region && (extents->x || extents->y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto CLEANUP_SURFACE; - - cairo_region_translate (fallback_region, - -extents->x, - -extents->y); - clip_region = fallback_region; - } - - status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, mask, - extents->x, extents->y, - extents, - clip_region); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - if (clip_surface) - status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); - - _cairo_pattern_init_for_surface (mask_pattern, mask); - - CLEANUP_SURFACE: - if (fallback_region) - cairo_region_destroy (fallback_region); - cairo_surface_destroy (mask); - - return status; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_status_t status; - - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - - _cairo_pattern_fini (&mask_pattern.base); - } - - return status; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *intermediate; - cairo_surface_pattern_t pattern; - cairo_surface_pattern_t clip_pattern; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - /* We'd be better off here creating a surface identical in format - * to dst, but we have no way of getting that information. Instead - * we ask the backend to create a similar surface of identical content, - * in the belief that the backend will do something useful - like use - * an identical format. For example, the xlib backend will endeavor to - * use a compatible depth to enable core protocol routines. - */ - intermediate = - _cairo_surface_create_similar_scratch (dst, dst->content, - extents->width, - extents->height); - if (intermediate == NULL) { - intermediate = - _cairo_image_surface_create_with_content (dst->content, - extents->width, - extents->width); - } - if (unlikely (intermediate->status)) - return intermediate->status; - - /* Initialize the intermediate surface from the destination surface */ - _cairo_pattern_init_for_surface (&pattern, dst); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL, intermediate, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - status = (*draw_func) (draw_closure, op, - src, intermediate, - extents->x, extents->y, - extents, - NULL); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); - - /* Combine that with the clip */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, - &clip_pattern.base, NULL, intermediate, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Punch the clip out of the destination */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &clip_pattern.base, NULL, dst, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Now add the two results together */ - _cairo_pattern_init_for_surface (&pattern, intermediate); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - - CLEANUP_CLIP: - _cairo_pattern_fini (&clip_pattern.base); - CLEANUP_SURFACE: - cairo_surface_destroy (intermediate); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - } - - /* Create a surface that is mask IN clip */ - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (unlikely (status)) - return status; - - /* Compute dest' = dest OUT (mask IN clip) */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &mask_pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - if (unlikely (status)) - goto CLEANUP_MASK_PATTERN; - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - CLEANUP_MASK_PATTERN: - _cairo_pattern_fini (&mask_pattern.base); - return status; -} - -static int -_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -/** - * _clip_and_composite: - * @clip: a #cairo_clip_t - * @op: the operator to draw with - * @src: source pattern - * @draw_func: function that can be called to draw with the mask onto a surface. - * @draw_closure: data to pass to @draw_func. - * @dst: destination surface - * @extents: rectangle holding a bounding box for the operation; this - * rectangle will be used as the size for the temporary - * surface. - * - * When there is a surface clip, we typically need to create an intermediate - * surface. This function handles the logic of creating a temporary surface - * drawing to it, then compositing the result onto the target surface. - * - * @draw_func is to called to draw the mask; it will be called no more - * than once. - * - * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. - **/ -static cairo_status_t -_clip_and_composite (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - - if (_cairo_rectangle_empty (extents)) - /* Nothing to do */ - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, - src, - draw_func, draw_closure, - dst, extents); - } else { - cairo_bool_t clip_surface = FALSE; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_surface) { - if (_cairo_operator_bounded_by_mask (op)) { - status = _clip_and_composite_with_mask (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } else { - status = _clip_and_composite_combine (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } - } else { - status = draw_func (draw_closure, op, - src, dst, - 0, 0, - extents, - clip_region); - } - } - - return status; -} - -/* Composites a region representing a set of trapezoids. - */ -static cairo_status_t -_composite_trap_region (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_surface_pattern_t mask_pattern; - cairo_pattern_t *mask = NULL; - int mask_x = 0, mask_y =0; - - if (clip != NULL) { - cairo_surface_t *clip_surface = NULL; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); - mask_x = extents->x - clip_x; - mask_y = extents->y - clip_y; - mask = &mask_pattern.base; - } - - status = _cairo_surface_composite (op, src, mask, dst, - extents->x, extents->y, - mask_x, mask_y, - extents->x, extents->y, - extents->width, extents->height, - trap_region); - - if (mask != NULL) - _cairo_pattern_fini (mask); - - return status; -} - -typedef struct { - cairo_traps_t *traps; - cairo_antialias_t antialias; -} cairo_composite_traps_info_t; - -static cairo_status_t -_composite_traps_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_traps_info_t *info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (dst_x != 0 || dst_y != 0) - _cairo_traps_translate (info->traps, - dst_x, - dst_y); - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - status = _cairo_surface_composite_trapezoids (op, - src, dst, info->antialias, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - info->traps->traps, - info->traps->num_traps, - clip_region); - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -enum { - HAS_CLEAR_REGION = 0x1, -}; - -static cairo_status_t -_clip_and_composite_region (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_region_t clear_region; - unsigned int has_region = 0; - cairo_status_t status; - - if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { - /* If we optimize drawing with an unbounded operator to - * _cairo_surface_fill_rectangles() or to drawing with a - * clip region, then we have an additional region to clear. - */ - _cairo_region_init_rectangle (&clear_region, extents); - status = cairo_region_subtract (&clear_region, trap_region); - if (unlikely (status)) - return status; - - if (! cairo_region_is_empty (&clear_region)) - has_region |= HAS_CLEAR_REGION; - } - - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && - clip == NULL) - { - const cairo_color_t *color; - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); - } else { - /* For a simple rectangle, we can just use composite(), for more - * rectangles, we have to set a clip region. The cost of rasterizing - * trapezoids is pretty high for most backends currently, so it's - * worthwhile even if a region is needed. - * - * If we have a clip surface, we set it as the mask; this only works - * for bounded operators other than SOURCE; for unbounded operators, - * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in its handling - * of the mask then what we want. - * - * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has - * more than rectangle and the destination doesn't support clip - * regions. In that case, we fall through. - */ - status = _composite_trap_region (clip, src, op, dst, - trap_region, extents); - } - - if (has_region & HAS_CLEAR_REGION) { - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_surface_fill_region (dst, - CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - } - _cairo_region_fini (&clear_region); - } - - return status; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_rectangles (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - const cairo_color_t *color; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: convert clip region to geometric boxes? */ - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: fallback for the region_subtract() operation */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->has_intersections) { - if (traps->is_rectangular) { - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - } - if (unlikely (status)) - return status; - } - - for (i = 0; i < traps->num_traps; i++) { - if (! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (traps->num_traps, - sizeof (cairo_rectangle_int_t)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - - rects[i].x = x1; - rects[i].y = y1; - rects[i].width = x2 - x1; - rects[i].height = y2 - y1; - } - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); - - if (rects != stack_rects) - free (rects); - - return status; -} - -/* fast-path for very common composite of a single rectangle */ -static cairo_status_t -_composite_rectangle (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - cairo_rectangle_int_t rect; - - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_fixed_is_integer (traps->traps[0].top) || - ! _cairo_fixed_is_integer (traps->traps[0].bottom) || - ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); - rect.y = _cairo_fixed_integer_part (traps->traps[0].top); - rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; - rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; - - return _cairo_surface_composite (op, src, NULL, dst, - rect.x, rect.y, - 0, 0, - rect.x, rect.y, - rect.width, rect.height, - NULL); -} - -/* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_clip_and_composite_trapezoids (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_composite_traps_info_t traps_info; - cairo_region_t *clip_region = NULL; - cairo_bool_t clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (! clip_surface || - (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) - { - cairo_region_t *trap_region = NULL; - - if (_cairo_operator_bounded_by_source (op)) { - status = _fill_rectangles (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _composite_rectangle (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_traps_extract_region (traps, &trap_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (trap_region != NULL) { - status = cairo_region_intersect_rectangle (trap_region, extents); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - - if (clip_region != NULL) { - status = cairo_region_intersect (trap_region, clip_region); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - } - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t trap_extents; - - cairo_region_get_extents (trap_region, &trap_extents); - if (! _cairo_rectangle_intersect (extents, &trap_extents)) { - cairo_region_destroy (trap_region); - return CAIRO_STATUS_SUCCESS; - } - } - - status = _clip_and_composite_region (src, op, dst, - trap_region, - clip_surface ? clip : NULL, - extents); - cairo_region_destroy (trap_region); - - if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) - return status; - } - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - traps_info.traps = traps; - traps_info.antialias = antialias; - - return _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, extents); -} - -cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_clip_path_t *clip_path = clip ? clip->path : NULL; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_boxes_t boxes; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - cairo_traps_t traps; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && clip_path->prev == NULL && - _cairo_operator_bounded_by_mask (op)) - { - return _cairo_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - - /* meh, surface-fallback is dying anyway... */ - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _cairo_traps_init_boxes (&traps, &boxes); - if (unlikely (status)) - goto CLEANUP_BOXES; - - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, CAIRO_ANTIALIAS_DEFAULT, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - _cairo_traps_fini (&traps); - -CLEANUP_BOXES: - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -static cairo_status_t -_cairo_surface_mask_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_pattern_t *mask = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - if (src) { - status = _cairo_surface_composite (op, - src, mask, dst, - extents->x, extents->y, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - status = _cairo_surface_composite (op, - mask, NULL, dst, - extents->x, extents->y, - 0, 0, /* unused */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - return _clip_and_composite (clip, op, source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); -} - -cairo_status_t -_cairo_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, stroke_style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_bool_t is_rectilinear; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - if (path->is_empty_fill) - goto DO_TRAPS; - - is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); - if (is_rectilinear) { - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - fill_rule); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -typedef struct { - cairo_scaled_font_t *font; - cairo_glyph_t *glyphs; - int num_glyphs; -} cairo_show_glyphs_info_t; - -static cairo_status_t -_cairo_surface_old_show_glyphs_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_show_glyphs_info_t *glyph_info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - /* Modifying the glyph array is fine because we know that this function - * will be called only once, and we've already made a copy of the - * glyphs in the wrapper. - */ - if (dst_x != 0 || dst_y != 0) { - int i; - - for (i = 0; i < glyph_info->num_glyphs; ++i) { - ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; - ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; - } - } - - status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, - dst, - extents->x, extents->y, - extents->x - dst_x, - extents->y - dst_y, - extents->width, - extents->height, - glyph_info->glyphs, - glyph_info->num_glyphs, - clip_region); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_scaled_font_show_glyphs (glyph_info->font, - op, - src, dst, - extents->x, extents->y, - extents->x - dst_x, - extents->y - dst_y, - extents->width, extents->height, - glyph_info->glyphs, - glyph_info->num_glyphs, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_show_glyphs_info_t glyph_info; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - NULL); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_rectangle (clip, &extents.mask)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - return _clip_and_composite (clip, op, source, - _cairo_surface_old_show_glyphs_draw_func, - &glyph_info, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); -} - -cairo_surface_t * -_cairo_surface_fallback_snapshot (cairo_surface_t *surface) -{ - cairo_surface_t *snapshot; - cairo_status_t status; - cairo_format_t format; - cairo_surface_pattern_t pattern; - cairo_image_surface_t *image; - void *image_extra; - - status = _cairo_surface_acquire_source_image (surface, - &image, &image_extra); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - format = image->format; - if (format == CAIRO_FORMAT_INVALID) { - /* Non-standard images formats can be generated when retrieving - * images from unusual xservers, for example. - */ - format = _cairo_format_from_content (image->base.content); - } - snapshot = cairo_image_surface_create (format, - image->width, - image->height); - if (cairo_surface_status (snapshot)) { - _cairo_surface_release_source_image (surface, image, image_extra); - return snapshot; - } - - _cairo_pattern_init_for_surface (&pattern, &image->base); - status = _cairo_surface_paint (snapshot, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - _cairo_surface_release_source_image (surface, image, image_extra); - if (unlikely (status)) { - cairo_surface_destroy (snapshot); - return _cairo_surface_create_in_error (status); - } - - return snapshot; -} - -cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - fallback_state_t state; - cairo_region_t *fallback_region = NULL; - cairo_status_t status; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (unlikely (status)) - return status; - - /* We know this will never fail with the image backend; but - * instead of calling into it directly, we call - * _cairo_surface_composite so that we get the correct device - * offset handling. - */ - - if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto FAIL; - - cairo_region_translate (fallback_region, - -state.image_rect.x, - -state.image_rect.y); - clip_region = fallback_region; - } - - status = _cairo_surface_composite (op, src, mask, - &state.image->base, - src_x, src_y, mask_x, mask_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height, - clip_region); - FAIL: - if (fallback_region != NULL) - cairo_region_destroy (fallback_region); - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - fallback_state_t state; - cairo_rectangle_int_t *offset_rects = NULL; - cairo_status_t status; - int x1, y1, x2, y2; - int i; - - assert (surface->snapshot_of == NULL); - - if (num_rects <= 0) - return CAIRO_STATUS_SUCCESS; - - /* Compute the bounds of the rectangles, so that we know what area of the - * destination surface to fetch - */ - x1 = rects[0].x; - y1 = rects[0].y; - x2 = rects[0].x + rects[0].width; - y2 = rects[0].y + rects[0].height; - - for (i = 1; i < num_rects; i++) { - if (rects[i].x < x1) - x1 = rects[i].x; - if (rects[i].y < y1) - y1 = rects[i].y; - - if ((int) (rects[i].x + rects[i].width) > x2) - x2 = rects[i].x + rects[i].width; - if ((int) (rects[i].y + rects[i].height) > y2) - y2 = rects[i].y + rects[i].height; - } - - status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); - if (unlikely (status)) - return status; - - /* If the fetched image isn't at 0,0, we need to offset the rectangles */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); - if (unlikely (offset_rects == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; - } - - for (i = 0; i < num_rects; i++) { - offset_rects[i].x = rects[i].x - state.image_rect.x; - offset_rects[i].y = rects[i].y - state.image_rect.y; - offset_rects[i].width = rects[i].width; - offset_rects[i].height = rects[i].height; - } - - rects = offset_rects; - } - - status = _cairo_surface_fill_rectangles (&state.image->base, - op, color, - rects, num_rects); - - free (offset_rects); - - DONE: - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - fallback_state_t state; - cairo_region_t *fallback_region = NULL; - cairo_trapezoid_t *offset_traps = NULL; - cairo_status_t status; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (unlikely (status)) - return status; - - /* If the destination image isn't at 0,0, we need to offset the trapezoids */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); - if (offset_traps == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, - - state.image_rect.x, - state.image_rect.y, - 1.0, 1.0); - traps = offset_traps; - - /* similarly we need to adjust the region */ - if (clip_region != NULL) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto FAIL; - - cairo_region_translate (fallback_region, - -state.image_rect.x, - -state.image_rect.y); - clip_region = fallback_region; - } - } - - status = _cairo_surface_composite_trapezoids (op, pattern, - &state.image->base, - antialias, - src_x, src_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height, - traps, num_traps, - clip_region); - FAIL: - if (offset_traps != NULL) - free (offset_traps); - - if (fallback_region != NULL) - cairo_region_destroy (fallback_region); - - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_surface_t *new_surface; - cairo_surface_pattern_t pattern; - cairo_status_t status; - - new_surface = _cairo_surface_create_similar_scratch (surface, - src->content, - width, height); - if (new_surface == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (new_surface->status)) - return new_surface->status; - - /* We have to copy these here, so that the coordinate spaces are correct */ - new_surface->device_transform = src->device_transform; - new_surface->device_transform_inverse = src->device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, src); - cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y); - pattern.base.filter = CAIRO_FILTER_NEAREST; - - status = _cairo_surface_paint (new_surface, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (new_surface); - return status; - } - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = new_surface; - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-surface-offset-private.h b/libs/cairo/cairo/src/cairo-surface-offset-private.h deleted file mode 100644 index 94525a689..000000000 --- a/libs/cairo/cairo/src/cairo-surface-offset-private.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_OFFSET_PRIVATE_H -#define CAIRO_SURFACE_OFFSET_PRIVATE_H - -#include "cairo-types-private.h" - -CAIRO_BEGIN_DECLS - -cairo_private cairo_status_t -_cairo_surface_offset_paint (cairo_surface_t *target, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_offset_mask (cairo_surface_t *target, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_offset_stroke (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_offset_fill (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t*source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_offset_glyphs (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_clip_t *clip); - -#endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-offset.c b/libs/cairo/cairo/src/cairo-surface-offset.c deleted file mode 100644 index a5070617d..000000000 --- a/libs/cairo/cairo/src/cairo-surface-offset.c +++ /dev/null @@ -1,309 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-surface-offset-private.h" - -/* A collection of routines to facilitate drawing to an alternate surface. */ - -static void -_copy_transformed_pattern (cairo_pattern_t *pattern, - const cairo_pattern_t *original, - const cairo_matrix_t *ctm_inverse) -{ - _cairo_pattern_init_static_copy (pattern, original); - - if (! _cairo_matrix_is_identity (ctm_inverse)) - _cairo_pattern_transform (pattern, ctm_inverse); -} - -cairo_status_t -_cairo_surface_offset_paint (cairo_surface_t *target, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - - if (unlikely (target->status)) - return target->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (x | y) { - cairo_matrix_t m; - - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_init_translate (&m, x, y); - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - - status = _cairo_surface_paint (target, op, source, dev_clip); - - FINISH: - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - - return status; -} - -cairo_status_t -_cairo_surface_offset_mask (cairo_surface_t *target, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - cairo_pattern_union_t mask_copy; - - if (unlikely (target->status)) - return target->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (x | y) { - cairo_matrix_t m; - - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_init_translate (&m, x, y); - _copy_transformed_pattern (&source_copy.base, source, &m); - _copy_transformed_pattern (&mask_copy.base, mask, &m); - source = &source_copy.base; - mask = &mask_copy.base; - } - - status = _cairo_surface_mask (target, op, - source, mask, - dev_clip); - - FINISH: - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - - return status; -} - -cairo_status_t -_cairo_surface_offset_stroke (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t*stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_matrix_t dev_ctm = *ctm; - cairo_matrix_t dev_ctm_inverse = *ctm_inverse; - cairo_pattern_union_t source_copy; - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (x | y) { - cairo_matrix_t m; - - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - goto FINISH; - - _cairo_path_fixed_translate (&path_copy, - _cairo_fixed_from_int (-x), - _cairo_fixed_from_int (-y)); - dev_path = &path_copy; - - cairo_matrix_init_translate (&m, -x, -y); - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_init_translate (&m, x, y); - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); - } - - status = _cairo_surface_stroke (surface, op, source, - dev_path, stroke_style, - &dev_ctm, &dev_ctm_inverse, - tolerance, antialias, - dev_clip); - - FINISH: - if (dev_path != path) - _cairo_path_fixed_fini (dev_path); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - - return status; -} - -cairo_status_t -_cairo_surface_offset_fill (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t*source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (x | y) { - cairo_matrix_t m; - - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - goto FINISH; - - _cairo_path_fixed_translate (&path_copy, - _cairo_fixed_from_int (-x), - _cairo_fixed_from_int (-y)); - dev_path = &path_copy; - - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_init_translate (&m, x, y); - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - - status = _cairo_surface_fill (surface, op, source, - dev_path, fill_rule, - tolerance, antialias, - dev_clip); - - FINISH: - if (dev_path != path) - _cairo_path_fixed_fini (dev_path); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - - return status; -} - -cairo_status_t -_cairo_surface_offset_glyphs (cairo_surface_t *surface, - int x, int y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - cairo_glyph_t *dev_glyphs; - int i; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (dev_glyphs == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - - if (x | y) { - cairo_matrix_t m; - - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_init_translate (&m, x, y); - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - - for (i = 0; i < num_glyphs; i++) { - dev_glyphs[i].x -= x; - dev_glyphs[i].y -= y; - } - } - - status = _cairo_surface_show_text_glyphs (surface, op, source, - NULL, 0, - dev_glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, - dev_clip); - - FINISH: - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - free (dev_glyphs); - - return status; -} diff --git a/libs/cairo/cairo/src/cairo-surface-private.h b/libs/cairo/cairo/src/cairo-surface-private.h deleted file mode 100644 index 96cd4a371..000000000 --- a/libs/cairo/cairo/src/cairo-surface-private.h +++ /dev/null @@ -1,70 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_PRIVATE_H -#define CAIRO_SURFACE_PRIVATE_H - -#include "cairo.h" - -#include "cairo-types-private.h" -#include "cairo-list-private.h" -#include "cairo-reference-count-private.h" -#include "cairo-clip-private.h" - -struct _cairo_surface { - const cairo_surface_backend_t *backend; - cairo_device_t *device; - - /* We allow surfaces to override the backend->type by shoving something - * else into surface->type. This is for "wrapper" surfaces that want to - * hide their internal type from the user-level API. */ - cairo_surface_type_t type; - - cairo_content_t content; - - cairo_reference_count_t ref_count; - cairo_status_t status; - unsigned int unique_id; - - unsigned finished : 1; - unsigned is_clear : 1; - unsigned has_font_options : 1; - unsigned owns_device : 1; - unsigned permit_subpixel_antialiasing : 1; - - cairo_user_data_array_t user_data; - cairo_user_data_array_t mime_data; - - cairo_matrix_t device_transform; - cairo_matrix_t device_transform_inverse; - cairo_list_t device_transform_observers; - - /* The actual resolution of the device, in dots per inch. */ - double x_resolution; - double y_resolution; - - /* The resolution that should be used when generating image-based - * fallback; generally only used by the analysis/paginated - * surfaces - */ - double x_fallback_resolution; - double y_fallback_resolution; - - /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ - cairo_surface_t *snapshot_of; - cairo_surface_func_t snapshot_detach; - /* current snapshots of this surface*/ - cairo_list_t snapshots; - /* place upon snapshot list */ - cairo_list_t snapshot; - - /* - * Surface font options, falling back to backend's default options, - * and set using _cairo_surface_set_font_options(), and propagated by - * cairo_surface_create_similar(). - */ - cairo_font_options_t font_options; -}; - -#endif /* CAIRO_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-snapshot-private.h b/libs/cairo/cairo/src/cairo-surface-snapshot-private.h deleted file mode 100644 index a9520332a..000000000 --- a/libs/cairo/cairo/src/cairo-surface-snapshot-private.h +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H -#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H - -#include "cairo-surface-private.h" - -struct _cairo_surface_snapshot { - cairo_surface_t base; - - cairo_surface_t *target; - cairo_surface_t *clone; -}; - -#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-snapshot.c b/libs/cairo/cairo/src/cairo-surface-snapshot.c deleted file mode 100644 index 030636056..000000000 --- a/libs/cairo/cairo/src/cairo-surface-snapshot.c +++ /dev/null @@ -1,220 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-surface-snapshot-private.h" - -static cairo_status_t -_cairo_surface_snapshot_finish (void *abstract_surface) -{ - cairo_surface_snapshot_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (surface->clone != NULL) { - cairo_surface_finish (surface->clone); - status = surface->clone->status; - - cairo_surface_destroy (surface->clone); - } - - return status; -} - -static cairo_status_t -_cairo_surface_snapshot_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **extra_out) -{ - cairo_surface_snapshot_t *surface = abstract_surface; - - return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out); -} - -static void -_cairo_surface_snapshot_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *extra) -{ - cairo_surface_snapshot_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->target, image, extra); -} - -static cairo_bool_t -_cairo_surface_snapshot_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_surface_snapshot_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->target, extents); -} - -static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT, - - NULL, /* create similar */ - _cairo_surface_snapshot_finish, - - _cairo_surface_snapshot_acquire_source_image, - _cairo_surface_snapshot_release_source_image, - NULL, NULL, /* acquire, release dest */ - NULL, /* clone similar */ - NULL, /* composite */ - NULL, /* fill rectangles */ - NULL, /* composite trapezoids */ - NULL, /* create span renderer */ - NULL, /* check span renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_surface_snapshot_get_extents, -}; - -static void -_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) -{ - cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; - cairo_image_surface_t *image; - cairo_image_surface_t *clone; - void *extra; - cairo_status_t status; - - /* We need to make an image copy of the original surface since the - * snapshot may exceed the lifetime of the original device, i.e. - * when we later need to use the snapshot the data may have already - * been lost. - */ - - status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra); - if (unlikely (status)) { - snapshot->target = _cairo_surface_create_in_error (status); - status = _cairo_surface_set_error (surface, status); - return; - } - - clone = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - image->pixman_format, - image->width, - image->height, - 0); - if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) { - if (clone->stride == image->stride) { - memcpy (clone->data, image->data, image->stride * image->height); - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - image->pixman_image, NULL, clone->pixman_image, - 0, 0, - 0, 0, - 0, 0, - image->width, image->height); - } - clone->base.is_clear = FALSE; - - snapshot->clone = &clone->base; - } else { - snapshot->clone = &clone->base; - status = _cairo_surface_set_error (surface, clone->base.status); - } - - _cairo_surface_release_source_image (snapshot->target, image, extra); - snapshot->target = snapshot->clone; - snapshot->base.type = snapshot->target->type; -} - -/** - * _cairo_surface_snapshot - * @surface: a #cairo_surface_t - * - * Make an immutable reference to @surface. It is an error to call a - * surface-modifying function on the result of this function. The - * resulting 'snapshot' is a lazily copied-on-write surface i.e. it - * remains a reference to the original surface until that surface is - * written to again, at which time a copy is made of the original surface - * and the snapshot then points to that instead. Multiple snapshots of the - * same unmodified surface point to the same copy. - * - * The caller owns the return value and should call - * cairo_surface_destroy() when finished with it. This function will not - * return %NULL, but will return a nil surface instead. - * - * Return value: The snapshot surface. Note that the return surface - * may not necessarily be of the same type as @surface. - **/ -cairo_surface_t * -_cairo_surface_snapshot (cairo_surface_t *surface) -{ - cairo_surface_snapshot_t *snapshot; - cairo_status_t status; - - if (unlikely (surface->status)) - return _cairo_surface_create_in_error (surface->status); - - if (surface->finished) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - - if (surface->snapshot_of != NULL) - return cairo_surface_reference (surface); - - if (surface->backend->snapshot != NULL) { - cairo_surface_t *snap; - - snap = _cairo_surface_has_snapshot (surface, surface->backend); - if (snap != NULL) - return cairo_surface_reference (snap); - - snap = surface->backend->snapshot (surface); - if (snap != NULL) { - if (unlikely (snap->status)) - return snap; - - status = _cairo_surface_copy_mime_data (snap, surface); - if (unlikely (status)) { - cairo_surface_destroy (snap); - return _cairo_surface_create_in_error (status); - } - - snap->device_transform = surface->device_transform; - snap->device_transform_inverse = surface->device_transform_inverse; - - cairo_surface_attach_snapshot (surface, snap, NULL); - - return snap; - } - } - - snapshot = (cairo_surface_snapshot_t *) - _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend); - if (snapshot != NULL) - return cairo_surface_reference (&snapshot->base); - - snapshot = malloc (sizeof (cairo_surface_snapshot_t)); - if (unlikely (snapshot == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - - _cairo_surface_init (&snapshot->base, - &_cairo_surface_snapshot_backend, - NULL, /* device */ - surface->content); - snapshot->base.type = surface->type; - - snapshot->target = surface; - snapshot->clone = NULL; - - status = _cairo_surface_copy_mime_data (&snapshot->base, surface); - if (unlikely (status)) { - cairo_surface_destroy (&snapshot->base); - return _cairo_surface_create_in_error (status); - } - - snapshot->base.device_transform = surface->device_transform; - snapshot->base.device_transform_inverse = surface->device_transform_inverse; - - cairo_surface_attach_snapshot (surface, - &snapshot->base, - _cairo_surface_snapshot_copy_on_write); - - return &snapshot->base; -} diff --git a/libs/cairo/cairo/src/cairo-surface-subsurface-private.h b/libs/cairo/cairo/src/cairo-surface-subsurface-private.h deleted file mode 100644 index 3fe48f83b..000000000 --- a/libs/cairo/cairo/src/cairo-surface-subsurface-private.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H -#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H - -#include "cairo-surface-private.h" - -struct _cairo_surface_subsurface { - cairo_surface_t base; - - cairo_rectangle_int_t extents; - - cairo_surface_t *target; -}; - -#endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-subsurface.c b/libs/cairo/cairo/src/cairo-surface-subsurface.c deleted file mode 100644 index b9d8eacd6..000000000 --- a/libs/cairo/cairo/src/cairo-surface-subsurface.c +++ /dev/null @@ -1,521 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-offset-private.h" -#include "cairo-surface-subsurface-private.h" - -static const cairo_surface_backend_t _cairo_surface_subsurface_backend; - -static cairo_status_t -_cairo_surface_subsurface_finish (void *abstract_surface) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->target); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_surface_subsurface_create_similar (void *other, - cairo_content_t content, - int width, int height) -{ - cairo_surface_subsurface_t *surface = other; - return surface->target->backend->create_similar (surface->target, content, width, height); -} - -static cairo_int_status_t -_cairo_surface_subsurface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; - cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_surface_offset_paint (surface->target, - -surface->extents.x, -surface->extents.y, - op, source, &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); - return status; -} - -static cairo_int_status_t -_cairo_surface_subsurface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; - cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_surface_offset_mask (surface->target, - -surface->extents.x, -surface->extents.y, - op, source, mask, &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); - return status; -} - -static cairo_int_status_t -_cairo_surface_subsurface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; - cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_surface_offset_fill (surface->target, - -surface->extents.x, -surface->extents.y, - op, source, path, fill_rule, tolerance, antialias, - &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); - return status; -} - -static cairo_int_status_t -_cairo_surface_subsurface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; - cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_surface_offset_stroke (surface->target, - -surface->extents.x, -surface->extents.y, - op, source, path, stroke_style, ctm, ctm_inverse, - tolerance, antialias, - &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); - return status; -} - -static cairo_int_status_t -_cairo_surface_subsurface_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; - cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_surface_offset_glyphs (surface->target, - -surface->extents.x, -surface->extents.y, - op, source, - scaled_font, glyphs, num_glyphs, - &target_clip); - *remaining_glyphs = 0; - CLEANUP: - _cairo_clip_fini (&target_clip); - return status; -} - -static cairo_status_t -_cairo_surface_subsurface_flush (void *abstract_surface) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_status_t status; - - status = CAIRO_STATUS_SUCCESS; - if (surface->target->backend->flush != NULL) - status = surface->target->backend->flush (surface->target); - - return status; -} - -static cairo_status_t -_cairo_surface_subsurface_mark_dirty (void *abstract_surface, - int x, int y, - int width, int height) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_status_t status; - - status = CAIRO_STATUS_SUCCESS; - if (surface->target->backend->mark_dirty_rectangle != NULL) { - cairo_rectangle_int_t rect, extents; - - rect.x = x; - rect.y = y; - rect.width = width; - rect.height = height; - - extents.x = extents.y = 0; - extents.width = surface->extents.width; - extents.height = surface->extents.height; - - if (_cairo_rectangle_intersect (&rect, &extents)) { - status = surface->target->backend->mark_dirty_rectangle (surface->target, - rect.x + surface->extents.x, - rect.y + surface->extents.y, - rect.width, rect.height); - } - } - - return status; -} - -static cairo_bool_t -_cairo_surface_subsurface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - - extents->x = 0; - extents->y = 0; - extents->width = surface->extents.width; - extents->height = surface->extents.height; - - return TRUE; -} - -static void -_cairo_surface_subsurface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - - if (surface->target->backend->get_font_options != NULL) - surface->target->backend->get_font_options (surface->target, options); -} - -struct extra { - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -cairo_surface_paint_to_target (cairo_surface_t *target, - cairo_surface_subsurface_t *subsurface) -{ - cairo_t *cr; - - cr = cairo_create (target); - - cairo_set_source_surface (cr, - subsurface->target, - - subsurface->extents.x, - - subsurface->extents.y); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - cairo_destroy (cr); -} - -static cairo_status_t -_cairo_surface_subsurface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **extra_out) -{ - cairo_rectangle_int_t target_extents; - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - struct extra *extra; - uint8_t *data; - cairo_bool_t ret; - - if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) { - cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target; - cairo_surface_t *snapshot; - - snapshot = _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); - if (snapshot != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot); - *extra_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (! _cairo_surface_has_snapshot (&meta->base, - &_cairo_image_surface_backend)) - { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (meta->content, - surface->extents.width, - surface->extents.height); - if (unlikely (image->base.status)) - return image->base.status; - - cairo_surface_paint_to_target (&image->base, surface); - - cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); - - *image_out = image; - *extra_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - extra = malloc (sizeof (struct extra)); - if (unlikely (extra == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra); - if (unlikely (status)) - goto CLEANUP; - - ret = _cairo_surface_get_extents (&extra->image->base, &target_extents); - assert (ret); - - /* only copy if we need to perform sub-byte manipulation */ - if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 && - target_extents.x <= surface->extents.x && - target_extents.y <= surface->extents.y && - surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width && - surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) { - - assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0); - - data = extra->image->data + surface->extents.y * extra->image->stride; - data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (data, - extra->image->pixman_format, - surface->extents.width, - surface->extents.height, - extra->image->stride); - if (unlikely ((status = image->base.status))) - goto CLEANUP_IMAGE; - - image->base.is_clear = FALSE; - } else { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - extra->image->pixman_format, - surface->extents.width, - surface->extents.height, - 0); - if (unlikely ((status = image->base.status))) - goto CLEANUP_IMAGE; - - cairo_surface_paint_to_target (&image->base, surface); - } - - *image_out = image; - *extra_out = extra; - return CAIRO_STATUS_SUCCESS; - -CLEANUP_IMAGE: - _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); -CLEANUP: - free (extra); - return status; -} - -static void -_cairo_surface_subsurface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *abstract_extra) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - - if (abstract_extra != NULL) { - struct extra *extra = abstract_extra; - - _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); - free (extra); - } - - cairo_surface_destroy (&image->base); -} - -static cairo_surface_t * -_cairo_surface_subsurface_snapshot (void *abstract_surface) -{ - cairo_surface_subsurface_t *surface = abstract_surface; - cairo_surface_subsurface_t *snapshot; - - snapshot = malloc (sizeof (cairo_surface_subsurface_t)); - if (unlikely (snapshot == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&snapshot->base, - &_cairo_surface_subsurface_backend, - NULL, /* device */ - surface->target->content); - snapshot->target = _cairo_surface_snapshot (surface->target); - if (unlikely (snapshot->target->status)) { - cairo_status_t status; - - status = snapshot->target->status; - free (snapshot); - return _cairo_surface_create_in_error (status); - } - - snapshot->base.type = snapshot->target->type; - snapshot->extents = surface->extents; - - return &snapshot->base; -} - -static const cairo_surface_backend_t _cairo_surface_subsurface_backend = { - CAIRO_SURFACE_TYPE_SUBSURFACE, - _cairo_surface_subsurface_create_similar, - _cairo_surface_subsurface_finish, - - _cairo_surface_subsurface_acquire_source_image, - _cairo_surface_subsurface_release_source_image, - NULL, NULL, /* acquire, release dest */ - NULL, /* clone similar */ - NULL, /* composite */ - NULL, /* fill rectangles */ - NULL, /* composite trapezoids */ - NULL, /* create span renderer */ - NULL, /* check span renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_surface_subsurface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_surface_subsurface_get_font_options, - _cairo_surface_subsurface_flush, - _cairo_surface_subsurface_mark_dirty, - NULL, /* font_fini */ - NULL, /* glyph_fini */ - - _cairo_surface_subsurface_paint, - _cairo_surface_subsurface_mask, - _cairo_surface_subsurface_stroke, - _cairo_surface_subsurface_fill, - _cairo_surface_subsurface_glyphs, - - _cairo_surface_subsurface_snapshot, -}; - -/** - * cairo_surface_create_for_rectangle: - * @target: an existing surface for which the sub-surface will point to - * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units) - * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units) - * @width: width of the sub-surface (in device-space units) - * @height: height of the sub-surface (in device-space units) - * - * Create a new surface that is a rectangle within the target surface. - * All operations drawn to this surface are then clipped and translated - * onto the target surface. Nothing drawn via this sub-surface outside of - * its bounds is drawn onto the target surface, making this a useful method - * for passing constrained child surfaces to library routines that draw - * directly onto the parent surface, i.e. with no further backend allocations, - * double buffering or copies. - * - * The semantics of subsurfaces have not been finalized yet - * unless the rectangle is in full device units, is contained within - * the extents of the target surface, and the target or subsurface's - * device transforms are not changed. - * - * Return value: a pointer to the newly allocated surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if @other is already in an error state - * or any other error occurs. - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_surface_create_for_rectangle (cairo_surface_t *target, - double x, double y, - double width, double height) -{ - cairo_surface_subsurface_t *surface; - - if (unlikely (target->status)) - return _cairo_surface_create_in_error (target->status); - if (unlikely (target->finished)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - - surface = malloc (sizeof (cairo_surface_subsurface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - assert (_cairo_matrix_is_translation (&target->device_transform)); - x += target->device_transform.x0; - y += target->device_transform.y0; - - _cairo_surface_init (&surface->base, - &_cairo_surface_subsurface_backend, - NULL, /* device */ - target->content); - - /* XXX forced integer alignment */ - surface->extents.x = ceil (x); - surface->extents.y = ceil (y); - surface->extents.width = floor (x + width) - surface->extents.x; - surface->extents.height = floor (y + height) - surface->extents.y; - - if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - /* Maintain subsurfaces as 1-depth */ - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target; - surface->extents.x += sub->extents.x; - surface->extents.y += sub->extents.y; - target = sub->target; - } - - surface->target = cairo_surface_reference (target); - - return &surface->base; -} -/* XXX observe mark-dirty */ diff --git a/libs/cairo/cairo/src/cairo-surface-wrapper-private.h b/libs/cairo/cairo/src/cairo-surface-wrapper-private.h deleted file mode 100644 index c6d2bbe91..000000000 --- a/libs/cairo/cairo/src/cairo-surface-wrapper-private.h +++ /dev/null @@ -1,137 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H -#define CAIRO_SURFACE_WRAPPER_PRIVATE_H - -#include "cairo-types-private.h" - -CAIRO_BEGIN_DECLS - -struct _cairo_surface_wrapper { - cairo_surface_t *target; - - cairo_bool_t has_extents; - cairo_rectangle_int_t extents; -}; - -cairo_private void -_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, - cairo_surface_t *target); - -cairo_private void -_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, - const cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); - -cairo_private cairo_status_t -_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, - cairo_image_surface_t **image_out, - void **image_extra); - -cairo_private void -_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, - cairo_image_surface_t *image, - void *image_extra); - - -cairo_private cairo_status_t -_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); - -cairo_private cairo_surface_t * -_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, - cairo_content_t content, - int width, - int height); -cairo_private cairo_bool_t -_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, - cairo_font_options_t *options); - -cairo_private cairo_surface_t * -_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper); - -cairo_private cairo_bool_t -_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper); - -cairo_private cairo_status_t -_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper); - -static inline cairo_bool_t -_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) -{ - return wrapper->target != (cairo_surface_t *) 0; -} - -CAIRO_END_DECLS - -#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-surface-wrapper.c b/libs/cairo/cairo/src/cairo-surface-wrapper.c deleted file mode 100644 index 902e717cb..000000000 --- a/libs/cairo/cairo/src/cairo-surface-wrapper.c +++ /dev/null @@ -1,680 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-surface-wrapper-private.h" - -/* A collection of routines to facilitate surface wrapping */ - -static void -_copy_transformed_pattern (cairo_pattern_t *pattern, - const cairo_pattern_t *original, - const cairo_matrix_t *ctm_inverse) -{ - _cairo_pattern_init_static_copy (pattern, original); - - /* Device transform should already have been applied before cairo_surface_wrapper_* functions - * are called in _cairo_gstate_copy_transformed_pattern which all the gstate drawing - * functions call through _cairo_gstate_copy_transformed_source and such. */ - - if (! _cairo_matrix_is_identity (ctm_inverse)) - _cairo_pattern_transform (pattern, ctm_inverse); -} - -static inline cairo_bool_t -_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) -{ - return ! _cairo_matrix_is_identity (&wrapper->target->device_transform); -} - -static cairo_bool_t -_cairo_surface_wrapper_needs_extents_transform (cairo_surface_wrapper_t *wrapper) -{ - return wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y); -} - -cairo_status_t -_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, - cairo_image_surface_t **image_out, - void **image_extra) -{ - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - return _cairo_surface_acquire_source_image (wrapper->target, - image_out, image_extra); -} - -void -_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, - cairo_image_surface_t *image, - void *image_extra) -{ - _cairo_surface_release_source_image (wrapper->target, image, image_extra); -} - -cairo_status_t -_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - - status = _cairo_surface_paint (wrapper->target, op, source, dev_clip); - - FINISH: - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - return status; -} - -cairo_status_t -_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - cairo_pattern_union_t mask_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - - _copy_transformed_pattern (&mask_copy.base, mask, &m); - mask = &mask_copy.base; - } - - status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip); - - FINISH: - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - return status; -} - -cairo_status_t -_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_matrix_t dev_ctm = *ctm; - cairo_matrix_t dev_ctm_inverse = *ctm_inverse; - cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - goto FINISH; - - _cairo_path_fixed_transform (&path_copy, &m); - dev_path = &path_copy; - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); - - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } - - status = _cairo_surface_stroke (wrapper->target, op, source, - dev_path, stroke_style, - &dev_ctm, &dev_ctm_inverse, - tolerance, antialias, - dev_clip); - - FINISH: - if (dev_path != path) - _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - return status; -} - -cairo_status_t -_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_matrix_t dev_ctm = *stroke_ctm; - cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; - cairo_pattern_union_t stroke_source_copy; - cairo_pattern_union_t fill_source_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - goto FINISH; - - _cairo_path_fixed_transform (&path_copy, &m); - dev_path = &path_copy; - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); - - _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m); - stroke_source = &stroke_source_copy.base; - - _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); - fill_source = &fill_source_copy.base; - } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } - - status = _cairo_surface_fill_stroke (wrapper->target, - fill_op, fill_source, fill_rule, - fill_tolerance, fill_antialias, - dev_path, - stroke_op, stroke_source, - stroke_style, - &dev_ctm, &dev_ctm_inverse, - stroke_tolerance, stroke_antialias, - dev_clip); - - FINISH: - if (dev_path != path) - _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - return status; -} - -cairo_status_t -_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (unlikely (status)) - goto FINISH; - - _cairo_path_fixed_transform (&path_copy, &m); - dev_path = &path_copy; - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } - - status = _cairo_surface_fill (wrapper->target, op, source, - dev_path, fill_rule, - tolerance, antialias, - dev_clip); - - FINISH: - if (dev_path != path) - _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - return status; -} - -cairo_status_t -_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_glyph_t *dev_glyphs = glyphs; - cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; - - if (unlikely (wrapper->target->status)) - return wrapper->target->status; - - if (glyphs == NULL || num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; - - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { - cairo_matrix_t m; - int i; - - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - - dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (dev_glyphs == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FINISH; - } - - for (i = 0; i < num_glyphs; i++) { - dev_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); - } - - status = cairo_matrix_invert (&m); - assert (status == CAIRO_STATUS_SUCCESS); - - _copy_transformed_pattern (&source_copy.base, source, &m); - source = &source_copy.base; - } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } - - status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, - utf8, utf8_len, - dev_glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - dev_clip); - - FINISH: - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_glyphs != glyphs) - free (dev_glyphs); - return status; -} - -cairo_surface_t * -_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, - cairo_content_t content, - int width, - int height) -{ - return _cairo_surface_create_similar_scratch (wrapper->target, - content, width, height); -} - -cairo_bool_t -_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, - cairo_rectangle_int_t *extents) -{ - if (wrapper->has_extents) { - if (_cairo_surface_get_extents (wrapper->target, extents)) - _cairo_rectangle_intersect (extents, &wrapper->extents); - else - *extents = wrapper->extents; - - return TRUE; - } else { - return _cairo_surface_get_extents (wrapper->target, extents); - } -} - -void -_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, - const cairo_rectangle_int_t *extents) -{ - if (extents != NULL) { - wrapper->extents = *extents; - wrapper->has_extents = TRUE; - } else { - wrapper->has_extents = FALSE; - } -} - -void -_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, - cairo_font_options_t *options) -{ - cairo_surface_get_font_options (wrapper->target, options); -} - -cairo_surface_t * -_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) -{ - return _cairo_surface_snapshot (wrapper->target); -} - -cairo_bool_t -_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper) -{ - return cairo_surface_has_show_text_glyphs (wrapper->target); -} - -void -_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, - cairo_surface_t *target) -{ - wrapper->target = cairo_surface_reference (target); - wrapper->has_extents = FALSE; -} - -void -_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) -{ - cairo_surface_destroy (wrapper->target); -} - -cairo_status_t -_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper) -{ - if (wrapper->target->backend->flush) { - return wrapper->target->backend->flush(wrapper->target); - } - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-surface.c b/libs/cairo/cairo/src/cairo-surface.c deleted file mode 100644 index c80ea8553..000000000 --- a/libs/cairo/cairo/src/cairo-surface.c +++ /dev/null @@ -1,3292 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-surface-fallback-private.h" -#include "cairo-clip-private.h" -#include "cairo-device-private.h" -#include "cairo-error-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-region-private.h" -#include "cairo-tee-surface-private.h" - -/** - * SECTION:cairo-surface - * @Title: cairo_surface_t - * @Short_Description: Base class for surfaces - * @See_Also: #cairo_t, #cairo_pattern_t - * - * #cairo_surface_t is the abstract type representing all different drawing - * targets that cairo can render to. The actual drawings are - * performed using a cairo context. - * - * A cairo surface is created by using backend-specific - * constructors, typically of the form - * cairo_backend_surface_create(). - * - * Most surface types allow accessing the surface without using Cairo - * functions. If you do this, keep in mind that it is mandatory that you call - * cairo_surface_flush() before reading from or writing to the surface and that - * you must use cairo_surface_mark_dirty() after modifying it. - * - * Directly modifying an image surface - * - * void - * modify_image_surface (cairo_surface_t *surface) - * { - * unsigned char *data; - * int width, height, stride; - * - * // flush to ensure all writing to the image was done - * cairo_surface_flush (surface); - * - * // modify the image - * data = cairo_image_surface_get_data (surface); - * width = cairo_image_surface_get_width (surface); - * height = cairo_image_surface_get_height (surface); - * stride = cairo_image_surface_get_stride (surface); - * modify_image_data (data, width, height, stride); - * - * // mark the image dirty so Cairo clears its caches. - * cairo_surface_mark_dirty (surface); - * } - * - * - * Note that for other surface types it might be necessary to acquire the - * surface's device first. See cairo_device_acquire() for a discussion of - * devices. - */ - -#define DEFINE_NIL_SURFACE(status, name) \ -const cairo_surface_t name = { \ - NULL, /* backend */ \ - NULL, /* device */ \ - CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ - CAIRO_CONTENT_COLOR, /* content */ \ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ - status, /* status */ \ - 0, /* unique id */ \ - FALSE, /* finished */ \ - TRUE, /* is_clear */ \ - FALSE, /* has_font_options */ \ - FALSE, /* owns_device */ \ - FALSE, /* permit_subpixel_antialiasing */ \ - { 0, 0, 0, NULL, }, /* user_data */ \ - { 0, 0, 0, NULL, }, /* mime_data */ \ - { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ - { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ - { NULL, NULL }, /* device_transform_observers */ \ - 0.0, /* x_resolution */ \ - 0.0, /* y_resolution */ \ - 0.0, /* x_fallback_resolution */ \ - 0.0, /* y_fallback_resolution */ \ - NULL, /* snapshot_of */ \ - NULL, /* snapshot_detach */ \ - { NULL, NULL }, /* snapshots */ \ - { NULL, NULL }, /* snapshot */ \ - { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ - CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ - CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \ - CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ - CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \ - CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \ - } /* font_options */ \ -} - -/* XXX error object! */ - -static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch); -static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error); - -/** - * _cairo_surface_set_error: - * @surface: a surface - * @status: a status value indicating an error - * - * Atomically sets surface->status to @status and calls _cairo_error; - * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal - * status values. - * - * All assignments of an error status to surface->status should happen - * through _cairo_surface_set_error(). Note that due to the nature of - * the atomic operation, it is not safe to call this function on the - * nil objects. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - * - * Return value: the error status. - **/ -cairo_status_t -_cairo_surface_set_error (cairo_surface_t *surface, - cairo_status_t status) -{ - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - status = CAIRO_STATUS_SUCCESS; - - if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&surface->status, status); - - return _cairo_error (status); -} - -/** - * cairo_surface_get_type: - * @surface: a #cairo_surface_t - * - * This function returns the type of the backend used to create - * a surface. See #cairo_surface_type_t for available types. - * - * Return value: The type of @surface. - * - * Since: 1.2 - **/ -cairo_surface_type_t -cairo_surface_get_type (cairo_surface_t *surface) -{ - /* We don't use surface->backend->type here so that some of the - * special "wrapper" surfaces such as cairo_paginated_surface_t - * can override surface->type with the type of the "child" - * surface. */ - return surface->type; -} -slim_hidden_def (cairo_surface_get_type); - -/** - * cairo_surface_get_content: - * @surface: a #cairo_surface_t - * - * This function returns the content type of @surface which indicates - * whether the surface contains color and/or alpha information. See - * #cairo_content_t. - * - * Return value: The content type of @surface. - * - * Since: 1.2 - **/ -cairo_content_t -cairo_surface_get_content (cairo_surface_t *surface) -{ - return surface->content; -} -slim_hidden_def(cairo_surface_get_content); - -/** - * cairo_surface_status: - * @surface: a #cairo_surface_t - * - * Checks whether an error has previously occurred for this - * surface. - * - * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER, - * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR, - * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or - * %CAIRO_STATUS_INVALID_VISUAL. - **/ -cairo_status_t -cairo_surface_status (cairo_surface_t *surface) -{ - return surface->status; -} -slim_hidden_def (cairo_surface_status); - -static unsigned int -_cairo_surface_allocate_unique_id (void) -{ - static cairo_atomic_int_t unique_id; - -#if CAIRO_NO_MUTEX - if (++unique_id == 0) - unique_id = 1; - return unique_id; -#else - cairo_atomic_int_t old, id; - - do { - old = _cairo_atomic_uint_get (&unique_id); - id = old + 1; - if (id == 0) - id = 1; - } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id)); - - return id; -#endif -} - -/** - * cairo_surface_get_device: - * @surface: a #cairo_surface_t - * - * This function returns the device for a @surface. - * See #cairo_device_t. - * - * Return value: The device for @surface or %NULL if the surface does - * not have an associated device. - * - * Since: 1.10 - **/ -cairo_device_t * -cairo_surface_get_device (cairo_surface_t *surface) -{ - if (unlikely (surface->status)) - return _cairo_device_create_in_error (surface->status); - - return surface->device; -} - -static cairo_bool_t -_cairo_surface_has_snapshots (cairo_surface_t *surface) -{ - return ! cairo_list_is_empty (&surface->snapshots); -} - -static cairo_bool_t -_cairo_surface_has_mime_data (cairo_surface_t *surface) -{ - return surface->mime_data.num_elements != 0; -} - -static void -_cairo_surface_detach_mime_data (cairo_surface_t *surface) -{ - if (! _cairo_surface_has_mime_data (surface)) - return; - - _cairo_user_data_array_fini (&surface->mime_data); - _cairo_user_data_array_init (&surface->mime_data); -} - -static void -cairo_surface_detach_snapshots (cairo_surface_t *surface) -{ - while (_cairo_surface_has_snapshots (surface)) { - cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots, - cairo_surface_t, - snapshot)); - } -} - -void -cairo_surface_detach_snapshot (cairo_surface_t *snapshot) -{ - assert (snapshot->snapshot_of != NULL); - - snapshot->snapshot_of = NULL; - cairo_list_del (&snapshot->snapshot); - - if (snapshot->snapshot_detach != NULL) - snapshot->snapshot_detach (snapshot); - - cairo_surface_destroy (snapshot); -} - -void -cairo_surface_attach_snapshot (cairo_surface_t *surface, - cairo_surface_t *snapshot, - cairo_surface_func_t detach_func) -{ - assert (surface != snapshot); - assert (snapshot->snapshot_of != surface); - - cairo_surface_reference (snapshot); - - if (snapshot->snapshot_of != NULL) - cairo_surface_detach_snapshot (snapshot); - - snapshot->snapshot_of = surface; - snapshot->snapshot_detach = detach_func; - - cairo_list_add (&snapshot->snapshot, &surface->snapshots); - - assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot); -} - -cairo_surface_t * -_cairo_surface_has_snapshot (cairo_surface_t *surface, - const cairo_surface_backend_t *backend) -{ - cairo_surface_t *snapshot; - - cairo_list_foreach_entry (snapshot, cairo_surface_t, - &surface->snapshots, snapshot) - { - /* XXX is_similar? */ - if (snapshot->backend == backend) - return snapshot; - } - - return NULL; -} - -static cairo_bool_t -_cairo_surface_is_writable (cairo_surface_t *surface) -{ - return ! surface->finished && - surface->snapshot_of == NULL && - ! _cairo_surface_has_snapshots (surface) && - ! _cairo_surface_has_mime_data (surface); -} - -static void -_cairo_surface_begin_modification (cairo_surface_t *surface) -{ - assert (surface->status == CAIRO_STATUS_SUCCESS); - assert (! surface->finished); - assert (surface->snapshot_of == NULL); - - cairo_surface_detach_snapshots (surface); - _cairo_surface_detach_mime_data (surface); -} - -void -_cairo_surface_init (cairo_surface_t *surface, - const cairo_surface_backend_t *backend, - cairo_device_t *device, - cairo_content_t content) -{ - CAIRO_MUTEX_INITIALIZE (); - - surface->backend = backend; - surface->device = cairo_device_reference (device); - surface->content = content; - surface->type = backend->type; - - CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); - surface->status = CAIRO_STATUS_SUCCESS; - surface->unique_id = _cairo_surface_allocate_unique_id (); - surface->finished = FALSE; - surface->is_clear = FALSE; - surface->owns_device = (device != NULL); - surface->has_font_options = FALSE; - surface->permit_subpixel_antialiasing = TRUE; - - _cairo_user_data_array_init (&surface->user_data); - _cairo_user_data_array_init (&surface->mime_data); - - cairo_matrix_init_identity (&surface->device_transform); - cairo_matrix_init_identity (&surface->device_transform_inverse); - cairo_list_init (&surface->device_transform_observers); - - surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; - surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; - - surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; - surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; - - cairo_list_init (&surface->snapshots); - surface->snapshot_of = NULL; -} - -static void -_cairo_surface_copy_similar_properties (cairo_surface_t *surface, - cairo_surface_t *other) -{ - if (other->has_font_options || other->backend != surface->backend) { - cairo_font_options_t options; - - cairo_surface_get_font_options (other, &options); - _cairo_surface_set_font_options (surface, &options); - } - - surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; - - cairo_surface_set_fallback_resolution (surface, - other->x_fallback_resolution, - other->y_fallback_resolution); -} - -cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *surface; - - if (unlikely (other->status)) - return _cairo_surface_create_in_error (other->status); - - if (other->backend->create_similar == NULL) - return NULL; - - surface = other->backend->create_similar (other, - content, width, height); - if (surface == NULL || surface->status) - return surface; - - _cairo_surface_copy_similar_properties (surface, other); - - return surface; -} - -/** - * cairo_surface_create_similar: - * @other: an existing surface used to select the backend of the new surface - * @content: the content for the new surface - * @width: width of the new surface, (in device-space units) - * @height: height of the new surface (in device-space units) - * - * Create a new surface that is as compatible as possible with an - * existing surface. For example the new surface will have the same - * fallback resolution and font options as @other. Generally, the new - * surface will also use the same backend as @other, unless that is - * not possible for some reason. The type of the returned surface may - * be examined with cairo_surface_get_type(). - * - * Initially the surface contents are all 0 (transparent if contents - * have transparency, black otherwise.) - * - * Return value: a pointer to the newly allocated surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if @other is already in an error state - * or any other error occurs. - **/ -cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_content_t content, - int width, - int height) -{ - if (unlikely (other->status)) - return _cairo_surface_create_in_error (other->status); - if (unlikely (other->finished)) - return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); - - if (unlikely (! CAIRO_CONTENT_VALID (content))) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - return _cairo_surface_create_similar_solid (other, - content, width, height, - CAIRO_COLOR_TRANSPARENT, - TRUE); -} - -cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_content_t content, - int width, - int height, - const cairo_color_t *color, - cairo_bool_t allow_fallback) -{ - cairo_status_t status; - cairo_surface_t *surface; - cairo_solid_pattern_t pattern; - - surface = _cairo_surface_create_similar_scratch (other, content, - width, height); - if (surface == NULL && allow_fallback) - surface = _cairo_image_surface_create_with_content (content, - width, height); - if (surface == NULL || surface->status) - return surface; - - _cairo_pattern_init_solid (&pattern, color); - status = _cairo_surface_paint (surface, - color == CAIRO_COLOR_TRANSPARENT ? - CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); - if (unlikely (status)) { - cairo_surface_destroy (surface); - surface = _cairo_surface_create_in_error (status); - } - - return surface; -} - -cairo_surface_t * -_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - const cairo_solid_pattern_t *solid_pattern) -{ - if (other->backend->create_solid_pattern_surface != NULL) { - cairo_surface_t *surface; - - surface = other->backend->create_solid_pattern_surface (other, - solid_pattern); - if (surface) - return surface; - } - - return _cairo_surface_create_similar_solid (other, - _cairo_color_get_content (&solid_pattern->color), - 1, 1, - &solid_pattern->color, - FALSE); -} - -cairo_int_status_t -_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, - cairo_surface_t *solid_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - /* Solid pattern surface for these backends are special and not trivial - * to repaint. Skip repainting. - * - * This does not work optimally with things like analysis surface that - * are proxies. But returning UNSUPPORTED is *safe* as it only - * disables some caching. - */ - if (other->backend->create_solid_pattern_surface != NULL && - ! other->backend->can_repaint_solid_pattern_surface (solid_surface, - solid_pattern)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - return _cairo_surface_paint (solid_surface, - CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, - NULL); -} - -/** - * cairo_surface_reference: - * @surface: a #cairo_surface_t - * - * Increases the reference count on @surface by one. This prevents - * @surface from being destroyed until a matching call to - * cairo_surface_destroy() is made. - * - * The number of references to a #cairo_surface_t can be get using - * cairo_surface_get_reference_count(). - * - * Return value: the referenced #cairo_surface_t. - **/ -cairo_surface_t * -cairo_surface_reference (cairo_surface_t *surface) -{ - if (surface == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return surface; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); - - _cairo_reference_count_inc (&surface->ref_count); - - return surface; -} -slim_hidden_def (cairo_surface_reference); - -/** - * cairo_surface_destroy: - * @surface: a #cairo_surface_t - * - * Decreases the reference count on @surface by one. If the result is - * zero, then @surface and all associated resources are freed. See - * cairo_surface_reference(). - **/ -void -cairo_surface_destroy (cairo_surface_t *surface) -{ - if (surface == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&surface->ref_count)) - return; - - assert (surface->snapshot_of == NULL); - - if (! surface->finished) - cairo_surface_finish (surface); - - /* paranoid check that nobody took a reference whilst finishing */ - assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); - - _cairo_user_data_array_fini (&surface->user_data); - _cairo_user_data_array_fini (&surface->mime_data); - - if (surface->owns_device) - cairo_device_destroy (surface->device); - - free (surface); -} -slim_hidden_def(cairo_surface_destroy); - -/** - * cairo_surface_get_reference_count: - * @surface: a #cairo_surface_t - * - * Returns the current reference count of @surface. - * - * Return value: the current reference count of @surface. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.4 - **/ -unsigned int -cairo_surface_get_reference_count (cairo_surface_t *surface) -{ - if (surface == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count); -} - -/** - * cairo_surface_finish: - * @surface: the #cairo_surface_t to finish - * - * This function finishes the surface and drops all references to - * external resources. For example, for the Xlib backend it means - * that cairo will no longer access the drawable, which can be freed. - * After calling cairo_surface_finish() the only valid operations on a - * surface are getting and setting user, referencing and - * destroying, and flushing and finishing it. - * Further drawing to the surface will not affect the - * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED - * error. - * - * When the last call to cairo_surface_destroy() decreases the - * reference count to zero, cairo will call cairo_surface_finish() if - * it hasn't been called already, before freeing the resources - * associated with the surface. - **/ -void -cairo_surface_finish (cairo_surface_t *surface) -{ - cairo_status_t status; - - if (surface == NULL) - return; - - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return; - - if (surface->finished) - return; - - /* update the snapshots *before* we declare the surface as finished */ - cairo_surface_detach_snapshots (surface); - if (surface->snapshot_of != NULL) - cairo_surface_detach_snapshot (surface); - - cairo_surface_flush (surface); - surface->finished = TRUE; - - /* call finish even if in error mode */ - if (surface->backend->finish) { - status = surface->backend->finish (surface); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - } -} -slim_hidden_def (cairo_surface_finish); - -/** - * _cairo_surface_release_device_reference: - * @surface: a #cairo_surface_t - * - * This function makes @surface release the reference to its device. The - * function is intended to be used for avoiding cycling references for - * surfaces that are owned by their device, for example cache surfaces. - * Note that the @surface will still assume that the device is available. - * So it is the caller's responsibility to ensure the device stays around - * until the @surface is destroyed. Just calling cairo_surface_finish() is - * not enough. - **/ -void -_cairo_surface_release_device_reference (cairo_surface_t *surface) -{ - assert (surface->owns_device); - - cairo_device_destroy (surface->device); - surface->owns_device = FALSE; -} - -/** - * cairo_surface_get_user_data: - * @surface: a #cairo_surface_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @surface using the specified - * key. If no user data has been attached with the given key this - * function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - **/ -void * -cairo_surface_get_user_data (cairo_surface_t *surface, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&surface->user_data, - key); -} - -/** - * cairo_surface_set_user_data: - * @surface: a #cairo_surface_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the surface - * @destroy: a #cairo_destroy_func_t which will be called when the - * surface is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @surface. To remove user data from a surface, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - **/ -cairo_status_t -cairo_surface_set_user_data (cairo_surface_t *surface, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) - return surface->status; - - return _cairo_user_data_array_set_data (&surface->user_data, - key, user_data, destroy); -} - -/** - * cairo_surface_get_mime_data: - * @surface: a #cairo_surface_t - * @mime_type: the mime type of the image data - * @data: the image data to attached to the surface - * @length: the length of the image data - * - * Return mime data previously attached to @surface using the - * specified mime type. If no data has been attached with the given - * mime type, @data is set %NULL. - * - * Since: 1.10 - **/ -void -cairo_surface_get_mime_data (cairo_surface_t *surface, - const char *mime_type, - const unsigned char **data, - unsigned long *length) -{ - cairo_user_data_slot_t *slots; - int i, num_slots; - - *data = NULL; - *length = 0; - if (unlikely (surface->status)) - return; - - /* The number of mime-types attached to a surface is usually small, - * typically zero. Therefore it is quicker to do a strcmp() against - * each key than it is to intern the string (i.e. compute a hash, - * search the hash table, and do a final strcmp). - */ - num_slots = surface->mime_data.num_elements; - slots = _cairo_array_index (&surface->mime_data, 0); - for (i = 0; i < num_slots; i++) { - if (strcmp ((char *) slots[i].key, mime_type) == 0) { - cairo_mime_data_t *mime_data = slots[i].user_data; - - *data = mime_data->data; - *length = mime_data->length; - return; - } - } -} -slim_hidden_def (cairo_surface_get_mime_data); - -static void -_cairo_mime_data_destroy (void *ptr) -{ - cairo_mime_data_t *mime_data = ptr; - - if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count)) - return; - - if (mime_data->destroy && mime_data->closure) - mime_data->destroy (mime_data->closure); - - free (mime_data); -} - -/** - * CAIRO_MIME_TYPE_JP2: - * - * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1). - * - * @Since: 1.10 - */ - -/** - * CAIRO_MIME_TYPE_JPEG: - * - * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1). - * - * @Since: 1.10 - */ - -/** - * CAIRO_MIME_TYPE_PNG: - * - * The Portable Network Graphics image file format (ISO/IEC 15948). - * - * @Since: 1.10 - */ - -/** - * CAIRO_MIME_TYPE_URI: - * - * URI for an image file (unofficial MIME type). - * - * @Since: 1.10 - */ - -/** - * cairo_surface_set_mime_data: - * @surface: a #cairo_surface_t - * @mime_type: the MIME type of the image data - * @data: the image data to attach to the surface - * @length: the length of the image data - * @destroy: a #cairo_destroy_func_t which will be called when the - * surface is destroyed or when new image data is attached using the - * same mime type. - * @closure: the data to be passed to the @destroy notifier - * - * Attach an image in the format @mime_type to @surface. To remove - * the data from a surface, call this function with same mime type - * and %NULL for @data. - * - * The attached image (or filename) data can later be used by backends - * which support it (currently: PDF, PS, SVG and Win32 Printing - * surfaces) to emit this data instead of making a snapshot of the - * @surface. This approach tends to be faster and requires less - * memory and disk space. - * - * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG, - * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI. - * - * See corresponding backend surface docs for details about which MIME - * types it can handle. Caution: the associated MIME data will be - * discarded if you draw on the surface afterwards. Use this function - * with care. - * - * Since: 1.10 - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - **/ -cairo_status_t -cairo_surface_set_mime_data (cairo_surface_t *surface, - const char *mime_type, - const unsigned char *data, - unsigned long length, - cairo_destroy_func_t destroy, - void *closure) -{ - cairo_status_t status; - cairo_mime_data_t *mime_data; - - if (unlikely (surface->status)) - return surface->status; - if (surface->finished) - return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - - status = _cairo_intern_string (&mime_type, -1); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - if (data != NULL) { - mime_data = malloc (sizeof (cairo_mime_data_t)); - if (unlikely (mime_data == NULL)) - return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY)); - - CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1); - - mime_data->data = (unsigned char *) data; - mime_data->length = length; - mime_data->destroy = destroy; - mime_data->closure = closure; - } else - mime_data = NULL; - - status = _cairo_user_data_array_set_data (&surface->mime_data, - (cairo_user_data_key_t *) mime_type, - mime_data, - _cairo_mime_data_destroy); - if (unlikely (status)) { - if (mime_data != NULL) - free (mime_data); - - return _cairo_surface_set_error (surface, status); - } - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def (cairo_surface_set_mime_data); - -static void -_cairo_mime_data_reference (const void *key, void *elt, void *closure) -{ - cairo_mime_data_t *mime_data = elt; - - _cairo_reference_count_inc (&mime_data->ref_count); -} - -cairo_status_t -_cairo_surface_copy_mime_data (cairo_surface_t *dst, - cairo_surface_t *src) -{ - cairo_status_t status; - - if (dst->status) - return dst->status; - - if (src->status) - return _cairo_surface_set_error (dst, src->status); - - /* first copy the mime-data, discarding any already set on dst */ - status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data); - if (unlikely (status)) - return _cairo_surface_set_error (dst, status); - - /* now increment the reference counters for the copies */ - _cairo_user_data_array_foreach (&dst->mime_data, - _cairo_mime_data_reference, - NULL); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_set_font_options: - * @surface: a #cairo_surface_t - * @options: a #cairo_font_options_t object that contains the - * options to use for this surface instead of backend's default - * font options. - * - * Sets the default font rendering options for the surface. - * This is useful to correctly propagate default font options when - * falling back to an image surface in a backend implementation. - * This affects the options returned in cairo_surface_get_font_options(). - * - * If @options is %NULL the surface options are reset to those of - * the backend default. - **/ -void -_cairo_surface_set_font_options (cairo_surface_t *surface, - cairo_font_options_t *options) -{ - cairo_status_t status; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (options) { - surface->has_font_options = TRUE; - _cairo_font_options_init_copy (&surface->font_options, options); - } else { - surface->has_font_options = FALSE; - } -} - -/** - * cairo_surface_get_font_options: - * @surface: a #cairo_surface_t - * @options: a #cairo_font_options_t object into which to store - * the retrieved options. All existing values are overwritten - * - * Retrieves the default font rendering options for the surface. - * This allows display surfaces to report the correct subpixel order - * for rendering on them, print surfaces to disable hinting of - * metrics and so forth. The result can then be used with - * cairo_scaled_font_create(). - **/ -void -cairo_surface_get_font_options (cairo_surface_t *surface, - cairo_font_options_t *options) -{ - if (cairo_font_options_status (options)) - return; - - if (surface->status) { - _cairo_font_options_init_default (options); - return; - } - - if (! surface->has_font_options) { - surface->has_font_options = TRUE; - - _cairo_font_options_init_default (&surface->font_options); - - if (!surface->finished && surface->backend->get_font_options) { - surface->backend->get_font_options (surface, &surface->font_options); - } - } - - _cairo_font_options_init_copy (options, &surface->font_options); -} -slim_hidden_def (cairo_surface_get_font_options); - -/** - * cairo_surface_flush: - * @surface: a #cairo_surface_t - * - * Do any pending drawing for the surface and also restore any - * temporary modifications cairo has made to the surface's - * state. This function must be called before switching from - * drawing on the surface with cairo to drawing on it directly - * with native APIs. If the surface doesn't support direct access, - * then this function does nothing. - **/ -void -cairo_surface_flush (cairo_surface_t *surface) -{ - cairo_status_t status; - - if (surface->status) - return; - - if (surface->finished) - return; - - /* update the current snapshots *before* the user updates the surface */ - cairo_surface_detach_snapshots (surface); - - if (surface->backend->flush) { - status = surface->backend->flush (surface); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - } -} -slim_hidden_def (cairo_surface_flush); - -/** - * cairo_surface_mark_dirty: - * @surface: a #cairo_surface_t - * - * Tells cairo that drawing has been done to surface using means other - * than cairo, and that cairo should reread any cached areas. Note - * that you must call cairo_surface_flush() before doing such drawing. - */ -void -cairo_surface_mark_dirty (cairo_surface_t *surface) -{ - cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1); -} -slim_hidden_def (cairo_surface_mark_dirty); - -/** - * cairo_surface_mark_dirty_rectangle: - * @surface: a #cairo_surface_t - * @x: X coordinate of dirty rectangle - * @y: Y coordinate of dirty rectangle - * @width: width of dirty rectangle - * @height: height of dirty rectangle - * - * Like cairo_surface_mark_dirty(), but drawing has been done only to - * the specified rectangle, so that cairo can retain cached contents - * for other parts of the surface. - * - * Any cached clip set on the surface will be reset by this function, - * to make sure that future cairo calls have the clip set that they - * expect. - */ -void -cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, - int x, - int y, - int width, - int height) -{ - cairo_status_t status; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - /* The application *should* have called cairo_surface_flush() before - * modifying the surface independently of cairo (and thus having to - * call mark_dirty()). */ - assert (! _cairo_surface_has_snapshots (surface)); - assert (! _cairo_surface_has_mime_data (surface)); - - surface->is_clear = FALSE; - - if (surface->backend->mark_dirty_rectangle != NULL) { - /* XXX: FRAGILE: We're ignoring the scaling component of - * device_transform here. I don't know what the right thing to - * do would actually be if there were some scaling here, but - * we avoid this since device_transfom scaling is not exported - * publicly and mark_dirty is not used internally. */ - status = surface->backend->mark_dirty_rectangle (surface, - x + surface->device_transform.x0, - y + surface->device_transform.y0, - width, height); - - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - } -} -slim_hidden_def (cairo_surface_mark_dirty_rectangle); - -/** - * _cairo_surface_set_device_scale: - * @surface: a #cairo_surface_t - * @sx: a scale factor in the X direction - * @sy: a scale factor in the Y direction - * - * Private function for setting an extra scale factor to affect all - * drawing to a surface. This is used, for example, when replaying a - * recording surface to an image fallback intended for an eventual - * vector-oriented backend. Since the recording surface will record - * coordinates in one backend space, but the image fallback uses a - * different backend space, (differing by the fallback resolution - * scale factors), we need a scale factor correction. - * - * Caution: Not all places we use device transform correctly handle - * both a translate and a scale. An audit would be nice. - **/ -void -_cairo_surface_set_device_scale (cairo_surface_t *surface, - double sx, - double sy) -{ - cairo_status_t status; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - _cairo_surface_begin_modification (surface); - - surface->device_transform.xx = sx; - surface->device_transform.yy = sy; - surface->device_transform.xy = 0.0; - surface->device_transform.yx = 0.0; - - surface->device_transform_inverse = surface->device_transform; - status = cairo_matrix_invert (&surface->device_transform_inverse); - /* should always be invertible unless given pathological input */ - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_observers_notify (&surface->device_transform_observers, surface); -} - -/** - * cairo_surface_set_device_offset: - * @surface: a #cairo_surface_t - * @x_offset: the offset in the X direction, in device units - * @y_offset: the offset in the Y direction, in device units - * - * Sets an offset that is added to the device coordinates determined - * by the CTM when drawing to @surface. One use case for this function - * is when we want to create a #cairo_surface_t that redirects drawing - * for a portion of an onscreen surface to an offscreen surface in a - * way that is completely invisible to the user of the cairo - * API. Setting a transformation via cairo_translate() isn't - * sufficient to do this, since functions like - * cairo_device_to_user() will expose the hidden offset. - * - * Note that the offset affects drawing to the surface as well as - * using the surface in a source pattern. - **/ -void -cairo_surface_set_device_offset (cairo_surface_t *surface, - double x_offset, - double y_offset) -{ - cairo_status_t status; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - _cairo_surface_begin_modification (surface); - - surface->device_transform.x0 = x_offset; - surface->device_transform.y0 = y_offset; - - surface->device_transform_inverse = surface->device_transform; - status = cairo_matrix_invert (&surface->device_transform_inverse); - /* should always be invertible unless given pathological input */ - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_observers_notify (&surface->device_transform_observers, surface); -} -slim_hidden_def (cairo_surface_set_device_offset); - -/** - * cairo_surface_get_device_offset: - * @surface: a #cairo_surface_t - * @x_offset: the offset in the X direction, in device units - * @y_offset: the offset in the Y direction, in device units - * - * This function returns the previous device offset set by - * cairo_surface_set_device_offset(). - * - * Since: 1.2 - **/ -void -cairo_surface_get_device_offset (cairo_surface_t *surface, - double *x_offset, - double *y_offset) -{ - if (x_offset) - *x_offset = surface->device_transform.x0; - if (y_offset) - *y_offset = surface->device_transform.y0; -} -slim_hidden_def (cairo_surface_get_device_offset); - -/** - * cairo_surface_set_fallback_resolution: - * @surface: a #cairo_surface_t - * @x_pixels_per_inch: horizontal setting for pixels per inch - * @y_pixels_per_inch: vertical setting for pixels per inch - * - * Set the horizontal and vertical resolution for image fallbacks. - * - * When certain operations aren't supported natively by a backend, - * cairo will fallback by rendering operations to an image and then - * overlaying that image onto the output. For backends that are - * natively vector-oriented, this function can be used to set the - * resolution used for these image fallbacks, (larger values will - * result in more detailed images, but also larger file sizes). - * - * Some examples of natively vector-oriented backends are the ps, pdf, - * and svg backends. - * - * For backends that are natively raster-oriented, image fallbacks are - * still possible, but they are always performed at the native - * device resolution. So this function has no effect on those - * backends. - * - * Note: The fallback resolution only takes effect at the time of - * completing a page (with cairo_show_page() or cairo_copy_page()) so - * there is currently no way to have more than one fallback resolution - * in effect on a single page. - * - * The default fallback resoultion is 300 pixels per inch in both - * dimensions. - * - * Since: 1.2 - **/ -void -cairo_surface_set_fallback_resolution (cairo_surface_t *surface, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_status_t status; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) { - /* XXX Could delay raising the error until we fallback, but throwing - * the error here means that we can catch the real culprit. - */ - status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); - return; - } - - _cairo_surface_begin_modification (surface); - - surface->x_fallback_resolution = x_pixels_per_inch; - surface->y_fallback_resolution = y_pixels_per_inch; -} -slim_hidden_def (cairo_surface_set_fallback_resolution); - -/** - * cairo_surface_get_fallback_resolution: - * @surface: a #cairo_surface_t - * @x_pixels_per_inch: horizontal pixels per inch - * @y_pixels_per_inch: vertical pixels per inch - * - * This function returns the previous fallback resolution set by - * cairo_surface_set_fallback_resolution(), or default fallback - * resolution if never set. - * - * Since: 1.8 - **/ -void -cairo_surface_get_fallback_resolution (cairo_surface_t *surface, - double *x_pixels_per_inch, - double *y_pixels_per_inch) -{ - if (x_pixels_per_inch) - *x_pixels_per_inch = surface->x_fallback_resolution; - if (y_pixels_per_inch) - *y_pixels_per_inch = surface->y_fallback_resolution; -} - -int -_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface) -{ - return surface->backend->fill == NULL ? 10240 : 256; -} - -cairo_bool_t -_cairo_surface_has_device_transform (cairo_surface_t *surface) -{ - return ! _cairo_matrix_is_identity (&surface->device_transform); -} - -/** - * _cairo_surface_acquire_source_image: - * @surface: a #cairo_surface_t - * @image_out: location to store a pointer to an image surface that - * has identical contents to @surface. This surface could be @surface - * itself, a surface held internal to @surface, or it could be a new - * surface with a copy of the relevant portion of @surface. - * @image_extra: location to store image specific backend data - * - * Gets an image surface to use when drawing as a fallback when drawing with - * @surface as a source. _cairo_surface_release_source_image() must be called - * when finished. - * - * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out. - * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified - * surface. Or %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -_cairo_surface_acquire_source_image (cairo_surface_t *surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - assert (!surface->finished); - - if (surface->backend->acquire_source_image == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = surface->backend->acquire_source_image (surface, - image_out, image_extra); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - if (PIXMAN_FORMAT_BPP((*image_out)->pixman_format) == 0) { - volatile char* acquire_source_image_ptr[10]; - volatile char* crasher; - int i; - for (i = 0; i < 10; i++) { - acquire_source_image_ptr[i] = (char*)surface->backend->acquire_source_image; - } - crasher = NULL; - *crasher = acquire_source_image_ptr[5]; - } - _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_release_source_image: - * @surface: a #cairo_surface_t - * @image_extra: same as return from the matching _cairo_surface_acquire_source_image() - * - * Releases any resources obtained with _cairo_surface_acquire_source_image() - **/ -void -_cairo_surface_release_source_image (cairo_surface_t *surface, - cairo_image_surface_t *image, - void *image_extra) -{ - assert (!surface->finished); - - if (surface->backend->release_source_image) - surface->backend->release_source_image (surface, image, image_extra); -} - -/** - * _cairo_surface_acquire_dest_image: - * @surface: a #cairo_surface_t - * @interest_rect: area of @surface for which fallback drawing is being done. - * A value of %NULL indicates that the entire surface is desired. - * XXXX I'd like to get rid of being able to pass %NULL here (nothing seems to) - * @image_out: location to store a pointer to an image surface that includes at least - * the intersection of @interest_rect with the visible area of @surface. - * This surface could be @surface itself, a surface held internal to @surface, - * or it could be a new surface with a copy of the relevant portion of @surface. - * If a new surface is created, it should have the same channels and depth - * as @surface so that copying to and from it is exact. - * @image_rect: location to store area of the original surface occupied - * by the surface stored in @image. - * @image_extra: location to store image specific backend data - * - * Retrieves a local image for a surface for implementing a fallback drawing - * operation. After calling this function, the implementation of the fallback - * drawing operation draws the primitive to the surface stored in @image_out - * then calls _cairo_surface_release_dest_image(), - * which, if a temporary surface was created, copies the bits back to the - * main surface and frees the temporary surface. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. - * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that - * the backend can't draw with fallbacks. It's possible for the routine - * to store %NULL in @local_out and return %CAIRO_STATUS_SUCCESS; - * that indicates that no part of @interest_rect is visible, so no drawing - * is necessary. _cairo_surface_release_dest_image() should not be called in that - * case. - **/ -cairo_status_t -_cairo_surface_acquire_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - if (surface->backend->acquire_dest_image == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = surface->backend->acquire_dest_image (surface, - interest_rect, - image_out, - image_rect, - image_extra); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_release_dest_image: - * @surface: a #cairo_surface_t - * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() - * @image: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() - * - * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if - * necessary, copying the image from @image back to @surface and freeing any - * resources that were allocated. - **/ -void -_cairo_surface_release_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - assert (_cairo_surface_is_writable (surface)); - - if (surface->backend->release_dest_image) - surface->backend->release_dest_image (surface, interest_rect, - image, image_rect, image_extra); -} - -static cairo_status_t -_cairo_recording_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_recording_surface_t *recorder = (cairo_recording_surface_t *) src; - cairo_surface_t *similar; - cairo_status_t status; - - similar = _cairo_surface_has_snapshot (src, surface->backend); - if (similar != NULL) { - *clone_out = cairo_surface_reference (similar); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (recorder->unbounded || - width*height*8 < recorder->extents.width*recorder->extents.height) - { - similar = _cairo_surface_create_similar_solid (surface, - src->content, - width, height, - CAIRO_COLOR_TRANSPARENT, - FALSE); - if (similar == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (similar->status)) - return similar->status; - - cairo_surface_set_device_offset (similar, -src_x, -src_y); - - status = _cairo_recording_surface_replay (src, similar); - if (unlikely (status)) { - cairo_surface_destroy (similar); - return status; - } - } else { - similar = _cairo_surface_create_similar_scratch (surface, - src->content, - recorder->extents.width, - recorder->extents.height); - if (similar == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (similar->status)) - return similar->status; - - status = _cairo_recording_surface_replay (src, similar); - if (unlikely (status)) { - cairo_surface_destroy (similar); - return status; - } - - cairo_surface_attach_snapshot (src, similar, NULL); - - src_x = src_y = 0; - } - - *clone_out = similar; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - return CAIRO_STATUS_SUCCESS; -} - -struct acquire_source_image_data -{ - cairo_surface_t *src; - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -_wrap_release_source_image (void *data) -{ - struct acquire_source_image_data *acquire_data = data; - _cairo_surface_release_source_image (acquire_data->src, - acquire_data->image, - acquire_data->image_extra); - free(data); -} - -static cairo_status_t -_wrap_image (cairo_surface_t *src, - cairo_image_surface_t *image, - void *image_extra, - cairo_image_surface_t **out) -{ - static cairo_user_data_key_t wrap_image_key; - cairo_image_surface_t *surface; - cairo_status_t status; - - struct acquire_source_image_data *data = malloc (sizeof (*data)); - if (unlikely (data == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - data->src = src; - data->image = image; - data->image_extra = image_extra; - - surface = (cairo_image_surface_t*) - _cairo_image_surface_create_with_pixman_format (image->data, - image->pixman_format, - image->width, - image->height, - image->stride); - status = surface->base.status; - if (status) { - free (data); - return status; - } - - status = _cairo_user_data_array_set_data (&surface->base.user_data, - &wrap_image_key, - data, - _wrap_release_source_image); - if (status) { - cairo_surface_destroy (&surface->base); - free (data); - return status; - } - - pixman_image_set_component_alpha ( - surface->pixman_image, - pixman_image_get_component_alpha (image->pixman_image)); - - *out = surface; - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_clone_similar: - * @surface: a #cairo_surface_t - * @src: the source image - * @src_x: extent for the rectangle in src we actually care about - * @src_y: extent for the rectangle in src we actually care about - * @width: extent for the rectangle in src we actually care about - * @height: extent for the rectangle in src we actually care about - * @clone_out: location to store a surface compatible with @surface - * and with contents identical to @src. The caller must call - * cairo_surface_destroy() on the result. - * - * Creates a surface with contents identical to @src but that - * can be used efficiently with @surface. If @surface and @src are - * already compatible then it may return a new reference to @src. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored - * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another - * error like %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -_cairo_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_image_surface_t *image; - void *image_extra; - - if (unlikely (surface->status)) - return surface->status; - - if (unlikely (surface->finished)) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - -#if CAIRO_HAS_TEE_SURFACE - - if (src->type == CAIRO_SURFACE_TYPE_TEE) { - cairo_surface_t *match; - - match = _cairo_tee_surface_find_match (src, - surface->backend, - src->content); - if (match != NULL) - src = match; - } - -#endif - - if (surface->backend->clone_similar != NULL) { - status = surface->backend->clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - if (_cairo_surface_is_image (src)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* First check to see if we can replay to a similar surface */ - if (_cairo_surface_is_recording (src)) { - return _cairo_recording_surface_clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - } - - /* If we failed, try again with an image surface */ - status = _cairo_surface_acquire_source_image (src, &image, &image_extra); - if (status == CAIRO_STATUS_SUCCESS) { - status = _wrap_image(src, image, image_extra, &image); - if (status != CAIRO_STATUS_SUCCESS) { - _cairo_surface_release_source_image (src, image, image_extra); - } else { - status = - surface->backend->clone_similar (surface, &image->base, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - cairo_surface_destroy(&image->base); - } - } - } - } - - /* If we're still unsupported, hit our fallback path to get a clone */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = - _cairo_surface_fallback_clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - } - - if (unlikely (status)) - return status; - - /* Update the clone's device_transform (which the underlying surface - * backend knows nothing about) */ - if (*clone_out != src) { - (*clone_out)->device_transform = src->device_transform; - (*clone_out)->device_transform_inverse = src->device_transform_inverse; - } - - return status; -} - -/** - * _cairo_surface_is_similar - * @surface_a: a #cairo_surface_t - * @surface_b: a #cairo_surface_t - * @content: a #cairo_content_t - * - * Find out whether the given surfaces share the same backend, - * and if so, whether they can be considered similar. - * - * The definition of "similar" depends on the backend. In - * general, it means that the surface is equivalent to one - * that would have been generated by a call to cairo_surface_create_similar(). - * - * Return value: %TRUE if the surfaces are similar. - **/ -cairo_bool_t -_cairo_surface_is_similar (cairo_surface_t *surface_a, - cairo_surface_t *surface_b) -{ - if (surface_a->backend != surface_b->backend) - return FALSE; - - if (surface_a->backend->is_similar != NULL) - return surface_a->backend->is_similar (surface_a, surface_b); - - return TRUE; -} - -cairo_status_t -_cairo_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_int_status_t status; - - if (unlikely (dst->status)) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - if (mask) { - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - } - - if (dst->backend->composite) { - status = dst->backend->composite (op, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (dst, status); - } - - return _cairo_surface_set_error (dst, - _cairo_surface_fallback_composite (op, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region)); -} - -/** - * _cairo_surface_fill_rectangle: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the rectangle - * @color: the source color - * @x: X coordinate of rectangle, in backend coordinates - * @y: Y coordinate of rectangle, in backend coordinates - * @width: width of rectangle, in backend coordinates - * @height: height of rectangle, in backend coordinates - * - * Applies an operator to a rectangle using a solid color as the source. - * See _cairo_surface_fill_rectangles() for full details. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - int x, - int y, - int width, - int height) -{ - cairo_rectangle_int_t rect; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - rect.x = x; - rect.y = y; - rect.width = width; - rect.height = height; - - return _cairo_surface_fill_rectangles (surface, op, color, &rect, 1); -} - -/** - * _cairo_surface_fill_region: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the region - * @color: the source color - * @region: the region to modify, in backend coordinates - * - * Applies an operator to a set of rectangles specified as a - * #cairo_region_t using a solid color as the source. - * See _cairo_surface_fill_rectangles() for full details. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_region (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_region_t *region) -{ - int num_rects; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - num_rects = cairo_region_num_rectangles (region); - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - - /* catch a common reduction of _cairo_clip_combine_with_surface() */ - if (op == CAIRO_OPERATOR_IN && - _cairo_color_equal (color, CAIRO_COLOR_WHITE)) - { - return CAIRO_STATUS_SUCCESS; - } - - if (num_rects > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (num_rects, - sizeof (cairo_rectangle_int_t)); - if (rects == NULL) { - return _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - for (i = 0; i < num_rects; i++) - cairo_region_get_rectangle (region, i, &rects[i]); - - status = _cairo_surface_fill_rectangles (surface, - op, color, rects, num_rects); - - if (rects != stack_rects) - free (rects); - - return _cairo_surface_set_error (surface, status); -} - -/** - * _cairo_surface_fill_rectangles: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the region - * @color: the source color - * @rects: the rectangles to modify, in backend coordinates - * @num_rects: the number of rectangles in @rects - * - * Applies an operator to a set of rectangles using a solid color - * as the source. Note that even if the operator is an unbounded operator - * such as %CAIRO_OPERATOR_IN, only the given set of rectangles - * is affected. This differs from _cairo_surface_composite_trapezoids() - * where the entire destination rectangle is cleared. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_int_status_t status; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - - if (surface->backend->fill_rectangles) { - status = surface->backend->fill_rectangles (surface, - op, color, - rects, num_rects); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (surface, status); - } - - return _cairo_surface_set_error (surface, - _cairo_surface_fallback_fill_rectangles (surface, - op, color, - rects, num_rects)); -} - -static cairo_status_t -_pattern_has_error (const cairo_pattern_t *pattern) -{ - const cairo_surface_pattern_t *spattern; - - if (unlikely (pattern->status)) - return pattern->status; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_STATUS_SUCCESS; - - spattern = (const cairo_surface_pattern_t *) pattern; - if (unlikely (spattern->surface->status)) - return spattern->surface->status; - - if (unlikely (spattern->surface->finished)) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_rectangle_int_t extents; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->paint != NULL) { - status = surface->backend->paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_paint (surface, op, source, clip); - - FINISH: - surface->is_clear = op == CAIRO_OPERATOR_CLEAR && - (clip == NULL || - (_cairo_surface_get_extents (surface, &extents) && - _cairo_clip_contains_rectangle (clip, &extents))); - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - /* If the mask is blank, this is just an expensive no-op */ - if (_cairo_pattern_is_clear (mask) && - _cairo_operator_bounded_by_mask (op)) - { - return CAIRO_STATUS_SUCCESS; - } - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - status = _pattern_has_error (mask); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->mask != NULL) { - status = surface->backend->mask (surface, op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_mask (surface, op, source, mask, clip); - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_fill_stroke (cairo_surface_t *surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (surface->is_clear && - fill_op == CAIRO_OPERATOR_CLEAR && - stroke_op == CAIRO_OPERATOR_CLEAR) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (fill_source); - if (unlikely (status)) - return status; - - status = _pattern_has_error (stroke_source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->fill_stroke) { - cairo_matrix_t dev_ctm = *stroke_ctm; - cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; - - status = surface->backend->fill_stroke (surface, - fill_op, fill_source, fill_rule, - fill_tolerance, fill_antialias, - path, - stroke_op, stroke_source, - stroke_style, - &dev_ctm, &dev_ctm_inverse, - stroke_tolerance, stroke_antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fill (surface, fill_op, fill_source, path, - fill_rule, fill_tolerance, fill_antialias, - clip); - if (unlikely (status)) - goto FINISH; - - status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, - stroke_style, stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, stroke_antialias, - clip); - if (unlikely (status)) - goto FINISH; - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->stroke != NULL) { - status = surface->backend->stroke (surface, op, source, - path, stroke_style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_stroke (surface, op, source, - path, stroke_style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->fill != NULL) { - status = surface->backend->fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_int_status_t status; - - if (dst->status) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - - if (dst->backend->composite_trapezoids) { - status = dst->backend->composite_trapezoids (op, - pattern, dst, - antialias, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (dst, status); - } - - return _cairo_surface_set_error (dst, - _cairo_surface_fallback_composite_trapezoids (op, pattern, dst, - antialias, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps, - clip_region)); -} - -cairo_span_renderer_t * -_cairo_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) -{ - assert (dst->snapshot_of == NULL); - - if (unlikely (dst->status)) - return _cairo_span_renderer_create_in_error (dst->status); - - if (unlikely (dst->finished)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); - - if (dst->backend->create_span_renderer) { - return dst->backend->create_span_renderer (op, - pattern, dst, - antialias, - rects, - clip_region); - } - ASSERT_NOT_REACHED; - return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED); -} - -cairo_bool_t -_cairo_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias) -{ - assert (dst->snapshot_of == NULL); - assert (dst->status == CAIRO_STATUS_SUCCESS); - assert (! dst->finished); - - /* XXX: Currently we have no mono span renderer */ - if (antialias == CAIRO_ANTIALIAS_NONE) - return FALSE; - - if (dst->backend->check_span_renderer != NULL) - return dst->backend->check_span_renderer (op, pattern, dst, antialias); - - return FALSE; -} - -/** - * cairo_surface_copy_page: - * @surface: a #cairo_surface_t - * - * Emits the current page for backends that support multiple pages, - * but doesn't clear it, so that the contents of the current page will - * be retained for the next page. Use cairo_surface_show_page() if you - * want to get an empty page after the emission. - * - * There is a convenience function for this that takes a #cairo_t, - * namely cairo_copy_page(). - * - * Since: 1.6 - */ -void -cairo_surface_copy_page (cairo_surface_t *surface) -{ - cairo_status_t status_ignored; - - if (surface->status) - return; - - assert (surface->snapshot_of == NULL); - - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); - return; - } - - /* It's fine if some backends don't implement copy_page */ - if (surface->backend->copy_page == NULL) - return; - - status_ignored = _cairo_surface_set_error (surface, - surface->backend->copy_page (surface)); -} -slim_hidden_def (cairo_surface_copy_page); - -/** - * cairo_surface_show_page: - * @surface: a #cairo_Surface_t - * - * Emits and clears the current page for backends that support multiple - * pages. Use cairo_surface_copy_page() if you don't want to clear the page. - * - * There is a convenience function for this that takes a #cairo_t, - * namely cairo_show_page(). - * - * Since: 1.6 - **/ -void -cairo_surface_show_page (cairo_surface_t *surface) -{ - cairo_status_t status_ignored; - - if (surface->status) - return; - - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); - return; - } - - _cairo_surface_begin_modification (surface); - - /* It's fine if some backends don't implement show_page */ - if (surface->backend->show_page == NULL) - return; - - status_ignored = _cairo_surface_set_error (surface, - surface->backend->show_page (surface)); -} -slim_hidden_def (cairo_surface_show_page); - -/** - * _cairo_surface_get_extents: - * @surface: the #cairo_surface_t to fetch extents for - * - * This function returns a bounding box for the surface. The surface - * bounds are defined as a region beyond which no rendering will - * possibly be recorded, in other words, it is the maximum extent of - * potentially usable coordinates. - * - * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface - * might be conceived as unbounded, but we force the user to provide a - * maximum size at the time of surface_create. So get_extents uses - * that size. - * - * Note: The coordinates returned are in "backend" space rather than - * "surface" space. That is, they are relative to the true (0,0) - * origin rather than the device_transform origin. This might seem a - * bit inconsistent with other #cairo_surface_t interfaces, but all - * current callers are within the surface layer where backend space is - * desired. - * - * This behavior would have to be changed is we ever exported a public - * variant of this function. - */ -cairo_bool_t -_cairo_surface_get_extents (cairo_surface_t *surface, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t bounded; - - bounded = FALSE; - if (! surface->status && surface->backend->get_extents != NULL) - bounded = surface->backend->get_extents (surface, extents); - - if (! bounded) - _cairo_unbounded_rectangle_init (extents); - - return bounded; -} - -/** - * cairo_surface_has_show_text_glyphs: - * @surface: a #cairo_surface_t - * - * Returns whether the surface supports - * sophisticated cairo_show_text_glyphs() operations. That is, - * whether it actually uses the provided text and cluster data - * to a cairo_show_text_glyphs() call. - * - * Note: Even if this function returns %FALSE, a - * cairo_show_text_glyphs() operation targeted at @surface will - * still succeed. It just will - * act like a cairo_show_glyphs() operation. Users can use this - * function to avoid computing UTF-8 text and cluster mapping if the - * target surface does not use it. - * - * Return value: %TRUE if @surface supports - * cairo_show_text_glyphs(), %FALSE otherwise - * - * Since: 1.8 - **/ -cairo_bool_t -cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) -{ - cairo_status_t status_ignored; - - if (surface->status) - return FALSE; - - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); - return FALSE; - } - - if (surface->backend->has_show_text_glyphs) - return surface->backend->has_show_text_glyphs (surface); - else - return surface->backend->show_text_glyphs != NULL; -} -slim_hidden_def (cairo_surface_has_show_text_glyphs); - -/** - * cairo_surface_set_subpixel_antialiasing: - * @surface: a #cairo_surface_t - * - * Sets whether the surface permits subpixel antialiasing. By default, - * surfaces permit subpixel antialiasing. - * - * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally - * requires that the pixels in the areas under a subpixel antialiasing - * operation already be opaque. - * - * Since: 1.12 - **/ -void -cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, - cairo_subpixel_antialiasing_t enabled) -{ - if (surface->status) - return; - - if (surface->finished) { - _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); - return; - } - - surface->permit_subpixel_antialiasing = - enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; -} -slim_hidden_def (cairo_surface_set_subpixel_antialiasing); - -/** - * cairo_surface_get_subpixel_antialiasing: - * @surface: a #cairo_surface_t - * - * Gets whether the surface supports subpixel antialiasing. By default, - * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other - * surfaces do not. - * - * Since: 1.12 - **/ -cairo_subpixel_antialiasing_t -cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) -{ - if (surface->status) - return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; - - return surface->permit_subpixel_antialiasing ? - CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; -} -slim_hidden_def (cairo_surface_get_subpixel_antialiasing); - -/* Note: the backends may modify the contents of the glyph array as long as - * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to - * avoid copying the array again and again, and edit it in-place. - * Backends are in fact free to use the array as a generic buffer as they - * see fit. - * - * For show_glyphs backend method, and NOT for show_text_glyphs method, - * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify - * that they have successfully rendered some of the glyphs (from the beginning - * of the array), but not all. If they don't touch remaining_glyphs, it - * defaults to all glyphs. - * - * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and - * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale. - */ -cairo_status_t -_cairo_surface_show_text_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_scaled_font_t *dev_scaled_font = scaled_font; - - if (unlikely (surface->status)) - return surface->status; - - if (num_glyphs == 0 && utf8_len == 0) - return CAIRO_STATUS_SUCCESS; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (_cairo_surface_has_device_transform (surface) && - ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL)) - { - cairo_font_options_t font_options; - cairo_matrix_t dev_ctm, font_matrix; - - cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); - cairo_scaled_font_get_ctm (scaled_font, &dev_ctm); - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform); - cairo_scaled_font_get_font_options (scaled_font, &font_options); - dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font), - &font_matrix, - &dev_ctm, - &font_options); - } - status = cairo_scaled_font_status (dev_scaled_font); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and - * show_text_glyphs. Keep in synch. */ - if (clusters) { - /* A real show_text_glyphs call. Try show_text_glyphs backend - * method first */ - if (surface->backend->show_text_glyphs != NULL) { - status = surface->backend->show_text_glyphs (surface, op, - source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - dev_scaled_font, - clip); - } - if (status == CAIRO_INT_STATUS_UNSUPPORTED && - surface->backend->show_glyphs) - { - int remaining_glyphs = num_glyphs; - status = surface->backend->show_glyphs (surface, op, - source, - glyphs, num_glyphs, - dev_scaled_font, - clip, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; - } - } else { - /* A mere show_glyphs call. Try show_glyphs backend method first */ - if (surface->backend->show_glyphs != NULL) { - int remaining_glyphs = num_glyphs; - status = surface->backend->show_glyphs (surface, op, - source, - glyphs, num_glyphs, - dev_scaled_font, - clip, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; - } else if (surface->backend->show_text_glyphs != NULL) { - /* Intentionally only try show_text_glyphs method for show_glyphs - * calls if backend does not have show_glyphs. If backend has - * both methods implemented, we don't fallback from show_glyphs to - * show_text_glyphs, and hence the backend can assume in its - * show_text_glyphs call that clusters is not NULL (which also - * implies that UTF-8 is not NULL, unless the text is - * zero-length). - */ - status = surface->backend->show_text_glyphs (surface, op, - source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - dev_scaled_font, - clip); - } - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_surface_fallback_show_glyphs (surface, op, - source, - glyphs, num_glyphs, - dev_scaled_font, - clip); - } - - if (dev_scaled_font != scaled_font) - cairo_scaled_font_destroy (dev_scaled_font); - - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -/* XXX: Previously, we had a function named _cairo_surface_show_glyphs - * with not-so-useful semantics. We've now got a - * _cairo_surface_show_text_glyphs with the proper semantics, and its - * fallback still uses this old function (which still needs to be - * cleaned up in terms of both semantics and naming). */ -cairo_status_t -_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region) -{ - cairo_status_t status; - - if (dst->status) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - if (dst->backend->old_show_glyphs) { - status = dst->backend->old_show_glyphs (scaled_font, - op, pattern, dst, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs, - clip_region); - } else - status = CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_surface_set_error (dst, status); -} - -static cairo_status_t -_cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, - cairo_rectangle_int_t *src_rectangle, - cairo_rectangle_int_t *mask_rectangle, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t dst_rectangle; - cairo_region_t clear_region; - cairo_status_t status; - - /* The area that was drawn is the area in the destination rectangle but - * not within the source or the mask. - */ - dst_rectangle.x = dst_x; - dst_rectangle.y = dst_y; - dst_rectangle.width = width; - dst_rectangle.height = height; - - _cairo_region_init_rectangle (&clear_region, &dst_rectangle); - - if (clip_region != NULL) { - status = cairo_region_intersect (&clear_region, clip_region); - if (unlikely (status)) - goto CLEANUP_REGIONS; - } - - if (src_rectangle != NULL) { - if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) - goto EMPTY; - } - - if (mask_rectangle != NULL) { - if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) - goto EMPTY; - } - - /* Now compute the area that is in dst but not drawn */ - status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle); - if (unlikely (status) || cairo_region_is_empty (&clear_region)) - goto CLEANUP_REGIONS; - - EMPTY: - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - - CLEANUP_REGIONS: - _cairo_region_fini (&clear_region); - - return _cairo_surface_set_error (dst, status); -} - -/** - * _cairo_surface_composite_fixup_unbounded: - * @dst: the destination surface - * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) - * @src_width: width of source surface - * @src_height: height of source surface - * @mask_attr: mask surface attributes or %NULL if no mask - * @mask_width: width of mask surface - * @mask_height: height of mask surface - * @src_x: @src_x from _cairo_surface_composite() - * @src_y: @src_y from _cairo_surface_composite() - * @mask_x: @mask_x from _cairo_surface_composite() - * @mask_y: @mask_y from _cairo_surface_composite() - * @dst_x: @dst_x from _cairo_surface_composite() - * @dst_y: @dst_y from _cairo_surface_composite() - * @width: @width from _cairo_surface_composite() - * @height: @height_x from _cairo_surface_composite() - * - * Eeek! Too many parameters! This is a helper function to take care of fixing - * up for bugs in libpixman and RENDER where, when asked to composite an - * untransformed surface with an unbounded operator (like CLEAR or SOURCE) - * only the region inside both the source and the mask is affected. - * This function clears the region that should have been drawn but was wasn't. - **/ -cairo_status_t -_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - cairo_surface_attributes_t *mask_attr, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t src_tmp, mask_tmp; - cairo_rectangle_int_t *src_rectangle = NULL; - cairo_rectangle_int_t *mask_rectangle = NULL; - - if (unlikely (dst->status)) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, - * non-repeating sources and masks. Other sources and masks can be ignored. - */ - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - src_attr->extend == CAIRO_EXTEND_NONE) - { - src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); - src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); - src_tmp.width = src_width; - src_tmp.height = src_height; - - src_rectangle = &src_tmp; - } - - if (mask_attr && - _cairo_matrix_is_integer_translation (&mask_attr->matrix, NULL, NULL) && - mask_attr->extend == CAIRO_EXTEND_NONE) - { - mask_tmp.x = (dst_x - (mask_x + mask_attr->x_offset)); - mask_tmp.y = (dst_y - (mask_y + mask_attr->y_offset)); - mask_tmp.width = mask_width; - mask_tmp.height = mask_height; - - mask_rectangle = &mask_tmp; - } - - return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, - dst_x, dst_y, width, height, - clip_region); -} - -/** - * _cairo_surface_composite_shape_fixup_unbounded: - * @dst: the destination surface - * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) - * @src_width: width of source surface - * @src_height: height of source surface - * @mask_width: width of mask surface - * @mask_height: height of mask surface - * @src_x: @src_x from _cairo_surface_composite() - * @src_y: @src_y from _cairo_surface_composite() - * @mask_x: @mask_x from _cairo_surface_composite() - * @mask_y: @mask_y from _cairo_surface_composite() - * @dst_x: @dst_x from _cairo_surface_composite() - * @dst_y: @dst_y from _cairo_surface_composite() - * @width: @width from _cairo_surface_composite() - * @height: @height_x from _cairo_surface_composite() - * - * Like _cairo_surface_composite_fixup_unbounded(), but instead of - * handling the case where we have a source pattern and a mask - * pattern, handle the case where we are compositing a source pattern - * using a mask we create ourselves, as in - * _cairo_surface_composite_glyphs() or _cairo_surface_composite_trapezoids() - **/ -cairo_status_t -_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t src_tmp, *src= NULL; - cairo_rectangle_int_t mask; - - if (dst->status) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, - * non-repeating sources and masks. Other sources and masks can be ignored. - */ - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - src_attr->extend == CAIRO_EXTEND_NONE) - { - src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); - src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); - src_tmp.width = src_width; - src_tmp.height = src_height; - - src = &src_tmp; - } - - mask.x = dst_x - mask_x; - mask.y = dst_y - mask_y; - mask.width = mask_width; - mask.height = mask_height; - - return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask, - dst_x, dst_y, width, height, - clip_region); -} - -/** - * _cairo_surface_set_resolution - * @surface: the surface - * @x_res: x resolution, in dpi - * @y_res: y resolution, in dpi - * - * Set the actual surface resolution of @surface to the given x and y DPI. - * Mainly used for correctly computing the scale factor when fallback - * rendering needs to take place in the paginated surface. - */ -void -_cairo_surface_set_resolution (cairo_surface_t *surface, - double x_res, - double y_res) -{ - if (surface->status) - return; - - surface->x_resolution = x_res; - surface->y_resolution = y_res; -} - -/* Generic methods for determining operation extents. */ - -static void -_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) -{ - const cairo_rectangle_int_t *clip_extents; - cairo_bool_t is_empty; - - clip_extents = NULL; - if (clip != NULL) - clip_extents = _cairo_clip_get_extents (clip); - - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (extents, clip_extents); -} - -static void -_cairo_surface_operation_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - is_empty = _cairo_surface_get_extents (surface, extents); - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - _cairo_pattern_get_extents (source, &source_extents); - is_empty = _cairo_rectangle_intersect (extents, &source_extents); - } - - _rectangle_intersect_clip (extents, clip); -} - -cairo_status_t -_cairo_surface_paint_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - _cairo_surface_operation_extents (surface, op, source, clip, extents); - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_mask_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - _cairo_pattern_get_extents (mask, &mask_extents); - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_stroke_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &mask_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_fill_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &mask_extents); - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_glyphs_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t glyph_extents; - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (extents, &glyph_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_surface_t * -_cairo_surface_create_in_error (cairo_status_t status) -{ - switch (status) { - case CAIRO_STATUS_NO_MEMORY: - return (cairo_surface_t *) &_cairo_surface_nil; - case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: - return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch; - case CAIRO_STATUS_INVALID_STATUS: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_status; - case CAIRO_STATUS_INVALID_CONTENT: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_content; - case CAIRO_STATUS_INVALID_FORMAT: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_format; - case CAIRO_STATUS_INVALID_VISUAL: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual; - case CAIRO_STATUS_READ_ERROR: - return (cairo_surface_t *) &_cairo_surface_nil_read_error; - case CAIRO_STATUS_WRITE_ERROR: - return (cairo_surface_t *) &_cairo_surface_nil_write_error; - case CAIRO_STATUS_FILE_NOT_FOUND: - return (cairo_surface_t *) &_cairo_surface_nil_file_not_found; - case CAIRO_STATUS_TEMP_FILE_ERROR: - return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error; - case CAIRO_STATUS_INVALID_STRIDE: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; - case CAIRO_STATUS_INVALID_SIZE: - return (cairo_surface_t *) &_cairo_surface_nil_invalid_size; - case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: - return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch; - case CAIRO_STATUS_DEVICE_ERROR: - return (cairo_surface_t *) &_cairo_surface_nil_device_error; - case CAIRO_STATUS_SUCCESS: - case CAIRO_STATUS_LAST_STATUS: - ASSERT_NOT_REACHED; - /* fall-through */ - case CAIRO_STATUS_INVALID_RESTORE: - case CAIRO_STATUS_INVALID_POP_GROUP: - case CAIRO_STATUS_NO_CURRENT_POINT: - case CAIRO_STATUS_INVALID_MATRIX: - case CAIRO_STATUS_NULL_POINTER: - case CAIRO_STATUS_INVALID_STRING: - case CAIRO_STATUS_INVALID_PATH_DATA: - case CAIRO_STATUS_SURFACE_FINISHED: - case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: - case CAIRO_STATUS_INVALID_DASH: - case CAIRO_STATUS_INVALID_DSC_COMMENT: - case CAIRO_STATUS_INVALID_INDEX: - case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: - case CAIRO_STATUS_FONT_TYPE_MISMATCH: - case CAIRO_STATUS_USER_FONT_IMMUTABLE: - case CAIRO_STATUS_USER_FONT_ERROR: - case CAIRO_STATUS_NEGATIVE_COUNT: - case CAIRO_STATUS_INVALID_CLUSTERS: - case CAIRO_STATUS_INVALID_SLANT: - case CAIRO_STATUS_INVALID_WEIGHT: - case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: - default: - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t *) &_cairo_surface_nil; - } -} - -/* LocalWords: rasterized - */ diff --git a/libs/cairo/cairo/src/cairo-svg-surface-private.h b/libs/cairo/cairo/src/cairo-svg-surface-private.h deleted file mode 100644 index eea51cbbf..000000000 --- a/libs/cairo/cairo/src/cairo-svg-surface-private.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-svg-surface.c b/libs/cairo/cairo/src/cairo-svg-surface.c deleted file mode 100644 index b18527f0c..000000000 --- a/libs/cairo/cairo/src/cairo-svg-surface.c +++ /dev/null @@ -1,2811 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for snprintf() */ -#include "cairoint.h" -#include "cairo-svg.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" -#include "cairo-image-info-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-paginated-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-svg-surface-private.h" - -/** - * SECTION:cairo-svg - * @Title: SVG Surfaces - * @Short_Description: Rendering SVG documents - * @See_Also: #cairo_surface_t - * - * The SVG surface is used to render cairo graphics to - * SVG files and is a multi-page vector surface backend. - */ - -/** - * CAIRO_HAS_SVG_SURFACE: - * - * Defined if the SVG surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -typedef struct cairo_svg_page cairo_svg_page_t; - -static const int invalid_pattern_id = -1; - -static const cairo_svg_version_t _cairo_svg_versions[] = -{ - CAIRO_SVG_VERSION_1_1, - CAIRO_SVG_VERSION_1_2 -}; - -#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) - -static void -_cairo_svg_surface_emit_path (cairo_output_stream_t *output, - cairo_path_fixed_t *path, - const cairo_matrix_t *ctm_inverse); - -static cairo_bool_t -_cairo_svg_version_has_page_set_support (cairo_svg_version_t version) -{ - return version > CAIRO_SVG_VERSION_1_1; -} - -static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] = -{ - "SVG 1.1", - "SVG 1.2" -}; - -static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] = -{ - "1.1", - "1.2" -}; - -struct cairo_svg_page { - unsigned int surface_id; - unsigned int clip_level; - cairo_output_stream_t *xml_node; -}; - -struct cairo_svg_document { - cairo_output_stream_t *output_stream; - unsigned long refcount; - cairo_surface_t *owner; - cairo_bool_t finished; - - double width; - double height; - - cairo_output_stream_t *xml_node_defs; - cairo_output_stream_t *xml_node_glyphs; - - unsigned int linear_pattern_id; - unsigned int radial_pattern_id; - unsigned int pattern_id; - unsigned int filter_id; - unsigned int clip_id; - unsigned int mask_id; - - cairo_bool_t alpha_filter; - - cairo_svg_version_t svg_version; - - cairo_scaled_font_subsets_t *font_subsets; -}; - -static cairo_status_t -_cairo_svg_document_create (cairo_output_stream_t *stream, - double width, - double height, - cairo_svg_version_t version, - cairo_svg_document_t **document_out); - -static cairo_status_t -_cairo_svg_document_destroy (cairo_svg_document_t *document); - -static cairo_status_t -_cairo_svg_document_finish (cairo_svg_document_t *document); - -static cairo_svg_document_t * -_cairo_svg_document_reference (cairo_svg_document_t *document); - -static unsigned int -_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document); - -static cairo_surface_t * -_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, - cairo_content_t content, - double width, - double height); -static cairo_surface_t * -_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, - double width, - double height, - cairo_svg_version_t version); - -static const cairo_surface_backend_t cairo_svg_surface_backend; -static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend; - -/** - * cairo_svg_surface_create_for_stream: - * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL - * to indicate a no-op @write_func. With a no-op @write_func, - * the surface may be queried or used as a source without - * generating any temporary files. - * @closure: the closure argument for @write_func - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a SVG surface of the specified size in points to be written - * incrementally to the stream represented by @write_func and @closure. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - */ -cairo_surface_t * -cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width, - double height) -{ - cairo_output_stream_t *stream; - - stream = _cairo_output_stream_create (write_func, NULL, closure); - if (_cairo_output_stream_get_status (stream)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); - - return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); -} - -/** - * cairo_svg_surface_create: - * @filename: a filename for the SVG output (must be writable), %NULL may be - * used to specify no output. This will generate a SVG surface that - * may be queried and used as a source, without generating a - * temporary file. - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a SVG surface of the specified size in points to be written - * to @filename. - * - * The SVG surface backend recognizes the following MIME types for the - * data attached to a surface (see cairo_surface_set_mime_data()) when - * it is used as a source pattern for drawing on this surface: - * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, - * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend - * emits a href with the content of MIME data instead of a surface - * snapshot (PNG, Base64-encoded) in the corresponding image tag. - * - * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined - * first. If present, the URI is emitted as is: assuring the - * correctness of URI is left to the client code. - * - * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG - * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is - * Base64-encoded and emitted. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_svg_surface_create (const char *filename, - double width, - double height) -{ - cairo_output_stream_t *stream; - - stream = _cairo_output_stream_create_for_filename (filename); - if (_cairo_output_stream_get_status (stream)) - return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); - - return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); -} - -static cairo_bool_t -_cairo_surface_is_svg (cairo_surface_t *surface) -{ - return surface->backend == &cairo_svg_surface_backend; -} - -/* If the abstract_surface is a paginated surface, and that paginated - * surface's target is a svg_surface, then set svg_surface to that - * target. Otherwise return FALSE. - */ -static cairo_bool_t -_extract_svg_surface (cairo_surface_t *surface, - cairo_svg_surface_t **svg_surface) -{ - cairo_surface_t *target; - cairo_status_t status_ignored; - - if (surface->status) - return FALSE; - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_paginated (surface)) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - target = _cairo_paginated_surface_get_target (surface); - if (target->status) { - status_ignored = _cairo_surface_set_error (surface, - target->status); - return FALSE; - } - if (target->finished) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return FALSE; - } - - if (! _cairo_surface_is_svg (target)) { - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return FALSE; - } - - *svg_surface = (cairo_svg_surface_t *) target; - return TRUE; -} - -/** - * cairo_svg_surface_restrict_to_version: - * @surface: a SVG #cairo_surface_t - * @version: SVG version - * - * Restricts the generated SVG file to @version. See cairo_svg_get_versions() - * for a list of available version values that can be used here. - * - * This function should only be called before any drawing operations - * have been performed on the given surface. The simplest way to do - * this is to call this function immediately after creating the - * surface. - * - * Since: 1.2 - **/ -void -cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, - cairo_svg_version_t version) -{ - cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ - - if (! _extract_svg_surface (abstract_surface, &surface)) - return; - - if (version < CAIRO_SVG_VERSION_LAST) - surface->document->svg_version = version; -} - -/** - * cairo_svg_get_versions: - * @versions: supported version list - * @num_versions: list length - * - * Used to retrieve the list of supported versions. See - * cairo_svg_surface_restrict_to_version(). - * - * Since: 1.2 - **/ -void -cairo_svg_get_versions (cairo_svg_version_t const **versions, - int *num_versions) -{ - if (versions != NULL) - *versions = _cairo_svg_versions; - - if (num_versions != NULL) - *num_versions = CAIRO_SVG_VERSION_LAST; -} - -/** - * cairo_svg_version_to_string: - * @version: a version id - * - * Get the string representation of the given @version id. This function - * will return %NULL if @version isn't valid. See cairo_svg_get_versions() - * for a way to get the list of valid version ids. - * - * Return value: the string associated to given version. - * - * Since: 1.2 - **/ -const char * -cairo_svg_version_to_string (cairo_svg_version_t version) -{ - if (version >= CAIRO_SVG_VERSION_LAST) - return NULL; - - return _cairo_svg_version_strings[version]; -} - -static cairo_bool_t -_cliprect_covers_surface (cairo_svg_surface_t *surface, - cairo_path_fixed_t *path) -{ - cairo_box_t box; - - if (_cairo_path_fixed_is_box (path, &box)) { - if (box.p1.x <= 0 && - box.p1.y <= 0 && - _cairo_fixed_to_double (box.p2.x) >= surface->width && - _cairo_fixed_to_double (box.p2.y) >= surface->height) - { - return TRUE; - } - } - - return FALSE; -} - -static cairo_status_t -_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_svg_surface_t *surface = cairo_container_of (clipper, - cairo_svg_surface_t, - clipper); - cairo_svg_document_t *document = surface->document; - unsigned int i; - - if (path == NULL) { - for (i = 0; i < surface->clip_level; i++) - _cairo_output_stream_printf (surface->xml_node, "\n"); - - surface->clip_level = 0; - return CAIRO_STATUS_SUCCESS; - } - - /* skip trivial whole-page clips */ - if (_cliprect_covers_surface (surface, path)) - return CAIRO_STATUS_SUCCESS; - - _cairo_output_stream_printf (document->xml_node_defs, - "\n" - " clip_id); - _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); - - _cairo_output_stream_printf (document->xml_node_defs, - "/>\n" - "\n"); - - _cairo_output_stream_printf (surface->xml_node, - "\n", - document->clip_id, - fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? - "evenodd" : "nonzero"); - - document->clip_id++; - surface->clip_level++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, - cairo_content_t content, - double width, - double height) -{ - cairo_svg_surface_t *surface; - cairo_surface_t *paginated; - cairo_status_t status, status_ignored; - - surface = malloc (sizeof (cairo_svg_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &cairo_svg_surface_backend, - NULL, /* device */ - content); - - surface->width = width; - surface->height = height; - - surface->document = _cairo_svg_document_reference (document); - - surface->clip_level = 0; - _cairo_surface_clipper_init (&surface->clipper, - _cairo_svg_surface_clipper_intersect_clip_path); - - surface->base_clip = document->clip_id++; - surface->is_base_clip_emitted = FALSE; - - surface->xml_node = _cairo_memory_stream_create (); - status = _cairo_output_stream_get_status (surface->xml_node); - if (unlikely (status)) - goto CLEANUP; - - _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); - - if (content == CAIRO_CONTENT_COLOR) { - _cairo_output_stream_printf (surface->xml_node, - "\n", - width, height); - status = _cairo_output_stream_get_status (surface->xml_node); - if (unlikely (status)) - goto CLEANUP; - } - - surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; - surface->force_fallbacks = FALSE; - surface->content = content; - - paginated = _cairo_paginated_surface_create (&surface->base, - surface->content, - &cairo_svg_surface_paginated_backend); - status = paginated->status; - if (status == CAIRO_STATUS_SUCCESS) { - /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); - return paginated; - } - - /* ignore status as we are on the error path */ -CLEANUP: - status_ignored = _cairo_output_stream_destroy (surface->xml_node); - status_ignored = _cairo_svg_document_destroy (document); - - free (surface); - - return _cairo_surface_create_in_error (status); -} - -static cairo_surface_t * -_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, - double width, - double height, - cairo_svg_version_t version) -{ - cairo_svg_document_t *document = NULL; /* silence compiler */ - cairo_surface_t *surface; - cairo_status_t status; - - status = _cairo_svg_document_create (stream, - width, height, version, - &document); - if (unlikely (status)) { - surface = _cairo_surface_create_in_error (status); - /* consume the output stream on behalf of caller */ - status = _cairo_output_stream_destroy (stream); - return surface; - } - - surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, - width, height); - if (surface->status) { - status = _cairo_svg_document_destroy (document); - return surface; - } - - document->owner = surface; - status = _cairo_svg_document_destroy (document); - /* the ref count should be 2 at this point */ - assert (status == CAIRO_STATUS_SUCCESS); - - return surface; -} - -static cairo_svg_page_t * -_cairo_svg_surface_store_page (cairo_svg_surface_t *surface) -{ - unsigned int i; - cairo_svg_page_t page; - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_memory_stream_create (); - if (_cairo_output_stream_get_status (stream)) { - status = _cairo_output_stream_destroy (stream); - return NULL; - } - - page.surface_id = surface->base.unique_id; - page.clip_level = surface->clip_level; - page.xml_node = surface->xml_node; - - if (_cairo_array_append (&surface->page_set, &page)) { - status = _cairo_output_stream_destroy (stream); - return NULL; - } - - surface->xml_node = stream; - surface->clip_level = 0; - for (i = 0; i < page.clip_level; i++) - _cairo_output_stream_printf (page.xml_node, "\n"); - - _cairo_surface_clipper_reset (&surface->clipper); - - return _cairo_array_index (&surface->page_set, - surface->page_set.num_elements - 1); -} - -static cairo_int_status_t -_cairo_svg_surface_copy_page (void *abstract_surface) -{ - cairo_svg_surface_t *surface = abstract_surface; - cairo_svg_page_t *page; - - page = _cairo_svg_surface_store_page (surface); - if (unlikely (page == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_memory_stream_copy (page->xml_node, surface->xml_node); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_show_page (void *abstract_surface) -{ - cairo_svg_surface_t *surface = abstract_surface; - - if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_svg_surface_emit_transform (cairo_output_stream_t *output, - char const *attribute_str, - const cairo_matrix_t *object_matrix, - const cairo_matrix_t *parent_matrix) -{ - cairo_matrix_t matrix = *object_matrix; - - if (parent_matrix != NULL) - cairo_matrix_multiply (&matrix, &matrix, parent_matrix); - - if (!_cairo_matrix_is_identity (&matrix)) - _cairo_output_stream_printf (output, - "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"", - attribute_str, - matrix.xx, matrix.yx, - matrix.xy, matrix.yy, - matrix.x0, matrix.y0); -} - -typedef struct { - cairo_output_stream_t *output; - const cairo_matrix_t *ctm_inverse; -} svg_path_info_t; - -static cairo_status_t -_cairo_svg_path_move_to (void *closure, - const cairo_point_t *point) -{ - svg_path_info_t *info = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (info->ctm_inverse) - cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - - _cairo_output_stream_printf (info->output, "M %f %f ", x, y); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_path_line_to (void *closure, - const cairo_point_t *point) -{ - svg_path_info_t *info = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (info->ctm_inverse) - cairo_matrix_transform_point (info->ctm_inverse, &x, &y); - - _cairo_output_stream_printf (info->output, "L %f %f ", x, y); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_path_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - svg_path_info_t *info = closure; - double bx = _cairo_fixed_to_double (b->x); - double by = _cairo_fixed_to_double (b->y); - double cx = _cairo_fixed_to_double (c->x); - double cy = _cairo_fixed_to_double (c->y); - double dx = _cairo_fixed_to_double (d->x); - double dy = _cairo_fixed_to_double (d->y); - - if (info->ctm_inverse) { - cairo_matrix_transform_point (info->ctm_inverse, &bx, &by); - cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy); - cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); - } - - _cairo_output_stream_printf (info->output, - "C %f %f %f %f %f %f ", - bx, by, cx, cy, dx, dy); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_path_close_path (void *closure) -{ - svg_path_info_t *info = closure; - - _cairo_output_stream_printf (info->output, "Z "); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_svg_surface_emit_path (cairo_output_stream_t *output, - cairo_path_fixed_t *path, - const cairo_matrix_t *ctm_inverse) -{ - cairo_status_t status; - svg_path_info_t info; - - _cairo_output_stream_printf (output, "d=\""); - - info.output = output; - info.ctm_inverse = ctm_inverse; - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_svg_path_move_to, - _cairo_svg_path_line_to, - _cairo_svg_path_curve_to, - _cairo_svg_path_close_path, - &info); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_output_stream_printf (output, "\""); -} - -static cairo_int_status_t -_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, - cairo_scaled_font_t *scaled_font, - unsigned long glyph_index) -{ - cairo_scaled_glyph_t *scaled_glyph; - cairo_int_status_t status; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS| - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (document->xml_node_glyphs, - "xml_node_glyphs, - scaled_glyph->path, NULL); - - _cairo_output_stream_printf (document->xml_node_glyphs, - "/>\n"); - - return status; -} - -static cairo_int_status_t -_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, - cairo_scaled_font_t *scaled_font, - unsigned long glyph_index) -{ - cairo_scaled_glyph_t *scaled_glyph; - cairo_image_surface_t *image; - cairo_status_t status; - uint8_t *row, *byte; - int rows, cols; - int x, y, bit; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (unlikely (status)) - return status; - - image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface, - CAIRO_FORMAT_A1); - status = image->base.status; - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, " transform", - &image->base.device_transform_inverse, NULL); - _cairo_output_stream_printf (document->xml_node_glyphs, ">/n"); - - for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) { - for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { - uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); - for (bit = 7; bit >= 0 && x < image->width; bit--, x++) { - if (output_byte & (1 << bit)) { - _cairo_output_stream_printf (document->xml_node_glyphs, - "\n", - x, y); - } - } - } - } - _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); - - cairo_surface_destroy (&image->base); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_document_emit_glyph (cairo_svg_document_t *document, - cairo_scaled_font_t *scaled_font, - unsigned long scaled_font_glyph_index, - unsigned int font_id, - unsigned int subset_glyph_index) -{ - cairo_status_t status; - - _cairo_output_stream_printf (document->xml_node_glyphs, - "\n", - font_id, - subset_glyph_index); - - status = _cairo_svg_document_emit_outline_glyph_data (document, - scaled_font, - scaled_font_glyph_index); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_svg_document_emit_bitmap_glyph_data (document, - scaled_font, - scaled_font_glyph_index); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_svg_document_t *document = closure; - unsigned int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - _cairo_scaled_font_freeze_cache (font_subset->scaled_font); - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_svg_document_emit_glyph (document, - font_subset->scaled_font, - font_subset->glyphs[i], - font_subset->font_id, i); - if (unlikely (status)) - break; - } - _cairo_scaled_font_thaw_cache (font_subset->scaled_font); - - return status; -} - -static cairo_status_t -_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) -{ - cairo_status_t status; - - status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, - _cairo_svg_document_emit_font_subset, - document); - if (unlikely (status)) - goto FAIL; - - status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, - _cairo_svg_document_emit_font_subset, - document); - - FAIL: - _cairo_scaled_font_subsets_destroy (document->font_subsets); - document->font_subsets = NULL; - - return status; -} - -static char const * -_cairo_svg_surface_operators[] = { - "clear", - - "src", "src-over", "src-in", - "src-out", "src-atop", - - "dst", "dst-over", "dst-in", - "dst-out", "dst-atop", - - "xor", "plus", - "color-dodge", /* FIXME: saturate ? */ - - "multiply", "screen", "overlay", - "darken", "lighten", - "color-dodge", "color-burn", - "hard-light", "soft-light", - "difference", "exclusion" -}; - -static cairo_bool_t -_cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface, - cairo_operator_t op) -{ - /* guard against newly added operators */ - if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* allow operators being NULL if they are unsupported */ - if (_cairo_svg_surface_operators[op] == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - cairo_svg_document_t *document = surface->document; - - if (surface->force_fallbacks && - surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* SVG doesn't support extend reflect for image pattern */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && - pattern->extend == CAIRO_EXTEND_REFLECT) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (document->svg_version >= CAIRO_SVG_VERSION_1_2) - return _cairo_svg_surface_analyze_operator (surface, op); - - if (op == CAIRO_OPERATOR_OVER) - return CAIRO_STATUS_SUCCESS; - - /* The SOURCE operator is only supported if there is nothing - * painted underneath. */ - if (op == CAIRO_OPERATOR_SOURCE) - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_svg_surface_finish (void *abstract_surface) -{ - cairo_status_t status, status2; - cairo_svg_surface_t *surface = abstract_surface; - cairo_svg_document_t *document = surface->document; - cairo_svg_page_t *page; - unsigned int i; - - if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) - status = _cairo_svg_document_finish (document); - else - status = CAIRO_STATUS_SUCCESS; - - if (surface->xml_node != NULL) { - status2 = _cairo_output_stream_destroy (surface->xml_node); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - for (i = 0; i < surface->page_set.num_elements; i++) { - page = _cairo_array_index (&surface->page_set, i); - status2 = _cairo_output_stream_destroy (page->xml_node); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - _cairo_array_fini (&surface->page_set); - - _cairo_surface_clipper_reset (&surface->clipper); - - status2 = _cairo_svg_document_destroy (document); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - - -static void -_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) -{ - if (document->alpha_filter) - return; - - _cairo_output_stream_printf (document->xml_node_defs, - "\n" - " \n" - "\n"); - - document->alpha_filter = TRUE; -} - -typedef struct { - cairo_output_stream_t *output; - unsigned int in_mem; - unsigned int trailing; - unsigned char src[3]; -} base64_write_closure_t; - -static char const base64_table[64] = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static cairo_status_t -base64_write_func (void *closure, - const unsigned char *data, - unsigned int length) -{ - base64_write_closure_t *info = (base64_write_closure_t *) closure; - unsigned int i; - unsigned char *src; - - src = info->src; - - if (info->in_mem + length < 3) { - for (i = 0; i < length; i++) { - src[i + info->in_mem] = *data++; - } - info->in_mem += length; - return CAIRO_STATUS_SUCCESS; - } - - do { - unsigned char dst[4]; - - for (i = info->in_mem; i < 3; i++) { - src[i] = *data++; - length--; - } - info->in_mem = 0; - - dst[0] = base64_table[src[0] >> 2]; - dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; - dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; - dst[3] = base64_table[src[2] & 0xfc >> 2]; - /* Special case for the last missing bits */ - switch (info->trailing) { - case 2: - dst[2] = '='; - case 1: - dst[3] = '='; - default: - break; - } - _cairo_output_stream_write (info->output, dst, 4); - } while (length >= 3); - - for (i = 0; i < length; i++) { - src[i] = *data++; - } - info->in_mem = length; - - return _cairo_output_stream_get_status (info->output); -} - -static cairo_int_status_t -_cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, - cairo_output_stream_t *output) -{ - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t image_info; - base64_write_closure_t info; - cairo_status_t status; - - cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); - - info.output = output; - info.in_mem = 0; - info.trailing = 0; - - status = base64_write_func (&info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - if (info.in_mem > 0) { - memset (info.src + info.in_mem, 0, 3 - info.in_mem); - info.trailing = 3 - info.in_mem; - info.in_mem = 3; - status = base64_write_func (&info, NULL, 0); - } - - return status; -} - -static cairo_int_status_t -_cairo_surface_base64_encode_png (cairo_surface_t *surface, - cairo_output_stream_t *output) -{ - const unsigned char *mime_data; - unsigned long mime_data_length; - base64_write_closure_t info; - cairo_status_t status; - - cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, - &mime_data, &mime_data_length); - if (unlikely (surface->status)) - return surface->status; - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_output_stream_printf (output, "data:image/png;base64,"); - - info.output = output; - info.in_mem = 0; - info.trailing = 0; - - status = base64_write_func (&info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - if (info.in_mem > 0) { - memset (info.src + info.in_mem, 0, 3 - info.in_mem); - info.trailing = 3 - info.in_mem; - info.in_mem = 3; - status = base64_write_func (&info, NULL, 0); - } - - return status; -} - -static cairo_int_status_t -_cairo_surface_base64_encode (cairo_surface_t *surface, - cairo_output_stream_t *output) -{ - cairo_status_t status; - base64_write_closure_t info; - - status = _cairo_surface_base64_encode_jpeg (surface, output); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_surface_base64_encode_png (surface, output); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - info.output = output; - info.in_mem = 0; - info.trailing = 0; - - _cairo_output_stream_printf (info.output, "data:image/png;base64,"); - - status = cairo_surface_write_to_png_stream (surface, base64_write_func, - (void *) &info); - - if (unlikely (status)) - return status; - - if (info.in_mem > 0) { - memset (info.src + info.in_mem, 0, 3 - info.in_mem); - info.trailing = 3 - info.in_mem; - info.in_mem = 3; - status = base64_write_func (&info, NULL, 0); - } - - return status; -} - -static void -_cairo_svg_surface_emit_operator (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op) -{ - if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && - op != CAIRO_OPERATOR_OVER) { - _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]); - if (!_cairo_operator_bounded_by_source (op)) - _cairo_output_stream_printf (output, " clip-to-self=\"true\""); - } -} - -static void -_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op) -{ - if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && - op != CAIRO_OPERATOR_OVER) { - _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]); - if (!_cairo_operator_bounded_by_source (op)) - _cairo_output_stream_printf (output, "clip-to-self:true;"); - } -} - -/** - * _cairo_svg_surface_emit_attr_value: - * - * Write the value to output the stream as a sequence of characters, - * while escaping those which have special meaning in the XML - * attribute's value context: & and ". - **/ -static void -_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, - const unsigned char *value, - unsigned int length) -{ - const unsigned char *p; - const unsigned char *q; - unsigned int i; - - /* we'll accumulate non-special chars in [q, p) range */ - p = value; - q = p; - for (i = 0; i < length; i++, p++) { - if (*p == '&' || *p == '"') { - /* flush what's left before special char */ - if (p != q) { - _cairo_output_stream_write (stream, q, p - q); - q = p + 1; - } - - if (*p == '&') - _cairo_output_stream_printf (stream, "&"); - else // p == '"' - _cairo_output_stream_printf (stream, """); - } - } - - /* flush the trailing chars if any */ - if (p != q) - _cairo_output_stream_write (stream, q, p - q); -} - -static cairo_status_t -_cairo_svg_surface_emit_surface (cairo_svg_document_t *document, - cairo_surface_t *surface) -{ - 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, - "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, - "\n "); - } - - _cairo_output_stream_printf (output, - "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, "\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, - cairo_recording_surface_t *source) -{ - cairo_status_t status; - cairo_surface_t *paginated_surface; - cairo_svg_surface_t *svg_surface; - cairo_array_t *page_set; - - cairo_output_stream_t *contents; - - if (_cairo_user_data_array_get_data (&source->base.user_data, - (cairo_user_data_key_t *) document)) - { - return CAIRO_STATUS_SUCCESS; - } - - paginated_surface = _cairo_svg_surface_create_for_document (document, - source->content, - source->extents_pixels.width, - source->extents_pixels.height); - if (unlikely (paginated_surface->status)) - return paginated_surface->status; - - svg_surface = (cairo_svg_surface_t *) - _cairo_paginated_surface_get_target (paginated_surface); - cairo_surface_set_fallback_resolution (paginated_surface, - document->owner->x_fallback_resolution, - document->owner->y_fallback_resolution); - cairo_surface_set_device_offset (&svg_surface->base, - -source->extents_pixels.x, - -source->extents_pixels.y); - - status = _cairo_recording_surface_replay (&source->base, paginated_surface); - if (unlikely (status)) { - cairo_surface_destroy (paginated_surface); - return status; - } - - cairo_surface_show_page (paginated_surface); - status = cairo_surface_status (paginated_surface); - if (unlikely (status)) { - cairo_surface_destroy (paginated_surface); - return status; - } - - if (! svg_surface->is_base_clip_emitted) { - svg_surface->is_base_clip_emitted = TRUE; - _cairo_output_stream_printf (document->xml_node_defs, - "\n" - " \n" - "\n", - svg_surface->base_clip, - svg_surface->width, - svg_surface->height); - } - - if (source->content == CAIRO_CONTENT_ALPHA) { - _cairo_svg_surface_emit_alpha_filter (document); - _cairo_output_stream_printf (document->xml_node_defs, - "\n", - source->base.unique_id, - svg_surface->base_clip); - } else { - _cairo_output_stream_printf (document->xml_node_defs, - "\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, "\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, - "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, - "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, "\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, - "\n", - pattern->stops[0].offset, - pattern->stops[0].color.red * 100.0, - pattern->stops[0].color.green * 100.0, - pattern->stops[0].color.blue * 100.0, - pattern->stops[0].color.alpha); - return CAIRO_STATUS_SUCCESS; - } - - if (emulate_reflect || reverse_stops) { - n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; - stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); - if (unlikely (stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < pattern->n_stops; i++) { - if (reverse_stops) { - stops[i] = pattern->stops[pattern->n_stops - i - 1]; - stops[i].offset = 1.0 - stops[i].offset; - } else - stops[i] = pattern->stops[i]; - if (emulate_reflect) { - stops[i].offset /= 2; - if (i > 0 && i < (pattern->n_stops - 1)) { - if (reverse_stops) { - stops[i + pattern->n_stops - 1] = pattern->stops[i]; - stops[i + pattern->n_stops - 1].offset = - 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset; - } else { - stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1]; - stops[i + pattern->n_stops - 1].offset = - 1 - 0.5 * stops[i + pattern->n_stops - 1].offset; - } - } - } - } - } else { - n_stops = pattern->n_stops; - stops = pattern->stops; - } - - if (start_offset >= 0.0) - for (i = 0; i < n_stops; i++) { - offset = start_offset + (1 - start_offset ) * stops[i].offset; - _cairo_output_stream_printf (output, - "\n", - offset, - stops[i].color.red * 100.0, - stops[i].color.green * 100.0, - stops[i].color.blue * 100.0, - stops[i].color.alpha); - } - else { - cairo_bool_t found = FALSE; - unsigned int offset_index; - cairo_color_stop_t offset_color_start, offset_color_stop; - - for (i = 0; i < n_stops; i++) { - if (stops[i].offset >= -start_offset) { - if (i > 0) { - if (stops[i].offset != stops[i-1].offset) { - double x0, x1; - cairo_color_stop_t *color0, *color1; - - x0 = stops[i-1].offset; - x1 = stops[i].offset; - color0 = &stops[i-1].color; - color1 = &stops[i].color; - offset_color_start.red = color0->red + (color1->red - color0->red) - * (-start_offset - x0) / (x1 - x0); - offset_color_start.green = color0->green + (color1->green - color0->green) - * (-start_offset - x0) / (x1 - x0); - offset_color_start.blue = color0->blue + (color1->blue - color0->blue) - * (-start_offset - x0) / (x1 - x0); - offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha) - * (-start_offset - x0) / (x1 - x0); - offset_color_stop = offset_color_start; - } else { - offset_color_stop = stops[i-1].color; - offset_color_start = stops[i].color; - } - } else - offset_color_stop = offset_color_start = stops[i].color; - offset_index = i; - found = TRUE; - break; - } - } - - if (!found) { - offset_index = n_stops - 1; - offset_color_stop = offset_color_start = stops[offset_index].color; - } - - _cairo_output_stream_printf (output, - "\n", - offset_color_start.red * 100.0, - offset_color_start.green * 100.0, - offset_color_start.blue * 100.0, - offset_color_start.alpha); - for (i = offset_index; i < n_stops; i++) { - _cairo_output_stream_printf (output, - "\n", - stops[i].offset + start_offset, - stops[i].color.red * 100.0, - stops[i].color.green * 100.0, - stops[i].color.blue * 100.0, - stops[i].color.alpha); - } - for (i = 0; i < offset_index; i++) { - _cairo_output_stream_printf (output, - "\n", - 1.0 + stops[i].offset + start_offset, - stops[i].color.red * 100.0, - stops[i].color.green * 100.0, - stops[i].color.blue * 100.0, - stops[i].color.alpha); - } - - _cairo_output_stream_printf (output, - "\n", - offset_color_stop.red * 100.0, - offset_color_stop.green * 100.0, - offset_color_stop.blue * 100.0, - offset_color_stop.alpha); - - } - - if (reverse_stops || emulate_reflect) - free (stops); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output, - cairo_pattern_t *pattern) -{ - switch (pattern->extend) { - case CAIRO_EXTEND_REPEAT: - _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); - break; - case CAIRO_EXTEND_REFLECT: - _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); - break; - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_PAD: - break; - } -} - -static cairo_status_t -_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, - cairo_linear_pattern_t *pattern, - cairo_output_stream_t *style, - cairo_bool_t is_stroke, - const cairo_matrix_t *parent_matrix) -{ - cairo_svg_document_t *document = surface->document; - double x0, y0, x1, y1; - cairo_matrix_t p2u; - cairo_status_t status; - - p2u = pattern->base.base.matrix; - status = cairo_matrix_invert (&p2u); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - x0 = _cairo_fixed_to_double (pattern->p1.x); - y0 = _cairo_fixed_to_double (pattern->p1.y); - x1 = _cairo_fixed_to_double (pattern->p2.x); - y1 = _cairo_fixed_to_double (pattern->p2.y); - - _cairo_output_stream_printf (document->xml_node_defs, - "linear_pattern_id, - x0, y0, x1, y1); - - _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base), - _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); - _cairo_output_stream_printf (document->xml_node_defs, ">\n"); - - status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, - &pattern->base, 0.0, - FALSE, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (document->xml_node_defs, - "\n"); - - _cairo_output_stream_printf (style, - "%s:url(#linear%d);", - is_stroke ? "stroke" : "fill", - document->linear_pattern_id); - - document->linear_pattern_id++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, - cairo_radial_pattern_t *pattern, - cairo_output_stream_t *style, - cairo_bool_t is_stroke, - const cairo_matrix_t *parent_matrix) -{ - cairo_svg_document_t *document = surface->document; - cairo_matrix_t p2u; - cairo_extend_t extend; - double x0, y0, x1, y1, r0, r1; - double fx, fy; - cairo_bool_t reverse_stops; - cairo_status_t status; - cairo_point_t *c0, *c1; - cairo_fixed_t radius0, radius1; - - extend = pattern->base.base.extend; - - if (pattern->r1 < pattern->r2) { - c0 = &pattern->c1; - c1 = &pattern->c2; - radius0 = pattern->r1; - radius1 = pattern->r2; - reverse_stops = FALSE; - } else { - c0 = &pattern->c2; - c1 = &pattern->c1; - radius0 = pattern->r2; - radius1 = pattern->r1; - reverse_stops = TRUE; - } - - x0 = _cairo_fixed_to_double (c0->x); - y0 = _cairo_fixed_to_double (c0->y); - r0 = _cairo_fixed_to_double (radius0); - x1 = _cairo_fixed_to_double (c1->x); - y1 = _cairo_fixed_to_double (c1->y); - r1 = _cairo_fixed_to_double (radius1); - - p2u = pattern->base.base.matrix; - status = cairo_matrix_invert (&p2u); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - if (pattern->r1 == pattern->r2) { - unsigned int n_stops = pattern->base.n_stops; - - _cairo_output_stream_printf (document->xml_node_defs, - "radial_pattern_id, - x1, y1, - x1, y1, r1); - _cairo_svg_surface_emit_transform (document->xml_node_defs, - "gradientTransform", - &p2u, parent_matrix); - _cairo_output_stream_printf (document->xml_node_defs, ">\n"); - - if (extend == CAIRO_EXTEND_NONE || n_stops < 1) - _cairo_output_stream_printf (document->xml_node_defs, - "\n"); - else { - _cairo_output_stream_printf (document->xml_node_defs, - "\n", - pattern->base.stops[0].color.red * 100.0, - pattern->base.stops[0].color.green * 100.0, - pattern->base.stops[0].color.blue * 100.0, - pattern->base.stops[0].color.alpha); - if (n_stops > 1) - _cairo_output_stream_printf (document->xml_node_defs, - "\n", - pattern->base.stops[n_stops - 1].color.red * 100.0, - pattern->base.stops[n_stops - 1].color.green * 100.0, - pattern->base.stops[n_stops - 1].color.blue * 100.0, - pattern->base.stops[n_stops - 1].color.alpha); - } - - } else { - double offset, r, x, y; - cairo_bool_t emulate_reflect = FALSE; - - fx = (r1 * x0 - r0 * x1) / (r1 - r0); - fy = (r1 * y0 - r0 * y1) / (r1 - r0); - - /* SVG doesn't support the inner circle and use instead a gradient focal. - * That means we need to emulate the cairo behaviour by processing the - * cairo gradient stops. - * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, - * it's just a matter of stop position translation and calculation of - * the corresponding SVG radial gradient focal. - * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new - * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT - * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop - * list that maps to the original cairo stop list. - */ - if ((extend == CAIRO_EXTEND_REFLECT - || extend == CAIRO_EXTEND_REPEAT) - && r0 > 0.0) { - double r_org = r1; - - if (extend == CAIRO_EXTEND_REFLECT) { - r1 = 2 * r1 - r0; - emulate_reflect = TRUE; - } - - offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; - r = r1 - r0; - - /* New position of outer circle. */ - x = r * (x1 - fx) / r_org + fx; - y = r * (y1 - fy) / r_org + fy; - - x1 = x; - y1 = y; - r1 = r; - r0 = 0.0; - } else { - offset = r0 / r1; - } - - _cairo_output_stream_printf (document->xml_node_defs, - "radial_pattern_id, - x1, y1, - fx, fy, r1); - - if (emulate_reflect) - _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" "); - else - _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base); - _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); - _cairo_output_stream_printf (document->xml_node_defs, ">\n"); - - /* To support cairo's EXTEND_NONE, (for which SVG has no similar - * notion), we add transparent color stops on either end of the - * user-provided stops. */ - if (extend == CAIRO_EXTEND_NONE) { - _cairo_output_stream_printf (document->xml_node_defs, - "\n"); - if (r0 != 0.0) - _cairo_output_stream_printf (document->xml_node_defs, - "\n", - r0 / r1); - } - status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, - &pattern->base, offset, - reverse_stops, - emulate_reflect); - if (unlikely (status)) - return status; - - if (pattern->base.base.extend == CAIRO_EXTEND_NONE) - _cairo_output_stream_printf (document->xml_node_defs, - "\n"); - } - - _cairo_output_stream_printf (document->xml_node_defs, - "\n"); - - _cairo_output_stream_printf (style, - "%s:url(#radial%d);", - is_stroke ? "stroke" : "fill", - document->radial_pattern_id); - - document->radial_pattern_id++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_output_stream_t *output, - cairo_bool_t is_stroke, - const cairo_matrix_t *parent_matrix) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, - output, is_stroke); - - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, - output, is_stroke, parent_matrix); - - case CAIRO_PATTERN_TYPE_LINEAR: - return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, - output, is_stroke, parent_matrix); - - case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, - output, is_stroke, parent_matrix); - } - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); -} - -static cairo_status_t -_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_fill_rule_t fill_rule, - const cairo_matrix_t *parent_matrix) -{ - _cairo_output_stream_printf (output, - "fill-rule:%s;", - fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? - "evenodd" : "nonzero"); - _cairo_svg_surface_emit_operator_for_style (output, surface, op); - return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); -} - -static cairo_status_t -_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *parent_matrix) -{ - cairo_status_t status; - const char *line_cap, *line_join; - unsigned int i; - - switch (stroke_style->line_cap) { - case CAIRO_LINE_CAP_BUTT: - line_cap = "butt"; - break; - case CAIRO_LINE_CAP_ROUND: - line_cap = "round"; - break; - case CAIRO_LINE_CAP_SQUARE: - line_cap = "square"; - break; - default: - ASSERT_NOT_REACHED; - } - - switch (stroke_style->line_join) { - case CAIRO_LINE_JOIN_MITER: - line_join = "miter"; - break; - case CAIRO_LINE_JOIN_ROUND: - line_join = "round"; - break; - case CAIRO_LINE_JOIN_BEVEL: - line_join = "bevel"; - break; - default: - ASSERT_NOT_REACHED; - } - - _cairo_output_stream_printf (output, - "stroke-width:%f;" - "stroke-linecap:%s;" - "stroke-linejoin:%s;", - stroke_style->line_width, - line_cap, - line_join); - - status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); - if (unlikely (status)) - return status; - - _cairo_svg_surface_emit_operator_for_style (output, surface, op); - - if (stroke_style->num_dashes > 0) { - _cairo_output_stream_printf (output, "stroke-dasharray:"); - for (i = 0; i < stroke_style->num_dashes; i++) { - _cairo_output_stream_printf (output, "%f", - stroke_style->dash[i]); - if (i + 1 < stroke_style->num_dashes) - _cairo_output_stream_printf (output, ","); - else - _cairo_output_stream_printf (output, ";"); - } - if (stroke_style->dash_offset != 0.0) { - _cairo_output_stream_printf (output, - "stroke-dashoffset:%f;", - stroke_style->dash_offset); - } - } - - _cairo_output_stream_printf (output, - "stroke-miterlimit:%f;", - stroke_style->miter_limit); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_fill_stroke (void *abstract_surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) -{ - cairo_svg_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, fill_op, - fill_source, fill_rule, stroke_ctm_inverse); - if (unlikely (status)) - return status; - - status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, - stroke_source, stroke_style, stroke_ctm_inverse); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, "\" "); - - _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); - - _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); - _cairo_output_stream_printf (surface->xml_node, "/>\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_svg_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_svg_surface_analyze_operation (surface, op, source); - - assert (_cairo_svg_surface_operation_supported (surface, op, source)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, "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, - "width, surface->height); - _cairo_svg_surface_emit_operator_for_style (output, surface, op); - status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (output, "stroke:none;\""); - - if (extra_attributes) - _cairo_output_stream_printf (output, " %s", extra_attributes); - - _cairo_output_stream_printf (output, "/>\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_svg_surface_t *surface = abstract_surface; - - /* Emulation of clear and source operators, when no clipping region - * is defined. We just delete existing content of surface root node, - * and exit early if operator is clear. - */ - if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && - clip == NULL) - { - switch (surface->paginated_mode) { - case CAIRO_PAGINATED_MODE_FALLBACK: - ASSERT_NOT_REACHED; - case CAIRO_PAGINATED_MODE_ANALYZE: - return CAIRO_STATUS_SUCCESS; - - case CAIRO_PAGINATED_MODE_RENDER: - status = _cairo_output_stream_destroy (surface->xml_node); - if (unlikely (status)) { - surface->xml_node = NULL; - return status; - } - - surface->xml_node = _cairo_memory_stream_create (); - if (_cairo_output_stream_get_status (surface->xml_node)) { - status = _cairo_output_stream_destroy (surface->xml_node); - surface->xml_node = NULL; - return status; - } - - if (op == CAIRO_OPERATOR_CLEAR) { - if (surface->content == CAIRO_CONTENT_COLOR) { - _cairo_output_stream_printf (surface->xml_node, - "\n", - surface->width, surface->height); - } - return CAIRO_STATUS_SUCCESS; - } - break; - } - } else { - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_svg_surface_analyze_operation (surface, op, source); - - assert (_cairo_svg_surface_operation_supported (surface, op, source)); - } - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - return _cairo_svg_surface_emit_paint (surface->xml_node, - surface, op, source, 0, NULL); -} - -static cairo_int_status_t -_cairo_svg_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_svg_surface_t *surface = abstract_surface; - cairo_svg_document_t *document = surface->document; - cairo_output_stream_t *mask_stream; - char buffer[64]; - cairo_bool_t discard_filter = FALSE; - unsigned int mask_id; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - cairo_status_t source_status, mask_status; - - source_status = _cairo_svg_surface_analyze_operation (surface, op, source); - if (_cairo_status_is_error (source_status)) - return source_status; - - if (mask->has_component_alpha) { - mask_status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); - if (_cairo_status_is_error (mask_status)) - return mask_status; - } - - return _cairo_analysis_surface_merge_status (source_status, - mask_status); - } - - assert (_cairo_svg_surface_operation_supported (surface, op, source)); - assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; - cairo_content_t content = cairo_surface_get_content (surface_pattern->surface); - if (content == CAIRO_CONTENT_ALPHA) - discard_filter = TRUE; - } - - if (!discard_filter) - _cairo_svg_surface_emit_alpha_filter (document); - - /* _cairo_svg_surface_emit_paint() will output a pattern definition to - * document->xml_node_defs so we need to write the mask element to - * a temporary stream and then copy that to xml_node_defs. */ - mask_stream = _cairo_memory_stream_create (); - if (_cairo_output_stream_get_status (mask_stream)) - return _cairo_output_stream_destroy (mask_stream); - - mask_id = _cairo_svg_document_allocate_mask_id (document); - - _cairo_output_stream_printf (mask_stream, - "\n" - "%s", - mask_id, - discard_filter ? "" : " \n"); - status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); - if (unlikely (status)) { - cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); - return status; - (void) ignore; - } - - _cairo_output_stream_printf (mask_stream, - "%s" - "\n", - discard_filter ? "" : " \n"); - _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); - - status = _cairo_output_stream_destroy (mask_stream); - if (unlikely (status)) - return status; - - snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", - mask_id); - status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_stroke (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_svg_surface_t *surface = abstract_dst; - cairo_status_t status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_svg_surface_analyze_operation (surface, op, source); - - assert (_cairo_svg_surface_operation_supported (surface, op, source)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, - source, stroke_style, ctm_inverse); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, "\" "); - - _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); - - _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); - _cairo_output_stream_printf (surface->xml_node, "/>\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_svg_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_svg_surface_t *surface = abstract_surface; - cairo_svg_document_t *document = surface->document; - cairo_path_fixed_t path; - cairo_status_t status; - cairo_scaled_font_subsets_glyph_t subset_glyph; - int i; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_svg_surface_analyze_operation (surface, op, pattern); - - assert (_cairo_svg_surface_operation_supported (surface, op, pattern)); - - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - /* FIXME it's probably possible to apply a pattern of a gradient to - * a group of symbols, but I don't know how yet. Gradients or patterns - * are translated by x and y properties of use element. */ - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - goto FALLBACK; - - _cairo_output_stream_printf (surface->xml_node, "xml_node, FALSE, NULL); - if (unlikely (status)) - return status; - - _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); - - _cairo_output_stream_printf (surface->xml_node, "\">\n"); - - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, - scaled_font, glyphs[i].index, - NULL, 0, - &subset_glyph); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - _cairo_output_stream_printf (surface->xml_node, "\n"); - - glyphs += i; - num_glyphs -= i; - goto FALLBACK; - } - - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->xml_node, - " \n", - subset_glyph.font_id, - subset_glyph.subset_glyph_index, - glyphs[i].x, glyphs[i].y); - } - - _cairo_output_stream_printf (surface->xml_node, "\n"); - - return CAIRO_STATUS_SUCCESS; - -FALLBACK: - _cairo_path_fixed_init (&path); - - status = _cairo_scaled_font_glyph_path (scaled_font, - (cairo_glyph_t *) glyphs, - num_glyphs, &path); - - if (unlikely (status)) { - _cairo_path_fixed_fini (&path); - return status; - } - - status = _cairo_svg_surface_fill (abstract_surface, op, pattern, - &path, CAIRO_FILL_RULE_WINDING, - 0.0, CAIRO_ANTIALIAS_SUBPIXEL, - clip); - - _cairo_path_fixed_fini (&path); - - return status; -} - -static void -_cairo_svg_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); - cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); -} - -static const cairo_surface_backend_t cairo_svg_surface_backend = { - CAIRO_SURFACE_TYPE_SVG, - NULL, /* create_similar: handled by wrapper */ - _cairo_svg_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* _cairo_svg_surface_composite, */ - NULL, /* _cairo_svg_surface_fill_rectangles, */ - NULL, /* _cairo_svg_surface_composite_trapezoids,*/ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - _cairo_svg_surface_copy_page, - _cairo_svg_surface_show_page, - _cairo_svg_surface_get_extents, - NULL, /* _cairo_svg_surface_old_show_glyphs, */ - _cairo_svg_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark dirty rectangle */ - NULL, /* scaled font fini */ - NULL, /* scaled glyph fini */ - _cairo_svg_surface_paint, - _cairo_svg_surface_mask, - _cairo_svg_surface_stroke, - _cairo_svg_surface_fill, - _cairo_svg_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - _cairo_svg_surface_fill_stroke -}; - -static cairo_status_t -_cairo_svg_document_create (cairo_output_stream_t *output_stream, - double width, - double height, - cairo_svg_version_t version, - cairo_svg_document_t **document_out) -{ - cairo_svg_document_t *document; - cairo_status_t status, status_ignored; - - if (output_stream->status) - return output_stream->status; - - document = malloc (sizeof (cairo_svg_document_t)); - if (unlikely (document == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* The use of defs for font glyphs imposes no per-subset limit. */ - document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); - if (unlikely (document->font_subsets == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_DOCUMENT; - } - - document->output_stream = output_stream; - document->refcount = 1; - document->owner = NULL; - document->finished = FALSE; - document->width = width; - document->height = height; - - document->linear_pattern_id = 0; - document->radial_pattern_id = 0; - document->pattern_id = 0; - document->filter_id = 0; - document->clip_id = 0; - document->mask_id = 0; - - document->xml_node_defs = _cairo_memory_stream_create (); - status = _cairo_output_stream_get_status (document->xml_node_defs); - if (unlikely (status)) - goto CLEANUP_NODE_DEFS; - - document->xml_node_glyphs = _cairo_memory_stream_create (); - status = _cairo_output_stream_get_status (document->xml_node_glyphs); - if (unlikely (status)) - goto CLEANUP_NODE_GLYPHS; - - document->alpha_filter = FALSE; - - document->svg_version = version; - - *document_out = document; - return CAIRO_STATUS_SUCCESS; - - CLEANUP_NODE_GLYPHS: - status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs); - CLEANUP_NODE_DEFS: - status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); - _cairo_scaled_font_subsets_destroy (document->font_subsets); - CLEANUP_DOCUMENT: - free (document); - return status; -} - -static cairo_svg_document_t * -_cairo_svg_document_reference (cairo_svg_document_t *document) -{ - document->refcount++; - - return document; -} - -static unsigned int -_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document) -{ - return document->mask_id++; -} - -static cairo_status_t -_cairo_svg_document_destroy (cairo_svg_document_t *document) -{ - cairo_status_t status; - - document->refcount--; - if (document->refcount > 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_svg_document_finish (document); - - free (document); - - return status; -} - -static cairo_status_t -_cairo_svg_document_finish (cairo_svg_document_t *document) -{ - cairo_status_t status, status2; - cairo_output_stream_t *output = document->output_stream; - cairo_svg_page_t *page; - unsigned int i; - - if (document->finished) - return CAIRO_STATUS_SUCCESS; - - /* - * Should we add DOCTYPE? - * - * Google says no. - * - * http://tech.groups.yahoo.com/group/svg-developers/message/48562: - * There's a bunch of issues, but just to pick a few: - * - they'll give false positives. - * - they'll give false negatives. - * - they're namespace-unaware. - * - they don't wildcard. - * So when they say OK they really haven't checked anything, when - * they say NOT OK they might be on crack, and like all - * namespace-unaware things they're a dead branch of the XML tree. - * - * http://jwatt.org/svg/authoring/: - * Unfortunately the SVG DTDs are a source of so many issues that the - * SVG WG has decided not to write one for the upcoming SVG 1.2 - * standard. In fact SVG WG members are even telling people not to use - * a DOCTYPE declaration in SVG 1.0 and 1.1 documents. - */ - - _cairo_output_stream_printf (output, - "\n" - "\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, "\n"); - if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { - _cairo_output_stream_printf (output, "\n"); - _cairo_memory_stream_copy (document->xml_node_glyphs, output); - _cairo_output_stream_printf (output, "\n"); - } - _cairo_memory_stream_copy (document->xml_node_defs, output); - _cairo_output_stream_printf (output, "\n"); - } - - if (document->owner != NULL) { - cairo_svg_surface_t *surface; - - surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); - if (surface->xml_node != NULL && - _cairo_memory_stream_length (surface->xml_node) > 0) { - if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - if (surface->page_set.num_elements > 1 && - _cairo_svg_version_has_page_set_support (document->svg_version)) { - _cairo_output_stream_printf (output, "\n"); - for (i = 0; i < surface->page_set.num_elements; i++) { - page = _cairo_array_index (&surface->page_set, i); - _cairo_output_stream_printf (output, "\n"); - _cairo_output_stream_printf (output, - "\n", - page->surface_id); - _cairo_memory_stream_copy (page->xml_node, output); - _cairo_output_stream_printf (output, "\n\n"); - } - _cairo_output_stream_printf (output, "\n"); - } else if (surface->page_set.num_elements > 0) { - page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); - _cairo_output_stream_printf (output, - "\n", - page->surface_id); - _cairo_memory_stream_copy (page->xml_node, output); - _cairo_output_stream_printf (output, "\n"); - } - } - - _cairo_output_stream_printf (output, "\n"); - - status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - status2 = _cairo_output_stream_destroy (document->xml_node_defs); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - status2 = _cairo_output_stream_destroy (output); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - document->finished = TRUE; - - return status; -} - -static void -_cairo_svg_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t paginated_mode) -{ - cairo_svg_surface_t *surface = abstract_surface; - - surface->paginated_mode = paginated_mode; -} - -static cairo_bool_t -_cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) -{ - cairo_svg_surface_t *surface = abstract_surface; - cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) { - status = _cairo_svg_surface_analyze_operator (surface, - CAIRO_OPERATOR_SOURCE); - } - - return status == CAIRO_STATUS_SUCCESS; -} - -static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { - NULL /*_cairo_svg_surface_start_page*/, - _cairo_svg_surface_set_paginated_mode, - NULL, /* _cairo_svg_surface_set_bounding_box */ - NULL, /* _cairo_svg_surface_set_fallback_images_required */ - _cairo_svg_surface_supports_fine_grained_fallbacks, - -}; diff --git a/libs/cairo/cairo/src/cairo-svg.h b/libs/cairo/cairo/src/cairo-svg.h deleted file mode 100644 index 2c07aeedf..000000000 --- a/libs/cairo/cairo/src/cairo-svg.h +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_SVG_H -#define CAIRO_SVG_H - -#include "cairo.h" - -#if CAIRO_HAS_SVG_SURFACE - -CAIRO_BEGIN_DECLS - -/** - * cairo_svg_version_t: - * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. - * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. - * - * #cairo_svg_version_t is used to describe the version number of the SVG - * specification that a generated SVG file will conform to. - */ -typedef enum _cairo_svg_version { - CAIRO_SVG_VERSION_1_1, - CAIRO_SVG_VERSION_1_2 -} cairo_svg_version_t; - -cairo_public cairo_surface_t * -cairo_svg_surface_create (const char *filename, - double width_in_points, - double height_in_points); - -cairo_public cairo_surface_t * -cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width_in_points, - double height_in_points); - -cairo_public void -cairo_svg_surface_restrict_to_version (cairo_surface_t *surface, - cairo_svg_version_t version); - -cairo_public void -cairo_svg_get_versions (cairo_svg_version_t const **versions, - int *num_versions); - -cairo_public const char * -cairo_svg_version_to_string (cairo_svg_version_t version); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_SVG_SURFACE */ -# error Cairo was not compiled with support for the svg backend -#endif /* CAIRO_HAS_SVG_SURFACE */ - -#endif /* CAIRO_SVG_H */ diff --git a/libs/cairo/cairo/src/cairo-system.c b/libs/cairo/cairo/src/cairo-system.c deleted file mode 100644 index 22e4934c7..000000000 --- a/libs/cairo/cairo/src/cairo-system.c +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This file should include code that is system-specific, not - * feature-specific. For example, the DLL initialization/finalization - * code on Win32 or OS/2 must live here (not in cairo-whatever-surface.c). - * Same about possible ELF-specific code. - * - * And no other function should live here. - */ - - -#include "cairoint.h" - - - -#if CAIRO_MUTEX_IMPL_WIN32 -#if !CAIRO_WIN32_STATIC_BUILD - -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - -#include "cairo-clip-private.h" -#include "cairo-paginated-private.h" -#include "cairo-win32-private.h" -#include "cairo-scaled-font-subsets-private.h" - -#include - -/* declare to avoid "no previous prototype for 'DllMain'" warning */ -BOOL WINAPI -DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved); - -BOOL WINAPI -DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - switch (fdwReason) { - case DLL_PROCESS_ATTACH: - CAIRO_MUTEX_INITIALIZE (); - break; - - case DLL_PROCESS_DETACH: - CAIRO_MUTEX_FINALIZE (); - break; - } - - return TRUE; -} - -#endif -#endif - diff --git a/libs/cairo/cairo/src/cairo-tee-surface-private.h b/libs/cairo/cairo/src/cairo-tee-surface-private.h deleted file mode 100644 index dd8aeda4d..000000000 --- a/libs/cairo/cairo/src/cairo-tee-surface-private.h +++ /dev/null @@ -1,15 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-tee-surface.c b/libs/cairo/cairo/src/cairo-tee-surface.c deleted file mode 100644 index 755a0b7ee..000000000 --- a/libs/cairo/cairo/src/cairo-tee-surface.c +++ /dev/null @@ -1,685 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This surface supports redirecting all its input to multiple surfaces. - */ - -#include "cairoint.h" - -#include "cairo-tee.h" - -#include "cairo-error-private.h" -#include "cairo-tee-surface-private.h" -#include "cairo-surface-wrapper-private.h" - -typedef struct _cairo_tee_surface { - cairo_surface_t base; - - cairo_surface_wrapper_t master; - cairo_array_t slaves; -} cairo_tee_surface_t; - -slim_hidden_proto (cairo_tee_surface_create); -slim_hidden_proto (cairo_tee_surface_add); - -static cairo_surface_t * -_cairo_tee_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - - cairo_tee_surface_t *other = abstract_surface; - cairo_surface_t *similar; - cairo_surface_t *surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - - similar = _cairo_surface_wrapper_create_similar (&other->master, - content, width, height); - surface = cairo_tee_surface_create (similar); - cairo_surface_destroy (similar); - if (unlikely (surface->status)) - return surface; - - num_slaves = _cairo_array_num_elements (&other->slaves); - slaves = _cairo_array_index (&other->slaves, 0); - for (n = 0; n < num_slaves; n++) { - - similar = _cairo_surface_wrapper_create_similar (&slaves[n], - content, - width, height); - cairo_tee_surface_add (surface, similar); - cairo_surface_destroy (similar); - } - - if (unlikely (surface->status)) { - cairo_status_t status = surface->status; - cairo_surface_destroy (surface); - surface = _cairo_surface_create_in_error (status); - } - - return surface; -} - -static cairo_status_t -_cairo_tee_surface_finish (void *abstract_surface) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - - _cairo_surface_wrapper_fini (&surface->master); - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) - _cairo_surface_wrapper_fini (&slaves[n]); - - _cairo_array_fini (&surface->slaves); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_tee_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int num_slaves, n; - - /* we prefer to use a real image surface if available */ - if (_cairo_surface_is_image (surface->master.target)) { - return _cairo_surface_wrapper_acquire_source_image (&surface->master, - image_out, image_extra); - } - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (_cairo_surface_is_image (slaves[n].target)) { - return _cairo_surface_wrapper_acquire_source_image (&slaves[n], - image_out, - image_extra); - } - } - - return _cairo_surface_wrapper_acquire_source_image (&surface->master, - image_out, image_extra); -} - -static void -_cairo_tee_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_tee_surface_t *surface = abstract_surface; - - _cairo_surface_wrapper_release_source_image (&surface->master, - image, image_extra); -} - -static cairo_surface_t * -_cairo_tee_surface_snapshot (void *abstract_surface) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int num_slaves, n; - - /* we prefer to use a recording surface for our snapshots */ - if (_cairo_surface_is_recording (surface->master.target)) - return _cairo_surface_wrapper_snapshot (&surface->master); - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (_cairo_surface_is_recording (slaves[n].target)) - return _cairo_surface_wrapper_snapshot (&slaves[n]); - } - - return _cairo_surface_wrapper_snapshot (&surface->master); -} - -static cairo_bool_t -_cairo_tee_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_tee_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_get_extents (&surface->master, rectangle); -} - -static void -_cairo_tee_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - cairo_tee_surface_t *surface = abstract_surface; - - _cairo_surface_wrapper_get_font_options (&surface->master, options); -} - -static const cairo_pattern_t * -_cairo_tee_surface_match_source (cairo_tee_surface_t *surface, - const cairo_pattern_t *source, - int index, - cairo_surface_wrapper_t *dest, - cairo_surface_pattern_t *temp) -{ - cairo_surface_t *s; - cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s); - if (status == CAIRO_STATUS_SUCCESS && - cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) { - cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index); - if (tee_surf->status == CAIRO_STATUS_SUCCESS && - tee_surf->backend == dest->target->backend) { - status = _cairo_pattern_init_copy (&temp->base, source); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_destroy (temp->surface); - temp->surface = tee_surf; - cairo_surface_reference (temp->surface); - return &temp->base; - } - } - } - - return source; -} - -static cairo_int_status_t -_cairo_tee_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_tee_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_mask (&surface->master, - op, matched_source, mask, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_mask (&slaves[n], - op, matched_source, mask, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_tee_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_stroke (&surface->master, - op, matched_source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_stroke (&slaves[n], - op, matched_source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_tee_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_fill (&surface->master, - op, matched_source, - path, fill_rule, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_fill (&slaves[n], - op, matched_source, - path, fill_rule, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static cairo_int_status_t -_cairo_tee_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - cairo_glyph_t *glyphs_copy; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - /* XXX: This copying is ugly. */ - glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (glyphs_copy == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, - matched_source, - utf8, utf8_len, - glyphs_copy, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - goto CLEANUP; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, - matched_source, - utf8, utf8_len, - glyphs_copy, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - goto CLEANUP; - } - - CLEANUP: - free (glyphs_copy); - return status; -} - -static cairo_status_t -_cairo_tee_surface_flush (void *abstract_surface) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - - status = _cairo_surface_wrapper_flush(&surface->master); - if (unlikely (status)) - return status; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - status = _cairo_surface_wrapper_flush(&slaves[n]); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_tee_surface_backend = { - CAIRO_SURFACE_TYPE_TEE, - _cairo_tee_surface_create_similar, - _cairo_tee_surface_finish, - _cairo_tee_surface_acquire_source_image, - _cairo_tee_surface_release_source_image, - NULL, NULL, /* dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_tee_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_tee_surface_get_font_options, - _cairo_tee_surface_flush, - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_tee_surface_paint, - _cairo_tee_surface_mask, - _cairo_tee_surface_stroke, - _cairo_tee_surface_fill, - NULL, /* replaced by show_text_glyphs */ - - _cairo_tee_surface_snapshot, - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - _cairo_tee_surface_has_show_text_glyphs, - _cairo_tee_surface_show_text_glyphs -}; - -cairo_surface_t * -cairo_tee_surface_create (cairo_surface_t *master) -{ - cairo_tee_surface_t *surface; - - if (unlikely (master->status)) - return _cairo_surface_create_in_error (master->status); - - surface = malloc (sizeof (cairo_tee_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &cairo_tee_surface_backend, - master->device, - master->content); - - _cairo_surface_wrapper_init (&surface->master, master); - /* we trust that these are already set and remain constant */ - surface->base.device_transform = master->device_transform; - surface->base.device_transform_inverse = master->device_transform_inverse; - - _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); - - return &surface->base; -} -slim_hidden_def (cairo_tee_surface_create); - -void -cairo_tee_surface_add (cairo_surface_t *abstract_surface, - cairo_surface_t *target) -{ - cairo_tee_surface_t *surface; - cairo_surface_wrapper_t slave; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (abstract_surface->backend != &cairo_tee_surface_backend) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - if (unlikely (target->status)) { - status = _cairo_surface_set_error (abstract_surface, target->status); - return; - } - - surface = (cairo_tee_surface_t *) abstract_surface; - - _cairo_surface_wrapper_init (&slave, target); - status = _cairo_array_append (&surface->slaves, &slave); - if (unlikely (status)) { - _cairo_surface_wrapper_fini (&slave); - status = _cairo_surface_set_error (&surface->base, status); - } -} -slim_hidden_def (cairo_tee_surface_add); - -void -cairo_tee_surface_remove (cairo_surface_t *abstract_surface, - cairo_surface_t *target) -{ - cairo_tee_surface_t *surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (abstract_surface->backend != &cairo_tee_surface_backend) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - surface = (cairo_tee_surface_t *) abstract_surface; - if (target == surface->master.target) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_INDEX)); - return; - } - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (slaves[n].target == target) - break; - } - - if (n == num_slaves) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_INDEX)); - return; - } - - _cairo_surface_wrapper_fini (&slaves[n]); - for (n++; n < num_slaves; n++) - slaves[n-1] = slaves[n]; - surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */ -} - -cairo_surface_t * -cairo_tee_surface_index (cairo_surface_t *abstract_surface, - int index) -{ - cairo_tee_surface_t *surface; - - if (unlikely (abstract_surface->status)) - return _cairo_surface_create_in_error (abstract_surface->status); - if (unlikely (abstract_surface->finished)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - - if (abstract_surface->backend != &cairo_tee_surface_backend) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - surface = (cairo_tee_surface_t *) abstract_surface; - if (index == 0) { - return surface->master.target; - } else { - cairo_surface_wrapper_t *slave; - - index--; - - if (index >= _cairo_array_num_elements (&surface->slaves)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); - - slave = _cairo_array_index (&surface->slaves, index); - return slave->target; - } -} - -cairo_surface_t * -_cairo_tee_surface_find_match (void *abstract_surface, - const cairo_surface_backend_t *backend, - cairo_content_t content) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int num_slaves, n; - - /* exact match first */ - if (surface->master.target->backend == backend && - surface->master.target->content == content) - { - return surface->master.target; - } - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (slaves[n].target->backend == backend && - slaves[n].target->content == content) - { - return slaves[n].target; - } - } - - /* matching backend? */ - if (surface->master.target->backend == backend) - return surface->master.target; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (slaves[n].target->backend == backend) - return slaves[n].target; - } - - return NULL; -} diff --git a/libs/cairo/cairo/src/cairo-tee.h b/libs/cairo/cairo/src/cairo-tee.h deleted file mode 100644 index c60cb5327..000000000 --- a/libs/cairo/cairo/src/cairo-tee.h +++ /dev/null @@ -1,35 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_TEE_H -#define CAIRO_TEE_H - -#include "cairo.h" - -#if CAIRO_HAS_TEE_SURFACE - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_tee_surface_create (cairo_surface_t *master); - -cairo_public void -cairo_tee_surface_add (cairo_surface_t *surface, - cairo_surface_t *target); - -cairo_public void -cairo_tee_surface_remove (cairo_surface_t *surface, - cairo_surface_t *target); - -cairo_public cairo_surface_t * -cairo_tee_surface_index (cairo_surface_t *surface, - int index); - -CAIRO_END_DECLS - -#else /*CAIRO_HAS_TEE_SURFACE*/ -# error Cairo was not compiled with support for the TEE backend -#endif /*CAIRO_HAS_TEE_SURFACE*/ - -#endif /*CAIRO_TEE_H*/ diff --git a/libs/cairo/cairo/src/cairo-tor-scan-converter.c b/libs/cairo/cairo/src/cairo-tor-scan-converter.c deleted file mode 100644 index 0dc20fef5..000000000 --- a/libs/cairo/cairo/src/cairo-tor-scan-converter.c +++ /dev/null @@ -1,2221 +0,0 @@ -/* 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 -#include -#include -#include - -/*------------------------------------------------------------------------- - * cairo specific config - */ -#define I static - -/* Prefer cairo's status type. */ -#define GLITTER_HAVE_STATUS_T 1 -#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS -#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY -typedef cairo_status_t glitter_status_t; - -/* The input coordinate scale and the rasterisation grid scales. */ -#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS -#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS -#define GRID_Y 15 - -/* Set glitter up to use a cairo span renderer to do the coverage - * blitting. */ -struct pool; -struct cell_list; - -static glitter_status_t -blit_with_span_renderer( - struct cell_list *coverages, - cairo_span_renderer_t *span_renderer, - struct pool *span_pool, - int y, - int height, - int xmin, - int xmax); - -static glitter_status_t -blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height); - -#define GLITTER_BLIT_COVERAGES_ARGS \ - cairo_span_renderer_t *span_renderer, \ - struct pool *span_pool - -#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \ - cairo_status_t status = blit_with_span_renderer (cells, \ - span_renderer, \ - span_pool, \ - y, height, \ - xmin, xmax); \ - if (unlikely (status)) \ - return status; \ -} while (0) - -#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \ - cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \ - if (unlikely (status)) \ - return status; \ -} while (0) - -/*------------------------------------------------------------------------- - * glitter-paths.h - */ - -/* "Input scaled" numbers are fixed precision reals with multiplier - * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as - * pixel scaled numbers. These get converted to the internal grid - * scaled numbers as soon as possible. Internal overflow is possible - * if GRID_X/Y inside glitter-paths.c is larger than - * 1< -#include -#include - -/* All polygon coordinates are snapped onto a subsample grid. "Grid - * scaled" numbers are fixed precision reals with multiplier GRID_X or - * GRID_Y. */ -typedef int grid_scaled_t; -typedef int grid_scaled_x_t; -typedef int grid_scaled_y_t; - -/* Default x/y scale factors. - * You can either define GRID_X/Y_BITS to get a power-of-two scale - * or define GRID_X/Y separately. */ -#if !defined(GRID_X) && !defined(GRID_X_BITS) -# define GRID_X_BITS 8 -#endif -#if !defined(GRID_Y) && !defined(GRID_Y_BITS) -# define GRID_Y 15 -#endif - -/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ -#ifdef GRID_X_BITS -# define GRID_X (1 << GRID_X_BITS) -#endif -#ifdef GRID_Y_BITS -# define GRID_Y (1 << GRID_Y_BITS) -#endif - -/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into - * integer and fractional parts. The integer part is floored. */ -#if defined(GRID_X_TO_INT_FRAC) - /* do nothing */ -#elif defined(GRID_X_BITS) -# define GRID_X_TO_INT_FRAC(x, i, f) \ - _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) -#else -# define GRID_X_TO_INT_FRAC(x, i, f) \ - _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) -#endif - -#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ - (i) = (t) / (m); \ - (f) = (t) % (m); \ - if ((f) < 0) { \ - --(i); \ - (f) += (m); \ - } \ -} while (0) - -#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ - (f) = (t) & ((1 << (b)) - 1); \ - (i) = (t) >> (b); \ -} while (0) - -/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want - * to be able to represent exactly areas of subpixel trapezoids whose - * vertices are given in grid scaled coordinates. The scale factor - * comes from needing to accurately represent the area 0.5*dx*dy of a - * triangle with base dx and height dy in grid scaled numbers. */ -typedef int grid_area_t; -#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ - -/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ -#if GRID_XY == 510 -# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) -#elif GRID_XY == 255 -# define GRID_AREA_TO_ALPHA(c) (c) -#elif GRID_XY == 64 -# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) -#elif GRID_XY == 128 -# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) -#elif GRID_XY == 256 -# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) -#elif GRID_XY == 15 -# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) -#elif GRID_XY == 2*256*15 -# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) -#else -# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) -#endif - -#define UNROLL3(x) x x x - -struct quorem { - int32_t quo; - int32_t rem; -}; - -/* Header for a chunk of memory in a memory pool. */ -struct _pool_chunk { - /* # bytes used in this chunk. */ - size_t size; - - /* # bytes total in this chunk */ - size_t capacity; - - /* Pointer to the previous chunk or %NULL if this is the sentinel - * chunk in the pool header. */ - struct _pool_chunk *prev_chunk; - - /* Actual data starts here. Well aligned for pointers. */ -}; - -/* A memory pool. This is supposed to be embedded on the stack or - * within some other structure. It may optionally be followed by an - * embedded array from which requests are fulfilled until - * malloc needs to be called to allocate a first real chunk. */ -struct pool { - /* Chunk we're allocating from. */ - struct _pool_chunk *current; - - /* Free list of previously allocated chunks. All have >= default - * capacity. */ - struct _pool_chunk *first_free; - - /* The default capacity of a chunk. */ - size_t default_capacity; - - /* Header for the sentinel chunk. Directly following the pool - * struct should be some space for embedded elements from which - * the sentinel chunk allocates from. */ - struct _pool_chunk sentinel[1]; -}; - -/* A polygon edge. */ -struct edge { - /* Next in y-bucket or active list. */ - struct edge *next; - - /* Current x coordinate while the edge is on the active - * list. Initialised to the x coordinate of the top of the - * edge. The quotient is in grid_scaled_x_t units and the - * remainder is mod dy in grid_scaled_y_t units.*/ - struct quorem x; - - /* Advance of the current x when moving down a subsample line. */ - struct quorem dxdy; - - /* Advance of the current x when moving down a full pixel - * row. Only initialised when the height of the edge is large - * enough that there's a chance the edge could be stepped by a - * full row's worth of subsample rows at a time. */ - struct quorem dxdy_full; - - /* The clipped y of the top of the edge. */ - grid_scaled_y_t ytop; - - /* y2-y1 after orienting the edge downwards. */ - grid_scaled_y_t dy; - - /* Number of subsample rows remaining to scan convert of this - * edge. */ - grid_scaled_y_t height_left; - - /* Original sign of the edge: +1 for downwards, -1 for upwards - * edges. */ - int dir; - int vertical; -}; - -/* Number of subsample rows per y-bucket. Must be GRID_Y. */ -#define EDGE_Y_BUCKET_HEIGHT GRID_Y - -#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) - -struct bucket { - /* Unsorted list of edges starting within this bucket. */ - struct edge *edges; - - /* Set to non-zero if there are edges starting strictly within the - * bucket. */ - unsigned have_inside_edges; -}; - -/* A collection of sorted and vertically clipped edges of the polygon. - * Edges are moved from the polygon to an active list while scan - * converting. */ -struct polygon { - /* The clip extents. */ - grid_scaled_x_t xmin, xmax; - grid_scaled_y_t ymin, ymax; - - /* Array of edges all starting in the same bucket. An edge is put - * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when - * it is added to the polygon. */ - struct bucket *y_buckets; - struct bucket y_buckets_embedded[64]; - - struct { - struct pool base[1]; - struct edge embedded[32]; - } edge_pool; -}; - -/* A cell records the effect on pixel coverage of polygon edges - * passing through a pixel. It contains two accumulators of pixel - * coverage. - * - * Consider the effects of a polygon edge on the coverage of a pixel - * it intersects and that of the following one. The coverage of the - * following pixel is the height of the edge multiplied by the width - * of the pixel, and the coverage of the pixel itself is the area of - * the trapezoid formed by the edge and the right side of the pixel. - * - * +-----------------------+-----------------------+ - * | | | - * | | | - * |_______________________|_______________________| - * | \...................|.......................|\ - * | \..................|.......................| | - * | \.................|.......................| | - * | \....covered.....|.......................| | - * | \....area.......|.......................| } covered height - * | \..............|.......................| | - * |uncovered\.............|.......................| | - * | area \............|.......................| | - * |___________\...........|.......................|/ - * | | | - * | | | - * | | | - * +-----------------------+-----------------------+ - * - * Since the coverage of the following pixel will always be a multiple - * of the width of the pixel, we can store the height of the covered - * area instead. The coverage of the pixel itself is the total - * coverage minus the area of the uncovered area to the left of the - * edge. As it's faster to compute the uncovered area we only store - * that and subtract it from the total coverage later when forming - * spans to blit. - * - * The heights and areas are signed, with left edges of the polygon - * having positive sign and right edges having negative sign. When - * two edges intersect they swap their left/rightness so their - * contribution above and below the intersection point must be - * computed separately. */ -struct cell { - struct cell *next; - int x; - grid_area_t uncovered_area; - grid_scaled_y_t covered_height; -}; - -/* A cell list represents the scan line sparsely as cells ordered by - * ascending x. It is geared towards scanning the cells in order - * using an internal cursor. */ -struct cell_list { - /* Points to the left-most cell in the scan line. */ - struct cell *head; - /* Sentinel node */ - struct cell tail; - - /* Cursor state for iterating through the cell list. Points to - * a pointer to the current cell: either &cell_list->head or the next - * field of the previous cell. */ - struct cell **cursor; - - /* Cells in the cell list are owned by the cell list and are - * allocated from this pool. */ - struct { - struct pool base[1]; - struct cell embedded[32]; - } cell_pool; -}; - -struct cell_pair { - struct cell *cell1; - struct cell *cell2; -}; - -/* The active list contains edges in the current scan line ordered by - * the x-coordinate of the intercept of the edge and the scan line. */ -struct active_list { - /* Leftmost edge on the current scan line. */ - struct edge *head; - - /* A lower bound on the height of the active edges is used to - * estimate how soon some active edge ends. We can't advance the - * scan conversion by a full pixel row if an edge ends somewhere - * within it. */ - grid_scaled_y_t min_height; -}; - -struct glitter_scan_converter { - struct polygon polygon[1]; - struct active_list active[1]; - struct cell_list coverages[1]; - - /* Clip box. */ - grid_scaled_x_t xmin, xmax; - grid_scaled_y_t ymin, ymax; -}; - -/* Compute the floored division a/b. Assumes / and % perform symmetric - * division. */ -inline static struct quorem -floored_divrem(int a, int b) -{ - struct quorem qr; - qr.quo = a/b; - qr.rem = a%b; - if ((a^b)<0 && qr.rem) { - qr.quo -= 1; - qr.rem += b; - } - return qr; -} - -/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric - * division. */ -static struct quorem -floored_muldivrem(int x, int a, int b) -{ - struct quorem qr; - long long xa = (long long)x*a; - qr.quo = xa/b; - qr.rem = xa%b; - if ((xa>=0) != (b>=0) && qr.rem) { - qr.quo -= 1; - qr.rem += b; - } - return qr; -} - -static void -_pool_chunk_init( - struct _pool_chunk *p, - struct _pool_chunk *prev_chunk, - size_t capacity) -{ - p->prev_chunk = prev_chunk; - p->size = 0; - p->capacity = capacity; -} - -static struct _pool_chunk * -_pool_chunk_create( - struct _pool_chunk *prev_chunk, - size_t size) -{ - struct _pool_chunk *p; - size_t size_with_head = size + sizeof(struct _pool_chunk); - if (size_with_head < size) - return NULL; - p = malloc(size_with_head); - if (p) - _pool_chunk_init(p, prev_chunk, size); - return p; -} - -static void -pool_init( - struct pool *pool, - size_t default_capacity, - size_t embedded_capacity) -{ - pool->current = pool->sentinel; - pool->first_free = NULL; - pool->default_capacity = default_capacity; - _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); -} - -static void -pool_fini(struct pool *pool) -{ - struct _pool_chunk *p = pool->current; - do { - while (NULL != p) { - struct _pool_chunk *prev = p->prev_chunk; - if (p != pool->sentinel) - free(p); - p = prev; - } - p = pool->first_free; - pool->first_free = NULL; - } while (NULL != p); - pool_init(pool, 0, 0); -} - -/* Satisfy an allocation by first allocating a new large enough chunk - * and adding it to the head of the pool's chunk list. This function - * is called as a fallback if pool_alloc() couldn't do a quick - * allocation from the current chunk in the pool. */ -static void * -_pool_alloc_from_new_chunk( - struct pool *pool, - size_t size) -{ - struct _pool_chunk *chunk; - void *obj; - size_t capacity; - - /* If the allocation is smaller than the default chunk size then - * try getting a chunk off the free list. Force alloc of a new - * chunk for large requests. */ - capacity = size; - chunk = NULL; - if (size < pool->default_capacity) { - capacity = pool->default_capacity; - chunk = pool->first_free; - if (chunk) { - pool->first_free = chunk->prev_chunk; - _pool_chunk_init(chunk, pool->current, chunk->capacity); - } - } - - if (NULL == chunk) { - chunk = _pool_chunk_create (pool->current, capacity); - if (unlikely (NULL == chunk)) - return NULL; - } - pool->current = chunk; - - obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); - chunk->size += size; - return obj; -} - -/* Allocate size bytes from the pool. The first allocated address - * returned from a pool is aligned to sizeof(void*). Subsequent - * addresses will maintain alignment as long as multiples of void* are - * allocated. Returns the address of a new memory area or %NULL on - * allocation failures. The pool retains ownership of the returned - * memory. */ -inline static void * -pool_alloc (struct pool *pool, size_t size) -{ - struct _pool_chunk *chunk = pool->current; - - if (size <= chunk->capacity - chunk->size) { - void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); - chunk->size += size; - return obj; - } else { - return _pool_alloc_from_new_chunk(pool, size); - } -} - -/* Relinquish all pool_alloced memory back to the pool. */ -static void -pool_reset (struct pool *pool) -{ - /* Transfer all used chunks to the chunk free list. */ - struct _pool_chunk *chunk = pool->current; - if (chunk != pool->sentinel) { - while (chunk->prev_chunk != pool->sentinel) { - chunk = chunk->prev_chunk; - } - chunk->prev_chunk = pool->first_free; - pool->first_free = pool->current; - } - /* Reset the sentinel as the current chunk. */ - pool->current = pool->sentinel; - pool->sentinel->size = 0; -} - -/* Rewinds the cell list's cursor to the beginning. After rewinding - * we're good to cell_list_find() the cell any x coordinate. */ -inline static void -cell_list_rewind (struct cell_list *cells) -{ - cells->cursor = &cells->head; -} - -/* Rewind the cell list if its cursor has been advanced past x. */ -inline static void -cell_list_maybe_rewind (struct cell_list *cells, int x) -{ - struct cell *tail = *cells->cursor; - if (tail->x > x) - cell_list_rewind (cells); -} - -static void -cell_list_init(struct cell_list *cells) -{ - pool_init(cells->cell_pool.base, - 256*sizeof(struct cell), - sizeof(cells->cell_pool.embedded)); - cells->tail.next = NULL; - cells->tail.x = INT_MAX; - cells->head = &cells->tail; - cell_list_rewind (cells); -} - -static void -cell_list_fini(struct cell_list *cells) -{ - pool_fini (cells->cell_pool.base); -} - -/* Empty the cell list. This is called at the start of every pixel - * row. */ -inline static void -cell_list_reset (struct cell_list *cells) -{ - cell_list_rewind (cells); - cells->head = &cells->tail; - pool_reset (cells->cell_pool.base); -} - -static struct cell * -cell_list_alloc (struct cell_list *cells, - struct cell **cursor, - struct cell *tail, - int x) -{ - struct cell *cell; - - cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); - if (unlikely (NULL == cell)) - return NULL; - - *cursor = cell; - cell->next = tail; - cell->x = x; - cell->uncovered_area = 0; - cell->covered_height = 0; - return cell; -} - -/* Find a cell at the given x-coordinate. Returns %NULL if a new cell - * needed to be allocated but couldn't be. Cells must be found with - * non-decreasing x-coordinate until the cell list is rewound using - * cell_list_rewind(). Ownership of the returned cell is retained by - * the cell list. */ -inline static struct cell * -cell_list_find (struct cell_list *cells, int x) -{ - struct cell **cursor = cells->cursor; - struct cell *tail; - - while (1) { - UNROLL3({ - tail = *cursor; - if (tail->x >= x) { - break; - } - cursor = &tail->next; - }); - } - cells->cursor = cursor; - - if (tail->x == x) - return tail; - - return cell_list_alloc (cells, cursor, tail, x); -} - -/* Find two cells at x1 and x2. This is exactly equivalent - * to - * - * pair.cell1 = cell_list_find(cells, x1); - * pair.cell2 = cell_list_find(cells, x2); - * - * except with less function call overhead. */ -inline static struct cell_pair -cell_list_find_pair(struct cell_list *cells, int x1, int x2) -{ - struct cell_pair pair; - struct cell **cursor = cells->cursor; - struct cell *cell1; - struct cell *cell2; - struct cell *newcell; - - /* Find first cell at x1. */ - while (1) { - UNROLL3({ - cell1 = *cursor; - if (cell1->x > x1) - break; - - if (cell1->x == x1) - goto found_first; - - cursor = &cell1->next; - }); - } - - /* New first cell at x1. */ - newcell = pool_alloc (cells->cell_pool.base, - sizeof (struct cell)); - if (likely (NULL != newcell)) { - *cursor = newcell; - newcell->next = cell1; - newcell->x = x1; - newcell->uncovered_area = 0; - newcell->covered_height = 0; - } - cell1 = newcell; - found_first: - - /* Find second cell at x2. */ - while (1) { - UNROLL3({ - cell2 = *cursor; - if (cell2->x > x2) - break; - if (cell2->x == x2) - goto found_second; - cursor = &cell2->next; - }); - } - - /* New second cell at x2. */ - newcell = pool_alloc (cells->cell_pool.base, - sizeof (struct cell)); - if (likely (NULL != newcell)) { - *cursor = newcell; - newcell->next = cell2; - newcell->x = x2; - newcell->uncovered_area = 0; - newcell->covered_height = 0; - } - cell2 = newcell; - found_second: - - cells->cursor = cursor; - pair.cell1 = cell1; - pair.cell2 = cell2; - return pair; -} - -/* Add an unbounded subpixel span covering subpixels >= x to the - * coverage cells. */ -static glitter_status_t -cell_list_add_unbounded_subspan (struct cell_list *cells, - grid_scaled_x_t x) -{ - struct cell *cell; - int ix, fx; - - GRID_X_TO_INT_FRAC(x, ix, fx); - - cell = cell_list_find (cells, ix); - if (likely (cell != NULL)) { - cell->uncovered_area += 2*fx; - cell->covered_height++; - return GLITTER_STATUS_SUCCESS; - } - - return GLITTER_STATUS_NO_MEMORY; -} - -/* Add a subpixel span covering [x1, x2) to the coverage cells. */ -inline static glitter_status_t -cell_list_add_subspan( - struct cell_list *cells, - grid_scaled_x_t x1, - grid_scaled_x_t x2) -{ - int ix1, fx1; - int ix2, fx2; - - GRID_X_TO_INT_FRAC(x1, ix1, fx1); - GRID_X_TO_INT_FRAC(x2, ix2, fx2); - - if (ix1 != ix2) { - struct cell_pair p; - p = cell_list_find_pair(cells, ix1, ix2); - if (likely (p.cell1 != NULL && p.cell2 != NULL)) { - p.cell1->uncovered_area += 2*fx1; - ++p.cell1->covered_height; - p.cell2->uncovered_area -= 2*fx2; - --p.cell2->covered_height; - return GLITTER_STATUS_SUCCESS; - } - } else { - struct cell *cell = cell_list_find(cells, ix1); - if (likely (cell != NULL)) { - cell->uncovered_area += 2*(fx1-fx2); - return GLITTER_STATUS_SUCCESS; - } - } - return GLITTER_STATUS_NO_MEMORY; -} - -/* Adds the analytical coverage of an edge crossing the current pixel - * row to the coverage cells and advances the edge's x position to the - * following row. - * - * This function is only called when we know that during this pixel row: - * - * 1) The relative order of all edges on the active list doesn't - * change. In particular, no edges intersect within this row to pixel - * precision. - * - * 2) No new edges start in this row. - * - * 3) No existing edges end mid-row. - * - * This function depends on being called with all edges from the - * active list in the order they appear on the list (i.e. with - * non-decreasing x-coordinate.) */ -static glitter_status_t -cell_list_render_edge( - struct cell_list *cells, - struct edge *edge, - int sign) -{ - grid_scaled_y_t y1, y2, dy; - grid_scaled_x_t dx; - int ix1, ix2; - grid_scaled_x_t fx1, fx2; - - struct quorem x1 = edge->x; - struct quorem x2 = x1; - - if (! edge->vertical) { - x2.quo += edge->dxdy_full.quo; - x2.rem += edge->dxdy_full.rem; - if (x2.rem >= 0) { - ++x2.quo; - x2.rem -= edge->dy; - } - - edge->x = x2; - } - - GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); - GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); - - /* Edge is entirely within a column? */ - if (ix1 == ix2) { - /* We always know that ix1 is >= the cell list cursor in this - * case due to the no-intersections precondition. */ - struct cell *cell = cell_list_find(cells, ix1); - if (unlikely (NULL == cell)) - return GLITTER_STATUS_NO_MEMORY; - - cell->covered_height += sign*GRID_Y; - cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; - return GLITTER_STATUS_SUCCESS; - } - - /* Orient the edge left-to-right. */ - dx = x2.quo - x1.quo; - if (dx >= 0) { - y1 = 0; - y2 = GRID_Y; - } else { - int tmp; - tmp = ix1; ix1 = ix2; ix2 = tmp; - tmp = fx1; fx1 = fx2; fx2 = tmp; - dx = -dx; - sign = -sign; - y1 = GRID_Y; - y2 = 0; - } - dy = y2 - y1; - - /* Add coverage for all pixels [ix1,ix2] on this row crossed - * by the edge. */ - { - struct cell_pair pair; - struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); - - /* When rendering a previous edge on the active list we may - * advance the cell list cursor past the leftmost pixel of the - * current edge even though the two edges don't intersect. - * e.g. consider two edges going down and rightwards: - * - * --\_+---\_+-----+-----+---- - * \_ \_ | | - * | \_ | \_ | | - * | \_| \_| | - * | \_ \_ | - * ----+-----+-\---+-\---+---- - * - * The left edge touches cells past the starting cell of the - * right edge. Fortunately such cases are rare. - * - * The rewinding is never necessary if the current edge stays - * within a single column because we've checked before calling - * this function that the active list order won't change. */ - cell_list_maybe_rewind(cells, ix1); - - pair = cell_list_find_pair(cells, ix1, ix1+1); - if (unlikely (!pair.cell1 || !pair.cell2)) - return GLITTER_STATUS_NO_MEMORY; - - pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); - pair.cell1->covered_height += sign*y.quo; - y.quo += y1; - - if (ix1+1 < ix2) { - struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); - struct cell *cell = pair.cell2; - - ++ix1; - do { - grid_scaled_y_t y_skip = dydx_full.quo; - y.rem += dydx_full.rem; - if (y.rem >= dx) { - ++y_skip; - y.rem -= dx; - } - - y.quo += y_skip; - - y_skip *= sign; - cell->uncovered_area += y_skip*GRID_X; - cell->covered_height += y_skip; - - ++ix1; - cell = cell_list_find(cells, ix1); - if (unlikely (NULL == cell)) - return GLITTER_STATUS_NO_MEMORY; - } while (ix1 != ix2); - - pair.cell2 = cell; - } - pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; - pair.cell2->covered_height += sign*(y2 - y.quo); - } - - return GLITTER_STATUS_SUCCESS; -} - -static void -polygon_init (struct polygon *polygon) -{ - polygon->ymin = polygon->ymax = 0; - polygon->xmin = polygon->xmax = 0; - polygon->y_buckets = polygon->y_buckets_embedded; - pool_init (polygon->edge_pool.base, - 8192 - sizeof (struct _pool_chunk), - sizeof (polygon->edge_pool.embedded)); -} - -static void -polygon_fini (struct polygon *polygon) -{ - if (polygon->y_buckets != polygon->y_buckets_embedded) - free (polygon->y_buckets); - - pool_fini (polygon->edge_pool.base); -} - -/* Empties the polygon of all edges. The polygon is then prepared to - * receive new edges and clip them to the vertical range - * [ymin,ymax). */ -static glitter_status_t -polygon_reset (struct polygon *polygon, - grid_scaled_x_t xmin, - grid_scaled_x_t xmax, - grid_scaled_y_t ymin, - grid_scaled_y_t ymax) -{ - unsigned h = ymax - ymin; - unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, - ymin); - - pool_reset(polygon->edge_pool.base); - - if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) - goto bail_no_mem; /* even if you could, you wouldn't want to. */ - - if (polygon->y_buckets != polygon->y_buckets_embedded) - free (polygon->y_buckets); - - polygon->y_buckets = polygon->y_buckets_embedded; - if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { - polygon->y_buckets = _cairo_malloc_ab (num_buckets, - sizeof (struct bucket)); - if (unlikely (NULL == polygon->y_buckets)) - goto bail_no_mem; - } - memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket)); - - polygon->ymin = ymin; - polygon->ymax = ymax; - polygon->xmin = xmin; - polygon->xmax = xmax; - return GLITTER_STATUS_SUCCESS; - - bail_no_mem: - polygon->ymin = 0; - polygon->ymax = 0; - return GLITTER_STATUS_NO_MEMORY; -} - -static void -_polygon_insert_edge_into_its_y_bucket( - struct polygon *polygon, - struct edge *e) -{ - unsigned j = e->ytop - polygon->ymin; - unsigned ix = j / EDGE_Y_BUCKET_HEIGHT; - unsigned offset = j % EDGE_Y_BUCKET_HEIGHT; - struct edge **ptail = &polygon->y_buckets[ix].edges; - e->next = *ptail; - *ptail = e; - polygon->y_buckets[ix].have_inside_edges |= offset; -} - -inline static glitter_status_t -polygon_add_edge (struct polygon *polygon, - const cairo_edge_t *edge) -{ - struct edge *e; - grid_scaled_x_t dx; - grid_scaled_y_t dy; - grid_scaled_y_t ytop, ybot; - grid_scaled_y_t ymin = polygon->ymin; - grid_scaled_y_t ymax = polygon->ymax; - - assert (edge->bottom > edge->top); - - if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) - return GLITTER_STATUS_SUCCESS; - - e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); - if (unlikely (NULL == e)) - return GLITTER_STATUS_NO_MEMORY; - - dx = edge->line.p2.x - edge->line.p1.x; - dy = edge->line.p2.y - edge->line.p1.y; - e->dy = dy; - e->dir = edge->dir; - - ytop = edge->top >= ymin ? edge->top : ymin; - ybot = edge->bottom <= ymax ? edge->bottom : ymax; - e->ytop = ytop; - e->height_left = ybot - ytop; - - if (dx == 0) { - e->vertical = TRUE; - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - e->dxdy.quo = 0; - e->dxdy.rem = 0; - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - - /* Drop edges to the right of the clip extents. */ - if (e->x.quo >= polygon->xmax) - return GLITTER_STATUS_SUCCESS; - - /* Offset vertical edges at the left side of the clip extents - * to just shy of the left side. We depend on this when - * checking for possible intersections within the clip - * rectangle. */ - if (e->x.quo <= polygon->xmin) { - e->x.quo = polygon->xmin - 1; - } - } else { - e->vertical = FALSE; - e->dxdy = floored_divrem (dx, dy); - if (ytop == edge->line.p1.y) { - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - } else { - e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); - e->x.quo += edge->line.p1.x; - } - - if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0) - return GLITTER_STATUS_SUCCESS; - - if (e->height_left >= GRID_Y) { - e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); - } else { - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - } - } - - _polygon_insert_edge_into_its_y_bucket (polygon, e); - - e->x.rem -= dy; /* Bias the remainder for faster - * edge advancement. */ - return GLITTER_STATUS_SUCCESS; -} - -static void -active_list_reset (struct active_list *active) -{ - active->head = NULL; - active->min_height = 0; -} - -static void -active_list_init(struct active_list *active) -{ - active_list_reset(active); -} - -/* - * Merge two sorted edge lists. - * Input: - * - head_a: The head of the first list. - * - head_b: The head of the second list; head_b cannot be NULL. - * Output: - * Returns the head of the merged list. - * - * Implementation notes: - * To make it fast (in particular, to reduce to an insertion sort whenever - * one of the two input lists only has a single element) we iterate through - * a list until its head becomes greater than the head of the other list, - * then we switch their roles. As soon as one of the two lists is empty, we - * just attach the other one to the current list and exit. - * Writes to memory are only needed to "switch" lists (as it also requires - * attaching to the output list the list which we will be iterating next) and - * to attach the last non-empty list. - */ -static struct edge * -merge_sorted_edges (struct edge *head_a, struct edge *head_b) -{ - struct edge *head, **next; - - head = head_a; - next = &head; - - while (1) { - while (head_a != NULL && head_a->x.quo <= head_b->x.quo) { - next = &head_a->next; - head_a = head_a->next; - } - - *next = head_b; - if (head_a == NULL) - return head; - - while (head_b != NULL && head_b->x.quo <= head_a->x.quo) { - next = &head_b->next; - head_b = head_b->next; - } - - *next = head_a; - if (head_b == NULL) - return head; - } -} - -/* - * Sort (part of) a list. - * Input: - * - list: The list to be sorted; list cannot be NULL. - * - limit: Recursion limit. - * Output: - * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the - * input list; if the input list has fewer elements, head_out be a sorted list - * containing all the elements of the input list. - * Returns the head of the list of unprocessed elements (NULL if the sorted list contains - * all the elements of the input list). - * - * Implementation notes: - * Special case single element list, unroll/inline the sorting of the first two elements. - * Some tail recursion is used since we iterate on the bottom-up solution of the problem - * (we start with a small sorted list and keep merging other lists of the same size to it). - */ -static struct edge * -sort_edges (struct edge *list, - unsigned int level, - struct edge **head_out) -{ - struct edge *head_other, *remaining; - unsigned int i; - - head_other = list->next; - - /* Single element list -> return */ - if (head_other == NULL) { - *head_out = list; - return NULL; - } - - /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges): - * - Initialize remaining to be the list containing the elements after the second in the input list. - * - Initialize *head_out to be the sorted list containing the first two element. - */ - remaining = head_other->next; - if (list->x.quo <= head_other->x.quo) { - *head_out = list; - /* list->next = head_other; */ /* The input list is already like this. */ - head_other->next = NULL; - } else { - *head_out = head_other; - head_other->next = list; - list->next = NULL; - } - - for (i = 0; i < level && remaining; i++) { - /* Extract a sorted list of the same size as *head_out - * (2^(i+1) elements) from the list of remaining elements. */ - remaining = sort_edges (remaining, i, &head_other); - *head_out = merge_sorted_edges (*head_out, head_other); - } - - /* *head_out now contains (at most) 2^(level+1) elements. */ - - return remaining; -} - -/* Test if the edges on the active list can be safely advanced by a - * full row without intersections or any edges ending. */ -inline static int -active_list_can_step_full_row (struct active_list *active, - grid_scaled_x_t xmin) -{ - const struct edge *e; - grid_scaled_x_t prev_x = INT_MIN; - - /* Recomputes the minimum height of all edges on the active - * list if we have been dropping edges. */ - if (active->min_height <= 0) { - int min_height = INT_MAX; - - e = active->head; - while (NULL != e) { - if (e->height_left < min_height) - min_height = e->height_left; - e = e->next; - } - - active->min_height = min_height; - } - - if (active->min_height < GRID_Y) - return 0; - - /* Check for intersections as no edges end during the next row. */ - e = active->head; - while (NULL != e) { - struct quorem x = e->x; - - if (! e->vertical) { - x.quo += e->dxdy_full.quo; - x.rem += e->dxdy_full.rem; - if (x.rem >= 0) - ++x.quo; - } - - /* There's may be an intersection if the edge sort order might - * change. */ - if (x.quo <= prev_x) { - /* Ignore intersections to the left of the clip extents. - * This assumes that all vertical edges on or at the left - * side of the clip rectangle have been shifted slightly - * to the left in polygon_add_edge(). */ - if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin) - return 0; - } - else { - prev_x = x.quo; - } - e = e->next; - } - - return 1; -} - -/* Merges edges on the given subpixel row from the polygon to the - * active_list. */ -inline static void -active_list_merge_edges_from_polygon( - struct active_list *active, - grid_scaled_y_t y, - struct polygon *polygon) -{ - /* Split off the edges on the current subrow and merge them into - * the active list. */ - unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); - int min_height = active->min_height; - struct edge *subrow_edges = NULL; - struct edge **ptail = &polygon->y_buckets[ix].edges; - - while (1) { - struct edge *tail = *ptail; - if (NULL == tail) break; - - if (y == tail->ytop) { - *ptail = tail->next; - tail->next = subrow_edges; - subrow_edges = tail; - if (tail->height_left < min_height) - min_height = tail->height_left; - } else { - ptail = &tail->next; - } - } - if (subrow_edges) { - sort_edges (subrow_edges, UINT_MAX, &subrow_edges); - active->head = merge_sorted_edges (active->head, subrow_edges); - active->min_height = min_height; - } -} - -/* Advance the edges on the active list by one subsample row by - * updating their x positions. Drop edges from the list that end. */ -inline static void -active_list_substep_edges( - struct active_list *active) -{ - struct edge **cursor = &active->head; - grid_scaled_x_t prev_x = INT_MIN; - struct edge *unsorted = NULL; - - while (1) { - struct edge *edge; - - UNROLL3({ - edge = *cursor; - if (NULL == edge) - break; - - if (0 != --edge->height_left) { - edge->x.quo += edge->dxdy.quo; - edge->x.rem += edge->dxdy.rem; - if (edge->x.rem >= 0) { - ++edge->x.quo; - edge->x.rem -= edge->dy; - } - - if (edge->x.quo < prev_x) { - *cursor = edge->next; - edge->next = unsorted; - unsorted = edge; - } else { - prev_x = edge->x.quo; - cursor = &edge->next; - } - - } else { - *cursor = edge->next; - } - }); - } - - if (unsorted) { - sort_edges (unsorted, UINT_MAX, &unsorted); - active->head = merge_sorted_edges (active->head, unsorted); - } -} - -inline static glitter_status_t -apply_nonzero_fill_rule_for_subrow (struct active_list *active, - struct cell_list *coverages) -{ - struct edge *edge = active->head; - int winding = 0; - int xstart; - int xend; - int status; - - cell_list_rewind (coverages); - - while (NULL != edge) { - xstart = edge->x.quo; - winding = edge->dir; - while (1) { - edge = edge->next; - if (NULL == edge) - return cell_list_add_unbounded_subspan (coverages, xstart); - - winding += edge->dir; - if (0 == winding) { - if (edge->next == NULL || edge->next->x.quo != edge->x.quo) - break; - } - } - - xend = edge->x.quo; - status = cell_list_add_subspan (coverages, xstart, xend); - if (unlikely (status)) - return status; - - edge = edge->next; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_evenodd_fill_rule_for_subrow (struct active_list *active, - struct cell_list *coverages) -{ - struct edge *edge = active->head; - int xstart; - int xend; - int status; - - cell_list_rewind (coverages); - - while (NULL != edge) { - xstart = edge->x.quo; - - while (1) { - edge = edge->next; - if (NULL == edge) - return cell_list_add_unbounded_subspan (coverages, xstart); - - if (edge->next == NULL || edge->next->x.quo != edge->x.quo) - break; - - edge = edge->next; - } - - xend = edge->x.quo; - status = cell_list_add_subspan (coverages, xstart, xend); - if (unlikely (status)) - return status; - - edge = edge->next; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_nonzero_fill_rule_and_step_edges (struct active_list *active, - struct cell_list *coverages) -{ - struct edge **cursor = &active->head; - struct edge *left_edge; - int status; - - left_edge = *cursor; - while (NULL != left_edge) { - struct edge *right_edge; - int winding = left_edge->dir; - - left_edge->height_left -= GRID_Y; - if (left_edge->height_left) - cursor = &left_edge->next; - else - *cursor = left_edge->next; - - while (1) { - right_edge = *cursor; - if (NULL == right_edge) - return cell_list_render_edge (coverages, left_edge, +1); - - right_edge->height_left -= GRID_Y; - if (right_edge->height_left) - cursor = &right_edge->next; - else - *cursor = right_edge->next; - - winding += right_edge->dir; - if (0 == winding) { - if (right_edge->next == NULL || - right_edge->next->x.quo != right_edge->x.quo) - { - break; - } - } - - if (! right_edge->vertical) { - right_edge->x.quo += right_edge->dxdy_full.quo; - right_edge->x.rem += right_edge->dxdy_full.rem; - if (right_edge->x.rem >= 0) { - ++right_edge->x.quo; - right_edge->x.rem -= right_edge->dy; - } - } - } - - status = cell_list_render_edge (coverages, left_edge, +1); - if (unlikely (status)) - return status; - - status = cell_list_render_edge (coverages, right_edge, -1); - if (unlikely (status)) - return status; - - left_edge = *cursor; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_evenodd_fill_rule_and_step_edges (struct active_list *active, - struct cell_list *coverages) -{ - struct edge **cursor = &active->head; - struct edge *left_edge; - int status; - - left_edge = *cursor; - while (NULL != left_edge) { - struct edge *right_edge; - int winding = left_edge->dir; - - left_edge->height_left -= GRID_Y; - if (left_edge->height_left) - cursor = &left_edge->next; - else - *cursor = left_edge->next; - - while (1) { - right_edge = *cursor; - if (NULL == right_edge) - return cell_list_render_edge (coverages, left_edge, +1); - - right_edge->height_left -= GRID_Y; - if (right_edge->height_left) - cursor = &right_edge->next; - else - *cursor = right_edge->next; - - winding += right_edge->dir; - if ((winding & 1) == 0) { - if (right_edge->next == NULL || - right_edge->next->x.quo != right_edge->x.quo) - { - break; - } - } - - if (! right_edge->vertical) { - right_edge->x.quo += right_edge->dxdy_full.quo; - right_edge->x.rem += right_edge->dxdy_full.rem; - if (right_edge->x.rem >= 0) { - ++right_edge->x.quo; - right_edge->x.rem -= right_edge->dy; - } - } - } - - status = cell_list_render_edge (coverages, left_edge, +1); - if (unlikely (status)) - return status; - - status = cell_list_render_edge (coverages, right_edge, -1); - if (unlikely (status)) - return status; - - left_edge = *cursor; - } - - return GLITTER_STATUS_SUCCESS; -} - -/* If the user hasn't configured a coverage blitter, use a default one - * that blits spans directly to an A8 raster. */ -#ifndef GLITTER_BLIT_COVERAGES - -inline static void -blit_span( - unsigned char *row_pixels, - int x, unsigned len, - grid_area_t coverage) -{ - int alpha = GRID_AREA_TO_ALPHA(coverage); - if (1 == len) { - row_pixels[x] = alpha; - } - else { - memset(row_pixels + x, alpha, len); - } -} - -#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \ - do { \ - int __y = y; \ - int __h = height; \ - do { \ - blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \ - } while (--__h); \ - } while (0) - -static void -blit_cells( - struct cell_list *cells, - unsigned char *row_pixels, - int xmin, int xmax) -{ - struct cell *cell = cells->head; - int prev_x = xmin; - int coverage = 0; - if (NULL == cell) - return; - - while (NULL != cell && cell->x < xmin) { - coverage += cell->covered_height; - cell = cell->next; - } - coverage *= GRID_X*2; - - for (; NULL != cell; cell = cell->next) { - int x = cell->x; - int area; - if (x >= xmax) - break; - if (x > prev_x && 0 != coverage) { - blit_span(row_pixels, prev_x, x - prev_x, coverage); - } - - coverage += cell->covered_height * GRID_X*2; - area = coverage - cell->uncovered_area; - if (area) { - blit_span(row_pixels, x, 1, area); - } - prev_x = x+1; - } - - if (0 != coverage && prev_x < xmax) { - blit_span(row_pixels, prev_x, xmax - prev_x, coverage); - } -} -#endif /* GLITTER_BLIT_COVERAGES */ - -static void -_glitter_scan_converter_init(glitter_scan_converter_t *converter) -{ - polygon_init(converter->polygon); - active_list_init(converter->active); - cell_list_init(converter->coverages); - converter->xmin=0; - converter->ymin=0; - converter->xmax=0; - converter->ymax=0; -} - -static void -_glitter_scan_converter_fini(glitter_scan_converter_t *converter) -{ - polygon_fini(converter->polygon); - cell_list_fini(converter->coverages); - converter->xmin=0; - converter->ymin=0; - converter->xmax=0; - converter->ymax=0; -} - -static grid_scaled_t -int_to_grid_scaled(int i, int scale) -{ - /* Clamp to max/min representable scaled number. */ - if (i >= 0) { - if (i >= INT_MAX/scale) - i = INT_MAX/scale; - } - else { - if (i <= INT_MIN/scale) - i = INT_MIN/scale; - } - return i*scale; -} - -#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) -#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) - -I glitter_status_t -glitter_scan_converter_reset( - glitter_scan_converter_t *converter, - int xmin, int ymin, - int xmax, int ymax) -{ - glitter_status_t status; - - converter->xmin = 0; converter->xmax = 0; - converter->ymin = 0; converter->ymax = 0; - - xmin = int_to_grid_scaled_x(xmin); - ymin = int_to_grid_scaled_y(ymin); - xmax = int_to_grid_scaled_x(xmax); - ymax = int_to_grid_scaled_y(ymax); - - active_list_reset(converter->active); - cell_list_reset(converter->coverages); - status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax); - if (status) - return status; - - converter->xmin = xmin; - converter->xmax = xmax; - converter->ymin = ymin; - converter->ymax = ymax; - return GLITTER_STATUS_SUCCESS; -} - -/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) - * These macros convert an input coordinate in the client's - * device space to the rasterisation grid. - */ -/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use - * shifts if possible, and something saneish if not. - */ -#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS -# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) -#else -# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) -#endif - -#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS -# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) -#else -# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) -#endif - -#define INPUT_TO_GRID_general(in, out, grid_scale) do { \ - long long tmp__ = (long long)(grid_scale) * (in); \ - tmp__ >>= GLITTER_INPUT_BITS; \ - (out) = tmp__; \ -} while (0) - -I glitter_status_t -glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, - const cairo_edge_t *edge) -{ - cairo_edge_t e; - - INPUT_TO_GRID_Y (edge->top, e.top); - INPUT_TO_GRID_Y (edge->bottom, e.bottom); - if (e.top >= e.bottom) - return GLITTER_STATUS_SUCCESS; - - /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ - INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); - INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); - if (e.line.p1.y == e.line.p2.y) - return GLITTER_STATUS_SUCCESS; - - INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); - INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); - - e.dir = edge->dir; - - return polygon_add_edge (converter->polygon, &e); -} - -#ifndef GLITTER_BLIT_COVERAGES_BEGIN -# define GLITTER_BLIT_COVERAGES_BEGIN -#endif - -#ifndef GLITTER_BLIT_COVERAGES_END -# define GLITTER_BLIT_COVERAGES_END -#endif - -#ifndef GLITTER_BLIT_COVERAGES_EMPTY -# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax) -#endif - -static cairo_bool_t -active_list_is_vertical (struct active_list *active) -{ - struct edge *e; - - for (e = active->head; e != NULL; e = e->next) { - if (! e->vertical) - return FALSE; - } - - return TRUE; -} - -static void -step_edges (struct active_list *active, int count) -{ - struct edge **cursor = &active->head; - struct edge *edge; - - for (edge = *cursor; edge != NULL; edge = *cursor) { - edge->height_left -= GRID_Y * count; - if (edge->height_left) - cursor = &edge->next; - else - *cursor = edge->next; - } -} - -I glitter_status_t -glitter_scan_converter_render( - glitter_scan_converter_t *converter, - int nonzero_fill, - GLITTER_BLIT_COVERAGES_ARGS) -{ - int i, j; - int ymax_i = converter->ymax / GRID_Y; - int ymin_i = converter->ymin / GRID_Y; - int xmin_i, xmax_i; - grid_scaled_x_t xmin = converter->xmin; - int h = ymax_i - ymin_i; - struct polygon *polygon = converter->polygon; - struct cell_list *coverages = converter->coverages; - struct active_list *active = converter->active; - - xmin_i = converter->xmin / GRID_X; - xmax_i = converter->xmax / GRID_X; - if (xmin_i >= xmax_i) - return GLITTER_STATUS_SUCCESS; - - /* Let the coverage blitter initialise itself. */ - GLITTER_BLIT_COVERAGES_BEGIN; - - /* Render each pixel row. */ - for (i = 0; i < h; i = j) { - int do_full_step = 0; - glitter_status_t status = 0; - - j = i + 1; - - /* Determine if we can ignore this row or use the full pixel - * stepper. */ - if (polygon->y_buckets[i].edges == NULL) { - if (! active->head) { - for (; j < h && ! polygon->y_buckets[j].edges; j++) - ; - GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); - continue; - } - do_full_step = active_list_can_step_full_row (active, xmin); - } - else if (! polygon->y_buckets[i].have_inside_edges) { - grid_scaled_y_t y = (i+ymin_i)*GRID_Y; - active_list_merge_edges_from_polygon (active, y, polygon); - do_full_step = active_list_can_step_full_row (active, xmin); - } - - if (do_full_step) { - /* Step by a full pixel row's worth. */ - if (nonzero_fill) { - status = apply_nonzero_fill_rule_and_step_edges (active, - coverages); - } else { - status = apply_evenodd_fill_rule_and_step_edges (active, - coverages); - } - - if (active_list_is_vertical (active)) { - while (j < h && - polygon->y_buckets[j].edges == NULL && - active->min_height >= 2*GRID_Y) - { - active->min_height -= GRID_Y; - j++; - } - if (j != i + 1) - step_edges (active, j - (i + 1)); - } - } else { - /* Supersample this row. */ - grid_scaled_y_t suby; - for (suby = 0; suby < GRID_Y; suby++) { - grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; - - active_list_merge_edges_from_polygon (active, y, polygon); - - if (nonzero_fill) { - status |= apply_nonzero_fill_rule_for_subrow (active, - coverages); - } else { - status |= apply_evenodd_fill_rule_for_subrow (active, - coverages); - } - - active_list_substep_edges(active); - } - } - - if (unlikely (status)) - return status; - - GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i); - cell_list_reset (coverages); - - if (! active->head) - active->min_height = INT_MAX; - else - active->min_height -= GRID_Y; - } - - /* Clean up the coverage blitter. */ - GLITTER_BLIT_COVERAGES_END; - - return GLITTER_STATUS_SUCCESS; -} - -/*------------------------------------------------------------------------- - * cairo specific implementation: the coverage blitter and - * scan converter subclass. */ - -static glitter_status_t -blit_with_span_renderer (struct cell_list *cells, - cairo_span_renderer_t *renderer, - struct pool *span_pool, - int y, int height, - int xmin, int xmax) -{ - struct cell *cell = cells->head; - int prev_x = xmin; - int cover = 0; - cairo_half_open_span_t *spans; - unsigned num_spans; - - if (cell == NULL) - return blit_empty_with_span_renderer (renderer, y, height); - - /* Skip cells to the left of the clip region. */ - while (cell != NULL && cell->x < xmin) { - cover += cell->covered_height; - cell = cell->next; - } - cover *= GRID_X*2; - - /* Count number of cells remaining. */ - { - struct cell *next = cell; - num_spans = 1; - while (next != NULL) { - next = next->next; - ++num_spans; - } - num_spans = 2*num_spans; - } - - /* Allocate enough spans for the row. */ - pool_reset (span_pool); - spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); - if (unlikely (spans == NULL)) - return GLITTER_STATUS_NO_MEMORY; - - num_spans = 0; - - /* Form the spans from the coverages and areas. */ - for (; cell != NULL; cell = cell->next) { - int x = cell->x; - int area; - - if (x >= xmax) - break; - - if (x > prev_x) { - spans[num_spans].x = prev_x; - spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); - ++num_spans; - } - - cover += cell->covered_height*GRID_X*2; - area = cover - cell->uncovered_area; - - spans[num_spans].x = x; - spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); - ++num_spans; - - prev_x = x+1; - } - - if (prev_x <= xmax) { - spans[num_spans].x = prev_x; - spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); - ++num_spans; - } - - if (prev_x < xmax && cover) { - spans[num_spans].x = xmax; - spans[num_spans].coverage = 0; - ++num_spans; - } - - /* Dump them into the renderer. */ - return renderer->render_rows (renderer, y, height, spans, num_spans); -} - -static glitter_status_t -blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height) -{ - return renderer->render_rows (renderer, y, height, NULL, 0); -} - -struct _cairo_tor_scan_converter { - cairo_scan_converter_t base; - - glitter_scan_converter_t converter[1]; - cairo_fill_rule_t fill_rule; - - struct { - struct pool base[1]; - cairo_half_open_span_t embedded[32]; - } span_pool; -}; - -typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; - -static void -_cairo_tor_scan_converter_destroy (void *converter) -{ - cairo_tor_scan_converter_t *self = converter; - if (self == NULL) { - return; - } - _glitter_scan_converter_fini (self->converter); - pool_fini (self->span_pool.base); - free(self); -} - -static cairo_status_t -_cairo_tor_scan_converter_add_edge (void *converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - cairo_tor_scan_converter_t *self = converter; - cairo_status_t status; - cairo_edge_t edge; - - edge.line.p1 = *p1; - edge.line.p2 = *p2; - edge.top = top; - edge.bottom = bottom; - edge.dir = dir; - - status = glitter_scan_converter_add_edge (self->converter, &edge); - if (unlikely (status)) - return _cairo_scan_converter_set_error (self, _cairo_error (status)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_tor_scan_converter_add_polygon (void *converter, - const cairo_polygon_t *polygon) -{ - cairo_tor_scan_converter_t *self = converter; - cairo_status_t status; - int i; - - for (i = 0; i < polygon->num_edges; i++) { - status = glitter_scan_converter_add_edge (self->converter, - &polygon->edges[i]); - if (unlikely (status)) { - return _cairo_scan_converter_set_error (self, - _cairo_error (status)); - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_tor_scan_converter_generate (void *converter, - cairo_span_renderer_t *renderer) -{ - cairo_tor_scan_converter_t *self = converter; - cairo_status_t status; - - status = glitter_scan_converter_render (self->converter, - self->fill_rule == CAIRO_FILL_RULE_WINDING, - renderer, - self->span_pool.base); - if (unlikely (status)) - return _cairo_scan_converter_set_error (self, _cairo_error (status)); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_scan_converter_t * -_cairo_tor_scan_converter_create (int xmin, - int ymin, - int xmax, - int ymax, - cairo_fill_rule_t fill_rule) -{ - cairo_tor_scan_converter_t *self; - cairo_status_t status; - - self = calloc (1, sizeof(struct _cairo_tor_scan_converter)); - if (unlikely (self == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto bail_nomem; - } - - self->base.destroy = _cairo_tor_scan_converter_destroy; - self->base.add_edge = _cairo_tor_scan_converter_add_edge; - self->base.add_polygon = _cairo_tor_scan_converter_add_polygon; - self->base.generate = _cairo_tor_scan_converter_generate; - - pool_init (self->span_pool.base, - 250 * sizeof(self->span_pool.embedded[0]), - sizeof(self->span_pool.embedded)); - - _glitter_scan_converter_init (self->converter); - status = glitter_scan_converter_reset (self->converter, - xmin, ymin, xmax, ymax); - if (unlikely (status)) - goto bail; - - self->fill_rule = fill_rule; - - return &self->base; - - bail: - self->base.destroy(&self->base); - bail_nomem: - return _cairo_scan_converter_create_in_error (status); -} diff --git a/libs/cairo/cairo/src/cairo-toy-font-face.c b/libs/cairo/cairo/src/cairo-toy-font-face.c deleted file mode 100644 index 92045ba3e..000000000 --- a/libs/cairo/cairo/src/cairo-toy-font-face.c +++ /dev/null @@ -1,489 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - - -static const cairo_font_face_t _cairo_font_face_null_pointer = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_NULL_POINTER, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL -}; - -static const cairo_font_face_t _cairo_font_face_invalid_string = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_STRING, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL -}; - -static const cairo_font_face_t _cairo_font_face_invalid_slant = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_SLANT, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL -}; - -static const cairo_font_face_t _cairo_font_face_invalid_weight = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_WEIGHT, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - NULL -}; - - -static const cairo_font_face_backend_t _cairo_toy_font_face_backend; - -static int -_cairo_toy_font_face_keys_equal (const void *key_a, - const void *key_b); - -/* We maintain a hash table from family/weight/slant => - * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of - * this mapping is to provide unique #cairo_font_face_t values so that - * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t - * works. Once the corresponding #cairo_font_face_t objects fall out of - * downstream caches, we don't need them in this hash table anymore. - * - * Modifications to this hash table are protected by - * _cairo_toy_font_face_mutex. - */ -static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; - -static cairo_hash_table_t * -_cairo_toy_font_face_hash_table_lock (void) -{ - CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); - - if (cairo_toy_font_face_hash_table == NULL) - { - cairo_toy_font_face_hash_table = - _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); - - if (cairo_toy_font_face_hash_table == NULL) { - CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); - return NULL; - } - } - - return cairo_toy_font_face_hash_table; -} - -static void -_cairo_toy_font_face_hash_table_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); -} - -/** - * _cairo_toy_font_face_init_key: - * - * Initialize those portions of #cairo_toy_font_face_t needed to use - * it as a hash table key, including the hash code buried away in - * font_face->base.hash_entry. No memory allocation is performed here - * so that no fini call is needed. We do this to make it easier to use - * an automatic #cairo_toy_font_face_t variable as a key. - **/ -static void -_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - unsigned long hash; - - key->family = family; - key->owns_family = FALSE; - - key->slant = slant; - key->weight = weight; - - /* 1607 and 1451 are just a couple of arbitrary primes. */ - hash = _cairo_hash_string (family); - hash += ((unsigned long) slant) * 1607; - hash += ((unsigned long) weight) * 1451; - - assert (hash != 0); - key->base.hash_entry.hash = hash; -} - -static cairo_status_t -_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face, - cairo_font_face_t **impl_font_face) -{ - const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT; - cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (unlikely (font_face->base.status)) - return font_face->base.status; - - if (backend->create_for_toy != NULL && - 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT, - strlen (CAIRO_USER_FONT_FAMILY_DEFAULT))) - { - status = backend->create_for_toy (font_face, impl_font_face); - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - backend = &_cairo_user_font_face_backend; - status = backend->create_for_toy (font_face, impl_font_face); - } - - return status; -} - -static cairo_status_t -_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - char *family_copy; - cairo_status_t status; - - family_copy = strdup (family); - if (unlikely (family_copy == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight); - font_face->owns_family = TRUE; - - _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); - - status = _cairo_toy_font_face_create_impl_face (font_face, - &font_face->impl_face); - if (unlikely (status)) { - free (family_copy); - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) -{ - /* We assert here that we own font_face->family before casting - * away the const qualifer. */ - assert (font_face->owns_family); - free ((char*) font_face->family); - - if (font_face->impl_face) - cairo_font_face_destroy (font_face->impl_face); -} - -static int -_cairo_toy_font_face_keys_equal (const void *key_a, - const void *key_b) -{ - const cairo_toy_font_face_t *face_a = key_a; - const cairo_toy_font_face_t *face_b = key_b; - - return (strcmp (face_a->family, face_b->family) == 0 && - face_a->slant == face_b->slant && - face_a->weight == face_b->weight); -} - -/** - * cairo_toy_font_face_create: - * @family: a font family name, encoded in UTF-8 - * @slant: the slant for the font - * @weight: the weight for the font - * - * Creates a font face from a triplet of family, slant, and weight. - * These font faces are used in implementation of the the #cairo_t "toy" - * font API. - * - * If @family is the zero-length string "", the platform-specific default - * family is assumed. The default family then can be queried using - * cairo_toy_font_face_get_family(). - * - * The cairo_select_font_face() function uses this to create font faces. - * See that function for limitations and other details of toy font faces. - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.8 - **/ -cairo_font_face_t * -cairo_toy_font_face_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_status_t status; - cairo_toy_font_face_t key, *font_face; - cairo_hash_table_t *hash_table; - - if (family == NULL) - return (cairo_font_face_t*) &_cairo_font_face_null_pointer; - - /* Make sure we've got valid UTF-8 for the family */ - status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); - if (unlikely (status)) { - if (status == CAIRO_STATUS_INVALID_STRING) - return (cairo_font_face_t*) &_cairo_font_face_invalid_string; - - return (cairo_font_face_t*) &_cairo_font_face_nil; - } - - switch (slant) { - case CAIRO_FONT_SLANT_NORMAL: - case CAIRO_FONT_SLANT_ITALIC: - case CAIRO_FONT_SLANT_OBLIQUE: - break; - default: - return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; - } - - switch (weight) { - case CAIRO_FONT_WEIGHT_NORMAL: - case CAIRO_FONT_WEIGHT_BOLD: - break; - default: - return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; - } - - if (*family == '\0') - family = CAIRO_FONT_FAMILY_DEFAULT; - - hash_table = _cairo_toy_font_face_hash_table_lock (); - if (unlikely (hash_table == NULL)) - goto UNWIND; - - _cairo_toy_font_face_init_key (&key, family, slant, weight); - - /* Return existing font_face if it exists in the hash table. */ - font_face = _cairo_hash_table_lookup (hash_table, - &key.base.hash_entry); - if (font_face != NULL) { - if (font_face->base.status == CAIRO_STATUS_SUCCESS) { - /* We increment the reference count here manually to avoid - double-locking. */ - _cairo_reference_count_inc (&font_face->base.ref_count); - _cairo_toy_font_face_hash_table_unlock (); - return &font_face->base; - } - - /* remove the bad font from the hash table */ - _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - font_face->base.hash_entry.hash = 0; - } - - /* Otherwise create it and insert into hash table. */ - font_face = malloc (sizeof (cairo_toy_font_face_t)); - if (unlikely (font_face == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto UNWIND_HASH_TABLE_LOCK; - } - - status = _cairo_toy_font_face_init (font_face, family, slant, weight); - if (unlikely (status)) - goto UNWIND_FONT_FACE_MALLOC; - - assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); - status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); - if (unlikely (status)) - goto UNWIND_FONT_FACE_INIT; - - _cairo_toy_font_face_hash_table_unlock (); - - return &font_face->base; - - UNWIND_FONT_FACE_INIT: - _cairo_toy_font_face_fini (font_face); - UNWIND_FONT_FACE_MALLOC: - free (font_face); - UNWIND_HASH_TABLE_LOCK: - _cairo_toy_font_face_hash_table_unlock (); - UNWIND: - return (cairo_font_face_t*) &_cairo_font_face_nil; -} -slim_hidden_def (cairo_toy_font_face_create); - -static void -_cairo_toy_font_face_destroy (void *abstract_face) -{ - cairo_toy_font_face_t *font_face = abstract_face; - cairo_hash_table_t *hash_table; - - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count)) - return; - - hash_table = _cairo_toy_font_face_hash_table_lock (); - /* All created objects must have been mapped in the hash table. */ - assert (hash_table != NULL); - - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { - /* somebody recreated the font whilst we waited for the lock */ - _cairo_toy_font_face_hash_table_unlock (); - return; - } - - if (font_face->base.hash_entry.hash != 0) - _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - - _cairo_toy_font_face_hash_table_unlock (); - - _cairo_toy_font_face_fini (font_face); -} - -static cairo_status_t -_cairo_toy_font_face_scaled_font_create (void *abstract_font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font) -{ - cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face; - - ASSERT_NOT_REACHED; - - return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH); -} - -static cairo_font_face_t * -_cairo_toy_font_face_get_implementation (void *abstract_font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - cairo_toy_font_face_t *font_face = abstract_font_face; - - if (font_face->impl_face) { - cairo_font_face_t *impl = font_face->impl_face; - - if (impl->backend->get_implementation != NULL) { - return impl->backend->get_implementation (impl, - font_matrix, - ctm, - options); - } - - return cairo_font_face_reference (impl); - } - - return abstract_font_face; -} - -static cairo_bool_t -_cairo_font_face_is_toy (cairo_font_face_t *font_face) -{ - return font_face->backend == &_cairo_toy_font_face_backend; -} - -/** - * cairo_toy_font_face_get_family: - * @font_face: A toy font face - * - * Gets the familly name of a toy font. - * - * Return value: The family name. This string is owned by the font face - * and remains valid as long as the font face is alive (referenced). - * - * Since: 1.8 - **/ -const char * -cairo_toy_font_face_get_family (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face; - - if (font_face->status) - return CAIRO_FONT_FAMILY_DEFAULT; - - toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_FAMILY_DEFAULT; - } - assert (toy_font_face->owns_family); - return toy_font_face->family; -} - -/** - * cairo_toy_font_face_get_slant: - * @font_face: A toy font face - * - * Gets the slant a toy font. - * - * Return value: The slant value - * - * Since: 1.8 - **/ -cairo_font_slant_t -cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face; - - if (font_face->status) - return CAIRO_FONT_SLANT_DEFAULT; - - toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_SLANT_DEFAULT; - } - return toy_font_face->slant; -} -slim_hidden_def (cairo_toy_font_face_get_slant); - -/** - * cairo_toy_font_face_get_weight: - * @font_face: A toy font face - * - * Gets the weight a toy font. - * - * Return value: The weight value - * - * Since: 1.8 - **/ -cairo_font_weight_t -cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face; - - if (font_face->status) - return CAIRO_FONT_WEIGHT_DEFAULT; - - toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_WEIGHT_DEFAULT; - } - return toy_font_face->weight; -} -slim_hidden_def (cairo_toy_font_face_get_weight); - -static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { - CAIRO_FONT_TYPE_TOY, - NULL, /* create_for_toy */ - _cairo_toy_font_face_destroy, - _cairo_toy_font_face_scaled_font_create, - _cairo_toy_font_face_get_implementation -}; - -void -_cairo_toy_font_face_reset_static_data (void) -{ - cairo_hash_table_t *hash_table; - - /* We manually acquire the lock rather than calling - * cairo_toy_font_face_hash_table_lock simply to avoid - * creating the table only to destroy it again. */ - CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); - hash_table = cairo_toy_font_face_hash_table; - cairo_toy_font_face_hash_table = NULL; - CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); - - if (hash_table != NULL) - _cairo_hash_table_destroy (hash_table); -} diff --git a/libs/cairo/cairo/src/cairo-traps.c b/libs/cairo/cairo/src/cairo-traps.c deleted file mode 100644 index c3f1d2e35..000000000 --- a/libs/cairo/cairo/src/cairo-traps.c +++ /dev/null @@ -1,570 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-slope-private.h" - -/* private functions */ - -void -_cairo_traps_init (cairo_traps_t *traps) -{ - VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); - - traps->status = CAIRO_STATUS_SUCCESS; - - traps->maybe_region = 1; - traps->is_rectilinear = 0; - traps->is_rectangular = 0; - - traps->num_traps = 0; - - traps->traps_size = ARRAY_LENGTH (traps->traps_embedded); - traps->traps = traps->traps_embedded; - - traps->num_limits = 0; - traps->has_intersections = FALSE; -} - -void -_cairo_traps_limit (cairo_traps_t *traps, - const cairo_box_t *limits, - int num_limits) -{ - traps->limits = limits; - traps->num_limits = num_limits; -} - -void -_cairo_traps_clear (cairo_traps_t *traps) -{ - traps->status = CAIRO_STATUS_SUCCESS; - - traps->maybe_region = 1; - traps->is_rectilinear = 0; - traps->is_rectangular = 0; - - traps->num_traps = 0; - traps->has_intersections = FALSE; -} - -void -_cairo_traps_fini (cairo_traps_t *traps) -{ - if (traps->traps != traps->traps_embedded) - free (traps->traps); - - VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); -} - -/* make room for at least one more trap */ -static cairo_bool_t -_cairo_traps_grow (cairo_traps_t *traps) -{ - cairo_trapezoid_t *new_traps; - int new_size = 4 * traps->traps_size; - - if (CAIRO_INJECT_FAULT ()) { - traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return FALSE; - } - - if (traps->traps == traps->traps_embedded) { - new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); - if (new_traps != NULL) - memcpy (new_traps, traps->traps, sizeof (traps->traps_embedded)); - } else { - new_traps = _cairo_realloc_ab (traps->traps, - new_size, sizeof (cairo_trapezoid_t)); - } - - if (unlikely (new_traps == NULL)) { - traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return FALSE; - } - - traps->traps = new_traps; - traps->traps_size = new_size; - return TRUE; -} - -void -_cairo_traps_add_trap (cairo_traps_t *traps, - cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right) -{ - cairo_trapezoid_t *trap; - - if (unlikely (traps->num_traps == traps->traps_size)) { - if (unlikely (! _cairo_traps_grow (traps))) - return; - } - - trap = &traps->traps[traps->num_traps++]; - trap->top = top; - trap->bottom = bottom; - trap->left = *left; - trap->right = *right; -} - -/** - * _cairo_traps_init_box: - * @traps: a #cairo_traps_t - * @box: an array box that will each be converted to a single trapezoid - * to store in @traps. - * - * Initializes a #cairo_traps_t to contain an array of rectangular - * trapezoids. - **/ -cairo_status_t -_cairo_traps_init_boxes (cairo_traps_t *traps, - const cairo_boxes_t *boxes) -{ - cairo_trapezoid_t *trap; - const struct _cairo_boxes_chunk *chunk; - - _cairo_traps_init (traps); - - while (traps->traps_size < boxes->num_boxes) { - if (unlikely (! _cairo_traps_grow (traps))) { - _cairo_traps_fini (traps); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - traps->num_traps = boxes->num_boxes; - traps->is_rectilinear = TRUE; - traps->is_rectangular = TRUE; - traps->maybe_region = boxes->is_pixel_aligned; - - trap = &traps->traps[0]; - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box; - int i; - - box = chunk->base; - for (i = 0; i < chunk->count; i++) { - trap->top = box->p1.y; - trap->bottom = box->p2.y; - - trap->left.p1 = box->p1; - trap->left.p2.x = box->p1.x; - trap->left.p2.y = box->p2.y; - - trap->right.p1.x = box->p2.x; - trap->right.p1.y = box->p1.y; - trap->right.p2 = box->p2; - - box++, trap++; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, - const cairo_point_t *top_left, - const cairo_point_t *bottom_right) -{ - cairo_line_t left; - cairo_line_t right; - cairo_fixed_t top, bottom; - - if (top_left->y == bottom_right->y) - return CAIRO_STATUS_SUCCESS; - - if (top_left->x == bottom_right->x) - return CAIRO_STATUS_SUCCESS; - - left.p1.x = left.p2.x = top_left->x; - left.p1.y = right.p1.y = top_left->y; - right.p1.x = right.p2.x = bottom_right->x; - left.p2.y = right.p2.y = bottom_right->y; - - top = top_left->y; - bottom = bottom_right->y; - - if (traps->num_limits) { - cairo_bool_t reversed; - int n; - - /* support counter-clockwise winding for rectangular tessellation */ - reversed = top_left->x > bottom_right->x; - if (reversed) { - right.p1.x = right.p2.x = top_left->x; - left.p1.x = left.p2.x = bottom_right->x; - } - - for (n = 0; n < traps->num_limits; n++) { - const cairo_box_t *limits = &traps->limits[n]; - cairo_line_t _left, _right; - cairo_fixed_t _top, _bottom; - - if (top >= limits->p2.y) - continue; - if (bottom <= limits->p1.y) - continue; - - /* Trivially reject if trapezoid is entirely to the right or - * to the left of the limits. */ - if (left.p1.x >= limits->p2.x) - continue; - if (right.p1.x <= limits->p1.x) - continue; - - /* Otherwise, clip the trapezoid to the limits. */ - _top = top; - if (_top < limits->p1.y) - _top = limits->p1.y; - - _bottom = bottom; - if (_bottom > limits->p2.y) - _bottom = limits->p2.y; - - if (_bottom <= _top) - continue; - - _left = left; - if (_left.p1.x < limits->p1.x) { - _left.p1.x = limits->p1.x; - _left.p1.y = limits->p1.y; - _left.p2.x = limits->p1.x; - _left.p2.y = limits->p2.y; - } - - _right = right; - if (_right.p1.x > limits->p2.x) { - _right.p1.x = limits->p2.x; - _right.p1.y = limits->p1.y; - _right.p2.x = limits->p2.x; - _right.p2.y = limits->p2.y; - } - - if (left.p1.x >= right.p1.x) - continue; - - if (reversed) - _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left); - else - _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right); - } - } else { - _cairo_traps_add_trap (traps, top, bottom, &left, &right); - } - - return traps->status; -} - -void -_cairo_traps_translate (cairo_traps_t *traps, int x, int y) -{ - cairo_fixed_t xoff, yoff; - cairo_trapezoid_t *t; - int i; - - /* Ugh. The cairo_composite/(Render) interface doesn't allow - an offset for the trapezoids. Need to manually shift all - the coordinates to align with the offset origin of the - intermediate surface. */ - - xoff = _cairo_fixed_from_int (x); - yoff = _cairo_fixed_from_int (y); - - for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { - t->top += yoff; - t->bottom += yoff; - t->left.p1.x += xoff; - t->left.p1.y += yoff; - t->left.p2.x += xoff; - t->left.p2.y += yoff; - t->right.p1.x += xoff; - t->right.p1.y += yoff; - t->right.p2.x += xoff; - t->right.p2.y += yoff; - } -} - -void -_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, - cairo_trapezoid_t *src_traps, - int num_traps, - double tx, double ty, - double sx, double sy) -{ - int i; - cairo_fixed_t xoff = _cairo_fixed_from_double (tx); - cairo_fixed_t yoff = _cairo_fixed_from_double (ty); - - if (sx == 1.0 && sy == 1.0) { - for (i = 0; i < num_traps; i++) { - offset_traps[i].top = src_traps[i].top + yoff; - offset_traps[i].bottom = src_traps[i].bottom + yoff; - offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff; - offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff; - offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff; - offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff; - offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff; - offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff; - offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff; - offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff; - } - } else { - cairo_fixed_t xsc = _cairo_fixed_from_double (sx); - cairo_fixed_t ysc = _cairo_fixed_from_double (sy); - - for (i = 0; i < num_traps; i++) { - offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc); - offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc); - offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc); - offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc); - offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc); - offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc); - offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc); - offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc); - offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc); - offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc); - } - } -} - -static cairo_bool_t -_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) -{ - cairo_slope_t slope_left, slope_pt, slope_right; - - if (t->top > pt->y) - return FALSE; - if (t->bottom < pt->y) - return FALSE; - - _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); - _cairo_slope_init (&slope_pt, &t->left.p1, pt); - - if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) - return FALSE; - - _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); - _cairo_slope_init (&slope_pt, &t->right.p1, pt); - - if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) - return FALSE; - - return TRUE; -} - -cairo_bool_t -_cairo_traps_contain (const cairo_traps_t *traps, - double x, double y) -{ - int i; - cairo_point_t point; - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - for (i = 0; i < traps->num_traps; i++) { - if (_cairo_trap_contains (&traps->traps[i], &point)) - return TRUE; - } - - return FALSE; -} - -static cairo_fixed_t -_line_compute_intersection_x_for_y (const cairo_line_t *line, - cairo_fixed_t y) -{ - return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y); -} - -void -_cairo_traps_extents (const cairo_traps_t *traps, - cairo_box_t *extents) -{ - int i; - - if (traps->num_traps == 0) { - extents->p1.x = extents->p1.y = 0; - extents->p2.x = extents->p2.y = 0; - return; - } - - extents->p1.x = extents->p1.y = INT32_MAX; - extents->p2.x = extents->p2.y = INT32_MIN; - - for (i = 0; i < traps->num_traps; i++) { - const cairo_trapezoid_t *trap = &traps->traps[i]; - - if (trap->top < extents->p1.y) - extents->p1.y = trap->top; - if (trap->bottom > extents->p2.y) - extents->p2.y = trap->bottom; - - if (trap->left.p1.x < extents->p1.x) { - cairo_fixed_t x = trap->left.p1.x; - if (trap->top != trap->left.p1.y) { - x = _line_compute_intersection_x_for_y (&trap->left, - trap->top); - if (x < extents->p1.x) - extents->p1.x = x; - } else - extents->p1.x = x; - } - if (trap->left.p2.x < extents->p1.x) { - cairo_fixed_t x = trap->left.p2.x; - if (trap->bottom != trap->left.p2.y) { - x = _line_compute_intersection_x_for_y (&trap->left, - trap->bottom); - if (x < extents->p1.x) - extents->p1.x = x; - } else - extents->p1.x = x; - } - - if (trap->right.p1.x > extents->p2.x) { - cairo_fixed_t x = trap->right.p1.x; - if (trap->top != trap->right.p1.y) { - x = _line_compute_intersection_x_for_y (&trap->right, - trap->top); - if (x > extents->p2.x) - extents->p2.x = x; - } else - extents->p2.x = x; - } - if (trap->right.p2.x > extents->p2.x) { - cairo_fixed_t x = trap->right.p2.x; - if (trap->bottom != trap->right.p2.y) { - x = _line_compute_intersection_x_for_y (&trap->right, - trap->bottom); - if (x > extents->p2.x) - extents->p2.x = x; - } else - extents->p2.x = x; - } - } -} - - -/** - * _cairo_traps_extract_region: - * @traps: a #cairo_traps_t - * @region: a #cairo_region_t - * - * Determines if a set of trapezoids are exactly representable as a - * cairo region. If so, the passed-in region is initialized to - * the area representing the given traps. It should be finalized - * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED - * is returned. - * - * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED - * or %CAIRO_STATUS_NO_MEMORY - **/ -cairo_int_status_t -_cairo_traps_extract_region (cairo_traps_t *traps, - cairo_region_t **region) -{ - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_int_status_t status; - int i, rect_count; - - /* we only treat this a hint... */ - if (! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - for (i = 0; i < traps->num_traps; i++) { - if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || - traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || - ! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t)); - - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - rect_count = 0; - for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - - rects[rect_count].x = x1; - rects[rect_count].y = y1; - rects[rect_count].width = x2 - x1; - rects[rect_count].height = y2 - y1; - - rect_count++; - } - - *region = cairo_region_create_rectangles (rects, rect_count); - status = (*region)->status; - - if (rects != stack_rects) - free (rects); - - return status; -} - -/* moves trap points such that they become the actual corners of the trapezoid */ -static void -_sanitize_trap (cairo_trapezoid_t *t) -{ - cairo_trapezoid_t s = *t; - -#define FIX(lr, tb, p) \ - if (t->lr.p.y != t->tb) { \ - t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ - t->lr.p.y = s.tb; \ - } - FIX (left, top, p1); - FIX (left, bottom, p2); - FIX (right, top, p1); - FIX (right, bottom, p2); -} - -cairo_private cairo_status_t -_cairo_traps_path (const cairo_traps_t *traps, - cairo_path_fixed_t *path) -{ - int i; - - for (i = 0; i < traps->num_traps; i++) { - cairo_status_t status; - cairo_trapezoid_t trap = traps->traps[i]; - - if (trap.top == trap.bottom) - continue; - - _sanitize_trap (&trap); - - status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top); - if (unlikely (status)) return status; - status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top); - if (unlikely (status)) return status; - status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom); - if (unlikely (status)) return status; - status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom); - if (unlikely (status)) return status; - status = _cairo_path_fixed_close_path (path); - if (unlikely (status)) return status; - } - - return CAIRO_STATUS_SUCCESS; -} diff --git a/libs/cairo/cairo/src/cairo-truetype-subset-private.h b/libs/cairo/cairo/src/cairo-truetype-subset-private.h deleted file mode 100644 index fee112b97..000000000 --- a/libs/cairo/cairo/src/cairo-truetype-subset-private.h +++ /dev/null @@ -1,171 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H -#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H - -#include "cairoint.h" - -CAIRO_BEGIN_DECLS - -#if CAIRO_HAS_FONT_SUBSET - -/* The structs defined here should strictly follow the TrueType - * specification and not be padded. We use only 16-bit integer - * in their definition to guarantee that. The fields of type - * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit - * parts, and 64-bit members are broken into four. - * - * The test truetype-tables in the test suite makes sure that - * these tables have the right size. Please update that test - * if you add new tables/structs that should be packed. - */ - -#define MAKE_TT_TAG(a, b, c, d) (a<<24 | b<<16 | c<<8 | d) -#define TT_TAG_CFF MAKE_TT_TAG('C','F','F',' ') -#define TT_TAG_cmap MAKE_TT_TAG('c','m','a','p') -#define TT_TAG_cvt MAKE_TT_TAG('c','v','t',' ') -#define TT_TAG_fpgm MAKE_TT_TAG('f','p','g','m') -#define TT_TAG_glyf MAKE_TT_TAG('g','l','y','f') -#define TT_TAG_head MAKE_TT_TAG('h','e','a','d') -#define TT_TAG_hhea MAKE_TT_TAG('h','h','e','a') -#define TT_TAG_hmtx MAKE_TT_TAG('h','m','t','x') -#define TT_TAG_loca MAKE_TT_TAG('l','o','c','a') -#define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p') -#define TT_TAG_name MAKE_TT_TAG('n','a','m','e') -#define TT_TAG_post MAKE_TT_TAG('p','o','s','t') -#define TT_TAG_prep MAKE_TT_TAG('p','r','e','p') - -/* All tt_* structs are big-endian */ -typedef struct _tt_cmap_index { - uint16_t platform; - uint16_t encoding; - uint32_t offset; -} tt_cmap_index_t; - -typedef struct _tt_cmap { - uint16_t version; - uint16_t num_tables; - tt_cmap_index_t index[1]; -} tt_cmap_t; - -typedef struct _segment_map { - uint16_t format; - uint16_t length; - uint16_t version; - uint16_t segCountX2; - uint16_t searchRange; - uint16_t entrySelector; - uint16_t rangeShift; - uint16_t endCount[1]; -} tt_segment_map_t; - -typedef struct _tt_head { - int16_t version_1; - int16_t version_2; - int16_t revision_1; - int16_t revision_2; - uint16_t checksum_1; - uint16_t checksum_2; - uint16_t magic_1; - uint16_t magic_2; - uint16_t flags; - uint16_t units_per_em; - int16_t created_1; - int16_t created_2; - int16_t created_3; - int16_t created_4; - int16_t modified_1; - int16_t modified_2; - int16_t modified_3; - int16_t modified_4; - int16_t x_min; /* FWORD */ - int16_t y_min; /* FWORD */ - int16_t x_max; /* FWORD */ - int16_t y_max; /* FWORD */ - uint16_t mac_style; - uint16_t lowest_rec_pppem; - int16_t font_direction_hint; - int16_t index_to_loc_format; - int16_t glyph_data_format; -} tt_head_t; - -typedef struct _tt_hhea { - int16_t version_1; - int16_t version_2; - int16_t ascender; /* FWORD */ - int16_t descender; /* FWORD */ - int16_t line_gap; /* FWORD */ - uint16_t advance_max_width; /* UFWORD */ - int16_t min_left_side_bearing; /* FWORD */ - int16_t min_right_side_bearing; /* FWORD */ - int16_t x_max_extent; /* FWORD */ - int16_t caret_slope_rise; - int16_t caret_slope_run; - int16_t reserved[5]; - int16_t metric_data_format; - uint16_t num_hmetrics; -} tt_hhea_t; - -typedef struct _tt_maxp { - int16_t version_1; - int16_t version_2; - uint16_t num_glyphs; - uint16_t max_points; - uint16_t max_contours; - uint16_t max_composite_points; - uint16_t max_composite_contours; - uint16_t max_zones; - uint16_t max_twilight_points; - uint16_t max_storage; - uint16_t max_function_defs; - uint16_t max_instruction_defs; - uint16_t max_stack_elements; - uint16_t max_size_of_instructions; - uint16_t max_component_elements; - uint16_t max_component_depth; -} tt_maxp_t; - -typedef struct _tt_name_record { - uint16_t platform; - uint16_t encoding; - uint16_t language; - uint16_t name; - uint16_t length; - uint16_t offset; -} tt_name_record_t; - -typedef struct _tt_name { - uint16_t format; - uint16_t num_records; - uint16_t strings_offset; - tt_name_record_t records[1]; -} tt_name_t; - - - -/* composite_glyph_t flags */ -#define TT_ARG_1_AND_2_ARE_WORDS 0x0001 -#define TT_WE_HAVE_A_SCALE 0x0008 -#define TT_MORE_COMPONENTS 0x0020 -#define TT_WE_HAVE_AN_X_AND_Y_SCALE 0x0040 -#define TT_WE_HAVE_A_TWO_BY_TWO 0x0080 - -typedef struct _tt_composite_glyph { - uint16_t flags; - uint16_t index; - uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ -} tt_composite_glyph_t; - -typedef struct _tt_glyph_data { - int16_t num_contours; - int8_t data[8]; - tt_composite_glyph_t glyph; -} tt_glyph_data_t; - -#endif /* CAIRO_HAS_FONT_SUBSET */ - -CAIRO_END_DECLS - -#endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-truetype-subset.c b/libs/cairo/cairo/src/cairo-truetype-subset.c deleted file mode 100644 index 219adbeb6..000000000 --- a/libs/cairo/cairo/src/cairo-truetype-subset.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Useful links: - * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html - * http://www.microsoft.com/typography/specs/default.htm - */ - -#define _BSD_SOURCE /* for snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-truetype-subset-private.h" - - -typedef struct subset_glyph subset_glyph_t; -struct subset_glyph { - int parent_index; - unsigned long location; -}; - -typedef struct _cairo_truetype_font cairo_truetype_font_t; - -typedef struct table table_t; -struct table { - unsigned long tag; - cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag); - int pos; /* position in the font directory */ -}; - -struct _cairo_truetype_font { - - cairo_scaled_font_subset_t *scaled_font_subset; - - table_t truetype_tables[10]; - int num_tables; - - struct { - char *font_name; - char *ps_name; - unsigned int num_glyphs; - int *widths; - long x_min, y_min, x_max, y_max; - long ascent, descent; - int units_per_em; - } base; - - subset_glyph_t *glyphs; - const cairo_scaled_font_backend_t *backend; - int num_glyphs_in_face; - int checksum_index; - cairo_array_t output; - cairo_array_t string_offsets; - unsigned long last_offset; - unsigned long last_boundary; - int *parent_to_subset; - cairo_status_t status; - -}; - -/* - * Test that the structs we define for TrueType tables have the - * correct size, ie. they are not padded. - */ -#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) -check (tt_head_t, 54); -check (tt_hhea_t, 36); -check (tt_maxp_t, 32); -check (tt_name_record_t, 12); -check (tt_name_t, 18); -check (tt_name_t, 18); -check (tt_composite_glyph_t, 16); -check (tt_glyph_data_t, 26); -#undef check - -static cairo_status_t -cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, - unsigned short glyph, - unsigned short *out); - -#define SFNT_VERSION 0x00010000 -#define SFNT_STRING_MAX_LENGTH 65535 - -static cairo_status_t -_cairo_truetype_font_set_error (cairo_truetype_font_t *font, - cairo_status_t status) -{ - if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - _cairo_status_set_error (&font->status, status); - - return _cairo_error (status); -} - -static cairo_status_t -_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_truetype_font_t **font_return) -{ - cairo_status_t status; - cairo_truetype_font_t *font; - const cairo_scaled_font_backend_t *backend; - tt_head_t head; - tt_hhea_t hhea; - tt_maxp_t maxp; - unsigned long size; - - backend = scaled_font_subset->scaled_font->backend; - if (!backend->load_truetype_table) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* FIXME: We should either support subsetting vertical fonts, or fail on - * vertical. Currently font_options_t doesn't have vertical flag, but - * it should be added in the future. For now, the freetype backend - * returns UNSUPPORTED in load_truetype_table if the font is vertical. - * - * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font)) - * return CAIRO_INT_STATUS_UNSUPPORTED; - */ - - size = sizeof (tt_head_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_head, 0, - (unsigned char *) &head, - &size); - if (unlikely (status)) - return status; - - size = sizeof (tt_maxp_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_maxp, 0, - (unsigned char *) &maxp, - &size); - if (unlikely (status)) - return status; - - size = sizeof (tt_hhea_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_hhea, 0, - (unsigned char *) &hhea, - &size); - if (unlikely (status)) - return status; - - font = malloc (sizeof (cairo_truetype_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->backend = backend; - font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); - font->scaled_font_subset = scaled_font_subset; - - font->last_offset = 0; - font->last_boundary = 0; - _cairo_array_init (&font->output, sizeof (char)); - status = _cairo_array_grow_by (&font->output, 4096); - if (unlikely (status)) - goto fail1; - - font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); - if (unlikely (font->glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); - if (unlikely (font->parent_to_subset == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - - font->base.num_glyphs = 0; - font->base.x_min = (int16_t) be16_to_cpu (head.x_min); - font->base.y_min = (int16_t) be16_to_cpu (head.y_min); - font->base.x_max = (int16_t) be16_to_cpu (head.x_max); - font->base.y_max = (int16_t) be16_to_cpu (head.y_max); - font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender); - font->base.descent = (int16_t) be16_to_cpu (hhea.descender); - font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em); - if (font->base.units_per_em == 0) - font->base.units_per_em = 2048; - - font->base.ps_name = NULL; - font->base.font_name = NULL; - status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, - &font->base.ps_name, - &font->base.font_name); - if (_cairo_status_is_error (status)) - goto fail3; - - /* If the PS name is not found, create a CairoFont-x-y name. */ - if (font->base.ps_name == NULL) { - font->base.ps_name = malloc (30); - if (unlikely (font->base.ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - - snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", - scaled_font_subset->font_id, - scaled_font_subset->subset_id); - } - - font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); - if (unlikely (font->base.widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; - } - - _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); - status = _cairo_array_grow_by (&font->string_offsets, 10); - if (unlikely (status)) - goto fail5; - - font->status = CAIRO_STATUS_SUCCESS; - - *font_return = font; - - return CAIRO_STATUS_SUCCESS; - - fail5: - _cairo_array_fini (&font->string_offsets); - free (font->base.widths); - fail4: - free (font->base.ps_name); - fail3: - free (font->parent_to_subset); - if (font->base.font_name) - free (font->base.font_name); - fail2: - free (font->glyphs); - fail1: - _cairo_array_fini (&font->output); - free (font); - - return status; -} - -static void -cairo_truetype_font_destroy (cairo_truetype_font_t *font) -{ - _cairo_array_fini (&font->string_offsets); - free (font->base.widths); - free (font->base.ps_name); - if (font->base.font_name) - free (font->base.font_name); - free (font->parent_to_subset); - free (font->glyphs); - _cairo_array_fini (&font->output); - free (font); -} - -static cairo_status_t -cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, - size_t length, - unsigned char **buffer) -{ - cairo_status_t status; - - if (font->status) - return font->status; - - status = _cairo_array_allocate (&font->output, length, (void **) buffer); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - return CAIRO_STATUS_SUCCESS; -} - -static void -cairo_truetype_font_write (cairo_truetype_font_t *font, - const void *data, - size_t length) -{ - cairo_status_t status; - - if (font->status) - return; - - status = _cairo_array_append_multiple (&font->output, data, length); - if (unlikely (status)) - status = _cairo_truetype_font_set_error (font, status); -} - -static void -cairo_truetype_font_write_be16 (cairo_truetype_font_t *font, - uint16_t value) -{ - uint16_t be16_value; - - if (font->status) - return; - - be16_value = cpu_to_be16 (value); - cairo_truetype_font_write (font, &be16_value, sizeof be16_value); -} - -static void -cairo_truetype_font_write_be32 (cairo_truetype_font_t *font, - uint32_t value) -{ - uint32_t be32_value; - - if (font->status) - return; - - be32_value = cpu_to_be32 (value); - cairo_truetype_font_write (font, &be32_value, sizeof be32_value); -} - -static cairo_status_t -cairo_truetype_font_align_output (cairo_truetype_font_t *font, - unsigned long *aligned) -{ - int length, pad; - unsigned char *padding; - - length = _cairo_array_num_elements (&font->output); - *aligned = (length + 3) & ~3; - pad = *aligned - length; - - if (pad) { - cairo_status_t status; - - status = cairo_truetype_font_allocate_write_buffer (font, pad, - &padding); - if (unlikely (status)) - return status; - - memset (padding, 0, pad); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, - unsigned long boundary) -{ - cairo_status_t status; - - if (font->status) - return font->status; - - if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH) - { - status = _cairo_array_append (&font->string_offsets, - &font->last_boundary); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - font->last_offset = font->last_boundary; - } - font->last_boundary = boundary; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - unsigned int i; - - cairo_truetype_font_write_be16 (font, 0); /* Table version */ - cairo_truetype_font_write_be16 (font, 2); /* Num tables */ - - cairo_truetype_font_write_be16 (font, 3); /* Platform */ - cairo_truetype_font_write_be16 (font, 0); /* Encoding */ - cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */ - - cairo_truetype_font_write_be16 (font, 1); /* Platform */ - cairo_truetype_font_write_be16 (font, 0); /* Encoding */ - cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */ - - /* Output a format 4 encoding table. */ - - cairo_truetype_font_write_be16 (font, 4); /* Format */ - cairo_truetype_font_write_be16 (font, 32); /* Length */ - cairo_truetype_font_write_be16 (font, 0); /* Version */ - cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */ - cairo_truetype_font_write_be16 (font, 4); /* searchrange */ - cairo_truetype_font_write_be16 (font, 1); /* entry selector */ - cairo_truetype_font_write_be16 (font, 0); /* rangeshift */ - cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */ - cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */ - cairo_truetype_font_write_be16 (font, 0); /* reserved */ - cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */ - cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */ - cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */ - cairo_truetype_font_write_be16 (font, 1); /* delta[1] */ - cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */ - cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */ - - /* Output a format 6 encoding table. */ - - cairo_truetype_font_write_be16 (font, 6); - cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs); - cairo_truetype_font_write_be16 (font, 0); - cairo_truetype_font_write_be16 (font, 0); /* First character */ - cairo_truetype_font_write_be16 (font, font->base.num_glyphs); - for (i = 0; i < font->base.num_glyphs; i++) - cairo_truetype_font_write_be16 (font, i); - - return font->status; -} - -static cairo_status_t -cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - cairo_status_t status; - unsigned char *buffer; - unsigned long size; - - if (font->status) - return font->status; - - size = 0; - status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, - tag, 0, NULL, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - tag, 0, buffer, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, - unsigned char *buffer, - unsigned long size) -{ - tt_glyph_data_t *glyph_data; - tt_composite_glyph_t *composite_glyph; - int num_args; - int has_more_components; - unsigned short flags; - unsigned short index; - cairo_status_t status; - unsigned char *end = buffer + size; - - if (font->status) - return font->status; - - glyph_data = (tt_glyph_data_t *) buffer; - if ((unsigned char *)(&glyph_data->data) >= end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0) - return CAIRO_STATUS_SUCCESS; - - composite_glyph = &glyph_data->glyph; - do { - if ((unsigned char *)(&composite_glyph->args[1]) > end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - flags = be16_to_cpu (composite_glyph->flags); - has_more_components = flags & TT_MORE_COMPONENTS; - status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); - if (unlikely (status)) - return status; - - composite_glyph->index = cpu_to_be16 (index); - num_args = 1; - if (flags & TT_ARG_1_AND_2_ARE_WORDS) - num_args += 1; - - if (flags & TT_WE_HAVE_A_SCALE) - num_args += 1; - else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) - num_args += 2; - else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) - num_args += 4; - - composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); - } while (has_more_components); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - unsigned long start_offset, index, size, next; - tt_head_t header; - unsigned long begin, end; - unsigned char *buffer; - unsigned int i; - union { - unsigned char *bytes; - uint16_t *short_offsets; - uint32_t *long_offsets; - } u; - cairo_status_t status; - - if (font->status) - return font->status; - - size = sizeof (tt_head_t); - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_head, 0, - (unsigned char*) &header, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - if (be16_to_cpu (header.index_to_loc_format) == 0) - size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); - else - size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); - - u.bytes = malloc (size); - if (unlikely (u.bytes == NULL)) - return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_loca, 0, u.bytes, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - start_offset = _cairo_array_num_elements (&font->output); - for (i = 0; i < font->base.num_glyphs; i++) { - index = font->glyphs[i].parent_index; - if (be16_to_cpu (header.index_to_loc_format) == 0) { - begin = be16_to_cpu (u.short_offsets[index]) * 2; - end = be16_to_cpu (u.short_offsets[index + 1]) * 2; - } - else { - begin = be32_to_cpu (u.long_offsets[index]); - end = be32_to_cpu (u.long_offsets[index + 1]); - } - - /* quick sanity check... */ - if (end < begin) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - - size = end - begin; - status = cairo_truetype_font_align_output (font, &next); - if (unlikely (status)) - goto FAIL; - - status = cairo_truetype_font_check_boundary (font, next); - if (unlikely (status)) - goto FAIL; - - font->glyphs[i].location = next - start_offset; - - status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (unlikely (status)) - goto FAIL; - - if (size != 0) { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_glyf, begin, buffer, &size); - if (unlikely (status)) - goto FAIL; - - status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); - if (unlikely (status)) - goto FAIL; - } - } - - status = cairo_truetype_font_align_output (font, &next); - if (unlikely (status)) - goto FAIL; - - font->glyphs[i].location = next - start_offset; - - status = font->status; -FAIL: - free (u.bytes); - - return _cairo_truetype_font_set_error (font, status); -} - -static cairo_status_t -cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - unsigned char *buffer; - unsigned long size; - cairo_status_t status; - - if (font->status) - return font->status; - - size = 0; - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - tag, 0, NULL, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - font->checksum_index = _cairo_array_num_elements (&font->output) + 8; - status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - tag, 0, buffer, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - /* set checkSumAdjustment to 0 for table checksum calculation */ - *(uint32_t *)(buffer + 8) = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag) -{ - tt_hhea_t *hhea; - unsigned long size; - cairo_status_t status; - - if (font->status) - return font->status; - - size = sizeof (tt_hhea_t); - status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - tag, 0, (unsigned char *) hhea, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - unsigned long size; - unsigned long long_entry_size; - unsigned long short_entry_size; - short *p; - unsigned int i; - tt_hhea_t hhea; - int num_hmetrics; - cairo_status_t status; - - if (font->status) - return font->status; - - size = sizeof (tt_hhea_t); - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hhea, 0, - (unsigned char*) &hhea, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - num_hmetrics = be16_to_cpu(hhea.num_hmetrics); - - for (i = 0; i < font->base.num_glyphs; i++) { - long_entry_size = 2 * sizeof (int16_t); - short_entry_size = sizeof (int16_t); - status = cairo_truetype_font_allocate_write_buffer (font, - long_entry_size, - (unsigned char **) &p); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - if (font->glyphs[i].parent_index < num_hmetrics) { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hmtx, - font->glyphs[i].parent_index * long_entry_size, - (unsigned char *) p, &long_entry_size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - } - else - { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hmtx, - (num_hmetrics - 1) * long_entry_size, - (unsigned char *) p, &short_entry_size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_hmtx, - num_hmetrics * long_entry_size + - (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, - (unsigned char *) (p + 1), &short_entry_size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - } - font->base.widths[i] = be16_to_cpu (p[0]); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - unsigned int i; - tt_head_t header; - unsigned long size; - cairo_status_t status; - - if (font->status) - return font->status; - - size = sizeof(tt_head_t); - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_head, 0, - (unsigned char*) &header, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - if (be16_to_cpu (header.index_to_loc_format) == 0) - { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2); - } else { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_truetype_font_write_be32 (font, font->glyphs[i].location); - } - - return font->status; -} - -static cairo_status_t -cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, - unsigned long tag) -{ - tt_maxp_t *maxp; - unsigned long size; - cairo_status_t status; - - if (font->status) - return font->status; - - size = sizeof (tt_maxp_t); - status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - tag, 0, (unsigned char *) maxp, &size); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) -{ - cairo_status_t status; - unsigned char *table_buffer; - size_t table_buffer_length; - unsigned short search_range, entry_selector, range_shift; - - if (font->status) - return font->status; - - search_range = 1; - entry_selector = 0; - while (search_range * 2 <= font->num_tables) { - search_range *= 2; - entry_selector++; - } - search_range *= 16; - range_shift = font->num_tables * 16 - search_range; - - cairo_truetype_font_write_be32 (font, SFNT_VERSION); - cairo_truetype_font_write_be16 (font, font->num_tables); - cairo_truetype_font_write_be16 (font, search_range); - cairo_truetype_font_write_be16 (font, entry_selector); - cairo_truetype_font_write_be16 (font, range_shift); - - /* Allocate space for the table directory. Each directory entry - * will be filled in by cairo_truetype_font_update_entry() after - * the table is written. */ - table_buffer_length = font->num_tables * 16; - status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, - &table_buffer); - if (unlikely (status)) - return _cairo_truetype_font_set_error (font, status); - - return CAIRO_STATUS_SUCCESS; -} - -static uint32_t -cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font, - unsigned long start, - unsigned long end) -{ - uint32_t *padded_end; - uint32_t *p; - uint32_t checksum; - char *data; - - checksum = 0; - data = _cairo_array_index (&font->output, 0); - p = (uint32_t *) (data + start); - padded_end = (uint32_t *) (data + ((end + 3) & ~3)); - while (p < padded_end) - checksum += be32_to_cpu(*p++); - - return checksum; -} - -static void -cairo_truetype_font_update_entry (cairo_truetype_font_t *font, - int index, - unsigned long tag, - unsigned long start, - unsigned long end) -{ - uint32_t *entry; - - entry = _cairo_array_index (&font->output, 12 + 16 * index); - entry[0] = cpu_to_be32 ((uint32_t)tag); - entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end)); - entry[2] = cpu_to_be32 ((uint32_t)start); - entry[3] = cpu_to_be32 ((uint32_t)(end - start)); -} - -static cairo_status_t -cairo_truetype_font_generate (cairo_truetype_font_t *font, - const char **data, - unsigned long *length, - const unsigned long **string_offsets, - unsigned long *num_strings) -{ - cairo_status_t status; - unsigned long start, end, next; - uint32_t checksum, *checksum_location; - int i; - - if (font->status) - return font->status; - - status = cairo_truetype_font_write_offset_table (font); - if (unlikely (status)) - goto FAIL; - - status = cairo_truetype_font_align_output (font, &start); - if (unlikely (status)) - goto FAIL; - - end = 0; - for (i = 0; i < font->num_tables; i++) { - status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); - if (unlikely (status)) - goto FAIL; - - end = _cairo_array_num_elements (&font->output); - status = cairo_truetype_font_align_output (font, &next); - if (unlikely (status)) - goto FAIL; - - cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, - font->truetype_tables[i].tag, start, end); - status = cairo_truetype_font_check_boundary (font, next); - if (unlikely (status)) - goto FAIL; - - start = next; - } - - checksum = - 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end); - checksum_location = _cairo_array_index (&font->output, font->checksum_index); - *checksum_location = cpu_to_be32 (checksum); - - *data = _cairo_array_index (&font->output, 0); - *length = _cairo_array_num_elements (&font->output); - *num_strings = _cairo_array_num_elements (&font->string_offsets); - if (*num_strings != 0) - *string_offsets = _cairo_array_index (&font->string_offsets, 0); - else - *string_offsets = NULL; - - FAIL: - return _cairo_truetype_font_set_error (font, status); -} - -static cairo_status_t -cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, - unsigned short glyph, - unsigned short *out) -{ - if (glyph >= font->num_glyphs_in_face) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (font->parent_to_subset[glyph] == 0) { - font->parent_to_subset[glyph] = font->base.num_glyphs; - font->glyphs[font->base.num_glyphs].parent_index = glyph; - font->base.num_glyphs++; - } - - *out = font->parent_to_subset[glyph]; - return CAIRO_STATUS_SUCCESS; -} - -static void -cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, - unsigned long tag, - cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag), - int pos) -{ - font->truetype_tables[font->num_tables].tag = tag; - font->truetype_tables[font->num_tables].write = write; - font->truetype_tables[font->num_tables].pos = pos; - font->num_tables++; -} - -/* cairo_truetype_font_create_truetype_table_list() builds the list of - * truetype tables to be embedded in the subsetted font. Each call to - * cairo_truetype_font_add_truetype_table() adds a table, the callback - * for generating the table, and the position in the table directory - * to the truetype_tables array. - * - * As we write out the glyf table we remap composite glyphs. - * Remapping composite glyphs will reference the sub glyphs the - * composite glyph is made up of. The "glyf" table callback needs to - * be called first so we have all the glyphs in the subset before - * going further. - * - * The order in which tables are added to the truetype_table array - * using cairo_truetype_font_add_truetype_table() specifies the order - * in which the callback functions will be called. - * - * The tables in the table directory must be listed in alphabetical - * order. The "cvt", "fpgm", and "prep" are optional tables. They - * will only be embedded in the subset if they exist in the source - * font. The pos parameter of cairo_truetype_font_add_truetype_table() - * specifies the position of the table in the table directory. - */ -static void -cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) -{ - cairo_bool_t has_cvt = FALSE; - cairo_bool_t has_fpgm = FALSE; - cairo_bool_t has_prep = FALSE; - unsigned long size; - int pos; - - size = 0; - if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_cvt, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) - has_cvt = TRUE; - - size = 0; - if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_fpgm, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) - has_fpgm = TRUE; - - size = 0; - if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, - TT_TAG_prep, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) - has_prep = TRUE; - - font->num_tables = 0; - pos = 1; - if (has_cvt) - pos++; - if (has_fpgm) - pos++; - cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); - - pos = 0; - cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); - if (has_cvt) - cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++); - if (has_fpgm) - cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++); - pos++; - cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++); - cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++); - cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++); - cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++); - cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++); - if (has_prep) - cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); -} - -cairo_status_t -_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_truetype_font_t *font = NULL; - cairo_status_t status; - const char *data = NULL; /* squelch bogus compiler warning */ - unsigned long length = 0; /* squelch bogus compiler warning */ - unsigned long offsets_length; - unsigned int i; - const unsigned long *string_offsets = NULL; - unsigned long num_strings = 0; - - status = _cairo_truetype_font_create (font_subset, &font); - if (unlikely (status)) - return status; - - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; - status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); - if (unlikely (status)) - goto fail1; - } - - cairo_truetype_font_create_truetype_table_list (font); - status = cairo_truetype_font_generate (font, &data, &length, - &string_offsets, &num_strings); - if (unlikely (status)) - goto fail1; - - truetype_subset->ps_name = strdup (font->base.ps_name); - if (unlikely (truetype_subset->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - if (font->base.font_name != NULL) { - truetype_subset->font_name = strdup (font->base.font_name); - if (unlikely (truetype_subset->font_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - } else { - truetype_subset->font_name = NULL; - } - - /* The widths array returned must contain only widths for the - * glyphs in font_subset. Any subglyphs appended after - * font_subset->num_glyphs are omitted. */ - truetype_subset->widths = calloc (sizeof (double), - font->scaled_font_subset->num_glyphs); - if (unlikely (truetype_subset->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; - - truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em; - truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em; - truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em; - truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em; - truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em; - truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; - - if (length) { - truetype_subset->data = malloc (length); - if (unlikely (truetype_subset->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; - } - - memcpy (truetype_subset->data, data, length); - } else - truetype_subset->data = NULL; - truetype_subset->data_length = length; - - if (num_strings) { - offsets_length = num_strings * sizeof (unsigned long); - truetype_subset->string_offsets = malloc (offsets_length); - if (unlikely (truetype_subset->string_offsets == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail5; - } - - memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); - truetype_subset->num_string_offsets = num_strings; - } else { - truetype_subset->string_offsets = NULL; - truetype_subset->num_string_offsets = 0; - } - - cairo_truetype_font_destroy (font); - - return CAIRO_STATUS_SUCCESS; - - fail5: - free (truetype_subset->data); - fail4: - free (truetype_subset->widths); - fail3: - if (truetype_subset->font_name) - free (truetype_subset->font_name); - fail2: - free (truetype_subset->ps_name); - fail1: - cairo_truetype_font_destroy (font); - - return status; -} - -void -_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) -{ - free (subset->ps_name); - if (subset->font_name) - free (subset->font_name); - free (subset->widths); - free (subset->data); - free (subset->string_offsets); -} - -static cairo_int_status_t -_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, - unsigned long table_offset, - unsigned long index, - uint32_t *ucs4) -{ - cairo_status_t status; - const cairo_scaled_font_backend_t *backend; - tt_segment_map_t *map; - char buf[4]; - unsigned int num_segments, i; - unsigned long size; - uint16_t *start_code; - uint16_t *end_code; - uint16_t *delta; - uint16_t *range_offset; - uint16_t *glyph_array; - uint16_t c; - - backend = scaled_font->backend; - size = 4; - status = backend->load_truetype_table (scaled_font, - TT_TAG_cmap, table_offset, - (unsigned char *) &buf, - &size); - if (unlikely (status)) - return status; - - /* All table formats have the same first two words */ - map = (tt_segment_map_t *) buf; - if (be16_to_cpu (map->format) != 4) - return CAIRO_INT_STATUS_UNSUPPORTED; - - size = be16_to_cpu (map->length); - map = malloc (size); - if (unlikely (map == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, - TT_TAG_cmap, table_offset, - (unsigned char *) map, - &size); - if (unlikely (status)) - goto fail; - - num_segments = be16_to_cpu (map->segCountX2)/2; - - /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of - * uint16_t each num_segments long. */ - if (size < (8 + 4*num_segments)*sizeof(uint16_t)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - end_code = map->endCount; - start_code = &(end_code[num_segments + 1]); - delta = &(start_code[num_segments]); - range_offset = &(delta[num_segments]); - glyph_array = &(range_offset[num_segments]); - - /* search for glyph in segments with rangeOffset=0 */ - for (i = 0; i < num_segments; i++) { - c = index - be16_to_cpu (delta[i]); - if (range_offset[i] == 0 && - c >= be16_to_cpu (start_code[i]) && - c <= be16_to_cpu (end_code[i])) - { - *ucs4 = c; - goto found; - } - } - - /* search for glyph in segments with rangeOffset=1 */ - for (i = 0; i < num_segments; i++) { - if (range_offset[i] != 0) { - uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; - int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; - uint16_t g_id_be = cpu_to_be16 (index); - int j; - - if (range_size > 0) { - if ((char*)glyph_ids + 2*range_size > (char*)map + size) - return CAIRO_INT_STATUS_UNSUPPORTED; - - for (j = 0; j < range_size; j++) { - if (glyph_ids[j] == g_id_be) { - *ucs4 = be16_to_cpu (start_code[i]) + j; - goto found; - } - } - } - } - } - - /* glyph not found */ - *ucs4 = -1; - -found: - status = CAIRO_STATUS_SUCCESS; - -fail: - free (map); - - return status; -} - -cairo_int_status_t -_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, - unsigned long index, - uint32_t *ucs4) -{ - cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - const cairo_scaled_font_backend_t *backend; - tt_cmap_t *cmap; - char buf[4]; - int num_tables, i; - unsigned long size; - - backend = scaled_font->backend; - if (!backend->load_truetype_table) - return CAIRO_INT_STATUS_UNSUPPORTED; - - size = 4; - status = backend->load_truetype_table (scaled_font, - TT_TAG_cmap, 0, - (unsigned char *) &buf, - &size); - if (unlikely (status)) - return status; - - cmap = (tt_cmap_t *) buf; - num_tables = be16_to_cpu (cmap->num_tables); - size = 4 + num_tables*sizeof(tt_cmap_index_t); - cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); - if (unlikely (cmap == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, - TT_TAG_cmap, 0, - (unsigned char *) cmap, - &size); - if (unlikely (status)) - goto cleanup; - - /* Find a table with Unicode mapping */ - for (i = 0; i < num_tables; i++) { - if (be16_to_cpu (cmap->index[i].platform) == 3 && - be16_to_cpu (cmap->index[i].encoding) == 1) { - status = _cairo_truetype_reverse_cmap (scaled_font, - be32_to_cpu (cmap->index[i].offset), - index, - ucs4); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - break; - } - } - -cleanup: - free (cmap); - - return status; -} - -cairo_int_status_t -_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, - char **ps_name_out, - char **font_name_out) -{ - cairo_status_t status; - const cairo_scaled_font_backend_t *backend; - tt_name_t *name; - tt_name_record_t *record; - unsigned long size; - int i, j; - char *ps_name = NULL; - char *font_name = NULL; - - backend = scaled_font->backend; - if (!backend->load_truetype_table) - return CAIRO_INT_STATUS_UNSUPPORTED; - - size = 0; - status = backend->load_truetype_table (scaled_font, - TT_TAG_name, 0, - NULL, - &size); - if (status) - return status; - - name = malloc (size); - if (name == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, - TT_TAG_name, 0, - (unsigned char *) name, - &size); - if (status) - goto fail; - - /* Extract the font name and PS name from the name table. At - * present this just looks for the Mac platform/Roman encoded font - * name. It should be extended to use any suitable font name in - * the name table. - */ - for (i = 0; i < be16_to_cpu(name->num_records); i++) { - record = &(name->records[i]); - if ((be16_to_cpu (record->platform) == 1) && - (be16_to_cpu (record->encoding) == 0)) { - - if (be16_to_cpu (record->name) == 4) { - font_name = malloc (be16_to_cpu(record->length) + 1); - if (font_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - strncpy(font_name, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - font_name[be16_to_cpu (record->length)] = 0; - } - - if (be16_to_cpu (record->name) == 6) { - ps_name = malloc (be16_to_cpu(record->length) + 1); - if (ps_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - strncpy(ps_name, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - ps_name[be16_to_cpu (record->length)] = 0; - } - - if (font_name && ps_name) - break; - } - } - - free (name); - - /* Ensure PS name does not contain any spaces */ - if (ps_name) { - for (i = 0, j = 0; ps_name[j]; j++) { - if (ps_name[j] == ' ') - continue; - ps_name[i++] = ps_name[j]; - } - ps_name[i] = '\0'; - } - - *ps_name_out = ps_name; - *font_name_out = font_name; - - return CAIRO_STATUS_SUCCESS; - -fail: - free (name); - - if (ps_name != NULL) - free (ps_name); - - if (font_name != NULL) - free (font_name); - - *ps_name_out = NULL; - *font_name_out = NULL; - - return status; -} - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-type1-fallback.c b/libs/cairo/cairo/src/cairo-type1-fallback.c deleted file mode 100644 index cc3a26966..000000000 --- a/libs/cairo/cairo/src/cairo-type1-fallback.c +++ /dev/null @@ -1,856 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define _BSD_SOURCE /* for snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-type1-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-output-stream-private.h" - -typedef enum { - CAIRO_CHARSTRING_TYPE1, - CAIRO_CHARSTRING_TYPE2 -} cairo_charstring_type_t; - -typedef struct _cairo_type1_font { - int *widths; - - cairo_scaled_font_subset_t *scaled_font_subset; - cairo_scaled_font_t *type1_scaled_font; - - cairo_array_t contents; - - double x_min, y_min, x_max, y_max; - - const char *data; - unsigned long header_size; - unsigned long data_size; - unsigned long trailer_size; - int bbox_position; - int bbox_max_chars; - - cairo_output_stream_t *output; - - unsigned short eexec_key; - cairo_bool_t hex_encode; - int hex_column; -} cairo_type1_font_t; - -static cairo_status_t -cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_type1_font_t **subset_return, - cairo_bool_t hex_encode) -{ - cairo_type1_font_t *font; - cairo_font_face_t *font_face; - cairo_matrix_t font_matrix; - cairo_matrix_t ctm; - cairo_font_options_t font_options; - cairo_status_t status; - - font = calloc (1, sizeof (cairo_type1_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); - if (unlikely (font->widths == NULL)) { - free (font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - font->scaled_font_subset = scaled_font_subset; - font->hex_encode = hex_encode; - - font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font); - - cairo_matrix_init_scale (&font_matrix, 1000, -1000); - cairo_matrix_init_identity (&ctm); - - _cairo_font_options_init_default (&font_options); - cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); - - font->type1_scaled_font = cairo_scaled_font_create (font_face, - &font_matrix, - &ctm, - &font_options); - status = font->type1_scaled_font->status; - if (unlikely (status)) - goto fail; - - _cairo_array_init (&font->contents, sizeof (unsigned char)); - font->output = NULL; - - *subset_return = font; - - return CAIRO_STATUS_SUCCESS; - -fail: - free (font->widths); - free (font); - - return status; -} - -/* Charstring commands. If the high byte is 0 the command is encoded - * with a single byte. */ -#define CHARSTRING_sbw 0x0c07 -#define CHARSTRING_rmoveto 0x0015 -#define CHARSTRING_rlineto 0x0005 -#define CHARSTRING_rcurveto 0x0008 -#define CHARSTRING_closepath 0x0009 -#define CHARSTRING_endchar 0x000e - -/* Before calling this function, the caller must allocate sufficient - * space in data (see _cairo_array_grow_by). The maximum number of - * bytes that will be used is 2. - */ -static void -charstring_encode_command (cairo_array_t *data, int command) -{ - cairo_status_t status; - int orig_size; - unsigned char buf[5]; - unsigned char *p = buf; - - if (command & 0xff00) - *p++ = command >> 8; - *p++ = command & 0x00ff; - - /* Ensure the array doesn't grow, which allows this function to - * have no possibility of failure. */ - orig_size = _cairo_array_size (data); - status = _cairo_array_append_multiple (data, buf, p - buf); - - assert (status == CAIRO_STATUS_SUCCESS); - assert (_cairo_array_size (data) == orig_size); -} - -/* Before calling this function, the caller must allocate sufficient - * space in data (see _cairo_array_grow_by). The maximum number of - * bytes that will be used is 5. - */ -static void -charstring_encode_integer (cairo_array_t *data, - int i, - cairo_charstring_type_t type) -{ - cairo_status_t status; - int orig_size; - unsigned char buf[10]; - unsigned char *p = buf; - - if (i >= -107 && i <= 107) { - *p++ = i + 139; - } else if (i >= 108 && i <= 1131) { - i -= 108; - *p++ = (i >> 8)+ 247; - *p++ = i & 0xff; - } else if (i >= -1131 && i <= -108) { - i = -i - 108; - *p++ = (i >> 8)+ 251; - *p++ = i & 0xff; - } else { - if (type == CAIRO_CHARSTRING_TYPE1) { - *p++ = 0xff; - *p++ = i >> 24; - *p++ = (i >> 16) & 0xff; - *p++ = (i >> 8) & 0xff; - *p++ = i & 0xff; - } else { - *p++ = 0xff; - *p++ = (i >> 8) & 0xff; - *p++ = i & 0xff; - *p++ = 0; - *p++ = 0; - } - } - - /* Ensure the array doesn't grow, which allows this function to - * have no possibility of failure. */ - orig_size = _cairo_array_size (data); - status = _cairo_array_append_multiple (data, buf, p - buf); - - assert (status == CAIRO_STATUS_SUCCESS); - assert (_cairo_array_size (data) == orig_size); -} - -typedef struct _ps_path_info { - cairo_array_t *data; - int current_x, current_y; - cairo_charstring_type_t type; -} t1_path_info_t; - -static cairo_status_t -_charstring_move_to (void *closure, - const cairo_point_t *point) -{ - t1_path_info_t *path_info = (t1_path_info_t *) closure; - int dx, dy; - cairo_status_t status; - - status = _cairo_array_grow_by (path_info->data, 12); - if (unlikely (status)) - return status; - - dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; - dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; - charstring_encode_integer (path_info->data, dx, path_info->type); - charstring_encode_integer (path_info->data, dy, path_info->type); - path_info->current_x += dx; - path_info->current_y += dy; - - charstring_encode_command (path_info->data, CHARSTRING_rmoveto); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_charstring_line_to (void *closure, - const cairo_point_t *point) -{ - t1_path_info_t *path_info = (t1_path_info_t *) closure; - int dx, dy; - cairo_status_t status; - - status = _cairo_array_grow_by (path_info->data, 12); - if (unlikely (status)) - return status; - - dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; - dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; - charstring_encode_integer (path_info->data, dx, path_info->type); - charstring_encode_integer (path_info->data, dy, path_info->type); - path_info->current_x += dx; - path_info->current_y += dy; - - charstring_encode_command (path_info->data, CHARSTRING_rlineto); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_charstring_curve_to (void *closure, - const cairo_point_t *point1, - const cairo_point_t *point2, - const cairo_point_t *point3) -{ - t1_path_info_t *path_info = (t1_path_info_t *) closure; - int dx1, dy1, dx2, dy2, dx3, dy3; - cairo_status_t status; - - status = _cairo_array_grow_by (path_info->data, 32); - if (unlikely (status)) - return status; - - dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; - dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y; - dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1; - dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1; - dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2; - dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2; - charstring_encode_integer (path_info->data, dx1, path_info->type); - charstring_encode_integer (path_info->data, dy1, path_info->type); - charstring_encode_integer (path_info->data, dx2, path_info->type); - charstring_encode_integer (path_info->data, dy2, path_info->type); - charstring_encode_integer (path_info->data, dx3, path_info->type); - charstring_encode_integer (path_info->data, dy3, path_info->type); - path_info->current_x += dx1 + dx2 + dx3; - path_info->current_y += dy1 + dy2 + dy3; - charstring_encode_command (path_info->data, CHARSTRING_rcurveto); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_charstring_close_path (void *closure) -{ - cairo_status_t status; - t1_path_info_t *path_info = (t1_path_info_t *) closure; - - if (path_info->type == CAIRO_CHARSTRING_TYPE2) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_array_grow_by (path_info->data, 2); - if (unlikely (status)) - return status; - - charstring_encode_command (path_info->data, CHARSTRING_closepath); - - return CAIRO_STATUS_SUCCESS; -} - -static void -charstring_encrypt (cairo_array_t *data) -{ - unsigned char *d, *end; - uint16_t c, p, r; - - r = CAIRO_TYPE1_CHARSTRING_KEY; - d = (unsigned char *) _cairo_array_index (data, 0); - end = d + _cairo_array_num_elements (data); - while (d < end) { - p = *d; - c = p ^ (r >> 8); - r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; - *d++ = c; - } -} - -static cairo_int_status_t -cairo_type1_font_create_charstring (cairo_type1_font_t *font, - int subset_index, - int glyph_index, - cairo_charstring_type_t type, - cairo_array_t *data) -{ - cairo_int_status_t status; - cairo_scaled_glyph_t *scaled_glyph; - t1_path_info_t path_info; - cairo_text_extents_t *metrics; - cairo_bool_t emit_path = TRUE; - - /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */ - status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS| - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - - /* It is ok for the .notdef glyph to not have a path available. We - * just need the metrics to emit an empty glyph. */ - if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) { - emit_path = FALSE; - status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - } - if (unlikely (status)) - return status; - - metrics = &scaled_glyph->metrics; - if (subset_index == 0) { - font->x_min = metrics->x_bearing; - font->y_min = metrics->y_bearing; - font->x_max = metrics->x_bearing + metrics->width; - font->y_max = metrics->y_bearing + metrics->height; - } else { - if (metrics->x_bearing < font->x_min) - font->x_min = metrics->x_bearing; - if (metrics->y_bearing < font->y_min) - font->y_min = metrics->y_bearing; - if (metrics->x_bearing + metrics->width > font->x_max) - font->x_max = metrics->x_bearing + metrics->width; - if (metrics->y_bearing + metrics->height > font->y_max) - font->y_max = metrics->y_bearing + metrics->height; - } - font->widths[subset_index] = metrics->x_advance; - - status = _cairo_array_grow_by (data, 30); - if (unlikely (status)) - return status; - - if (type == CAIRO_CHARSTRING_TYPE1) { - charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type); - charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type); - charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); - charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type); - charstring_encode_command (data, CHARSTRING_sbw); - - path_info.current_x = (int) scaled_glyph->metrics.x_bearing; - path_info.current_y = (int) scaled_glyph->metrics.y_bearing; - } else { - charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); - - path_info.current_x = 0; - path_info.current_y = 0; - } - path_info.data = data; - path_info.type = type; - if (emit_path) { - status = _cairo_path_fixed_interpret (scaled_glyph->path, - CAIRO_DIRECTION_FORWARD, - _charstring_move_to, - _charstring_line_to, - _charstring_curve_to, - _charstring_close_path, - &path_info); - if (unlikely (status)) - return status; - } - - status = _cairo_array_grow_by (data, 1); - if (unlikely (status)) - return status; - charstring_encode_command (path_info.data, CHARSTRING_endchar); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_type1_font_write_charstrings (cairo_type1_font_t *font, - cairo_output_stream_t *encrypted_output) -{ - cairo_status_t status; - unsigned char zeros[] = { 0, 0, 0, 0 }; - cairo_array_t data; - unsigned int i; - int length; - - _cairo_array_init (&data, sizeof (unsigned char)); - status = _cairo_array_grow_by (&data, 1024); - if (unlikely (status)) - goto fail; - - _cairo_output_stream_printf (encrypted_output, - "2 index /CharStrings %d dict dup begin\n", - font->scaled_font_subset->num_glyphs + 1); - - _cairo_scaled_font_freeze_cache (font->type1_scaled_font); - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - _cairo_array_truncate (&data, 0); - /* four "random" bytes required by encryption algorithm */ - status = _cairo_array_append_multiple (&data, zeros, 4); - if (unlikely (status)) - break; - - status = cairo_type1_font_create_charstring (font, i, - font->scaled_font_subset->glyphs[i], - CAIRO_CHARSTRING_TYPE1, - &data); - if (unlikely (status)) - break; - - charstring_encrypt (&data); - length = _cairo_array_num_elements (&data); - if (font->scaled_font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (encrypted_output, "/%s %d RD ", - font->scaled_font_subset->glyph_names[i], - length); - } else if (i == 0) { - _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length); - } else { - _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length); - } - _cairo_output_stream_write (encrypted_output, - _cairo_array_index (&data, 0), - length); - _cairo_output_stream_printf (encrypted_output, " ND\n"); - } - _cairo_scaled_font_thaw_cache (font->type1_scaled_font); - -fail: - _cairo_array_fini (&data); - return status; -} - -static void -cairo_type1_font_write_header (cairo_type1_font_t *font, - const char *name) -{ - unsigned int i; - const char spaces[50] = " "; - - _cairo_output_stream_printf (font->output, - "%%!FontType1-1.1 %s 1.0\n" - "11 dict begin\n" - "/FontName /%s def\n" - "/PaintType 0 def\n" - "/FontType 1 def\n" - "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n", - name, - name); - - /* We don't know the bbox values until after the charstrings have - * been generated. Reserve some space and fill in the bbox - * later. */ - - /* Worst case for four signed ints with spaces between each number */ - font->bbox_max_chars = 50; - - _cairo_output_stream_printf (font->output, "/FontBBox {"); - font->bbox_position = _cairo_output_stream_get_position (font->output); - _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars); - - _cairo_output_stream_printf (font->output, - "} readonly def\n" - "/Encoding 256 array\n" - "0 1 255 {1 index exch /.notdef put} for\n"); - for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { - if (font->scaled_font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (font->output, "dup %d /%s put\n", - i, font->scaled_font_subset->glyph_names[i]); - } else { - _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); - } - } - _cairo_output_stream_printf (font->output, - "readonly def\n" - "currentdict end\n" - "currentfile eexec\n"); -} - -static cairo_status_t -cairo_type1_write_stream_encrypted (void *closure, - const unsigned char *data, - unsigned int length) -{ - const unsigned char *in, *end; - uint16_t c, p; - static const char hex_digits[16] = "0123456789abcdef"; - char digits[3]; - cairo_type1_font_t *font = closure; - - in = (const unsigned char *) data; - end = (const unsigned char *) data + length; - while (in < end) { - p = *in++; - c = p ^ (font->eexec_key >> 8); - font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; - - if (font->hex_encode) { - digits[0] = hex_digits[c >> 4]; - digits[1] = hex_digits[c & 0x0f]; - digits[2] = '\n'; - font->hex_column += 2; - - if (font->hex_column == 78) { - _cairo_output_stream_write (font->output, digits, 3); - font->hex_column = 0; - } else { - _cairo_output_stream_write (font->output, digits, 2); - } - } else { - digits[0] = c; - _cairo_output_stream_write (font->output, digits, 1); - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_type1_font_write_private_dict (cairo_type1_font_t *font, - const char *name) -{ - cairo_int_status_t status; - cairo_status_t status2; - cairo_output_stream_t *encrypted_output; - - font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; - font->hex_column = 0; - encrypted_output = _cairo_output_stream_create ( - cairo_type1_write_stream_encrypted, - NULL, - font); - if (_cairo_output_stream_get_status (encrypted_output)) - return _cairo_output_stream_destroy (encrypted_output); - - /* Note: the first four spaces at the start of this private dict - * are the four "random" bytes of plaintext required by the - * encryption algorithm */ - _cairo_output_stream_printf (encrypted_output, - " dup /Private 9 dict dup begin\n" - "/RD {string currentfile exch readstring pop}" - " bind executeonly def\n" - "/ND {noaccess def} executeonly def\n" - "/NP {noaccess put} executeonly def\n" - "/BlueValues [] def\n" - "/MinFeature {16 16} def\n" - "/lenIV 4 def\n" - "/password 5839 def\n"); - - status = cairo_type1_font_write_charstrings (font, encrypted_output); - if (unlikely (status)) - goto fail; - - _cairo_output_stream_printf (encrypted_output, - "end\n" - "end\n" - "readonly put\n" - "noaccess put\n" - "dup /FontName get exch definefont pop\n" - "mark currentfile closefile\n"); - - fail: - status2 = _cairo_output_stream_destroy (encrypted_output); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - -static void -cairo_type1_font_write_trailer(cairo_type1_font_t *font) -{ - int i; - static const char zeros[65] = - "0000000000000000000000000000000000000000000000000000000000000000\n"; - - for (i = 0; i < 8; i++) - _cairo_output_stream_write (font->output, zeros, sizeof zeros); - - _cairo_output_stream_printf (font->output, "cleartomark\n"); -} - -static cairo_status_t -cairo_type1_write_stream (void *closure, - const unsigned char *data, - unsigned int length) -{ - cairo_type1_font_t *font = closure; - - return _cairo_array_append_multiple (&font->contents, data, length); -} - -static cairo_int_status_t -cairo_type1_font_write (cairo_type1_font_t *font, - const char *name) -{ - cairo_int_status_t status; - - cairo_type1_font_write_header (font, name); - font->header_size = _cairo_output_stream_get_position (font->output); - - status = cairo_type1_font_write_private_dict (font, name); - if (unlikely (status)) - return status; - - font->data_size = _cairo_output_stream_get_position (font->output) - - font->header_size; - - cairo_type1_font_write_trailer (font); - font->trailer_size = - _cairo_output_stream_get_position (font->output) - - font->header_size - font->data_size; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) -{ - cairo_int_status_t status; - - status = _cairo_array_grow_by (&font->contents, 4096); - if (unlikely (status)) - return status; - - font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); - if (_cairo_output_stream_get_status (font->output)) - return _cairo_output_stream_destroy (font->output); - - status = cairo_type1_font_write (font, name); - if (unlikely (status)) - return status; - - font->data = _cairo_array_index (&font->contents, 0); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_type1_font_destroy (cairo_type1_font_t *font) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - free (font->widths); - cairo_scaled_font_destroy (font->type1_scaled_font); - _cairo_array_fini (&font->contents); - if (font->output) - status = _cairo_output_stream_destroy (font->output); - free (font); - - return status; -} - -static cairo_status_t -_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, - const char *name, - cairo_scaled_font_subset_t *scaled_font_subset, - cairo_bool_t hex_encode) -{ - cairo_type1_font_t *font; - cairo_status_t status; - unsigned long length; - unsigned int i, len; - - status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); - if (unlikely (status)) - return status; - - status = cairo_type1_font_generate (font, name); - if (unlikely (status)) - goto fail1; - - type1_subset->base_font = strdup (name); - if (unlikely (type1_subset->base_font == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); - if (unlikely (type1_subset->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - type1_subset->widths[i] = (double)font->widths[i]/1000; - - type1_subset->x_min = (double)font->x_min/1000; - type1_subset->y_min = (double)font->y_min/1000; - type1_subset->x_max = (double)font->x_max/1000; - type1_subset->y_max = (double)font->y_max/1000; - type1_subset->ascent = (double)font->y_max/1000; - type1_subset->descent = (double)font->y_min/1000; - - length = font->header_size + font->data_size + - font->trailer_size; - type1_subset->data = malloc (length); - if (unlikely (type1_subset->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - memcpy (type1_subset->data, - _cairo_array_index (&font->contents, 0), length); - - len = snprintf(type1_subset->data + font->bbox_position, - font->bbox_max_chars, - "%d %d %d %d", - (int)type1_subset->x_min, - (int)type1_subset->y_min, - (int)type1_subset->x_max, - (int)type1_subset->y_max); - type1_subset->data[font->bbox_position + len] = ' '; - - type1_subset->header_length = font->header_size; - type1_subset->data_length = font->data_size; - type1_subset->trailer_length = font->trailer_size; - - return cairo_type1_font_destroy (font); - - fail3: - free (type1_subset->widths); - fail2: - free (type1_subset->base_font); - fail1: - /* status is already set, ignore further errors */ - cairo_type1_font_destroy (font); - - return status; -} - -cairo_status_t -_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset, - const char *name, - cairo_scaled_font_subset_t *scaled_font_subset) -{ - return _cairo_type1_fallback_init_internal (type1_subset, - name, - scaled_font_subset, FALSE); -} - -cairo_status_t -_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset, - const char *name, - cairo_scaled_font_subset_t *scaled_font_subset) -{ - return _cairo_type1_fallback_init_internal (type1_subset, - name, - scaled_font_subset, TRUE); -} - -void -_cairo_type1_fallback_fini (cairo_type1_subset_t *subset) -{ - free (subset->base_font); - free (subset->widths); - free (subset->data); -} - -cairo_status_t -_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, - cairo_scaled_font_subset_t *scaled_font_subset) -{ - cairo_type1_font_t *font; - cairo_status_t status; - unsigned int i; - cairo_array_t charstring; - - status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); - if (unlikely (status)) - return status; - - _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); - - type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); - if (unlikely (type2_subset->widths == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } - - _cairo_scaled_font_freeze_cache (font->type1_scaled_font); - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - _cairo_array_init (&charstring, sizeof (unsigned char)); - status = _cairo_array_grow_by (&charstring, 32); - if (unlikely (status)) - goto fail2; - - status = cairo_type1_font_create_charstring (font, i, - font->scaled_font_subset->glyphs[i], - CAIRO_CHARSTRING_TYPE2, - &charstring); - if (unlikely (status)) - goto fail2; - - status = _cairo_array_append (&type2_subset->charstrings, &charstring); - if (unlikely (status)) - goto fail2; - } - _cairo_scaled_font_thaw_cache (font->type1_scaled_font); - - for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - type2_subset->widths[i] = font->widths[i]; - - type2_subset->x_min = (int) font->x_min; - type2_subset->y_min = (int) font->y_min; - type2_subset->x_max = (int) font->x_max; - type2_subset->y_max = (int) font->y_max; - type2_subset->ascent = (int) font->y_max; - type2_subset->descent = (int) font->y_min; - - return cairo_type1_font_destroy (font); - -fail2: - _cairo_scaled_font_thaw_cache (font->type1_scaled_font); - _cairo_array_fini (&charstring); - _cairo_type2_charstrings_fini (type2_subset); -fail1: - cairo_type1_font_destroy (font); - return status; -} - -void -_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset) -{ - unsigned int i, num_charstrings; - cairo_array_t *charstring; - - num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings); - for (i = 0; i < num_charstrings; i++) { - charstring = _cairo_array_index (&type2_subset->charstrings, i); - _cairo_array_fini (charstring); - } - _cairo_array_fini (&type2_subset->charstrings); - - free (type2_subset->widths); -} - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-type1-private.h b/libs/cairo/cairo/src/cairo-type1-private.h deleted file mode 100644 index 0634b7061..000000000 --- a/libs/cairo/cairo/src/cairo-type1-private.h +++ /dev/null @@ -1,20 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-type1-subset.c b/libs/cairo/cairo/src/cairo-type1-subset.c deleted file mode 100644 index 7abcb10cb..000000000 --- a/libs/cairo/cairo/src/cairo-type1-subset.c +++ /dev/null @@ -1,1403 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Useful links: - * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF - */ - - -#define _BSD_SOURCE /* for snprintf(), strdup() */ -#include "cairoint.h" -#include "cairo-error-private.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-type1-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-output-stream-private.h" - -/* XXX: Eventually, we need to handle other font backends */ -#if CAIRO_HAS_FT_FONT - -#include "cairo-ft-private.h" - -#include -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_TYPE1_TABLES_H - -#include - -typedef struct _cairo_type1_font_subset { - cairo_scaled_font_subset_t *scaled_font_subset; - - struct { - cairo_unscaled_font_t *unscaled_font; - unsigned int font_id; - char *base_font; - unsigned int num_glyphs; - double x_min, y_min, x_max, y_max; - double ascent, descent; - - const char *data; - unsigned long header_size; - unsigned long data_size; - unsigned long trailer_size; - } base; - - FT_Face face; - int num_glyphs; - - struct { - int subset_index; - double width; - char *name; - } *glyphs; - - cairo_output_stream_t *output; - cairo_array_t contents; - - const char *rd, *nd; - - char *type1_data; - unsigned int type1_length; - char *type1_end; - - char *header_segment; - int header_segment_size; - char *eexec_segment; - int eexec_segment_size; - cairo_bool_t eexec_segment_is_ascii; - - char *cleartext; - char *cleartext_end; - - int header_size; - - unsigned short eexec_key; - cairo_bool_t hex_encode; - int hex_column; -} cairo_type1_font_subset_t; - - -static cairo_status_t -_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, - cairo_unscaled_font_t *unscaled_font, - cairo_bool_t hex_encode) -{ - cairo_ft_unscaled_font_t *ft_unscaled_font; - cairo_status_t status; - FT_Face face; - PS_FontInfoRec font_info; - int i, j; - - ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font; - - face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (unlikely (face == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (FT_Get_PS_Font_Info(face, &font_info) != 0) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto fail1; - } - - /* OpenType/CFF fonts also have a PS_FontInfoRec */ -#if HAVE_FT_LOAD_SFNT_TABLE - if (FT_IS_SFNT (face)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto fail1; - } -#endif - - memset (font, 0, sizeof (*font)); - font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font); - font->base.num_glyphs = face->num_glyphs; - font->base.x_min = face->bbox.xMin / (double)face->units_per_EM; - font->base.y_min = face->bbox.yMin / (double)face->units_per_EM; - font->base.x_max = face->bbox.xMax / (double)face->units_per_EM; - font->base.y_max = face->bbox.yMax / (double)face->units_per_EM; - font->base.ascent = face->ascender / (double)face->units_per_EM; - font->base.descent = face->descender / (double)face->units_per_EM; - - if (face->family_name) { - font->base.base_font = strdup (face->family_name); - if (unlikely (font->base.base_font == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - for (i = 0, j = 0; font->base.base_font[j]; j++) { - if (font->base.base_font[j] == ' ') - continue; - font->base.base_font[i++] = font->base.base_font[j]; - } - font->base.base_font[i] = '\0'; - } - - font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]); - if (unlikely (font->glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } - - font->hex_encode = hex_encode; - font->num_glyphs = 0; - for (i = 0; i < face->num_glyphs; i++) - font->glyphs[i].subset_index = -1; - - _cairo_array_init (&font->contents, sizeof (char)); - - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - - return CAIRO_STATUS_SUCCESS; - - fail3: - if (font->base.base_font) - free (font->base.base_font); - fail2: - _cairo_unscaled_font_destroy (unscaled_font); - fail1: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - - return status; -} - -static void -cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph) -{ - if (font->glyphs[glyph].subset_index >= 0) - return; - - font->glyphs[glyph].subset_index = font->num_glyphs++; -} - -static cairo_bool_t -is_ps_delimiter(int c) -{ - static const char delimiters[] = "()[]{}<>/% \t\r\n"; - - return strchr (delimiters, c) != NULL; -} - -static const char * -find_token (const char *buffer, const char *end, const char *token) -{ - int i, length; - /* FIXME: find substring really must be find_token */ - - if (buffer == NULL) - return NULL; - - length = strlen (token); - for (i = 0; buffer + i < end - length + 1; i++) - if (memcmp (buffer + i, token, length) == 0) - if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) && - (buffer + i == end - length || is_ps_delimiter(buffer[i + length]))) - return buffer + i; - - return NULL; -} - -static cairo_status_t -cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) -{ - unsigned char *p; - const char *eexec_token; - int size, i; - - p = (unsigned char *) font->type1_data; - font->type1_end = font->type1_data + font->type1_length; - if (p[0] == 0x80 && p[1] == 0x01) { - font->header_segment_size = - p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); - font->header_segment = (char *) p + 6; - - p += 6 + font->header_segment_size; - font->eexec_segment_size = - p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); - font->eexec_segment = (char *) p + 6; - font->eexec_segment_is_ascii = (p[1] == 1); - - p += 6 + font->eexec_segment_size; - while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) { - size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); - p += 6 + size; - } - font->type1_end = (char *) p; - } else { - eexec_token = find_token ((char *) p, font->type1_end, "eexec"); - if (eexec_token == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n"); - font->header_segment = (char *) p; - font->eexec_segment_size = font->type1_length - font->header_segment_size; - font->eexec_segment = (char *) p + font->header_segment_size; - font->eexec_segment_is_ascii = TRUE; - for (i = 0; i < 4; i++) { - if (!isxdigit(font->eexec_segment[i])) - font->eexec_segment_is_ascii = FALSE; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Search for the definition of key and erase it by overwriting with spaces. - * This function is looks for definitions of the form: - * - * /key1 1234 def - * /key2 [12 34 56] def - * - * ie a key defined as an integer or array of integers. - * - */ -static void -cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, - const char *key) -{ - const char *start, *p, *segment_end; - - segment_end = font->header_segment + font->header_segment_size; - - start = font->header_segment; - do { - start = find_token (start, segment_end, key); - if (start) { - p = start + strlen(key); - /* skip integers or array of integers */ - while (p < segment_end && - (_cairo_isspace(*p) || - _cairo_isdigit(*p) || - *p == '[' || - *p == ']')) - { - p++; - } - - if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) { - /* erase definition of the key */ - memset((char *) start, ' ', p + 3 - start); - } - start += strlen(key); - } - } while (start); -} - -static cairo_status_t -cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, - const char *name) -{ - const char *start, *end, *segment_end; - unsigned int i; - - /* FIXME: - * This function assumes that /FontName always appears - * before /Encoding. This appears to always be the case with Type1 - * fonts. - * - * The more recently added code for removing the UniqueID and XUID - * keys can not make any assumptions about the position of the - * keys in the dictionary so it is implemented by overwriting the - * key definition with spaces before we start copying the font to - * the output. - * - * This code should be rewritten to not make any assumptions about - * the order of dictionary keys. This will allow UniqueID to be - * stripped out instead of leaving a bunch of spaces in the - * output. - */ - cairo_type1_font_erase_dict_key (font, "/UniqueID"); - cairo_type1_font_erase_dict_key (font, "/XUID"); - - segment_end = font->header_segment + font->header_segment_size; - - /* Type 1 fonts created by Fontforge have some PostScript code at - * the start of the font that skips the font if the printer has a - * cached copy of the font with the same unique id. This breaks - * our subsetted font so we disable it by searching for the - * PostScript operator "known" when used to check for the - * "/UniqueID" dictionary key. We append " pop false " after it to - * pop the result of this check off the stack and replace it with - * "false" to make the PostScript code think "/UniqueID" does not - * exist. - */ - end = font->header_segment; - start = find_token (font->header_segment, segment_end, "/UniqueID"); - if (start) { - start += 9; - while (start < segment_end && _cairo_isspace (*start)) - start++; - if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { - _cairo_output_stream_write (font->output, font->header_segment, - start + 5 - font->header_segment); - _cairo_output_stream_printf (font->output, " pop false "); - end = start + 5; - } - } - - start = find_token (end, segment_end, "/FontName"); - if (start == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_output_stream_write (font->output, end, - start - end); - - _cairo_output_stream_printf (font->output, "/FontName /%s def", name); - - end = find_token (start, segment_end, "def"); - if (end == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - end += 3; - - start = find_token (end, segment_end, "/Encoding"); - if (start == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_output_stream_write (font->output, end, start - end); - - _cairo_output_stream_printf (font->output, - "/Encoding 256 array\n" - "0 1 255 {1 index exch /.notdef put} for\n"); - for (i = 1; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].subset_index < 0) - continue; - _cairo_output_stream_printf (font->output, - "dup %d /%s put\n", - font->glyphs[i].subset_index, - font->glyphs[i].name); - } - _cairo_output_stream_printf (font->output, "readonly def"); - - end = find_token (start, segment_end, "def"); - if (end == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - end += 3; - - _cairo_output_stream_write (font->output, end, segment_end - end); - - return font->output->status; -} - -static int -hex_to_int (int ch) -{ - if (ch <= '9') - return ch - '0'; - else if (ch <= 'F') - return ch - 'A' + 10; - else - return ch - 'a' + 10; -} - -static cairo_status_t -cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font, - const char *data, unsigned int length) -{ - const unsigned char *in, *end; - int c, p; - static const char hex_digits[16] = "0123456789abcdef"; - char digits[3]; - - in = (const unsigned char *) data; - end = (const unsigned char *) data + length; - while (in < end) { - p = *in++; - c = p ^ (font->eexec_key >> 8); - font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; - - if (font->hex_encode) { - digits[0] = hex_digits[c >> 4]; - digits[1] = hex_digits[c & 0x0f]; - digits[2] = '\n'; - font->hex_column += 2; - - if (font->hex_column == 78) { - _cairo_output_stream_write (font->output, digits, 3); - font->hex_column = 0; - } else { - _cairo_output_stream_write (font->output, digits, 2); - } - } else { - digits[0] = c; - _cairo_output_stream_write (font->output, digits, 1); - } - } - - return font->output->status; -} - -static cairo_status_t -cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) -{ - unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY; - unsigned char *in, *end; - char *out; - int c, p; - int i; - - in = (unsigned char *) font->eexec_segment; - end = (unsigned char *) in + font->eexec_segment_size; - - font->cleartext = malloc (font->eexec_segment_size); - if (unlikely (font->cleartext == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - out = font->cleartext; - while (in < end) { - if (font->eexec_segment_is_ascii) { - c = *in++; - if (_cairo_isspace (c)) - continue; - c = (hex_to_int (c) << 4) | hex_to_int (*in++); - } else { - c = *in++; - } - p = c ^ (r >> 8); - r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; - - *out++ = p; - } - font->cleartext_end = out; - - /* Overwrite random bytes with spaces. - * - * The first 4 bytes of the cleartext are the random bytes - * required by the encryption algorithm. When encrypting the - * cleartext, the first ciphertext byte must not be a white space - * character and the first 4 bytes must not be an ASCII Hex - * character. Some fonts do not check that their randomly chosen - * bytes results in ciphertext that complies with this - * restriction. This may cause problems for some PDF consumers. By - * replacing the random bytes with spaces, the first four bytes of - * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies - * with this restriction. Using spaces also means we don't have to - * skip over the random bytes when parsing the cleartext. - */ - for (i = 0; i < 4 && i < font->eexec_segment_size; i++) - font->cleartext[i] = ' '; - - return CAIRO_STATUS_SUCCESS; -} - -static const char * -skip_token (const char *p, const char *end) -{ - while (p < end && _cairo_isspace(*p)) - p++; - - while (p < end && !_cairo_isspace(*p)) - p++; - - if (p == end) - return NULL; - - return p; -} - -static int -cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font, - const char *glyph_name, int length) -{ - unsigned int i; - - for (i = 0; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].name && - strncmp (font->glyphs[i].name, glyph_name, length) == 0 && - font->glyphs[i].name[length] == '\0') - return i; - } - - return -1; -} - -static cairo_status_t -cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font) -{ - unsigned int i; - char buffer[256]; - FT_Error error; - - /* Get glyph names and width using the freetype API */ - for (i = 0; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].name != NULL) - continue; - - error = FT_Load_Glyph (font->face, i, - FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | - FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); - if (error != FT_Err_Ok) { - /* propagate fatal errors from FreeType */ - if (error == FT_Err_Out_Of_Memory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM; - - error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer); - if (error != FT_Err_Ok) { - /* propagate fatal errors from FreeType */ - if (error == FT_Err_Out_Of_Memory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - font->glyphs[i].name = strdup (buffer); - if (unlikely (font->glyphs[i].name == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) -{ - unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY; - int c, p, i; - - for (i = 0; i < size; i++) { - c = *in++; - p = c ^ (r >> 8); - r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; - *out++ = p; - } -} - -static const unsigned char * -cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) -{ - if (*p <= 246) { - *integer = *p++ - 139; - } else if (*p <= 250) { - *integer = (p[0] - 247) * 256 + p[1] + 108; - p += 2; - } else if (*p <= 254) { - *integer = -(p[0] - 251) * 256 - p[1] - 108; - p += 2; - } else { - *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; - p += 5; - } - - return p; -} - -#if 0 -/* - * The two tables that follow are generated using this perl code: - */ - -@encoding = ( - /* 0 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 16 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 32 */ - "space", "exclam", "quotedbl", "numbersign", - "dollar", "percent", "ampersand", "quoteright", - "parenleft", "parenright", "asterisk", "plus", - "comma", "hyphen", "period", "slash", - /* 48 */ - "zero", "one", "two", "three", - "four", "five", "six", "seven", - "eight", "nine", "colon", "semicolon", - "less", "equal", "greater", "question", - /* 64 */ - "at", "A", "B", "C", - "D", "E", "F", "G", - "H", "I", "J", "K", - "L", "M", "N", "O", - /* 80 */ - "P", "Q", "R", "S", - "T", "U", "V", "W", - "X", "Y", "Z", "bracketleft", - "backslash", "bracketright", "asciicircum", "underscore", - /* 96 */ - "quoteleft", "a", "b", "c", - "d", "e", "f", "g", - "h", "i", "j", "k", - "l", "m", "n", "o", - /* 112 */ - "p", "q", "r", "s", - "t", "u", "v", "w", - "x", "y", "z", "braceleft", - "bar", "braceright", "asciitilde", NULL, - /* 128 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 144 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 160 */ - NULL, "exclamdown", "cent", "sterling", - "fraction", "yen", "florin", "section", - "currency", "quotesingle", "quotedblleft", "guillemotleft", - "guilsinglleft","guilsinglright","fi", "fl", - /* 176 */ - NULL, "endash", "dagger", "daggerdbl", - "periodcentered",NULL, "paragraph", "bullet", - "quotesinglbase","quotedblbase","quotedblright","guillemotright", - "ellipsis", "perthousand", NULL, "questiondown", - /* 192 */ - NULL, "grave", "acute", "circumflex", - "tilde", "macron", "breve", "dotaccent", - "dieresis", NULL, "ring", "cedilla", - NULL, "hungarumlaut", "ogonek", "caron", - /* 208 */ - "emdash", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 224 */ - NULL, "AE", NULL, "ordfeminine", - NULL, NULL, NULL, NULL, - "Lslash", "Oslash", "OE", "ordmasculine", - NULL, NULL, NULL, NULL, - /* 240 */ - NULL, "ae", NULL, NULL, - NULL, "dotlessi", NULL, NULL, - "lslash", "oslash", "oe", "germandbls", - NULL, NULL, NULL, NULL - ); - -print "static const char ps_standard_encoding_symbol[] = {\n"; -$s = qq( "\\0"); -for $sym (@encoding) { - if (! ($sym eq NULL)) { - $ss = qq( "$sym\\0"); - if (length($s) + length($ss) > 78) { - print qq( $s\n); - $s = ""; - } - $s .= $ss; - } -} -print qq( $s\n); -print "};\n\n"; -print "static const int16_t ps_standard_encoding_offset[256] = {\n"; -$offset = 1; -$s = qq(); -for $sym (@encoding) { - if (! ($sym eq NULL)) { - $ss = qq( $offset/*$sym*/,); - $offset += length($sym) + 1; - } else { - $ss = qq( 0,); - } - if (length($s) + length($ss) > 78) { - print qq( $s\n); - $s = ""; - } - $s .= $ss; -} -print qq( $s\n); -print "};\n"; -exit; -#endif - -static const char ps_standard_encoding_symbol[] = { - "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0" - "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0" - "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0" - "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0" - "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0" - "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0" - "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0" - "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0" - "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0" - "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0" - "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0" - "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0" - "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0" - "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0" - "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0" - "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0" - "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0" - "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0" - "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0" - "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0" - "oslash\0" "oe\0" "germandbls\0" -}; - -static const int16_t ps_standard_encoding_offset[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, - 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/, - 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, - 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, - 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, - 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, - 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, - 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, - 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, - 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, - 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, - 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/, - 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, - 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, - 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, - 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, - 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/, - 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/, - 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/, - 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/, - 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/, - 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/, - 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0, - 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/, - 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/, - 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/, - 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/, - 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0, - 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/, - 900/*germandbls*/, 0, 0, 0, 0, -}; - -#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL) - -static cairo_status_t -use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index) -{ - const char *glyph_name; - - if (index < 0 || index > 255) - return CAIRO_STATUS_SUCCESS; - - glyph_name = ps_standard_encoding(index); - if (glyph_name == NULL) - return CAIRO_STATUS_SUCCESS; - - index = cairo_type1_font_subset_lookup_glyph (font, - glyph_name, - strlen(glyph_name)); - if (index < 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_type1_font_subset_use_glyph (font, index); - - return CAIRO_STATUS_SUCCESS; -} - -#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12) -#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6) - -static cairo_status_t -cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, - const char *name, int name_length, - const char *encrypted_charstring, int encrypted_charstring_length) -{ - cairo_status_t status; - unsigned char *charstring; - const unsigned char *end; - const unsigned char *p; - int stack[5], sp, value; - int command; - - charstring = malloc (encrypted_charstring_length); - if (unlikely (charstring == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) - encrypted_charstring, - encrypted_charstring_length, - charstring); - end = charstring + encrypted_charstring_length; - - p = charstring + 4; - sp = 0; - - while (p < end) { - if (*p < 32) { - command = *p++; - - if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE) - command = 32 + *p++; - - switch (command) { - case TYPE1_CHARSTRING_COMMAND_SEAC: - /* The seac command takes five integer arguments. The - * last two are glyph indices into the PS standard - * encoding give the names of the glyphs that this - * glyph is composed from. All we need to do is to - * make sure those glyphs are present in the subset - * under their standard names. */ - if (unlikely (sp < 5)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = use_standard_encoding_glyph (font, stack[3]); - if (unlikely (status)) - return status; - - status = use_standard_encoding_glyph (font, stack[4]); - if (unlikely (status)) - return status; - - sp = 0; - break; - - default: - sp = 0; - break; - } - } else { - /* integer argument */ - p = cairo_type1_font_subset_decode_integer (p, &value); - if (sp < 5) - stack[sp++] = value; - } - } - - free (charstring); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -write_used_glyphs (cairo_type1_font_subset_t *font, - const char *name, int name_length, - const char *charstring, int charstring_length) -{ - cairo_status_t status; - char buffer[256]; - int length; - - length = snprintf (buffer, sizeof buffer, - "/%.*s %d %s ", - name_length, name, charstring_length, font->rd); - status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (unlikely (status)) - return status; - - status = cairo_type1_font_subset_write_encrypted (font, - charstring, - charstring_length); - if (unlikely (status)) - return status; - - length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); - status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font, - const char *name, int name_length, - const char *charstring, int charstring_length); - -static cairo_status_t -cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, - const char *dict_start, - const char *dict_end, - glyph_func_t func, - const char **dict_out) -{ - int charstring_length, name_length, glyph_index; - const char *p, *charstring, *name; - char *end; - - /* We're looking at '/' in the name of the first glyph. The glyph - * definitions are on the form: - * - * /name 23 RD <23 binary bytes> ND - * - * or alternatively using -| and |- instead of RD and ND. - * - * We parse the glyph name and see if it is in the subset. If it - * is, we call the specified callback with the glyph name and - * glyph data, otherwise we just skip it. We need to parse - * through a glyph definition; we can't just find the next '/', - * since the binary data could contain a '/'. - */ - - p = dict_start; - - while (*p == '/') { - name = p + 1; - p = skip_token (p, dict_end); - name_length = p - name; - - charstring_length = strtol (p, &end, 10); - if (p == end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Skip past -| or RD to binary data. There is exactly one space - * between the -| or RD token and the encrypted data, thus '+ 1'. */ - charstring = skip_token (end, dict_end) + 1; - - /* Skip binary data and |- or ND token. */ - p = skip_token (charstring + charstring_length, dict_end); - while (p < dict_end && _cairo_isspace(*p)) - p++; - - /* In case any of the skip_token() calls above reached EOF, p will - * be equal to dict_end. */ - if (p == dict_end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glyph_index = cairo_type1_font_subset_lookup_glyph (font, - name, name_length); - if (font->glyphs[glyph_index].subset_index >= 0) { - cairo_status_t status = func (font, - name, name_length, - charstring, charstring_length); - if (unlikely (status)) - return status; - } - } - - *dict_out = p; - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, - const char *name) -{ - cairo_status_t status; - const char *p, *charstrings, *dict_start; - const char *closefile_token; - char buffer[32], *glyph_count_end; - int num_charstrings, length; - - /* The private dict holds hint information, common subroutines and - * the actual glyph definitions (charstrings). - * - * FIXME: update this comment. - * - * What we do here is scan directly the /CharString token, which - * marks the beginning of the glyph definitions. Then we parse - * through the glyph definitions and weed out the glyphs not in - * our subset. Everything else before and after the glyph - * definitions is copied verbatim to the output. It might be - * worthwile to figure out which of the common subroutines are - * used by the glyphs in the subset and get rid of the rest. */ - - /* FIXME: The /Subrs array contains binary data and could - * conceivably have "/CharStrings" in it, so we might need to skip - * this more cleverly. */ - charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings"); - if (charstrings == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Scan past /CharStrings and the integer following it. */ - p = charstrings + strlen ("/CharStrings"); - num_charstrings = strtol (p, &glyph_count_end, 10); - if (p == glyph_count_end) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Look for a '/' which marks the beginning of the first glyph - * definition. */ - for (p = glyph_count_end; p < font->cleartext_end; p++) - if (*p == '/') - break; - if (p == font->cleartext_end) - return CAIRO_INT_STATUS_UNSUPPORTED; - dict_start = p; - - status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (unlikely (status)) - return status; - - /* Now that we have the private dictionary broken down in - * sections, do the first pass through the glyph definitions to - * figure out which subrs and othersubrs are use and which extra - * glyphs may be required by the seac operator. */ - status = cairo_type1_font_subset_for_each_glyph (font, - dict_start, - font->cleartext_end, - cairo_type1_font_subset_look_for_seac, - &p); - if (unlikely (status)) - return status; - - closefile_token = find_token (p, font->cleartext_end, "closefile"); - if (closefile_token == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (unlikely (status)) - return status; - - /* We're ready to start outputting. First write the header, - * i.e. the public part of the font dict.*/ - status = cairo_type1_font_subset_write_header (font, name); - if (unlikely (status)) - return status; - - font->base.header_size = _cairo_output_stream_get_position (font->output); - - - /* Start outputting the private dict. First output everything up - * to the /CharStrings token. */ - status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, - charstrings - font->cleartext); - if (unlikely (status)) - return status; - - /* Write out new charstring count */ - length = snprintf (buffer, sizeof buffer, - "/CharStrings %d", font->num_glyphs); - status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (unlikely (status)) - return status; - - /* Write out text between the charstring count and the first - * charstring definition */ - status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, - dict_start - glyph_count_end); - if (unlikely (status)) - return status; - - /* Write out the charstring definitions for each of the glyphs in - * the subset. */ - status = cairo_type1_font_subset_for_each_glyph (font, - dict_start, - font->cleartext_end, - write_used_glyphs, - &p); - if (unlikely (status)) - return status; - - /* Output what's left between the end of the glyph definitions and - * the end of the private dict to the output. */ - status = cairo_type1_font_subset_write_encrypted (font, p, - closefile_token - p + strlen ("closefile") + 1); - if (unlikely (status)) - return status; - - if (font->hex_encode) - _cairo_output_stream_write (font->output, "\n", 1); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) -{ - const char *cleartomark_token; - int i; - static const char zeros[65] = - "0000000000000000000000000000000000000000000000000000000000000000\n"; - - - for (i = 0; i < 8; i++) - _cairo_output_stream_write (font->output, zeros, sizeof zeros); - - cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); - if (cleartomark_token) { - /* Some fonts have conditional save/restore around the entire - * font dict, so we need to retain whatever postscript code - * that may come after 'cleartomark'. */ - - _cairo_output_stream_write (font->output, cleartomark_token, - font->type1_end - cleartomark_token); - } else if (!font->eexec_segment_is_ascii) { - /* Fonts embedded in PDF may omit the fixed-content portion - * that includes the 'cleartomark' operator. Type 1 in PDF is - * always binary. */ - - _cairo_output_stream_printf (font->output, "cleartomark"); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* some fonts do not have a newline at the end of the last line */ - _cairo_output_stream_printf (font->output, "\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -type1_font_write (void *closure, const unsigned char *data, unsigned int length) -{ - cairo_type1_font_subset_t *font = closure; - - return _cairo_array_append_multiple (&font->contents, data, length); -} - -static cairo_status_t -cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, - const char *name) -{ - cairo_status_t status; - - status = cairo_type1_font_subset_find_segments (font); - if (unlikely (status)) - return status; - - status = cairo_type1_font_subset_decrypt_eexec_segment (font); - if (unlikely (status)) - return status; - - /* Determine which glyph definition delimiters to use. */ - if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) { - font->rd = "-|"; - font->nd = "|-"; - } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) { - font->rd = "RD"; - font->nd = "ND"; - } else { - /* Don't know *what* kind of font this is... */ - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; - font->hex_column = 0; - - status = cairo_type1_font_subset_write_private_dict (font, name); - if (unlikely (status)) - return status; - - font->base.data_size = _cairo_output_stream_get_position (font->output) - - font->base.header_size; - - status = cairo_type1_font_subset_write_trailer (font); - if (unlikely (status)) - return status; - - font->base.trailer_size = - _cairo_output_stream_get_position (font->output) - - font->base.header_size - font->base.data_size; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -cairo_type1_font_subset_generate (void *abstract_font, - const char *name) - -{ - cairo_type1_font_subset_t *font = abstract_font; - cairo_ft_unscaled_font_t *ft_unscaled_font; - unsigned long ret; - cairo_status_t status; - - ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font; - font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (unlikely (font->face == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->type1_length = font->face->stream->size; - font->type1_data = malloc (font->type1_length); - if (unlikely (font->type1_data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - - if (font->face->stream->read != NULL) { - /* Note that read() may be implemented as a macro, thanks POSIX!, so we - * need to wrap the following usage in parentheses in order to - * disambiguate it for the pre-processor - using the verbose function - * pointer dereference for clarity. - */ - ret = (* font->face->stream->read) (font->face->stream, 0, - (unsigned char *) font->type1_data, - font->type1_length); - if (ret != font->type1_length) { - status = _cairo_error (CAIRO_STATUS_READ_ERROR); - goto fail; - } - } else { - memcpy (font->type1_data, - font->face->stream->base, font->type1_length); - } - - status = _cairo_array_grow_by (&font->contents, 4096); - if (unlikely (status)) - goto fail; - - font->output = _cairo_output_stream_create (type1_font_write, NULL, font); - if (unlikely ((status = font->output->status))) - goto fail; - - status = cairo_type1_font_subset_write (font, name); - if (unlikely (status)) - goto fail; - - font->base.data = _cairo_array_index (&font->contents, 0); - - fail: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - - return status; -} - -static cairo_status_t -_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - unsigned int i; - - /* If the subset generation failed, some of the pointers below may - * be NULL depending on at which point the error occurred. */ - - _cairo_array_fini (&font->contents); - - free (font->type1_data); - if (font->glyphs != NULL) { - for (i = 0; i < font->base.num_glyphs; i++) - free (font->glyphs[i].name); - } - - _cairo_unscaled_font_destroy (font->base.unscaled_font); - - if (font->output != NULL) - status = _cairo_output_stream_destroy (font->output); - - if (font->base.base_font) - free (font->base.base_font); - free (font->glyphs); - - return status; -} - -cairo_status_t -_cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, - const char *name, - cairo_scaled_font_subset_t *scaled_font_subset, - cairo_bool_t hex_encode) -{ - cairo_type1_font_subset_t font; - cairo_status_t status, status_ignored; - unsigned long parent_glyph, length; - unsigned int i; - cairo_unscaled_font_t *unscaled_font; - char buf[30]; - - /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */ - if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font); - - status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode); - if (unlikely (status)) - return status; - - for (i = 0; i < scaled_font_subset->num_glyphs; i++) { - parent_glyph = scaled_font_subset->glyphs[i]; - cairo_type1_font_subset_use_glyph (&font, parent_glyph); - } - - status = cairo_type1_font_subset_generate (&font, name); - if (unlikely (status)) - goto fail1; - - if (font.base.base_font) { - type1_subset->base_font = strdup (font.base.base_font); - } else { - snprintf(buf, sizeof (buf), "CairoFont-%u-%u", - scaled_font_subset->font_id, scaled_font_subset->subset_id); - type1_subset->base_font = strdup (buf); - } - if (unlikely (type1_subset->base_font == NULL)) - goto fail1; - - type1_subset->widths = calloc (sizeof (double), font.num_glyphs); - if (unlikely (type1_subset->widths == NULL)) - goto fail2; - for (i = 0; i < font.base.num_glyphs; i++) { - if (font.glyphs[i].subset_index < 0) - continue; - type1_subset->widths[font.glyphs[i].subset_index] = - font.glyphs[i].width; - } - - type1_subset->x_min = font.base.x_min/1000.0; - type1_subset->y_min = font.base.y_min/1000.0; - type1_subset->x_max = font.base.x_max/1000.0; - type1_subset->y_max = font.base.y_max/1000.0; - type1_subset->ascent = font.base.ascent/1000.0; - type1_subset->descent = font.base.descent/1000.0; - - length = font.base.header_size + - font.base.data_size + - font.base.trailer_size; - type1_subset->data = malloc (length); - if (unlikely (type1_subset->data == NULL)) - goto fail3; - - memcpy (type1_subset->data, - _cairo_array_index (&font.contents, 0), length); - - type1_subset->header_length = font.base.header_size; - type1_subset->data_length = font.base.data_size; - type1_subset->trailer_length = font.base.trailer_size; - - return _cairo_type1_font_subset_fini (&font); - - fail3: - free (type1_subset->widths); - fail2: - free (type1_subset->base_font); - fail1: - status_ignored = _cairo_type1_font_subset_fini (&font); - - return status; -} - -void -_cairo_type1_subset_fini (cairo_type1_subset_t *subset) -{ - free (subset->base_font); - free (subset->widths); - free (subset->data); -} - -cairo_bool_t -_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) -{ - cairo_ft_unscaled_font_t *unscaled; - FT_Face face; - PS_FontInfoRec font_info; - cairo_bool_t is_type1 = FALSE; - - if (!_cairo_scaled_font_is_ft (scaled_font)) - return FALSE; - unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font); - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return FALSE; - - if (FT_Get_PS_Font_Info(face, &font_info) == 0) - is_type1 = TRUE; - - /* OpenType/CFF fonts also have a PS_FontInfoRec */ -#if HAVE_FT_LOAD_SFNT_TABLE - if (FT_IS_SFNT (face)) - is_type1 = FALSE; -#endif - - _cairo_ft_unscaled_font_unlock_face (unscaled); - - return is_type1; -} - -#endif /* CAIRO_HAS_FT_FONT */ - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-type3-glyph-surface-private.h b/libs/cairo/cairo/src/cairo-type3-glyph-surface-private.h deleted file mode 100644 index b101431fc..000000000 --- a/libs/cairo/cairo/src/cairo-type3-glyph-surface-private.h +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-type3-glyph-surface.c b/libs/cairo/cairo/src/cairo-type3-glyph-surface.c deleted file mode 100644 index e0ad08032..000000000 --- a/libs/cairo/cairo/src/cairo-type3-glyph-surface.c +++ /dev/null @@ -1,531 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#if CAIRO_HAS_FONT_SUBSET - -#include "cairo-type3-glyph-surface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" -#include "cairo-surface-clipper-private.h" - -static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; - -static cairo_status_t -_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper, - cairo_type3_glyph_surface_t, - clipper); - - if (path == NULL) { - _cairo_output_stream_printf (surface->stream, "Q q\n"); - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); -} - -cairo_surface_t * -_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, - cairo_output_stream_t *stream, - cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets) -{ - cairo_type3_glyph_surface_t *surface; - cairo_matrix_t invert_y_axis; - - if (unlikely (stream != NULL && stream->status)) - return _cairo_surface_create_in_error (stream->status); - - surface = malloc (sizeof (cairo_type3_glyph_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &cairo_type3_glyph_surface_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); - - surface->scaled_font = scaled_font; - surface->stream = stream; - surface->emit_image = emit_image; - - /* Setup the transform from the user-font device space to Type 3 - * font space. The Type 3 font space is defined by the FontMatrix - * entry in the Type 3 dictionary. In the PDF backend this is an - * identity matrix. */ - surface->cairo_to_pdf = scaled_font->scale_inverse; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis); - - _cairo_pdf_operators_init (&surface->pdf_operators, - surface->stream, - &surface->cairo_to_pdf, - font_subsets); - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_type3_glyph_surface_clipper_intersect_clip_path); - - return &surface->base; -} - -static cairo_status_t -_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, - cairo_image_surface_t *image, - cairo_matrix_t *image_matrix) -{ - cairo_status_t status; - - /* The only image type supported by Type 3 fonts are 1-bit masks */ - image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1); - status = image->base.status; - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->stream, - "q %f %f %f %f %f %f cm\n", - image_matrix->xx, - image_matrix->xy, - image_matrix->yx, - image_matrix->yy, - image_matrix->x0, - image_matrix->y0); - - status = surface->emit_image (image, surface->stream); - cairo_surface_destroy (&image->base); - - _cairo_output_stream_printf (surface->stream, - "Q\n"); - - return status; -} - -static cairo_status_t -_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface, - cairo_image_surface_t *image, - const cairo_matrix_t *pattern_matrix) -{ - cairo_matrix_t mat, upside_down; - cairo_status_t status; - - if (image->width == 0 || image->height == 0) - return CAIRO_STATUS_SUCCESS; - - mat = *pattern_matrix; - - /* Get the pattern space to user space matrix */ - status = cairo_matrix_invert (&mat); - - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - /* Make this a pattern space to Type 3 font space matrix */ - cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf); - - /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by - * 1 image upside down to convert to flip the Y-axis going from - * cairo to PDF. Then scale the image up to the required size. */ - cairo_matrix_scale (&mat, image->width, image->height); - cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1); - cairo_matrix_multiply (&mat, &upside_down, &mat); - - return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); -} - -static cairo_status_t -_cairo_type3_glyph_surface_finish (void *abstract_surface) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - - return _cairo_pdf_operators_fini (&surface->pdf_operators); -} - -static cairo_int_status_t -_cairo_type3_glyph_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - const cairo_surface_pattern_t *pattern; - cairo_image_surface_t *image; - void *image_extra; - cairo_status_t status; - - if (source->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_IMAGE_FALLBACK; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - pattern = (const cairo_surface_pattern_t *) source; - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, &image_extra); - if (unlikely (status)) - goto fail; - - status = _cairo_type3_glyph_surface_emit_image_pattern (surface, - image, - &pattern->base.matrix); - -fail: - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_int_status_t -_cairo_type3_glyph_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - return _cairo_type3_glyph_surface_paint (abstract_surface, - op, mask, - clip); -} - -static cairo_int_status_t -_cairo_type3_glyph_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - return _cairo_pdf_operators_stroke (&surface->pdf_operators, - path, - style, - ctm, - ctm_inverse); -} - -static cairo_int_status_t -_cairo_type3_glyph_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - return _cairo_pdf_operators_fill (&surface->pdf_operators, - path, - fill_rule); -} - -static cairo_int_status_t -_cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_int_status_t status; - cairo_scaled_font_t *font; - cairo_matrix_t new_ctm, invert_y_axis; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm); - cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm); - font = cairo_scaled_font_create (scaled_font->font_face, - &scaled_font->font_matrix, - &new_ctm, - &scaled_font->options); - if (unlikely (font->status)) - return font->status; - - status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE, - font); - - cairo_scaled_font_destroy (font); - - return status; -} - -static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, - NULL, /* _cairo_type3_glyph_surface_create_similar */ - _cairo_type3_glyph_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* cairo_type3_glyph_surface_copy_page */ - NULL, /* _cairo_type3_glyph_surface_show_page */ - NULL, /* _cairo_type3_glyph_surface_get_extents */ - NULL, /* old_show_glyphs */ - NULL, /* _cairo_type3_glyph_surface_get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _cairo_type3_glyph_surface_paint, - _cairo_type3_glyph_surface_mask, - _cairo_type3_glyph_surface_stroke, - _cairo_type3_glyph_surface_fill, - _cairo_type3_glyph_surface_show_glyphs, - NULL, /* snapshot */ -}; - -static void -_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface, - cairo_output_stream_t *stream) -{ - surface->stream = stream; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream); -} - -static cairo_status_t -_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface, - unsigned long glyph_index) -{ - cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status; - cairo_image_surface_t *image; - cairo_matrix_t mat; - double x, y; - - status = _cairo_scaled_glyph_lookup (surface->scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (unlikely (status)) - return status; - - image = scaled_glyph->surface; - if (image->width == 0 || image->height == 0) - return CAIRO_STATUS_SUCCESS; - - x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); - y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); - mat.xx = image->width; - mat.xy = 0; - mat.yx = 0; - mat.yy = image->height; - mat.x0 = x; - mat.y0 = y; - cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); - mat.y0 *= -1; - - return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); -} - -void -_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, - cairo_pdf_operators_use_font_subset_t use_font_subset, - void *closure) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - - if (unlikely (surface->base.status)) - return; - - _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, - use_font_subset, - closure); -} - -cairo_status_t -_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, - unsigned long glyph_index) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status, status2; - cairo_output_stream_t *null_stream; - - if (unlikely (surface->base.status)) - return surface->base.status; - - null_stream = _cairo_null_stream_create (); - if (unlikely (null_stream->status)) - return null_stream->status; - - _cairo_type3_glyph_surface_set_stream (surface, null_stream); - - _cairo_scaled_font_freeze_cache (surface->scaled_font); - status = _cairo_scaled_glyph_lookup (surface->scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, - &scaled_glyph); - - if (_cairo_status_is_error (status)) - goto cleanup; - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = CAIRO_STATUS_SUCCESS; - goto cleanup; - } - - status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, - &surface->base); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) - status = CAIRO_STATUS_SUCCESS; - -cleanup: - _cairo_scaled_font_thaw_cache (surface->scaled_font); - - status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - return status; -} - -cairo_status_t -_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, - cairo_output_stream_t *stream, - unsigned long glyph_index, - cairo_box_t *bbox, - double *width) -{ - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status, status2; - double x_advance, y_advance; - cairo_matrix_t font_matrix_inverse; - - if (unlikely (surface->base.status)) - return surface->base.status; - - _cairo_type3_glyph_surface_set_stream (surface, stream); - - _cairo_scaled_font_freeze_cache (surface->scaled_font); - status = _cairo_scaled_glyph_lookup (surface->scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, - &scaled_glyph); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_scaled_glyph_lookup (surface->scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (status == CAIRO_STATUS_SUCCESS) - status = CAIRO_INT_STATUS_IMAGE_FALLBACK; - } - if (_cairo_status_is_error (status)) { - _cairo_scaled_font_thaw_cache (surface->scaled_font); - return status; - } - - x_advance = scaled_glyph->metrics.x_advance; - y_advance = scaled_glyph->metrics.y_advance; - font_matrix_inverse = surface->scaled_font->font_matrix; - status2 = cairo_matrix_invert (&font_matrix_inverse); - - /* The invertability of font_matrix is tested in - * pdf_operators_show_glyphs before any glyphs are mapped to the - * subset. */ - assert (status2 == CAIRO_STATUS_SUCCESS); - - cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); - *width = x_advance; - - *bbox = scaled_glyph->bbox; - _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse, - bbox, NULL); - - _cairo_output_stream_printf (surface->stream, - "%f 0 %f %f %f %f d1\n", - x_advance, - _cairo_fixed_to_double (bbox->p1.x), - - _cairo_fixed_to_double (bbox->p2.y), - _cairo_fixed_to_double (bbox->p2.x), - - _cairo_fixed_to_double (bbox->p1.y)); - - if (status == CAIRO_STATUS_SUCCESS) { - cairo_output_stream_t *mem_stream; - - mem_stream = _cairo_memory_stream_create (); - status = mem_stream->status; - if (unlikely (status)) - goto FAIL; - - _cairo_type3_glyph_surface_set_stream (surface, mem_stream); - - _cairo_output_stream_printf (surface->stream, "q\n"); - status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, - &surface->base); - - status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_output_stream_printf (surface->stream, "Q\n"); - - _cairo_type3_glyph_surface_set_stream (surface, stream); - if (status == CAIRO_STATUS_SUCCESS) - _cairo_memory_stream_copy (mem_stream, stream); - - status2 = _cairo_output_stream_destroy (mem_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) - status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); - - FAIL: - _cairo_scaled_font_thaw_cache (surface->scaled_font); - - return status; -} - -#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/cairo/src/cairo-types-private.h b/libs/cairo/cairo/src/cairo-types-private.h deleted file mode 100644 index 00b9269c7..000000000 --- a/libs/cairo/cairo/src/cairo-types-private.h +++ /dev/null @@ -1,450 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_TYPES_PRIVATE_H -#define CAIRO_TYPES_PRIVATE_H - -#include "cairo.h" -#include "cairo-fixed-type-private.h" -#include "cairo-list-private.h" -#include "cairo-reference-count-private.h" - -/** - * SECTION:cairo-types - * @Title: Types - * @Short_Description: Generic data types - * - * This section lists generic data types used in the cairo API. - */ - -typedef struct _cairo_array cairo_array_t; -typedef struct _cairo_backend cairo_backend_t; -typedef struct _cairo_boxes_t cairo_boxes_t; -typedef struct _cairo_cache cairo_cache_t; -typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t; -typedef struct _cairo_clip cairo_clip_t; -typedef struct _cairo_clip_path cairo_clip_path_t; -typedef struct _cairo_color cairo_color_t; -typedef struct _cairo_color_stop cairo_color_stop_t; -typedef struct _cairo_device_backend cairo_device_backend_t; -typedef struct _cairo_font_face_backend cairo_font_face_backend_t; -typedef struct _cairo_gstate cairo_gstate_t; -typedef struct _cairo_hash_entry cairo_hash_entry_t; -typedef struct _cairo_hash_table cairo_hash_table_t; -typedef struct _cairo_image_surface cairo_image_surface_t; -typedef struct _cairo_mime_data cairo_mime_data_t; -typedef struct _cairo_observer cairo_observer_t; -typedef struct _cairo_output_stream cairo_output_stream_t; -typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; -typedef struct _cairo_path_fixed cairo_path_fixed_t; -typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; -typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; -typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; -typedef struct _cairo_solid_pattern cairo_solid_pattern_t; -typedef struct _cairo_surface_backend cairo_surface_backend_t; -typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t; -typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t; -typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t; -typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; -typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t; - -typedef cairo_array_t cairo_user_data_array_t; - -struct _cairo_observer { - cairo_list_t link; - void (*callback) (cairo_observer_t *self, void *arg); -}; - -/** - * cairo_hash_entry_t: - * - * A #cairo_hash_entry_t contains both a key and a value for - * #cairo_hash_table_t. User-derived types for #cairo_hash_entry_t must - * be type-compatible with this structure (eg. they must have an - * unsigned long as the first parameter. The easiest way to get this - * is to use: - * - * typedef _my_entry { - * cairo_hash_entry_t base; - * ... Remainder of key and value fields here .. - * } my_entry_t; - * - * which then allows a pointer to my_entry_t to be passed to any of - * the #cairo_hash_table_t functions as follows without requiring a cast: - * - * _cairo_hash_table_insert (hash_table, &my_entry->base); - * - * IMPORTANT: The caller is reponsible for initializing - * my_entry->base.hash with a hash code derived from the key. The - * essential property of the hash code is that keys_equal must never - * return %TRUE for two keys that have different hashes. The best hash - * code will reduce the frequency of two keys with the same code for - * which keys_equal returns %FALSE. - * - * Which parts of the entry make up the "key" and which part make up - * the value are entirely up to the caller, (as determined by the - * computation going into base.hash as well as the keys_equal - * function). A few of the #cairo_hash_table_t functions accept an entry - * which will be used exclusively as a "key", (indicated by a - * parameter name of key). In these cases, the value-related fields of - * the entry need not be initialized if so desired. - **/ -struct _cairo_hash_entry { - unsigned long hash; -}; - -struct _cairo_array { - unsigned int size; - unsigned int num_elements; - unsigned int element_size; - char **elements; - - cairo_bool_t is_snapshot; -}; - -/** - * cairo_lcd_filter_t: - * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for - * font backend and target device - * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering - * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter - * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel - * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel - * - * The LCD filter specifies the low-pass filter applied to LCD-optimized - * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. - * - * Note: This API was temporarily made available in the public - * interface during the 1.7.x development series, but was made private - * before 1.8. - **/ -typedef enum _cairo_lcd_filter { - CAIRO_LCD_FILTER_DEFAULT, - CAIRO_LCD_FILTER_NONE, - CAIRO_LCD_FILTER_INTRA_PIXEL, - CAIRO_LCD_FILTER_FIR3, - CAIRO_LCD_FILTER_FIR5 -} cairo_lcd_filter_t; - -typedef enum _cairo_round_glyph_positions { - CAIRO_ROUND_GLYPH_POS_DEFAULT, - CAIRO_ROUND_GLYPH_POS_ON, - CAIRO_ROUND_GLYPH_POS_OFF -} cairo_round_glyph_positions_t; - -struct _cairo_font_options { - cairo_antialias_t antialias; - cairo_subpixel_order_t subpixel_order; - cairo_lcd_filter_t lcd_filter; - cairo_hint_style_t hint_style; - cairo_hint_metrics_t hint_metrics; - cairo_round_glyph_positions_t round_glyph_positions; -}; - -/* XXX: Right now, the _cairo_color structure puts unpremultiplied - color in the doubles and premultiplied color in the shorts. Yes, - this is crazy insane, (but at least we don't export this - madness). I'm still working on a cleaner API, but in the meantime, - at least this does prevent precision loss in color when changing - alpha. */ -struct _cairo_color { - double red; - double green; - double blue; - double alpha; - - unsigned short red_short; - unsigned short green_short; - unsigned short blue_short; - unsigned short alpha_short; -}; - -struct _cairo_color_stop { - /* unpremultiplied */ - double red; - double green; - double blue; - double alpha; - - /* unpremultipled, for convenience */ - uint16_t red_short; - uint16_t green_short; - uint16_t blue_short; - uint16_t alpha_short; -}; - -typedef enum _cairo_paginated_mode { - CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */ - CAIRO_PAGINATED_MODE_RENDER, /* render page contents */ - CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ -} cairo_paginated_mode_t; - -/* Sure wish C had a real enum type so that this would be distinct - * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 - * offset. We want to keep it fit in int8_t as the compiler may choose - * that for #cairo_status_t */ -typedef enum _cairo_int_status { - CAIRO_INT_STATUS_UNSUPPORTED = 100, - CAIRO_INT_STATUS_DEGENERATE, - CAIRO_INT_STATUS_NOTHING_TO_DO, - CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, - CAIRO_INT_STATUS_IMAGE_FALLBACK, - CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, - - CAIRO_INT_STATUS_LAST_STATUS -} cairo_int_status_t; - -typedef enum _cairo_internal_surface_type { - CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000, - CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, - CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, - CAIRO_INTERNAL_SURFACE_TYPE_NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH -} cairo_internal_surface_type_t; - -#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1 -#define CAIRO_HAS_TEST_NULL_SURFACE 1 -#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1 - -typedef struct _cairo_slope { - cairo_fixed_t dx; - cairo_fixed_t dy; -} cairo_slope_t, cairo_distance_t; - -typedef struct _cairo_point_double { - double x; - double y; -} cairo_point_double_t; - -typedef struct _cairo_distance_double { - double dx; - double dy; -} cairo_distance_double_t; - -typedef struct _cairo_line { - cairo_point_t p1; - cairo_point_t p2; -} cairo_line_t, cairo_box_t; - -typedef struct _cairo_trapezoid { - cairo_fixed_t top, bottom; - cairo_line_t left, right; -} cairo_trapezoid_t; - -typedef struct _cairo_point_int { - int x, y; -} cairo_point_int_t; - -#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) -#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) - -typedef enum _cairo_direction { - CAIRO_DIRECTION_FORWARD, - CAIRO_DIRECTION_REVERSE -} cairo_direction_t; - -typedef struct _cairo_edge { - cairo_line_t line; - int top, bottom; - int dir; -} cairo_edge_t; - -typedef struct _cairo_polygon { - cairo_status_t status; - - cairo_point_t first_point; - cairo_point_t last_point; - cairo_point_t current_point; - cairo_slope_t current_edge; - cairo_bool_t has_current_point; - cairo_bool_t has_current_edge; - - cairo_box_t extents; - cairo_box_t limit; - const cairo_box_t *limits; - int num_limits; - - int num_edges; - int edges_size; - cairo_edge_t *edges; - cairo_edge_t edges_embedded[32]; -} cairo_polygon_t; - -typedef cairo_warn cairo_status_t -(*cairo_spline_add_point_func_t) (void *closure, - const cairo_point_t *point); - -typedef struct _cairo_spline_knots { - cairo_point_t a, b, c, d; -} cairo_spline_knots_t; - -typedef struct _cairo_spline { - cairo_spline_add_point_func_t add_point_func; - void *closure; - - cairo_spline_knots_t knots; - - cairo_slope_t initial_slope; - cairo_slope_t final_slope; - - cairo_bool_t has_point; - cairo_point_t last_point; -} cairo_spline_t; - -typedef struct _cairo_pen_vertex { - cairo_point_t point; - - cairo_slope_t slope_ccw; - cairo_slope_t slope_cw; -} cairo_pen_vertex_t; - -typedef struct _cairo_pen { - double radius; - double tolerance; - - int num_vertices; - cairo_pen_vertex_t *vertices; - cairo_pen_vertex_t vertices_embedded[32]; -} cairo_pen_t; - -typedef struct _cairo_stroke_style { - double line_width; - cairo_line_cap_t line_cap; - cairo_line_join_t line_join; - double miter_limit; - double *dash; - unsigned int num_dashes; - double dash_offset; -} cairo_stroke_style_t; - -typedef struct _cairo_format_masks { - int bpp; - unsigned long alpha_mask; - unsigned long red_mask; - unsigned long green_mask; - unsigned long blue_mask; -} cairo_format_masks_t; - -typedef enum { - CAIRO_STOCK_WHITE, - CAIRO_STOCK_BLACK, - CAIRO_STOCK_TRANSPARENT, - CAIRO_STOCK_NUM_COLORS, -} cairo_stock_t; - -typedef enum _cairo_image_transparency { - CAIRO_IMAGE_IS_OPAQUE, - CAIRO_IMAGE_HAS_BILEVEL_ALPHA, - CAIRO_IMAGE_HAS_ALPHA, - CAIRO_IMAGE_UNKNOWN -} cairo_image_transparency_t; - -struct _cairo_mime_data { - cairo_reference_count_t ref_count; - unsigned char *data; - unsigned long length; - cairo_destroy_func_t destroy; - void *closure; -}; - -struct _cairo_pattern { - cairo_pattern_type_t type; - cairo_reference_count_t ref_count; - cairo_status_t status; - cairo_user_data_array_t user_data; - - cairo_matrix_t matrix; - cairo_filter_t filter; - cairo_extend_t extend; - - cairo_bool_t has_component_alpha; -}; - -struct _cairo_solid_pattern { - cairo_pattern_t base; - cairo_color_t color; -}; - -typedef struct _cairo_surface_pattern { - cairo_pattern_t base; - - cairo_surface_t *surface; -} cairo_surface_pattern_t; - -typedef struct _cairo_gradient_stop { - double offset; - cairo_color_stop_t color; -} cairo_gradient_stop_t; - -typedef struct _cairo_gradient_pattern { - cairo_pattern_t base; - - unsigned int n_stops; - unsigned int stops_size; - cairo_gradient_stop_t *stops; - cairo_gradient_stop_t stops_embedded[2]; -} cairo_gradient_pattern_t; - -typedef struct _cairo_linear_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t p1; - cairo_point_t p2; -} cairo_linear_pattern_t; - -typedef struct _cairo_radial_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t c1; - cairo_fixed_t r1; - cairo_point_t c2; - cairo_fixed_t r2; -} cairo_radial_pattern_t; - -typedef union { - cairo_gradient_pattern_t base; - - cairo_linear_pattern_t linear; - cairo_radial_pattern_t radial; -} cairo_gradient_pattern_union_t; - -typedef union { - cairo_pattern_type_t type; - cairo_pattern_t base; - - cairo_solid_pattern_t solid; - cairo_surface_pattern_t surface; - cairo_gradient_pattern_union_t gradient; -} cairo_pattern_union_t; - -/* - * A #cairo_unscaled_font_t is just an opaque handle we use in the - * glyph cache. - */ -typedef struct _cairo_unscaled_font { - cairo_hash_entry_t hash_entry; - cairo_reference_count_t ref_count; - const cairo_unscaled_font_backend_t *backend; -} cairo_unscaled_font_t; - -typedef struct _cairo_scaled_glyph { - cairo_hash_entry_t hash_entry; - - cairo_text_extents_t metrics; /* user-space metrics */ - cairo_text_extents_t fs_metrics; /* font-space metrics */ - cairo_box_t bbox; /* device-space bounds */ - int16_t x_advance; /* device-space rounded X advance */ - int16_t y_advance; /* device-space rounded Y advance */ - - unsigned int has_info; - cairo_image_surface_t *surface; /* device-space image */ - cairo_path_fixed_t *path; /* device-space outline */ - cairo_surface_t *recording_surface; /* device-space recording-surface */ - - void *surface_private; /* for the surface backend */ -} cairo_scaled_glyph_t; -#endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-unicode.c b/libs/cairo/cairo/src/cairo-unicode.c deleted file mode 100644 index 3a60ff611..000000000 --- a/libs/cairo/cairo/src/cairo-unicode.c +++ /dev/null @@ -1,384 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "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/libs/cairo/cairo/src/cairo-user-font-private.h b/libs/cairo/cairo/src/cairo-user-font-private.h deleted file mode 100644 index c5995978f..000000000 --- a/libs/cairo/cairo/src/cairo-user-font-private.h +++ /dev/null @@ -1,14 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo-user-font.c b/libs/cairo/cairo/src/cairo-user-font.c deleted file mode 100644 index 9219a7301..000000000 --- a/libs/cairo/cairo/src/cairo-user-font.c +++ /dev/null @@ -1,795 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-user-font-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-analysis-surface-private.h" -#include "cairo-error-private.h" - -/** - * SECTION:cairo-user-fonts - * @Title:User Fonts - * @Short_Description: Font support with font data provided by the user - * - * The user-font feature allows the cairo user to provide drawings for glyphs - * in a font. This is most useful in implementing fonts in non-standard - * formats, like SVG fonts and Flash fonts, but can also be used by games and - * other application to draw "funky" fonts. - */ - -/** - * CAIRO_HAS_USER_FONT: - * - * Defined if the user font backend is available. - * This macro can be used to conditionally compile backend-specific code. - * The user font backend is always built in versions of cairo that support - * this feature (1.8 and later). - * - * @Since: 1.8 - */ - -typedef struct _cairo_user_scaled_font_methods { - cairo_user_scaled_font_init_func_t init; - cairo_user_scaled_font_render_glyph_func_t render_glyph; - cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; - cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; -} cairo_user_scaled_font_methods_t; - -typedef struct _cairo_user_font_face { - cairo_font_face_t base; - - /* Set to true after first scaled font is created. At that point, - * the scaled_font_methods cannot change anymore. */ - cairo_bool_t immutable; - - cairo_user_scaled_font_methods_t scaled_font_methods; -} cairo_user_font_face_t; - -typedef struct _cairo_user_scaled_font { - cairo_scaled_font_t base; - - cairo_text_extents_t default_glyph_extents; - - /* space to compute extents in, and factors to convert back to user space */ - cairo_matrix_t extent_scale; - double extent_x_scale; - double extent_y_scale; - - /* multiplier for metrics hinting */ - double snap_x_scale; - double snap_y_scale; - -} cairo_user_scaled_font_t; - -/* #cairo_user_scaled_font_t */ - -static cairo_surface_t * -_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font) -{ - cairo_content_t content; - - content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? - CAIRO_CONTENT_COLOR_ALPHA : - CAIRO_CONTENT_ALPHA; - - return cairo_recording_surface_create (content, NULL); -} - - -static cairo_t * -_cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface) -{ - cairo_t *cr; - - cr = cairo_create (recording_surface); - - if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { - cairo_matrix_t scale; - scale = scaled_font->base.scale; - scale.x0 = scale.y0 = 0.; - cairo_set_matrix (cr, &scale); - } - - cairo_set_font_size (cr, 1.0); - cairo_set_font_options (cr, &scaled_font->base.options); - cairo_set_source_rgb (cr, 1., 1., 1.); - - return cr; -} - -static cairo_int_status_t -_cairo_user_scaled_glyph_init (void *abstract_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_user_scaled_font_t *scaled_font = abstract_font; - cairo_surface_t *recording_surface = scaled_glyph->recording_surface; - - if (!scaled_glyph->recording_surface) { - cairo_user_font_face_t *face = - (cairo_user_font_face_t *) scaled_font->base.font_face; - cairo_text_extents_t extents = scaled_font->default_glyph_extents; - cairo_t *cr; - - if (!face->scaled_font_methods.render_glyph) - return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - - recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font); - - /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ - if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { - cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface); - status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, - _cairo_scaled_glyph_index(scaled_glyph), - cr, &extents); - if (status == CAIRO_STATUS_SUCCESS) - status = cairo_status (cr); - - cairo_destroy (cr); - - if (unlikely (status)) { - cairo_surface_destroy (recording_surface); - return status; - } - } - - _cairo_scaled_glyph_set_recording_surface (scaled_glyph, - &scaled_font->base, - recording_surface); - - - /* set metrics */ - - if (extents.width == 0.) { - cairo_box_t bbox; - double x1, y1, x2, y2; - double x_scale, y_scale; - - /* Compute extents.x/y/width/height from recording_surface, - * in font space. - */ - status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, - &scaled_font->extent_scale); - if (unlikely (status)) - return status; - - _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); - - x_scale = scaled_font->extent_x_scale; - y_scale = scaled_font->extent_y_scale; - extents.x_bearing = x1 * x_scale; - extents.y_bearing = y1 * y_scale; - extents.width = (x2 - x1) * x_scale; - extents.height = (y2 - y1) * y_scale; - } - - if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { - extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale; - extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale; - } - - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &scaled_font->base, - &extents); - } - - if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { - cairo_surface_t *surface; - cairo_format_t format; - int width, height; - - /* TODO - * extend the glyph cache to support argb glyphs. - * need to figure out the semantics and interaction with subpixel - * rendering first. - */ - - width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); - height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - - switch (scaled_font->base.options.antialias) { - default: - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; - case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; - case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; - } - surface = cairo_image_surface_create (format, width, height); - - cairo_surface_set_device_offset (surface, - - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), - - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); - status = _cairo_recording_surface_replay (recording_surface, surface); - - if (unlikely (status)) { - cairo_surface_destroy(surface); - return status; - } - - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - (cairo_image_surface_t *) surface); - } - - if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { - cairo_path_fixed_t *path = _cairo_path_fixed_create (); - if (!path) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_recording_surface_get_path (recording_surface, path); - if (unlikely (status)) { - _cairo_path_fixed_destroy (path); - return status; - } - - _cairo_scaled_glyph_set_path (scaled_glyph, - &scaled_font->base, - path); - } - - return status; -} - -static unsigned long -_cairo_user_ucs4_to_index (void *abstract_font, - uint32_t ucs4) -{ - cairo_user_scaled_font_t *scaled_font = abstract_font; - cairo_user_font_face_t *face = - (cairo_user_font_face_t *) scaled_font->base.font_face; - unsigned long glyph = 0; - - if (face->scaled_font_methods.unicode_to_glyph) { - cairo_status_t status; - - status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, - ucs4, &glyph); - - if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) - goto not_implemented; - - if (status != CAIRO_STATUS_SUCCESS) { - status = _cairo_scaled_font_set_error (&scaled_font->base, status); - glyph = 0; - } - - } else { -not_implemented: - glyph = ucs4; - } - - return glyph; -} - -static cairo_int_status_t -_cairo_user_text_to_glyphs (void *abstract_font, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags) -{ - cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_user_scaled_font_t *scaled_font = abstract_font; - cairo_user_font_face_t *face = - (cairo_user_font_face_t *) scaled_font->base.font_face; - - if (face->scaled_font_methods.text_to_glyphs) { - int i; - cairo_glyph_t *orig_glyphs = *glyphs; - int orig_num_glyphs = *num_glyphs; - - status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); - - if (status != CAIRO_STATUS_SUCCESS && - status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) - return status; - - if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) { - if (orig_glyphs != *glyphs) { - cairo_glyph_free (*glyphs); - *glyphs = orig_glyphs; - } - *num_glyphs = orig_num_glyphs; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Convert from font space to user space and add x,y */ - for (i = 0; i < *num_glyphs; i++) { - double gx = (*glyphs)[i].x; - double gy = (*glyphs)[i].y; - - cairo_matrix_transform_point (&scaled_font->base.font_matrix, - &gx, &gy); - - (*glyphs)[i].x = gx + x; - (*glyphs)[i].y = gy + y; - } - } - - return status; -} - -static cairo_status_t -_cairo_user_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font); - -static cairo_status_t -_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - return _cairo_font_face_twin_create_for_toy (toy_face, font_face); -} - -static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { - CAIRO_FONT_TYPE_USER, - NULL, /* scaled_font_fini */ - _cairo_user_scaled_glyph_init, - _cairo_user_text_to_glyphs, - _cairo_user_ucs4_to_index, - NULL, /* show_glyphs */ - NULL, /* load_truetype_table */ - NULL /* index_to_ucs4 */ -}; - -/* #cairo_user_font_face_t */ - -static cairo_status_t -_cairo_user_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_user_font_face_t *font_face = abstract_face; - cairo_user_scaled_font_t *user_scaled_font = NULL; - cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.}; - - font_face->immutable = TRUE; - - user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); - if (unlikely (user_scaled_font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_scaled_font_init (&user_scaled_font->base, - &font_face->base, - font_matrix, ctm, options, - &_cairo_user_scaled_font_backend); - - if (unlikely (status)) { - free (user_scaled_font); - return status; - } - - /* XXX metrics hinting? */ - - /* compute a normalized version of font scale matrix to compute - * extents in. This is to minimize error caused by the cairo_fixed_t - * representation. */ - { - double fixed_scale, x_scale, y_scale; - - user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse; - status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale, - &x_scale, &y_scale, - 1); - if (status == CAIRO_STATUS_SUCCESS) { - - if (x_scale == 0) x_scale = 1.; - if (y_scale == 0) y_scale = 1.; - - user_scaled_font->snap_x_scale = x_scale; - user_scaled_font->snap_y_scale = y_scale; - - /* since glyphs are pretty much 1.0x1.0, we can reduce error by - * scaling to a larger square. say, 1024.x1024. */ - fixed_scale = 1024.; - x_scale /= fixed_scale; - y_scale /= fixed_scale; - - cairo_matrix_scale (&user_scaled_font->extent_scale, 1. / x_scale, 1. / y_scale); - - user_scaled_font->extent_x_scale = x_scale; - user_scaled_font->extent_y_scale = y_scale; - } - } - - if (status == CAIRO_STATUS_SUCCESS && - font_face->scaled_font_methods.init != NULL) - { - /* Lock the scaled_font mutex such that user doesn't accidentally try - * to use it just yet. */ - CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex); - - /* Give away fontmap lock such that user-font can use other fonts */ - status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_t *recording_surface; - cairo_t *cr; - - recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font); - cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface); - cairo_surface_destroy (recording_surface); - - status = font_face->scaled_font_methods.init (&user_scaled_font->base, - cr, - &font_extents); - - if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) - status = CAIRO_STATUS_SUCCESS; - - if (status == CAIRO_STATUS_SUCCESS) - status = cairo_status (cr); - - cairo_destroy (cr); - - _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base); - } - - CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex); - } - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents); - - if (status != CAIRO_STATUS_SUCCESS) { - _cairo_scaled_font_fini (&user_scaled_font->base); - free (user_scaled_font); - } else { - user_scaled_font->default_glyph_extents.x_bearing = 0.; - user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent; - user_scaled_font->default_glyph_extents.width = 0.; - user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent; - user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance; - user_scaled_font->default_glyph_extents.y_advance = 0.; - - *scaled_font = &user_scaled_font->base; - } - - return status; -} - -const cairo_font_face_backend_t _cairo_user_font_face_backend = { - CAIRO_FONT_TYPE_USER, - _cairo_user_font_face_create_for_toy, - NULL, /* destroy */ - _cairo_user_font_face_scaled_font_create -}; - - -cairo_bool_t -_cairo_font_face_is_user (cairo_font_face_t *font_face) -{ - return font_face->backend == &_cairo_user_font_face_backend; -} - -/* Implement the public interface */ - -/** - * cairo_user_font_face_create: - * - * Creates a new user font-face. - * - * Use the setter functions to associate callbacks with the returned - * user font. The only mandatory callback is render_glyph. - * - * After the font-face is created, the user can attach arbitrary data - * (the actual font data) to it using cairo_font_face_set_user_data() - * and access it from the user-font callbacks by using - * cairo_scaled_font_get_font_face() followed by - * cairo_font_face_get_user_data(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.8 - **/ -cairo_font_face_t * -cairo_user_font_face_create (void) -{ - cairo_user_font_face_t *font_face; - - font_face = malloc (sizeof (cairo_user_font_face_t)); - if (!font_face) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } - - _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); - - font_face->immutable = FALSE; - memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); - - return &font_face->base; -} -slim_hidden_def(cairo_user_font_face_create); - -/* User-font method setters */ - - -/** - * cairo_user_font_face_set_init_func: - * @font_face: A user font face - * @init_func: The init callback, or %NULL - * - * Sets the scaled-font initialization function of a user-font. - * See #cairo_user_scaled_font_init_func_t for details of how the callback - * works. - * - * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE - * error will occur. A user font-face is immutable as soon as a scaled-font - * is created from it. - * - * Since: 1.8 - **/ -void -cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_init_func_t init_func) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - if (user_font_face->immutable) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) - return; - } - user_font_face->scaled_font_methods.init = init_func; -} -slim_hidden_def(cairo_user_font_face_set_init_func); - -/** - * cairo_user_font_face_set_render_glyph_func: - * @font_face: A user font face - * @render_glyph_func: The render_glyph callback, or %NULL - * - * Sets the glyph rendering function of a user-font. - * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback - * works. - * - * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE - * error will occur. A user font-face is immutable as soon as a scaled-font - * is created from it. - * - * The render_glyph callback is the only mandatory callback of a user-font. - * If the callback is %NULL and a glyph is tried to be rendered using - * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. - * - * Since: 1.8 - **/ -void -cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_render_glyph_func_t render_glyph_func) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - if (user_font_face->immutable) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) - return; - } - user_font_face->scaled_font_methods.render_glyph = render_glyph_func; -} -slim_hidden_def(cairo_user_font_face_set_render_glyph_func); - -/** - * cairo_user_font_face_set_text_to_glyphs_func: - * @font_face: A user font face - * @text_to_glyphs_func: The text_to_glyphs callback, or %NULL - * - * Sets th text-to-glyphs conversion function of a user-font. - * See #cairo_user_scaled_font_text_to_glyphs_func_t for details of how the callback - * works. - * - * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE - * error will occur. A user font-face is immutable as soon as a scaled-font - * is created from it. - * - * Since: 1.8 - **/ -void -cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - if (user_font_face->immutable) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) - return; - } - user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func; -} - -/** - * cairo_user_font_face_set_unicode_to_glyph_func: - * @font_face: A user font face - * @unicode_to_glyph_func: The unicode_to_glyph callback, or %NULL - * - * Sets the unicode-to-glyph conversion function of a user-font. - * See #cairo_user_scaled_font_unicode_to_glyph_func_t for details of how the callback - * works. - * - * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE - * error will occur. A user font-face is immutable as soon as a scaled-font - * is created from it. - * - * Since: 1.8 - **/ -void -cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func) -{ - cairo_user_font_face_t *user_font_face; - if (font_face->status) - return; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - if (user_font_face->immutable) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) - return; - } - user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func; -} -slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func); - -/* User-font method getters */ - -/** - * cairo_user_font_face_get_init_func: - * @font_face: A user font face - * - * Gets the scaled-font initialization function of a user-font. - * - * Return value: The init callback of @font_face - * or %NULL if none set or an error has occurred. - * - * Since: 1.8 - **/ -cairo_user_scaled_font_init_func_t -cairo_user_font_face_get_init_func (cairo_font_face_t *font_face) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return NULL; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return NULL; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - return user_font_face->scaled_font_methods.init; -} - -/** - * cairo_user_font_face_get_render_glyph_func: - * @font_face: A user font face - * - * Gets the glyph rendering function of a user-font. - * - * Return value: The render_glyph callback of @font_face - * or %NULL if none set or an error has occurred. - * - * Since: 1.8 - **/ -cairo_user_scaled_font_render_glyph_func_t -cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return NULL; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return NULL; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - return user_font_face->scaled_font_methods.render_glyph; -} - -/** - * cairo_user_font_face_get_text_to_glyphs_func: - * @font_face: A user font face - * - * Gets the text-to-glyphs conversion function of a user-font. - * - * Return value: The text_to_glyphs callback of @font_face - * or %NULL if none set or an error occurred. - * - * Since: 1.8 - **/ -cairo_user_scaled_font_text_to_glyphs_func_t -cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return NULL; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return NULL; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - return user_font_face->scaled_font_methods.text_to_glyphs; -} - -/** - * cairo_user_font_face_get_unicode_to_glyph_func: - * @font_face: A user font face - * - * Gets the unicode-to-glyph conversion function of a user-font. - * - * Return value: The unicode_to_glyph callback of @font_face - * or %NULL if none set or an error occurred. - * - * Since: 1.8 - **/ -cairo_user_scaled_font_unicode_to_glyph_func_t -cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) -{ - cairo_user_font_face_t *user_font_face; - - if (font_face->status) - return NULL; - - if (! _cairo_font_face_is_user (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return NULL; - } - - user_font_face = (cairo_user_font_face_t *) font_face; - return user_font_face->scaled_font_methods.unicode_to_glyph; -} diff --git a/libs/cairo/cairo/src/cairo-version.c b/libs/cairo/cairo/src/cairo-version.c deleted file mode 100644 index 5b7fa6827..000000000 --- a/libs/cairo/cairo/src/cairo-version.c +++ /dev/null @@ -1,210 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#define CAIRO_VERSION_H 1 - -#include "cairoint.h" - -/* get the "real" version info instead of dummy cairo-version.h */ -#undef CAIRO_VERSION_H -#include "cairo-features.h" - -/** - * SECTION:cairo-version - * @Title: Version Information - * @Short_Description: Compile-time and run-time version checks. - * - * Cairo has a three-part version number scheme. In this scheme, we use - * even vs. odd numbers to distinguish fixed points in the software - * vs. in-progress development, (such as from git instead of a tar file, - * or as a "snapshot" tar file as opposed to a "release" tar file). - * - * - * _____ Major. Always 1, until we invent a new scheme. - * / ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git) - * | / _ Micro. Even/Odd = Tar-file/git - * | | / - * 1.0.0 - * - * - * Here are a few examples of versions that one might see. - * - * Releases - * -------- - * 1.0.0 - A major release - * 1.0.2 - A subsequent maintenance release - * 1.2.0 - Another major release - * - * Snapshots - * --------- - * 1.1.2 - A snapshot (working toward the 1.2.0 release) - * - * In-progress development (eg. from git) - * -------------------------------------- - * 1.0.1 - Development on a maintenance branch (toward 1.0.2 release) - * 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release) - * - * - * - * Compatibility - * - * The API/ABI compatibility guarantees for various versions are as - * follows. First, let's assume some cairo-using application code that is - * successfully using the API/ABI "from" one version of cairo. Then let's - * ask the question whether this same code can be moved "to" the API/ABI - * of another version of cairo. - * - * Moving from a release to any later version (release, snapshot, - * development) is always guaranteed to provide compatibility. - * - * Moving from a snapshot to any later version is not guaranteed to - * provide compatibility, since snapshots may introduce new API that ends - * up being removed before the next release. - * - * Moving from an in-development version (odd micro component) to any - * later version is not guaranteed to provide compatibility. In fact, - * there's not even a guarantee that the code will even continue to work - * with the same in-development version number. This is because these - * numbers don't correspond to any fixed state of the software, but - * rather the many states between snapshots and releases. - * - * - * - * Examining the version - * - * Cairo provides the ability to examine the version at either - * compile-time or run-time and in both a human-readable form as well as - * an encoded form suitable for direct comparison. Cairo also provides the - * macro CAIRO_VERSION_ENCODE() to perform the encoding. - * - * - * Compile-time - * ------------ - * CAIRO_VERSION_STRING Human-readable - * CAIRO_VERSION Encoded, suitable for comparison - * - * Run-time - * -------- - * cairo_version_string() Human-readable - * cairo_version() Encoded, suitable for comparison - * - * - * For example, checking that the cairo version is greater than or equal - * to 1.0.0 could be achieved at compile-time or run-time as follows: - * - * - * ##if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 0, 0) - * printf ("Compiling with suitable cairo version: %s\n", %CAIRO_VERSION_STRING); - * ##endif - * - * if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 0, 0)) - * printf ("Running with suitable cairo version: %s\n", cairo_version_string ()); - * - * - * - */ - -/** - * CAIRO_VERSION: - * - * The version of cairo available at compile-time, encoded using - * CAIRO_VERSION_ENCODE(). - */ - -/** - * CAIRO_VERSION_MAJOR: - * - * The major component of the version of cairo available at compile-time. - */ - -/** - * CAIRO_VERSION_MINOR: - * - * The minor component of the version of cairo available at compile-time. - */ - -/** - * CAIRO_VERSION_MICRO: - * - * The micro component of the version of cairo available at compile-time. - */ - -/** - * CAIRO_VERSION_STRING: - * - * A human-readable string literal containing the version of cairo available - * at compile-time, in the form of "X.Y.Z". - */ - -/** - * CAIRO_VERSION_ENCODE: - * @major: the major component of the version number - * @minor: the minor component of the version number - * @micro: the micro component of the version number - * - * This macro encodes the given cairo version into an integer. The numbers - * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro. - * Two encoded version numbers can be compared as integers. The encoding ensures - * that later versions compare greater than earlier versions. - * - * @Returns: the encoded version. - */ - -/** - * CAIRO_VERSION_STRINGIZE: - * @major: the major component of the version number - * @minor: the minor component of the version number - * @micro: the micro component of the version number - * - * This macro encodes the given cairo version into an string. The numbers - * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro. - * The parameters to this macro must expand to numerical literals. - * - * @Returns: a string literal containing the version. - * - * @Since: 1.8 - */ - -/** - * cairo_version: - * - * Returns the version of the cairo library encoded in a single - * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that - * later versions compare greater than earlier versions. - * - * A run-time comparison to check that cairo's version is greater than - * or equal to version X.Y.Z could be performed as follows: - * - * - * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...} - * - * - * See also cairo_version_string() as well as the compile-time - * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING. - * - * Return value: the encoded version. - **/ -int -cairo_version (void) -{ - return CAIRO_VERSION; -} - -/** - * cairo_version_string: - * - * Returns the version of the cairo library as a human-readable string - * of the form "X.Y.Z". - * - * See also cairo_version() as well as the compile-time equivalents - * %CAIRO_VERSION_STRING and %CAIRO_VERSION. - * - * Return value: a string containing the version. - **/ -const char* -cairo_version_string (void) -{ - return CAIRO_VERSION_STRING; -} -slim_hidden_def (cairo_version_string); diff --git a/libs/cairo/cairo/src/cairo-version.h b/libs/cairo/cairo/src/cairo-version.h deleted file mode 100644 index ace09244c..000000000 --- a/libs/cairo/cairo/src/cairo-version.h +++ /dev/null @@ -1,16 +0,0 @@ -/* This is a dummy file. - * The actual version info is in toplevel cairo-version.h. - * The purpose of this file is to make most of the source files NOT depend - * on the real cairo-version.h, and as a result, changing library version - * would not cause a complete rebuild of all object files (just a relink). - * This is useful when bisecting. */ -#ifndef CAIRO_VERSION_H -#define CAIRO_VERSION_H - -#if 0 -#define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD -#define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD -#define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD -#endif - -#endif diff --git a/libs/cairo/cairo/src/cairo-vg-surface.c b/libs/cairo/cairo/src/cairo-vg-surface.c deleted file mode 100644 index 3a3d83e96..000000000 --- a/libs/cairo/cairo/src/cairo-vg-surface.c +++ /dev/null @@ -1,1894 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-vg.h" - -#include "cairo-cache-private.h" -#include "cairo-error-private.h" -#include "cairo-path-fixed-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-clipper-private.h" - -#include -#include - -//#define OPENVG_DEBUG - -/* - * Work that needs to be done: - * - Glyph cache / proper font support - * - * - First-class paths - * Paths are expensive for OpenVG, reuse paths whenever possible. - * So add a path cache, and first class paths! - */ - -typedef struct _cairo_vg_surface cairo_vg_surface_t; - -/* XXX need GL specific context control. :( */ -struct _cairo_vg_context { - cairo_status_t status; - cairo_reference_count_t ref_count; - - unsigned long target_id; - - VGPaint paint; - cairo_vg_surface_t *source; - double alpha; - - cairo_cache_t snapshot_cache; - - void *display; - void *context; - - cairo_status_t (*create_target) (cairo_vg_context_t *, - cairo_vg_surface_t *); - cairo_status_t (*set_target) (cairo_vg_context_t *, - cairo_vg_surface_t *); - void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *); -}; - -struct _cairo_vg_surface { - cairo_surface_t base; - - cairo_vg_context_t *context; - - VGImage image; - VGImageFormat format; - int width; - int height; - cairo_bool_t own_image; - - cairo_cache_entry_t snapshot_cache_entry; - - cairo_surface_clipper_t clipper; - - unsigned long target_id; -}; - -static const cairo_surface_backend_t cairo_vg_surface_backend; - -slim_hidden_proto (cairo_vg_surface_create); - -static cairo_surface_t * -_vg_surface_create_internal (cairo_vg_context_t *context, - VGImage image, - VGImageFormat format, - int width, int height); - -static cairo_vg_context_t * -_vg_context_reference (cairo_vg_context_t *context) -{ - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); - - _cairo_reference_count_inc (&context->ref_count); - - return context; -} - -static cairo_vg_context_t * -_vg_context_lock (cairo_vg_context_t *context) -{ - /* XXX if we need to add locking, then it has to be recursive */ - return context; -} - -static cairo_int_status_t -_vg_context_set_target (cairo_vg_context_t *context, - cairo_vg_surface_t *surface) -{ - cairo_status_t status; - - if (surface->target_id == 0) { - status = context->create_target (context, surface); - if (unlikely (status)) - return status; - } - - if (context->target_id == surface->target_id) - return CAIRO_STATUS_SUCCESS; - - context->target_id = surface->target_id; - - return context->set_target (context, surface); -} - -static void -_vg_context_destroy_target (cairo_vg_context_t *context, - cairo_vg_surface_t *surface) -{ - if (surface->target_id == 0) - return; - - if (context->target_id == surface->target_id) - context->set_target (context, NULL); - - context->destroy_target (context, surface); -} - -static cairo_bool_t -_vg_snapshot_cache_can_remove (const void *entry) -{ - return TRUE; -} - -static void -_vg_snapshot_cache_remove (void *cache_entry) -{ - cairo_vg_surface_t *surface = cairo_container_of (cache_entry, - cairo_vg_surface_t, - snapshot_cache_entry); - surface->snapshot_cache_entry.hash = 0; - cairo_surface_destroy (&surface->base); -} - -static cairo_status_t -_vg_context_init (cairo_vg_context_t *context) -{ - cairo_status_t status; - - context->status = CAIRO_STATUS_SUCCESS; - CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1); - - status = _cairo_cache_init (&context->snapshot_cache, - NULL, - _vg_snapshot_cache_can_remove, - _vg_snapshot_cache_remove, - 16*1024*1024); - if (unlikely (status)) - return status; - - context->target_id = 0; - context->source = NULL; - context->alpha = 1.0; - - context->paint = vgCreatePaint (); - vgLoadIdentity (); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_vg_context_destroy (cairo_vg_context_t *context) -{ - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&context->ref_count)) - return; - - if (context->paint != VG_INVALID_HANDLE) - vgDestroyPaint (context->paint); - - _cairo_cache_fini (&context->snapshot_cache); - free (context); -} - -static void -_vg_context_unlock (cairo_vg_context_t *context) -{ -} - -#ifdef OPENVG_DEBUG -static void check_vg_errors(const char*function,int line) -{ - int err = vgGetError(); - if (err != VG_NO_ERROR){ - printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err); - assert(err == VG_NO_ERROR); - } - -} -#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__) -#else -#define CHECK_VG_ERRORS() do{}while(0) -#endif //OPENVG_DEBUG - -static pixman_format_code_t -_vg_format_to_pixman (VGImageFormat format, - cairo_bool_t *needs_premult_fixup) -{ - *needs_premult_fixup = FALSE; - switch (format) { - /* RGB{A,X} channel ordering */ - case VG_sRGBX_8888: return 0; //PIXMAN_r8g8b8x8; - case VG_sRGBA_8888: return 0; - case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8; - case VG_sRGB_565: return PIXMAN_r5g6b5; - case VG_sRGBA_5551: return 0; - case VG_sRGBA_4444: return 0; - case VG_sL_8: return PIXMAN_g8; - case VG_lRGBX_8888: return 0; - case VG_lRGBA_8888: return 0; - case VG_lRGBA_8888_PRE: return 0; - case VG_lL_8: return 0; - case VG_A_8: return PIXMAN_a8; - case VG_BW_1: return PIXMAN_a1; - case VG_A_1: return PIXMAN_a1; - case VG_A_4: return PIXMAN_a4; - - /* {A,X}RGB channel ordering */ - case VG_sXRGB_8888: return PIXMAN_x8r8g8b8; - case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8; - case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8; - case VG_sARGB_1555: return 0; - case VG_sARGB_4444: return 0; - case VG_lXRGB_8888: return 0; - case VG_lARGB_8888: return 0; - case VG_lARGB_8888_PRE: return 0; - - /* BGR{A,X} channel ordering */ - case VG_sBGRX_8888: return PIXMAN_b8g8r8x8; - case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8; - case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8; - case VG_sBGR_565: return PIXMAN_b5g6r5; - case VG_sBGRA_5551: return 0; - case VG_sBGRA_4444: return 0; - case VG_lBGRX_8888: return 0; - case VG_lBGRA_8888: return 0; - case VG_lBGRA_8888_PRE: return 0; - - /* {A,X}BGR channel ordering */ - case VG_sXBGR_8888: return PIXMAN_x8b8g8r8; - case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8; - case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8; - case VG_sABGR_1555: return 0; - case VG_sABGR_4444: return 0; - case VG_lXBGR_8888: return 0; - case VG_lABGR_8888: return 0; - case VG_lABGR_8888_PRE: return 0; - default: return 0; - } -} - -static pixman_format_code_t -_vg_format_to_content (VGImageFormat format) -{ - /* XXX could use more simple bit tests */ - switch (format) { - /* RGB{A,X} channel ordering */ - case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR; - case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sRGB_565: return CAIRO_CONTENT_COLOR; - case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sL_8: return CAIRO_CONTENT_ALPHA; - case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR; - case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lL_8: return CAIRO_CONTENT_ALPHA; - case VG_A_8: return CAIRO_CONTENT_ALPHA; - case VG_A_4: return CAIRO_CONTENT_ALPHA; - case VG_A_1: return CAIRO_CONTENT_ALPHA; - case VG_BW_1: return CAIRO_CONTENT_ALPHA; - - /* {A,X}RGB channel ordering */ - case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR; - case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR; - case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - - /* BGR{A,X} channel ordering */ - case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR; - case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sBGR_565: return CAIRO_CONTENT_COLOR; - case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR; - case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - - /* {A,X}BGR channel ordering */ - case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR; - case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR; - case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; - case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; - default: return 0; - } -} - -static VGImageFormat -_vg_format_from_pixman (pixman_format_code_t format) -{ - /* XXX _PRE needs fixup */ - switch ((int) format) { - case PIXMAN_r5g6b5: return VG_sRGB_565; - case PIXMAN_g8: return VG_sL_8; - case PIXMAN_a8: return VG_A_8; - case PIXMAN_a1: return VG_BW_1; - case PIXMAN_x8r8g8b8: return VG_sXRGB_8888; - case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE - case PIXMAN_b8g8r8x8: return VG_sBGRX_8888; - case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE - case PIXMAN_b5g6r5: return VG_sBGR_565; - case PIXMAN_x8b8g8r8: return VG_sXBGR_8888; - case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE - default: return 0; - } -} - -static VGImageFormat -_vg_format_for_content (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_ALPHA: return VG_A_8; - case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888; - default: ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE - } -} - -static cairo_surface_t * -_vg_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_vg_surface_t *surface = abstract_surface; - - if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || - height > vgGeti (VG_MAX_IMAGE_HEIGHT)) - { - return NULL; - } - - return cairo_vg_surface_create (surface->context, content, width, height); -} - -static cairo_status_t -_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_vg_surface_t *surface = cairo_container_of (clipper, - cairo_vg_surface_t, - clipper); - cairo_vg_surface_t *mask; - cairo_status_t status; - - if (path == NULL) { - vgMask (VG_INVALID_HANDLE, - VG_FILL_MASK, 0, 0, surface->width, surface->height); - vgSeti (VG_MASKING, VG_FALSE); - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; - } - - mask = (cairo_vg_surface_t *) - _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, - surface->width, surface->height); - if (unlikely (mask == NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (mask->base.status)) - return mask->base.status; - - status = _cairo_surface_fill (&mask->base, - CAIRO_OPERATOR_SOURCE, - &_cairo_pattern_white.base, - path, fill_rule, tolerance, antialias, - NULL); - if (status) { - cairo_surface_destroy (&mask->base); - return status; - } - - vgSeti (VG_MASKING, VG_TRUE); - vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height); - - cairo_surface_destroy (&mask->base); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_vg_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_vg_surface_t *surface = abstract_surface; - - extents->x = 0; - extents->y = 0; - extents->width = surface->width; - extents->height = surface->height; - - return TRUE; -} - -#define MAX_SEG 16 /* max number of knots to upload in a batch */ - -typedef struct _vg_path { - VGPath path; - cairo_matrix_t *ctm_inverse; - - VGubyte gseg[MAX_SEG]; - VGfloat gdata[MAX_SEG*3*2]; - int dcount; - int scount; -} vg_path_t; - -static cairo_status_t -_vg_move_to (void *closure, - const cairo_point_t *point) -{ - vg_path_t *path = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (path->ctm_inverse) - cairo_matrix_transform_point (path->ctm_inverse, &x, &y); - - path->gseg[path->scount++] = VG_MOVE_TO; - path->gdata[path->dcount++] = x; - path->gdata[path->dcount++] = y; - - if (path->scount >= MAX_SEG-1) { - vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); - path->scount = 0; - path->dcount = 0; - } - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_vg_line_to (void *closure, - const cairo_point_t *point) -{ - vg_path_t *path = closure; - double x = _cairo_fixed_to_double (point->x); - double y = _cairo_fixed_to_double (point->y); - - if (path->ctm_inverse) - cairo_matrix_transform_point (path->ctm_inverse, &x, &y); - - path->gseg[path->scount++] = VG_LINE_TO; - path->gdata[path->dcount++] = x; - path->gdata[path->dcount++] = y; - - if (path->scount >= MAX_SEG-1) { - vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); - path->scount = 0; - path->dcount = 0; - } - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_vg_curve_to (void *closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2) -{ - vg_path_t *path = closure; - double x0 = _cairo_fixed_to_double (p0->x); - double y0 = _cairo_fixed_to_double (p0->y); - double x1 = _cairo_fixed_to_double (p1->x); - double y1 = _cairo_fixed_to_double (p1->y); - double x2 = _cairo_fixed_to_double (p2->x); - double y2 = _cairo_fixed_to_double (p2->y); - - if (path->ctm_inverse) { - cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0); - cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1); - cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2); - } - - path->gseg[path->scount++] = VG_CUBIC_TO; - path->gdata[path->dcount++] = x0; - path->gdata[path->dcount++] = y0; - path->gdata[path->dcount++] = x1; - path->gdata[path->dcount++] = y1; - path->gdata[path->dcount++] = x2; - path->gdata[path->dcount++] = y2; - - if (path->scount >= MAX_SEG-1) { - vgAppendPathData(path->path, path->scount, path->gseg, path->gdata); - path->scount = 0; - path->dcount = 0; - } - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_vg_close_path (void *closure) -{ - vg_path_t *path = closure; - - path->gseg[path->scount++] = VG_CLOSE_PATH; - - if (path->scount >= MAX_SEG-1) { - vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); - path->scount = 0; - path->dcount = 0; - } - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static void -_vg_path_from_cairo (vg_path_t *vg_path, - const cairo_path_fixed_t *path) -{ - cairo_status_t status; - - vg_path->scount = 0; - vg_path->dcount = 0; - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _vg_move_to, - _vg_line_to, - _vg_curve_to, - _vg_close_path, - vg_path); - assert (status == CAIRO_STATUS_SUCCESS); - - vgAppendPathData (vg_path->path, - vg_path->scount, vg_path->gseg, vg_path->gdata); - CHECK_VG_ERRORS(); -} - -static cairo_bool_t -_vg_is_supported_operator (cairo_operator_t op) -{ - switch ((int) op) { - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_ADD: - return TRUE; - - default: - return FALSE; - } -} - -static VGBlendMode -_vg_operator (cairo_operator_t op) -{ - switch ((int) op) { - case CAIRO_OPERATOR_SOURCE: - return VG_BLEND_SRC; - case CAIRO_OPERATOR_OVER: - return VG_BLEND_SRC_OVER; - case CAIRO_OPERATOR_IN: - return VG_BLEND_SRC_IN; - case CAIRO_OPERATOR_DEST_OVER: - return VG_BLEND_DST_OVER; - case CAIRO_OPERATOR_DEST_IN: - return VG_BLEND_DST_IN; - case CAIRO_OPERATOR_ADD: - return VG_BLEND_ADDITIVE; - default: - ASSERT_NOT_REACHED; - return VG_BLEND_SRC_OVER; - } -} - -static VGFillRule -_vg_fill_rule_from_cairo (cairo_fill_rule_t rule) -{ - switch (rule) { - case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD; - case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO; - } - - ASSERT_NOT_REACHED; - return VG_NON_ZERO; -} - -static VGRenderingQuality -_vg_rendering_quality_from_cairo (cairo_antialias_t aa) -{ - switch (aa) { - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_SUBPIXEL: - return VG_RENDERING_QUALITY_BETTER; - - case CAIRO_ANTIALIAS_GRAY: - return VG_RENDERING_QUALITY_FASTER; - - case CAIRO_ANTIALIAS_NONE: - return VG_RENDERING_QUALITY_NONANTIALIASED; - } - - ASSERT_NOT_REACHED; - return VG_RENDERING_QUALITY_BETTER; -} - -static VGCapStyle -_vg_line_cap_from_cairo (cairo_line_cap_t cap) -{ - switch (cap) { - case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; - case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; - case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; - } - - ASSERT_NOT_REACHED; - return VG_CAP_BUTT; -} - -static VGJoinStyle -_vg_line_join_from_cairo (cairo_line_join_t join) -{ - switch (join) { - case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER; - case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND; - case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; - } - - ASSERT_NOT_REACHED; - return VG_JOIN_MITER; -} - -static void -_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src) -{ - dst[0] = /* sx */ src->xx; - dst[1] = /* shy */ src->yx; - dst[2] = /* w0 */ 0; - dst[3] = /* shx */ src->xy; - dst[4] = /* sy */ src->yy; - dst[5] = /* w1 */ 0; - dst[6] = /* tx */ src->x0; - dst[7] = /* ty */ src->y0; - dst[8] = /* w2 */ 0; -} - -static cairo_status_t -_vg_setup_gradient_stops (cairo_vg_context_t *context, - const cairo_gradient_pattern_t *pattern) -{ - VGint numstops = pattern->n_stops; - VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)]; - int i; - - if (numstops*5 < ARRAY_LENGTH (stack_stops)) { - stops = stack_stops; - } else { - stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat)); - if (unlikely (stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < numstops; i++) { - stops[i*5 + 0] = pattern->stops[i].offset; - stops[i*5 + 1] = pattern->stops[i].color.red; - stops[i*5 + 2] = pattern->stops[i].color.green; - stops[i*5 + 3] = pattern->stops[i].color.blue; - stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha; - } - - vgSetParameterfv (context->paint, - VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops); - - if (stops != stack_stops) - free (stops); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static void -_vg_set_source_matrix (const cairo_pattern_t *pat) -{ - cairo_matrix_t mat; - cairo_status_t status; - VGfloat vmat[9]; - - mat = pat->matrix; - status = cairo_matrix_invert (&mat); - assert (status == CAIRO_STATUS_SUCCESS); - - _vg_matrix_from_cairo (vmat, &mat); - - vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); - vgLoadMatrix (vmat); - vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); - vgLoadMatrix (vmat); - vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); - - CHECK_VG_ERRORS(); -} - -static cairo_status_t -_vg_setup_linear_source (cairo_vg_context_t *context, - const cairo_linear_pattern_t *lpat) -{ - VGfloat linear[4]; - - linear[0] = _cairo_fixed_to_double (lpat->p1.x); - linear[1] = _cairo_fixed_to_double (lpat->p1.y); - linear[2] = _cairo_fixed_to_double (lpat->p2.x); - linear[3] = _cairo_fixed_to_double (lpat->p2.y); - - vgSetParameteri (context->paint, - VG_PAINT_COLOR_RAMP_SPREAD_MODE, - VG_COLOR_RAMP_SPREAD_PAD); - vgSetParameteri (context->paint, - VG_PAINT_TYPE, - VG_PAINT_TYPE_LINEAR_GRADIENT); - vgSetParameterfv (context->paint, - VG_PAINT_LINEAR_GRADIENT, 4, linear); - - _vg_set_source_matrix (&lpat->base.base); - - CHECK_VG_ERRORS(); - return _vg_setup_gradient_stops (context, &lpat->base); - -} - -static cairo_status_t -_vg_setup_radial_source (cairo_vg_context_t *context, - const cairo_radial_pattern_t *rpat) -{ - VGfloat radial[5]; - - radial[0] = _cairo_fixed_to_double (rpat->c1.x); - radial[1] = _cairo_fixed_to_double (rpat->c1.y); - radial[2] = _cairo_fixed_to_double (rpat->c2.x); - radial[3] = _cairo_fixed_to_double (rpat->c2.y); - radial[4] = _cairo_fixed_to_double (rpat->r2); - - vgSetParameteri (context->paint, - VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); - vgSetParameteri (context->paint, - VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); - vgSetParameterfv (context->paint, - VG_PAINT_RADIAL_GRADIENT, 5, radial); - - _vg_set_source_matrix (&rpat->base.base); - - /* FIXME: copy/adapt fixes from SVG backend to add inner radius */ - - CHECK_VG_ERRORS(); - return _vg_setup_gradient_stops (context, &rpat->base); -} - -static cairo_status_t -_vg_setup_solid_source (cairo_vg_context_t *context, - const cairo_solid_pattern_t *spat) -{ - VGfloat color[] = { - spat->color.red, - spat->color.green, - spat->color.blue, - spat->color.alpha * context->alpha - }; - - vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); - vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_vg_surface_t * -_vg_clone_recording_surface (cairo_vg_context_t *context, - cairo_surface_t *surface) -{ - VGImage vg_image; - VGImageFormat format; - cairo_status_t status; - cairo_rectangle_int_t extents; - cairo_vg_surface_t *clone; - - status = _cairo_surface_get_extents (surface, &extents); - if (status) - return NULL; - - if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || - extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) - { - return NULL; - } - - format = _vg_format_for_content (surface->content); - - /* NONALIASED, FASTER, BETTER */ - vg_image = vgCreateImage (format, - extents.width, extents.height, - VG_IMAGE_QUALITY_FASTER); - clone = (cairo_vg_surface_t *) - _vg_surface_create_internal (context, vg_image, format, - extents.width, extents.height); - cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y); - - status = _cairo_recording_surface_replay (surface, &clone->base); - if (unlikely (status)) { - cairo_surface_destroy (&clone->base); - return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); - } - - return clone; -} - -static cairo_vg_surface_t * -_vg_clone_image_surface (cairo_vg_context_t *context, - cairo_surface_t *surface) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_status_t status; - VGImage vg_image; - VGImageFormat format; - cairo_rectangle_int_t extents; - cairo_vg_surface_t *clone; - - if (surface->backend->acquire_source_image == NULL) - return NULL; - - status = _cairo_surface_get_extents (surface, &extents); - if (status) - return NULL; - - if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || - extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) - { - return NULL; - } - - status = _cairo_surface_acquire_source_image (surface, - &image, &image_extra); - if (unlikely (status)) - return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); - - format = _vg_format_from_pixman (image->pixman_format); - if (format == 0) - format = _vg_format_for_content (image->base.content); - - /* NONALIASED, FASTER, BETTER */ - vg_image = vgCreateImage (format, - image->width, image->height, - VG_IMAGE_QUALITY_FASTER); - clone = (cairo_vg_surface_t *) - _vg_surface_create_internal (context, vg_image, format, - image->width, image->height); - if (unlikely (clone->base.status)) - return clone; - - vgImageSubData (clone->image, - image->data, image->stride, - format, 0, 0, image->width, image->height); - - _cairo_surface_release_source_image (surface, image, image_extra); - - return clone; -} - -static void -_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface) -{ - cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface; - - if (surface->snapshot_cache_entry.hash) { - cairo_vg_context_t *context; - - context = _vg_context_lock (surface->context); - _cairo_cache_remove (&context->snapshot_cache, - &surface->snapshot_cache_entry); - _vg_context_unlock (context); - - surface->snapshot_cache_entry.hash = 0; - } -} - -static cairo_status_t -_vg_setup_surface_source (cairo_vg_context_t *context, - const cairo_surface_pattern_t *spat) -{ - cairo_surface_t *snapshot; - cairo_vg_surface_t *clone; - cairo_status_t status; - - snapshot = _cairo_surface_has_snapshot (spat->surface, - &cairo_vg_surface_backend); - if (snapshot != NULL) { - clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot); - goto DONE; - } - - if (_cairo_surface_is_recording (spat->surface)) - clone = _vg_clone_recording_surface (context, spat->surface); - else - clone = _vg_clone_image_surface (context, spat->surface); - if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (clone->base.status)) - return clone->base.status; - - clone->snapshot_cache_entry.hash = clone->base.unique_id; - status = _cairo_cache_insert (&context->snapshot_cache, - &clone->snapshot_cache_entry); - if (unlikely (status)) { - clone->snapshot_cache_entry.hash = 0; - cairo_surface_destroy (&clone->base); - return status; - } - - cairo_surface_attach_snapshot (spat->surface, &clone->base, - _vg_surface_remove_from_cache); - -DONE: - cairo_surface_destroy (&context->source->base); - context->source = clone; - - vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); - - switch (spat->base.extend) { - case CAIRO_EXTEND_PAD: - vgSetParameteri (context->paint, - VG_PAINT_PATTERN_TILING_MODE, - VG_TILE_PAD); - break; - - case CAIRO_EXTEND_NONE: - vgSetParameteri (context->paint, - VG_PAINT_PATTERN_TILING_MODE, - VG_TILE_FILL); - { - VGfloat color[] = {0,0,0,0}; - vgSetfv (VG_TILE_FILL_COLOR, 4, color); - } - break; - - case CAIRO_EXTEND_REPEAT: - vgSetParameteri (context->paint, - VG_PAINT_PATTERN_TILING_MODE, - VG_TILE_REPEAT); - break; - - default: - ASSERT_NOT_REACHED; - case CAIRO_EXTEND_REFLECT: - vgSetParameteri (context->paint, - VG_PAINT_PATTERN_TILING_MODE, - VG_TILE_REFLECT); - break; - } - vgPaintPattern (context->paint, context->source->image); - - _vg_set_source_matrix (&spat->base); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -setup_source (cairo_vg_context_t *context, - const cairo_pattern_t *source) -{ - switch (source->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _vg_setup_solid_source (context, - (cairo_solid_pattern_t *) source); - case CAIRO_PATTERN_TYPE_LINEAR: - return _vg_setup_linear_source (context, - (cairo_linear_pattern_t *) source); - case CAIRO_PATTERN_TYPE_RADIAL: - return _vg_setup_radial_source (context, - (cairo_radial_pattern_t *) source); - case CAIRO_PATTERN_TYPE_SURFACE: - return _vg_setup_surface_source (context, - (cairo_surface_pattern_t *) source); - default: - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; - } -} - -static cairo_int_status_t -_vg_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t*source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_vg_context_t *context; - cairo_status_t status; - VGfloat state[9]; - VGfloat strokeTransform[9]; - vg_path_t vg_path; - - if (! _vg_is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - context = _vg_context_lock (surface->context); - status = _vg_context_set_target (context, surface); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = setup_source (context, source); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) { - _vg_context_unlock (context); - return status; - } - - vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, - VG_PATH_DATATYPE_F, - 1, 0, 0, 0, - VG_PATH_CAPABILITY_ALL); - - vgGetMatrix (state); - _vg_matrix_from_cairo (strokeTransform, ctm); - vgMultMatrix (strokeTransform); - - vg_path.ctm_inverse = ctm_inverse; - - _vg_path_from_cairo (&vg_path, path); - - /* XXX DASH_PATTERN, DASH_PHASE */ - vgSetf (VG_STROKE_LINE_WIDTH, style->line_width); - vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit); - vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join)); - vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap)); - - vgSeti (VG_BLEND_MODE, _vg_operator (op)); - - vgSetPaint (context->paint, VG_STROKE_PATH); - - vgDrawPath (vg_path.path, VG_STROKE_PATH); - - vgDestroyPath (vg_path.path); - - vgLoadMatrix (state); - - CHECK_VG_ERRORS(); - _vg_context_unlock (context); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_vg_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_vg_context_t *context; - cairo_status_t status; - vg_path_t vg_path; - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (! _vg_is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - context = _vg_context_lock (surface->context); - status = _vg_context_set_target (context, surface); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = setup_source (context, source); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) { - _vg_context_unlock (context); - return status; - } - - vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, - VG_PATH_DATATYPE_F, - 1, 0, - 0, 0, - VG_PATH_CAPABILITY_ALL); - vg_path.ctm_inverse = NULL; - - _vg_path_from_cairo (&vg_path, path); - - /* XXX tolerance */ - - vgSeti (VG_BLEND_MODE, _vg_operator (op)); - vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule)); - vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias)); - - vgSetPaint (context->paint, VG_FILL_PATH); - - vgDrawPath (vg_path.path, VG_FILL_PATH); - - vgDestroyPath (vg_path.path); - - _vg_context_unlock (context); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_vg_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_vg_context_t *context; - cairo_status_t status; - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (! _vg_is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - context = _vg_context_lock (surface->context); - status = _vg_context_set_target (context, surface); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = setup_source (context, source); - if (status) { - _vg_context_unlock (context); - return status; - } - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) { - _vg_context_unlock (context); - return status; - } - - vgSeti (VG_BLEND_MODE, _vg_operator (op)); - vgSetPaint (context->paint, VG_FILL_PATH); - - { /* creating a rectangular path that should cover the extent */ - VGubyte segs[] = { - VG_MOVE_TO_ABS, VG_LINE_TO_ABS, - VG_LINE_TO_ABS, VG_LINE_TO_ABS, - VG_CLOSE_PATH - }; - VGfloat data[] = { - 0, 0, - surface->width, 0, - surface->width, surface->height, - 0, surface->height - }; - VGPath fullext; - - fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, - 1,0,0,0, VG_PATH_CAPABILITY_ALL); - vgAppendPathData (fullext, sizeof(segs), segs, data); - - vgDrawPath (fullext, VG_FILL_PATH); - - vgDestroyPath (fullext); - } - - _vg_context_unlock (context); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_vg_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (! _vg_is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Handle paint-with-alpha to do fades cheaply */ - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; - cairo_vg_context_t *context = _vg_context_lock (surface->context); - double alpha = context->alpha; - - context->alpha = solid->color.alpha; - status = _vg_surface_paint (abstract_surface, op, source, clip); - context->alpha = alpha; - - _vg_context_unlock (context); - - return status; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_vg_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); -} - -static cairo_int_status_t -_vg_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_path_fixed_t path; - - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; - - _cairo_path_fixed_init (&path); - - /* XXX Glyph cache! OpenVG font support in 1.1? */ - - status = _cairo_scaled_font_glyph_path (scaled_font, - glyphs, num_glyphs, - &path); - if (unlikely (status)) - goto BAIL; - - status = _vg_surface_fill (abstract_surface, - op, source, &path, - CAIRO_FILL_RULE_WINDING, - CAIRO_GSTATE_TOLERANCE_DEFAULT, - CAIRO_ANTIALIAS_SUBPIXEL, - clip); -BAIL: - _cairo_path_fixed_fini (&path); - return status; -} - -static inline int -multiply_alpha (int alpha, int color) -{ - int temp = alpha * color + 0x80; - return (temp + (temp >> 8)) >> 8; -} - -static void -premultiply_argb (uint8_t *data, - int width, - int height, - int stride) -{ - int i; - - while (height --) { - uint32_t *row = (uint32_t *) data; - - for (i = 0; i < width; i++) { - uint32_t p = row[i]; - uint8_t alpha; - - alpha = p >> 24; - if (alpha == 0) { - row[i] = 0; - } else if (alpha != 0xff) { - uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff); - uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff); - uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff); - row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); - } - } - - data += stride; - } -} - -static cairo_int_status_t -_vg_get_image (cairo_vg_surface_t *surface, - int x, int y, - int width, int height, - cairo_image_surface_t **image_out) -{ - cairo_image_surface_t *image; - pixman_image_t *pixman_image; - pixman_format_code_t pixman_format; - cairo_bool_t needs_premultiply; - - pixman_format = _vg_format_to_pixman (surface->format, - &needs_premultiply); - if (pixman_format == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - pixman_image = pixman_image_create_bits (pixman_format, - width, height, - NULL, 0); - if (unlikely (pixman_image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - vgFinish (); - CHECK_VG_ERRORS(); - - vgGetImageSubData (surface->image, - pixman_image_get_data (pixman_image), - pixman_image_get_stride (pixman_image), - surface->format, - x, y, width, height); - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - if (unlikely (image->base.status)) { - pixman_image_unref (pixman_image); - return image->base.status; - } - - if (needs_premultiply) - premultiply_argb (image->data, width, height, image->stride); - - *image_out = image; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_vg_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_vg_surface_t *surface = abstract_surface; - - CHECK_VG_ERRORS(); - *image_extra = NULL; - return _vg_get_image (surface, - 0, 0, surface->width, surface->height, - image_out); -} - -static void -_vg_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_vg_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_vg_surface_t *surface = abstract_surface; - - *image_rect_out = *interest_rect; - *image_extra = NULL; - return _vg_get_image (surface, - interest_rect->x, interest_rect->y, - interest_rect->width, interest_rect->height, - image_out); -} - -static void -unpremultiply_argb (uint8_t *data, - int width, - int height, - int stride) -{ - int i; - - while (height--) { - uint32_t *row = (uint32_t *) data; - - for (i = 0; i < width; i ++) { - uint32_t p = row[i]; - uint8_t alpha; - - alpha = p >> 24; - if (alpha == 0) { - row[i] = 0; - } else if (alpha != 0xff) { - uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha; - uint8_t g = (((p >> 8) & 0xff) * 255 + alpha / 2) / alpha; - uint8_t b = (((p >> 0) & 0xff) * 255 + alpha / 2) / alpha; - row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); - } - } - - data += stride; - } -} - -static void -_vg_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_bool_t needs_unpremultiply; - - _vg_format_to_pixman (surface->format, &needs_unpremultiply); - if (needs_unpremultiply) { - unpremultiply_argb (image->data, - image->width, image->height, - image->stride); - } - - vgImageSubData (surface->image, - image->data, image->stride, - surface->format, - image_rect->x, image_rect->y, - image_rect->width, image_rect->height); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_vg_surface_finish (void *abstract_surface) -{ - cairo_vg_surface_t *surface = abstract_surface; - cairo_vg_context_t *context = _vg_context_lock (surface->context); - - if (surface->snapshot_cache_entry.hash) { - _cairo_cache_remove (&context->snapshot_cache, - &surface->snapshot_cache_entry); - - surface->snapshot_cache_entry.hash = 0; - } - - _cairo_surface_clipper_reset (&surface->clipper); - - if (surface->own_image) - vgDestroyImage (surface->image); - - _vg_context_destroy_target (context, surface); - - _vg_context_unlock (context); - _vg_context_destroy (context); - - CHECK_VG_ERRORS(); - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_vg_surface_backend = { - CAIRO_SURFACE_TYPE_VG, - _vg_surface_create_similar, - _vg_surface_finish, - - _vg_surface_acquire_source_image, - _vg_surface_release_source_image, - _vg_surface_acquire_dest_image, - _vg_surface_release_dest_image, - - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - - NULL, /* copy_page */ - NULL, /* show_page */ - _vg_surface_get_extents, - NULL, /* old_show_glyphs */ - _vg_surface_get_font_options, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _vg_surface_paint, - _vg_surface_mask, - _vg_surface_stroke, - _vg_surface_fill, - _vg_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ -}; - -static cairo_surface_t * -_vg_surface_create_internal (cairo_vg_context_t *context, - VGImage image, - VGImageFormat format, - int width, int height) -{ - cairo_vg_surface_t *surface; - - surface = malloc (sizeof (cairo_vg_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->context = _vg_context_reference (context); - - surface->image = image; - surface->format = format; - - _cairo_surface_init (&surface->base, - &cairo_vg_surface_backend, - NULL, /* device */ - _vg_format_to_content (format)); - - surface->width = width; - surface->height = height; - - _cairo_surface_clipper_init (&surface->clipper, - _vg_surface_clipper_intersect_clip_path); - - surface->snapshot_cache_entry.hash = 0; - - surface->target_id = 0; - - CHECK_VG_ERRORS(); - return &surface->base; -} - -cairo_surface_t * -cairo_vg_surface_create_for_image (cairo_vg_context_t *context, - VGImage image, - VGImageFormat format, - int width, int height) -{ - cairo_bool_t premult; - - if (context->status) - return _cairo_surface_create_in_error (context->status); - - if (image == VG_INVALID_HANDLE) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - if (_vg_format_to_pixman (format, &premult) == 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - return _vg_surface_create_internal (context, image, format, width, height); -} - -cairo_surface_t * -cairo_vg_surface_create (cairo_vg_context_t *context, - cairo_content_t content, - int width, - int height) -{ - VGImage image; - VGImageFormat format; - cairo_surface_t *surface; - - if (context->status) - return _cairo_surface_create_in_error (context->status); - - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || - height > vgGeti (VG_MAX_IMAGE_HEIGHT)) - { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - } - - - format = _vg_format_for_content (content); - image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER); - if (image == VG_INVALID_HANDLE) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface = _vg_surface_create_internal (context, - image, format, width, height); - if (unlikely (surface->status)) - return surface; - - ((cairo_vg_surface_t *) surface)->own_image = TRUE; - return surface; -} -slim_hidden_def (cairo_vg_surface_create); - -VGImage -cairo_vg_surface_get_image (cairo_surface_t *abstract_surface) -{ - cairo_vg_surface_t *surface; - - if (abstract_surface->backend != &cairo_vg_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return VG_INVALID_HANDLE; - } - - surface = (cairo_vg_surface_t *) abstract_surface; - return surface->image; -} - -int -cairo_vg_surface_get_width (cairo_surface_t *abstract_surface) -{ - cairo_vg_surface_t *surface; - - if (abstract_surface->backend != &cairo_vg_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - surface = (cairo_vg_surface_t *) abstract_surface; - return surface->width; -} - -int -cairo_vg_surface_get_height (cairo_surface_t *abstract_surface) -{ - cairo_vg_surface_t *surface; - - if (abstract_surface->backend != &cairo_vg_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - surface = (cairo_vg_surface_t *) abstract_surface; - return surface->height; -} - -VGImageFormat -cairo_vg_surface_get_format (cairo_surface_t *abstract_surface) -{ - cairo_vg_surface_t *surface; - - if (abstract_surface->backend != &cairo_vg_surface_backend) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - surface = (cairo_vg_surface_t *) abstract_surface; - return surface->format; -} - -/* GL specific context support :-( - * - * OpenVG like cairo defers creation of surface (and the necessary - * paraphernalia to the application. - */ - -static const cairo_vg_context_t _vg_context_nil = { - CAIRO_STATUS_NO_MEMORY, - CAIRO_REFERENCE_COUNT_INVALID -}; - -static const cairo_vg_context_t _vg_context_nil_invalid_visual = { - CAIRO_STATUS_INVALID_VISUAL, - CAIRO_REFERENCE_COUNT_INVALID -}; - -#if CAIRO_HAS_GLX_FUNCTIONS -#include - -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/libs/cairo/cairo/src/cairo-vg.h b/libs/cairo/cairo/src/cairo-vg.h deleted file mode 100644 index 7f1097d04..000000000 --- a/libs/cairo/cairo/src/cairo-vg.h +++ /dev/null @@ -1,69 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_VG_H -#define CAIRO_VG_H - -#include "cairo.h" - -#if CAIRO_HAS_VG_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -typedef struct _cairo_vg_context cairo_vg_context_t; - -#if CAIRO_HAS_GLX_FUNCTIONS -typedef struct __GLXcontextRec *GLXContext; -typedef struct _XDisplay Display; - -cairo_public cairo_vg_context_t * -cairo_vg_context_create_for_glx (Display *dpy, - GLXContext ctx); -#endif - -#if CAIRO_HAS_EGL_FUNCTIONS -#include - -cairo_public cairo_vg_context_t * -cairo_vg_context_create_for_egl (EGLDisplay egl_display, - EGLContext egl_context); -#endif - -cairo_public cairo_status_t -cairo_vg_context_status (cairo_vg_context_t *context); - -cairo_public void -cairo_vg_context_destroy (cairo_vg_context_t *context); - -cairo_public cairo_surface_t * -cairo_vg_surface_create (cairo_vg_context_t *context, - cairo_content_t content, int width, int height); - -cairo_public cairo_surface_t * -cairo_vg_surface_create_for_image (cairo_vg_context_t *context, - VGImage image, - VGImageFormat format, - int width, int height); - -cairo_public VGImage -cairo_vg_surface_get_image (cairo_surface_t *abstract_surface); - -cairo_public VGImageFormat -cairo_vg_surface_get_format (cairo_surface_t *abstract_surface); - -cairo_public int -cairo_vg_surface_get_height (cairo_surface_t *abstract_surface); - -cairo_public int -cairo_vg_surface_get_width (cairo_surface_t *abstract_surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_VG_SURFACE*/ -# error Cairo was not compiled with support for the OpenVG backend -#endif /* CAIRO_HAS_VG_SURFACE*/ - -#endif /* CAIRO_VG_H */ diff --git a/libs/cairo/cairo/src/cairo-wideint-private.h b/libs/cairo/cairo/src/cairo-wideint-private.h deleted file mode 100644 index a1ae4dce8..000000000 --- a/libs/cairo/cairo/src/cairo-wideint-private.h +++ /dev/null @@ -1,288 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_WIDEINT_H -#define CAIRO_WIDEINT_H - -#include "cairo-wideint-type-private.h" - -#include "cairo-compiler-private.h" - -/* - * 64-bit datatypes. Two separate implementations, one using - * built-in 64-bit signed/unsigned types another implemented - * as a pair of 32-bit ints - */ - -#define I cairo_private cairo_const - -#if !HAVE_UINT64_T - -cairo_uquorem64_t I -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); - -cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i); -#define _cairo_uint64_to_uint32(a) ((a).lo) -cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b); -cairo_uint64_t I _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b); -cairo_uint64_t I _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b); -cairo_uint64_t I _cairo_uint32x32_64_mul (uint32_t a, uint32_t b); -cairo_uint64_t I _cairo_uint64_lsl (cairo_uint64_t a, int shift); -cairo_uint64_t I _cairo_uint64_rsl (cairo_uint64_t a, int shift); -cairo_uint64_t I _cairo_uint64_rsa (cairo_uint64_t a, int shift); -int I _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b); -int I _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b); -int I _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b); -cairo_uint64_t I _cairo_uint64_negate (cairo_uint64_t a); -#define _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0) -#define _cairo_uint64_negative(a) (((int32_t) ((a).hi)) < 0) -cairo_uint64_t I _cairo_uint64_not (cairo_uint64_t a); - -#define _cairo_uint64_to_int64(i) (i) -#define _cairo_int64_to_uint64(i) (i) - -cairo_int64_t I _cairo_int32_to_int64(int32_t i); -#define _cairo_int64_to_int32(a) ((int32_t) _cairo_uint64_to_uint32(a)) -#define _cairo_int64_add(a,b) _cairo_uint64_add (a,b) -#define _cairo_int64_sub(a,b) _cairo_uint64_sub (a,b) -#define _cairo_int64_mul(a,b) _cairo_uint64_mul (a,b) -cairo_int64_t I _cairo_int32x32_64_mul (int32_t a, int32_t b); -int I _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b); -int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); -#define _cairo_int64_is_zero(a) _cairo_uint64_is_zero (a) -#define _cairo_int64_eq(a,b) _cairo_uint64_eq (a,b) -#define _cairo_int64_lsl(a,b) _cairo_uint64_lsl (a,b) -#define _cairo_int64_rsl(a,b) _cairo_uint64_rsl (a,b) -#define _cairo_int64_rsa(a,b) _cairo_uint64_rsa (a,b) -#define _cairo_int64_negate(a) _cairo_uint64_negate(a) -#define _cairo_int64_negative(a) (((int32_t) ((a).hi)) < 0) -#define _cairo_int64_not(a) _cairo_uint64_not(a) - -#else - -static inline cairo_uquorem64_t -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) -{ - cairo_uquorem64_t qr; - - qr.quo = num / den; - qr.rem = num % den; - return qr; -} - -#define _cairo_uint32_to_uint64(i) ((uint64_t) (i)) -#define _cairo_uint64_to_uint32(i) ((uint32_t) (i)) -#define _cairo_uint64_add(a,b) ((a) + (b)) -#define _cairo_uint64_sub(a,b) ((a) - (b)) -#define _cairo_uint64_mul(a,b) ((a) * (b)) -#define _cairo_uint32x32_64_mul(a,b) ((uint64_t) (a) * (b)) -#define _cairo_uint64_lsl(a,b) ((a) << (b)) -#define _cairo_uint64_rsl(a,b) ((uint64_t) (a) >> (b)) -#define _cairo_uint64_rsa(a,b) ((uint64_t) ((int64_t) (a) >> (b))) -#define _cairo_uint64_lt(a,b) ((a) < (b)) -#define _cairo_uint64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) -#define _cairo_uint64_is_zero(a) ((a) == 0) -#define _cairo_uint64_eq(a,b) ((a) == (b)) -#define _cairo_uint64_negate(a) ((uint64_t) -((int64_t) (a))) -#define _cairo_uint64_negative(a) ((int64_t) (a) < 0) -#define _cairo_uint64_not(a) (~(a)) - -#define _cairo_uint64_to_int64(i) ((int64_t) (i)) -#define _cairo_int64_to_uint64(i) ((uint64_t) (i)) - -#define _cairo_int32_to_int64(i) ((int64_t) (i)) -#define _cairo_int64_to_int32(i) ((int32_t) (i)) -#define _cairo_int64_add(a,b) ((a) + (b)) -#define _cairo_int64_sub(a,b) ((a) - (b)) -#define _cairo_int64_mul(a,b) ((a) * (b)) -#define _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b)) -#define _cairo_int64_lt(a,b) ((a) < (b)) -#define _cairo_int64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) -#define _cairo_int64_is_zero(a) ((a) == 0) -#define _cairo_int64_eq(a,b) ((a) == (b)) -#define _cairo_int64_lsl(a,b) ((a) << (b)) -#define _cairo_int64_rsl(a,b) ((int64_t) ((uint64_t) (a) >> (b))) -#define _cairo_int64_rsa(a,b) ((int64_t) (a) >> (b)) -#define _cairo_int64_negate(a) (-(a)) -#define _cairo_int64_negative(a) ((a) < 0) -#define _cairo_int64_not(a) (~(a)) - -#endif - -/* - * 64-bit comparisions derived from lt or eq - */ -#define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b)) -#define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b)) -#define _cairo_uint64_ge(a,b) (!_cairo_uint64_lt(a,b)) -#define _cairo_uint64_gt(a,b) _cairo_uint64_lt(b,a) - -#define _cairo_int64_le(a,b) (!_cairo_int64_gt(a,b)) -#define _cairo_int64_ne(a,b) (!_cairo_int64_eq(a,b)) -#define _cairo_int64_ge(a,b) (!_cairo_int64_lt(a,b)) -#define _cairo_int64_gt(a,b) _cairo_int64_lt(b,a) - -/* - * As the C implementation always computes both, create - * a function which returns both for the 'native' type as well - */ - -static inline cairo_quorem64_t -_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) -{ - int num_neg = _cairo_int64_negative (num); - int den_neg = _cairo_int64_negative (den); - cairo_uquorem64_t uqr; - cairo_quorem64_t qr; - - if (num_neg) - num = _cairo_int64_negate (num); - if (den_neg) - den = _cairo_int64_negate (den); - uqr = _cairo_uint64_divrem (num, den); - if (num_neg) - qr.rem = _cairo_int64_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); - else - qr.quo = (cairo_int64_t) uqr.quo; - return qr; -} - -static inline int32_t -_cairo_int64_32_div (cairo_int64_t num, int32_t den) -{ -#if !HAVE_UINT64_T - return _cairo_int64_to_int32 - (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo); -#else - return num / den; -#endif -} - -/* - * 128-bit datatypes. Again, provide two implementations in - * case the machine has a native 128-bit datatype. GCC supports int128_t - * on ia64 - */ - -#if !HAVE_UINT128_T - -cairo_uint128_t I _cairo_uint32_to_uint128 (uint32_t i); -cairo_uint128_t I _cairo_uint64_to_uint128 (cairo_uint64_t i); -#define _cairo_uint128_to_uint64(a) ((a).lo) -#define _cairo_uint128_to_uint32(a) _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a)) -cairo_uint128_t I _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b); -cairo_uint128_t I _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b); -cairo_uint128_t I _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b); -cairo_uint128_t I _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b); -cairo_uint128_t I _cairo_uint128_lsl (cairo_uint128_t a, int shift); -cairo_uint128_t I _cairo_uint128_rsl (cairo_uint128_t a, int shift); -cairo_uint128_t I _cairo_uint128_rsa (cairo_uint128_t a, int shift); -int I _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b); -int I _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b); -int I _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b); -#define _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo)) -cairo_uint128_t I _cairo_uint128_negate (cairo_uint128_t a); -#define _cairo_uint128_negative(a) (_cairo_uint64_negative(a.hi)) -cairo_uint128_t I _cairo_uint128_not (cairo_uint128_t a); - -#define _cairo_uint128_to_int128(i) (i) -#define _cairo_int128_to_uint128(i) (i) - -cairo_int128_t I _cairo_int32_to_int128 (int32_t i); -cairo_int128_t I _cairo_int64_to_int128 (cairo_int64_t i); -#define _cairo_int128_to_int64(a) ((cairo_int64_t) (a).lo) -#define _cairo_int128_to_int32(a) _cairo_int64_to_int32(_cairo_int128_to_int64(a)) -#define _cairo_int128_add(a,b) _cairo_uint128_add(a,b) -#define _cairo_int128_sub(a,b) _cairo_uint128_sub(a,b) -#define _cairo_int128_mul(a,b) _cairo_uint128_mul(a,b) -cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b); -#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) -#define _cairo_int128_lsl(a,b) _cairo_uint128_lsl(a,b) -#define _cairo_int128_rsl(a,b) _cairo_uint128_rsl(a,b) -#define _cairo_int128_rsa(a,b) _cairo_uint128_rsa(a,b) -int I _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b); -int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b); -#define _cairo_int128_is_zero(a) _cairo_uint128_is_zero (a) -#define _cairo_int128_eq(a,b) _cairo_uint128_eq (a,b) -#define _cairo_int128_negate(a) _cairo_uint128_negate(a) -#define _cairo_int128_negative(a) (_cairo_uint128_negative(a)) -#define _cairo_int128_not(a) _cairo_uint128_not(a) - -#else /* !HAVE_UINT128_T */ - -#define _cairo_uint32_to_uint128(i) ((uint128_t) (i)) -#define _cairo_uint64_to_uint128(i) ((uint128_t) (i)) -#define _cairo_uint128_to_uint64(i) ((uint64_t) (i)) -#define _cairo_uint128_to_uint32(i) ((uint32_t) (i)) -#define _cairo_uint128_add(a,b) ((a) + (b)) -#define _cairo_uint128_sub(a,b) ((a) - (b)) -#define _cairo_uint128_mul(a,b) ((a) * (b)) -#define _cairo_uint64x64_128_mul(a,b) ((uint128_t) (a) * (b)) -#define _cairo_uint128_lsl(a,b) ((a) << (b)) -#define _cairo_uint128_rsl(a,b) ((uint128_t) (a) >> (b)) -#define _cairo_uint128_rsa(a,b) ((uint128_t) ((int128_t) (a) >> (b))) -#define _cairo_uint128_lt(a,b) ((a) < (b)) -#define _cairo_uint128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) -#define _cairo_uint128_is_zero(a) ((a) == 0) -#define _cairo_uint128_eq(a,b) ((a) == (b)) -#define _cairo_uint128_negate(a) ((uint128_t) -((int128_t) (a))) -#define _cairo_uint128_negative(a) ((int128_t) (a) < 0) -#define _cairo_uint128_not(a) (~(a)) - -#define _cairo_uint128_to_int128(i) ((int128_t) (i)) -#define _cairo_int128_to_uint128(i) ((uint128_t) (i)) - -#define _cairo_int32_to_int128(i) ((int128_t) (i)) -#define _cairo_int64_to_int128(i) ((int128_t) (i)) -#define _cairo_int128_to_int64(i) ((int64_t) (i)) -#define _cairo_int128_to_int32(i) ((int32_t) (i)) -#define _cairo_int128_add(a,b) ((a) + (b)) -#define _cairo_int128_sub(a,b) ((a) - (b)) -#define _cairo_int128_mul(a,b) ((a) * (b)) -#define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b)) -#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) -#define _cairo_int128_lt(a,b) ((a) < (b)) -#define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) -#define _cairo_int128_is_zero(a) ((a) == 0) -#define _cairo_int128_eq(a,b) ((a) == (b)) -#define _cairo_int128_lsl(a,b) ((a) << (b)) -#define _cairo_int128_rsl(a,b) ((int128_t) ((uint128_t) (a) >> (b))) -#define _cairo_int128_rsa(a,b) ((int128_t) (a) >> (b)) -#define _cairo_int128_negate(a) (-(a)) -#define _cairo_int128_negative(a) ((a) < 0) -#define _cairo_int128_not(a) (~(a)) - -#endif /* HAVE_UINT128_T */ - -cairo_uquorem128_t I -_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den); - -cairo_quorem128_t I -_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den); - -cairo_uquorem64_t I -_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, - cairo_uint64_t den); - -cairo_quorem64_t I -_cairo_int_96by64_32x64_divrem (cairo_int128_t num, - cairo_int64_t den); - -#define _cairo_uint128_le(a,b) (!_cairo_uint128_gt(a,b)) -#define _cairo_uint128_ne(a,b) (!_cairo_uint128_eq(a,b)) -#define _cairo_uint128_ge(a,b) (!_cairo_uint128_lt(a,b)) -#define _cairo_uint128_gt(a,b) _cairo_uint128_lt(b,a) - -#define _cairo_int128_le(a,b) (!_cairo_int128_gt(a,b)) -#define _cairo_int128_ne(a,b) (!_cairo_int128_eq(a,b)) -#define _cairo_int128_ge(a,b) (!_cairo_int128_lt(a,b)) -#define _cairo_int128_gt(a,b) _cairo_int128_lt(b,a) - -#undef I - -#endif /* CAIRO_WIDEINT_H */ diff --git a/libs/cairo/cairo/src/cairo-wideint-type-private.h b/libs/cairo/cairo/src/cairo-wideint-type-private.h deleted file mode 100644 index d7f0319a2..000000000 --- a/libs/cairo/cairo/src/cairo-wideint-type-private.h +++ /dev/null @@ -1,130 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_WIDEINT_TYPE_H -#define CAIRO_WIDEINT_TYPE_H - -#include "cairo.h" - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_STDINT_H -# include -#elif HAVE_INTTYPES_H -# include -#elif HAVE_SYS_INT_TYPES_H -# include -#elif defined(_MSC_VER) - typedef __int8 int8_t; - typedef unsigned __int8 uint8_t; - typedef __int16 int16_t; - typedef unsigned __int16 uint16_t; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#ifndef _UINTPTR_T_DEFINED -#ifdef _WIN64 - typedef unsigned __int64 uintptr_t; -#else - typedef unsigned int uintptr_t; -#endif -#endif -# ifndef HAVE_UINT64_T -# define HAVE_UINT64_T 1 -# endif -#else -#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.) -#endif - -#ifndef INT16_MIN -# define INT16_MIN (-32767-1) -#endif -#ifndef INT16_MAX -# define INT16_MAX (32767) -#endif -#ifndef UINT16_MAX -# define UINT16_MAX (65535) -#endif -#ifndef INT32_MIN -# define INT32_MIN (-2147483647-1) -#endif -#ifndef INT32_MAX -# define INT32_MAX (2147483647) -#endif - -#if HAVE_BYTESWAP_H -# include -#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/libs/cairo/cairo/src/cairo-wideint.c b/libs/cairo/cairo/src/cairo-wideint.c deleted file mode 100644 index 90809c005..000000000 --- a/libs/cairo/cairo/src/cairo-wideint.c +++ /dev/null @@ -1,788 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#if HAVE_UINT64_T - -#define uint64_lo32(i) ((i) & 0xffffffff) -#define uint64_hi32(i) ((i) >> 32) -#define uint64_lo(i) ((i) & 0xffffffff) -#define uint64_hi(i) ((i) >> 32) -#define uint64_shift32(i) ((i) << 32) -#define uint64_carry32 (((uint64_t) 1) << 32) - -#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) - -#else - -#define uint64_lo32(i) ((i).lo) -#define uint64_hi32(i) ((i).hi) - -static cairo_uint64_t -uint64_lo (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.lo; - s.hi = 0; - return s; -} - -static cairo_uint64_t -uint64_hi (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.hi; - s.hi = 0; - return s; -} - -static cairo_uint64_t -uint64_shift32 (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = 0; - s.hi = i.lo; - return s; -} - -static const cairo_uint64_t uint64_carry32 = { 0, 1 }; - -cairo_uint64_t -_cairo_uint32_to_uint64 (uint32_t i) -{ - cairo_uint64_t q; - - q.lo = i; - q.hi = 0; - return q; -} - -cairo_int64_t -_cairo_int32_to_int64 (int32_t i) -{ - cairo_uint64_t q; - - q.lo = i; - q.hi = i < 0 ? -1 : 0; - return q; -} - -static cairo_uint64_t -_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l) -{ - cairo_uint64_t q; - - q.lo = l; - q.hi = h; - return q; -} - -cairo_uint64_t -_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s.hi = a.hi + b.hi; - s.lo = a.lo + b.lo; - if (s.lo < a.lo) - s.hi++; - return s; -} - -cairo_uint64_t -_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s.hi = a.hi - b.hi; - s.lo = a.lo - b.lo; - if (s.lo > a.lo) - s.hi--; - return s; -} - -#define uint32_lo(i) ((i) & 0xffff) -#define uint32_hi(i) ((i) >> 16) -#define uint32_carry16 ((1) << 16) - -cairo_uint64_t -_cairo_uint32x32_64_mul (uint32_t a, uint32_t b) -{ - cairo_uint64_t s; - - uint16_t ah, al, bh, bl; - uint32_t r0, r1, r2, r3; - - al = uint32_lo (a); - ah = uint32_hi (a); - bl = uint32_lo (b); - bh = uint32_hi (b); - - r0 = (uint32_t) al * bl; - r1 = (uint32_t) al * bh; - r2 = (uint32_t) ah * bl; - r3 = (uint32_t) ah * bh; - - r1 += uint32_hi(r0); /* no carry possible */ - r1 += r2; /* but this can carry */ - if (r1 < r2) /* check */ - r3 += uint32_carry16; - - s.hi = r3 + uint32_hi(r1); - s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0); - return s; -} - -cairo_int64_t -_cairo_int32x32_64_mul (int32_t a, int32_t b) -{ - cairo_int64_t s; - s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b); - if (a < 0) - s.hi -= b; - if (b < 0) - s.hi -= a; - return s; -} - -cairo_uint64_t -_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s = _cairo_uint32x32_64_mul (a.lo, b.lo); - s.hi += a.lo * b.hi + a.hi * b.lo; - return s; -} - -cairo_uint64_t -_cairo_uint64_lsl (cairo_uint64_t a, int shift) -{ - if (shift >= 32) - { - a.hi = a.lo; - a.lo = 0; - shift -= 32; - } - if (shift) - { - a.hi = a.hi << shift | a.lo >> (32 - shift); - a.lo = a.lo << shift; - } - return a; -} - -cairo_uint64_t -_cairo_uint64_rsl (cairo_uint64_t a, int shift) -{ - if (shift >= 32) - { - a.lo = a.hi; - a.hi = 0; - shift -= 32; - } - if (shift) - { - a.lo = a.lo >> shift | a.hi << (32 - shift); - a.hi = a.hi >> shift; - } - return a; -} - -#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n))) - -cairo_int64_t -_cairo_uint64_rsa (cairo_int64_t a, int shift) -{ - if (shift >= 32) - { - a.lo = a.hi; - a.hi = _cairo_uint32_rsa (a.hi, 31); - shift -= 32; - } - if (shift) - { - a.lo = a.lo >> shift | a.hi << (32 - shift); - a.hi = _cairo_uint32_rsa (a.hi, shift); - } - return a; -} - -int -_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b) -{ - return (a.hi < b.hi || - (a.hi == b.hi && a.lo < b.lo)); -} - -int -_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b) -{ - return a.hi == b.hi && a.lo == b.lo; -} - -int -_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b) -{ - if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) - return 1; - if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) - return 0; - return _cairo_uint64_lt (a, b); -} - -int -_cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b) -{ - if (a.hi < b.hi) - return -1; - else if (a.hi > b.hi) - return 1; - else if (a.lo < b.lo) - return -1; - else if (a.lo > b.lo) - return 1; - else - return 0; -} - -int -_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b) -{ - if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) - return -1; - if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) - return 1; - - return _cairo_uint64_cmp (a, b); -} - -cairo_uint64_t -_cairo_uint64_not (cairo_uint64_t a) -{ - a.lo = ~a.lo; - a.hi = ~a.hi; - return a; -} - -cairo_uint64_t -_cairo_uint64_negate (cairo_uint64_t a) -{ - a.lo = ~a.lo; - a.hi = ~a.hi; - if (++a.lo == 0) - ++a.hi; - return a; -} - -/* - * Simple bit-at-a-time divide. - */ -cairo_uquorem64_t -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) -{ - cairo_uquorem64_t qr; - cairo_uint64_t bit; - cairo_uint64_t quo; - - bit = _cairo_uint32_to_uint64 (1); - - /* normalize to make den >= num, but not overflow */ - while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0) - { - bit = _cairo_uint64_lsl (bit, 1); - den = _cairo_uint64_lsl (den, 1); - } - quo = _cairo_uint32_to_uint64 (0); - - /* generate quotient, one bit at a time */ - while (bit.hi | bit.lo) - { - if (_cairo_uint64_le (den, num)) - { - num = _cairo_uint64_sub (num, den); - quo = _cairo_uint64_add (quo, bit); - } - bit = _cairo_uint64_rsl (bit, 1); - den = _cairo_uint64_rsl (den, 1); - } - qr.quo = quo; - qr.rem = num; - return qr; -} - -#endif /* !HAVE_UINT64_T */ - -#if HAVE_UINT128_T -cairo_uquorem128_t -_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) -{ - cairo_uquorem128_t qr; - - qr.quo = num / den; - qr.rem = num % den; - return qr; -} - -#else - -cairo_uint128_t -_cairo_uint32_to_uint128 (uint32_t i) -{ - cairo_uint128_t q; - - q.lo = _cairo_uint32_to_uint64 (i); - q.hi = _cairo_uint32_to_uint64 (0); - return q; -} - -cairo_int128_t -_cairo_int32_to_int128 (int32_t i) -{ - cairo_int128_t q; - - q.lo = _cairo_int32_to_int64 (i); - q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0); - return q; -} - -cairo_uint128_t -_cairo_uint64_to_uint128 (cairo_uint64_t i) -{ - cairo_uint128_t q; - - q.lo = i; - q.hi = _cairo_uint32_to_uint64 (0); - return q; -} - -cairo_int128_t -_cairo_int64_to_int128 (cairo_int64_t i) -{ - cairo_int128_t q; - - q.lo = i; - q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0); - return q; -} - -cairo_uint128_t -_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s.hi = _cairo_uint64_add (a.hi, b.hi); - s.lo = _cairo_uint64_add (a.lo, b.lo); - if (_cairo_uint64_lt (s.lo, a.lo)) - s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1)); - return s; -} - -cairo_uint128_t -_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s.hi = _cairo_uint64_sub (a.hi, b.hi); - s.lo = _cairo_uint64_sub (a.lo, b.lo); - if (_cairo_uint64_gt (s.lo, a.lo)) - s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1)); - return s; -} - -cairo_uint128_t -_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint128_t s; - uint32_t ah, al, bh, bl; - cairo_uint64_t r0, r1, r2, r3; - - al = uint64_lo32 (a); - ah = uint64_hi32 (a); - bl = uint64_lo32 (b); - bh = uint64_hi32 (b); - - r0 = _cairo_uint32x32_64_mul (al, bl); - r1 = _cairo_uint32x32_64_mul (al, bh); - r2 = _cairo_uint32x32_64_mul (ah, bl); - r3 = _cairo_uint32x32_64_mul (ah, bh); - - r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */ - r1 = _cairo_uint64_add (r1, r2); /* but this can carry */ - if (_cairo_uint64_lt (r1, r2)) /* check */ - r3 = _cairo_uint64_add (r3, uint64_carry32); - - s.hi = _cairo_uint64_add (r3, uint64_hi(r1)); - s.lo = _cairo_uint64_add (uint64_shift32 (r1), - uint64_lo (r0)); - return s; -} - -cairo_int128_t -_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b) -{ - cairo_int128_t s; - s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a), - _cairo_int64_to_uint64(b)); - if (_cairo_int64_negative (a)) - s.hi = _cairo_uint64_sub (s.hi, - _cairo_int64_to_uint64 (b)); - if (_cairo_int64_negative (b)) - s.hi = _cairo_uint64_sub (s.hi, - _cairo_int64_to_uint64 (a)); - return s; -} - -cairo_uint128_t -_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s = _cairo_uint64x64_128_mul (a.lo, b.lo); - s.hi = _cairo_uint64_add (s.hi, - _cairo_uint64_mul (a.lo, b.hi)); - s.hi = _cairo_uint64_add (s.hi, - _cairo_uint64_mul (a.hi, b.lo)); - return s; -} - -cairo_uint128_t -_cairo_uint128_lsl (cairo_uint128_t a, int shift) -{ - if (shift >= 64) - { - a.hi = a.lo; - a.lo = _cairo_uint32_to_uint64 (0); - shift -= 64; - } - if (shift) - { - a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift), - _cairo_uint64_rsl (a.lo, (64 - shift))); - a.lo = _cairo_uint64_lsl (a.lo, shift); - } - return a; -} - -cairo_uint128_t -_cairo_uint128_rsl (cairo_uint128_t a, int shift) -{ - if (shift >= 64) - { - a.lo = a.hi; - a.hi = _cairo_uint32_to_uint64 (0); - shift -= 64; - } - if (shift) - { - a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), - _cairo_uint64_lsl (a.hi, (64 - shift))); - a.hi = _cairo_uint64_rsl (a.hi, shift); - } - return a; -} - -cairo_uint128_t -_cairo_uint128_rsa (cairo_int128_t a, int shift) -{ - if (shift >= 64) - { - a.lo = a.hi; - a.hi = _cairo_uint64_rsa (a.hi, 64-1); - shift -= 64; - } - if (shift) - { - a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), - _cairo_uint64_lsl (a.hi, (64 - shift))); - a.hi = _cairo_uint64_rsa (a.hi, shift); - } - return a; -} - -int -_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b) -{ - return (_cairo_uint64_lt (a.hi, b.hi) || - (_cairo_uint64_eq (a.hi, b.hi) && - _cairo_uint64_lt (a.lo, b.lo))); -} - -int -_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b) -{ - if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) - return 1; - if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) - return 0; - return _cairo_uint128_lt (a, b); -} - -int -_cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b) -{ - int cmp; - - cmp = _cairo_uint64_cmp (a.hi, b.hi); - if (cmp) - return cmp; - return _cairo_uint64_cmp (a.lo, b.lo); -} - -int -_cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b) -{ - if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) - return -1; - if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) - return 1; - - return _cairo_uint128_cmp (a, b); -} - -int -_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b) -{ - return (_cairo_uint64_eq (a.hi, b.hi) && - _cairo_uint64_eq (a.lo, b.lo)); -} - -#if HAVE_UINT64_T -#define _cairo_msbset64(q) (q & ((uint64_t) 1 << 63)) -#else -#define _cairo_msbset64(q) (q.hi & ((uint32_t) 1 << 31)) -#endif - -cairo_uquorem128_t -_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) -{ - cairo_uquorem128_t qr; - cairo_uint128_t bit; - cairo_uint128_t quo; - - bit = _cairo_uint32_to_uint128 (1); - - /* normalize to make den >= num, but not overflow */ - while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi)) - { - bit = _cairo_uint128_lsl (bit, 1); - den = _cairo_uint128_lsl (den, 1); - } - quo = _cairo_uint32_to_uint128 (0); - - /* generate quotient, one bit at a time */ - while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0))) - { - if (_cairo_uint128_le (den, num)) - { - num = _cairo_uint128_sub (num, den); - quo = _cairo_uint128_add (quo, bit); - } - bit = _cairo_uint128_rsl (bit, 1); - den = _cairo_uint128_rsl (den, 1); - } - qr.quo = quo; - qr.rem = num; - return qr; -} - -cairo_int128_t -_cairo_int128_negate (cairo_int128_t a) -{ - a.lo = _cairo_uint64_not (a.lo); - a.hi = _cairo_uint64_not (a.hi); - return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1)); -} - -cairo_int128_t -_cairo_int128_not (cairo_int128_t a) -{ - a.lo = _cairo_uint64_not (a.lo); - a.hi = _cairo_uint64_not (a.hi); - return a; -} - -#endif /* !HAVE_UINT128_T */ - -cairo_quorem128_t -_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) -{ - int num_neg = _cairo_int128_negative (num); - int den_neg = _cairo_int128_negative (den); - cairo_uquorem128_t uqr; - cairo_quorem128_t qr; - - if (num_neg) - num = _cairo_int128_negate (num); - if (den_neg) - den = _cairo_int128_negate (den); - uqr = _cairo_uint128_divrem (num, den); - if (num_neg) - qr.rem = _cairo_int128_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = _cairo_int128_negate (uqr.quo); - else - qr.quo = uqr.quo; - return qr; -} - -/** - * _cairo_uint_96by64_32x64_divrem: - * - * Compute a 32 bit quotient and 64 bit remainder of a 96 bit unsigned - * dividend and 64 bit divisor. If the quotient doesn't fit into 32 - * bits then the returned remainder is equal to the divisor, and the - * quotient is the largest representable 64 bit integer. It is an - * error to call this function with the high 32 bits of @num being - * non-zero. */ -cairo_uquorem64_t -_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, - cairo_uint64_t den) -{ - cairo_uquorem64_t result; - cairo_uint64_t B = _cairo_uint32s_to_uint64 (1, 0); - - /* These are the high 64 bits of the *96* bit numerator. We're - * going to represent the numerator as xB + y, where x is a 64, - * and y is a 32 bit number. */ - cairo_uint64_t x = _cairo_uint128_to_uint64 (_cairo_uint128_rsl(num, 32)); - - /* Initialise the result to indicate overflow. */ - result.quo = _cairo_uint32s_to_uint64 (-1U, -1U); - result.rem = den; - - /* Don't bother if the quotient is going to overflow. */ - if (_cairo_uint64_ge (x, den)) { - return /* overflow */ result; - } - - if (_cairo_uint64_lt (x, B)) { - /* When the final quotient is known to fit in 32 bits, then - * num < 2^64 if and only if den < 2^32. */ - return _cairo_uint64_divrem (_cairo_uint128_to_uint64 (num), den); - } - else { - /* Denominator is >= 2^32. the numerator is >= 2^64, and the - * division won't overflow: need two divrems. Write the - * numerator and denominator as - * - * num = xB + y x : 64 bits, y : 32 bits - * den = uB + v u, v : 32 bits - */ - uint32_t y = _cairo_uint128_to_uint32 (num); - uint32_t u = uint64_hi32 (den); - uint32_t v = _cairo_uint64_to_uint32 (den); - - /* Compute a lower bound approximate quotient of num/den - * from x/(u+1). Then we have - * - * x = q(u+1) + r ; q : 32 bits, r <= u : 32 bits. - * - * xB + y = q(u+1)B + (rB+y) - * = q(uB + B + v - v) + (rB+y) - * = q(uB + v) + qB - qv + (rB+y) - * = q(uB + v) + q(B-v) + (rB+y) - * - * The true quotient of num/den then is q plus the - * contribution of q(B-v) + (rB+y). The main contribution - * comes from the term q(B-v), with the term (rB+y) only - * contributing at most one part. - * - * The term q(B-v) must fit into 64 bits, since q fits into 32 - * bits on account of being a lower bound to the true - * quotient, and as B-v <= 2^32, we may safely use a single - * 64/64 bit division to find its contribution. */ - - cairo_uquorem64_t quorem; - cairo_uint64_t remainder; /* will contain final remainder */ - uint32_t quotient; /* will contain final quotient. */ - uint32_t q; - uint32_t r; - - /* Approximate quotient by dividing the high 64 bits of num by - * u+1. Watch out for overflow of u+1. */ - if (u+1) { - quorem = _cairo_uint64_divrem (x, _cairo_uint32_to_uint64 (u+1)); - q = _cairo_uint64_to_uint32 (quorem.quo); - r = _cairo_uint64_to_uint32 (quorem.rem); - } - else { - q = uint64_hi32 (x); - r = _cairo_uint64_to_uint32 (x); - } - quotient = q; - - /* Add the main term's contribution to quotient. Note B-v = - * -v as an uint32 (unless v = 0) */ - if (v) - quorem = _cairo_uint64_divrem (_cairo_uint32x32_64_mul (q, -v), den); - else - quorem = _cairo_uint64_divrem (_cairo_uint32s_to_uint64 (q, 0), den); - quotient += _cairo_uint64_to_uint32 (quorem.quo); - - /* Add the contribution of the subterm and start computing the - * true remainder. */ - remainder = _cairo_uint32s_to_uint64 (r, y); - if (_cairo_uint64_ge (remainder, den)) { - remainder = _cairo_uint64_sub (remainder, den); - quotient++; - } - - /* Add the contribution of the main term's remainder. The - * funky test here checks that remainder + main_rem >= den, - * taking into account overflow of the addition. */ - remainder = _cairo_uint64_add (remainder, quorem.rem); - if (_cairo_uint64_ge (remainder, den) || - _cairo_uint64_lt (remainder, quorem.rem)) - { - remainder = _cairo_uint64_sub (remainder, den); - quotient++; - } - - result.quo = _cairo_uint32_to_uint64 (quotient); - result.rem = remainder; - } - return result; -} - -cairo_quorem64_t -_cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den) -{ - int num_neg = _cairo_int128_negative (num); - int den_neg = _cairo_int64_negative (den); - cairo_uint64_t nonneg_den; - cairo_uquorem64_t uqr; - cairo_quorem64_t qr; - - if (num_neg) - num = _cairo_int128_negate (num); - if (den_neg) - nonneg_den = _cairo_int64_negate (den); - else - nonneg_den = den; - - uqr = _cairo_uint_96by64_32x64_divrem (num, nonneg_den); - if (_cairo_uint64_eq (uqr.rem, nonneg_den)) { - /* bail on overflow. */ - qr.quo = _cairo_uint32s_to_uint64 (0x7FFFFFFF, -1U); - qr.rem = den; - return qr; - } - - if (num_neg) - qr.rem = _cairo_int64_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = _cairo_int64_negate (uqr.quo); - else - qr.quo = uqr.quo; - return qr; -} diff --git a/libs/cairo/cairo/src/cairo-win32-font.c b/libs/cairo/cairo/src/cairo-win32-font.c deleted file mode 100644 index ccdd16187..000000000 --- a/libs/cairo/cairo/src/cairo-win32-font.c +++ /dev/null @@ -1,2319 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -/* We require at least Windows 7 features */ -#if !defined(WINVER) || (WINVER < 0x0601) -# define WINVER 0x0601 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) -# define _WIN32_WINNT 0x0601 -#endif - -#include "cairoint.h" - -#include "cairo-win32-private.h" -#include "cairo-error-private.h" - -#include - -#ifndef SPI_GETFONTSMOOTHINGTYPE -#define SPI_GETFONTSMOOTHINGTYPE 0x200a -#endif -#ifndef FE_FONTSMOOTHINGCLEARTYPE -#define FE_FONTSMOOTHINGCLEARTYPE 2 -#endif -#ifndef CLEARTYPE_QUALITY -#define CLEARTYPE_QUALITY 5 -#endif -#ifndef TT_PRIM_CSPLINE -#define TT_PRIM_CSPLINE 3 -#endif - -#define CMAP_TAG 0x70616d63 - -/** - * SECTION:cairo-win32-fonts - * @Title: Win32 Fonts - * @Short_Description: Font support for Microsoft Windows - * @See_Also: #cairo_font_face_t - * - * The Microsoft Windows font backend is primarily used to render text on - * Microsoft Windows systems. - */ - -/** - * CAIRO_HAS_WIN32_FONT: - * - * Defined if the Microsoft Windows font backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend; - -typedef struct { - cairo_scaled_font_t base; - - LOGFONTW logfont; - - BYTE quality; - - /* We do drawing and metrics computation in a "logical space" which - * is similar to font space, except that it is scaled by a factor - * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication - * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision. - */ - double logical_scale; - - /* The size we should actually request the font at from Windows; differs - * from the logical_scale because it is quantized for orthogonal - * transformations - */ - double logical_size; - - /* Transformations from device <=> logical space - */ - cairo_matrix_t logical_to_device; - cairo_matrix_t device_to_logical; - - /* We special case combinations of 90-degree-rotations, scales and - * flips ... that is transformations that take the axes to the - * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y - * encode the 8 possibilities for orientation (4 rotation angles with - * and without a flip), and scale_x, scale_y the scale components. - */ - cairo_bool_t preserve_axes; - cairo_bool_t swap_axes; - cairo_bool_t swap_x; - cairo_bool_t swap_y; - double x_scale; - double y_scale; - - /* The size of the design unit of the font - */ - int em_square; - - HFONT scaled_hfont; - HFONT unscaled_hfont; - - cairo_bool_t is_bitmap; - cairo_bool_t is_type1; - cairo_bool_t delete_scaled_hfont; -} cairo_win32_scaled_font_t; - -static cairo_status_t -_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font); - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph); - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph); - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph); - -#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) - -static HDC -_get_global_font_dc (void) -{ - static HDC hdc; - - if (!hdc) { - hdc = CreateCompatibleDC (NULL); - if (!hdc) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); - return NULL; - } - - if (!SetGraphicsMode (hdc, GM_ADVANCED)) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); - DeleteDC (hdc); - return NULL; - } - } - - return hdc; -} - -static cairo_status_t -_compute_transform (cairo_win32_scaled_font_t *scaled_font, - cairo_matrix_t *sc) -{ - cairo_status_t status; - - if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) && - !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) { - scaled_font->preserve_axes = TRUE; - scaled_font->x_scale = sc->xx; - scaled_font->swap_x = (sc->xx < 0); - scaled_font->y_scale = sc->yy; - scaled_font->swap_y = (sc->yy < 0); - scaled_font->swap_axes = FALSE; - - } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) && - !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) { - scaled_font->preserve_axes = TRUE; - scaled_font->x_scale = sc->yx; - scaled_font->swap_x = (sc->yx < 0); - scaled_font->y_scale = sc->xy; - scaled_font->swap_y = (sc->xy < 0); - scaled_font->swap_axes = TRUE; - - } else { - scaled_font->preserve_axes = FALSE; - scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; - } - - if (scaled_font->preserve_axes) { - if (scaled_font->swap_x) - scaled_font->x_scale = - scaled_font->x_scale; - if (scaled_font->swap_y) - scaled_font->y_scale = - scaled_font->y_scale; - - scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; - scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE * - _cairo_lround (scaled_font->y_scale); - } - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. - */ - cairo_matrix_init (&scaled_font->logical_to_device, - sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); - - if (!scaled_font->preserve_axes) { - status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device, - &scaled_font->x_scale, &scaled_font->y_scale, - TRUE); /* XXX: Handle vertical text */ - if (status) - return status; - - scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE * - scaled_font->y_scale); - scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; - } - - cairo_matrix_scale (&scaled_font->logical_to_device, - 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); - - scaled_font->device_to_logical = scaled_font->logical_to_device; - - status = cairo_matrix_invert (&scaled_font->device_to_logical); - if (status) - cairo_matrix_init_identity (&scaled_font->device_to_logical); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_have_cleartype_quality (void) -{ - // All supported versions have cleartype - return TRUE; -} - -BYTE -cairo_win32_get_system_text_quality (void) -{ - BOOL font_smoothing; - UINT smoothing_type; - - if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { - _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); - return DEFAULT_QUALITY; - } - - if (font_smoothing) { - if (_have_cleartype_quality ()) { - if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, - 0, &smoothing_type, 0)) { - _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); - return DEFAULT_QUALITY; - } - - if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) - return CLEARTYPE_QUALITY; - } - - return ANTIALIASED_QUALITY; - } else { - return DEFAULT_QUALITY; - } -} - -/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some - * factor S, ctm must be the identity, logfont->lfHeight must be -S, - * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must - * all be 0, and face_hfont is the result of calling CreateFontIndirectW on - * logfont. - */ -static cairo_status_t -_win32_scaled_font_create (LOGFONTW *logfont, - HFONT face_hfont, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font_out) -{ - HDC hdc; - cairo_win32_scaled_font_t *f; - cairo_matrix_t scale; - cairo_status_t status; - - hdc = _get_global_font_dc (); - if (hdc == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - f = malloc (sizeof(cairo_win32_scaled_font_t)); - if (f == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - f->logfont = *logfont; - - /* We don't have any control over the hinting style or subpixel - * order in the Win32 font API, so we ignore those parts of - * cairo_font_options_t. We use the 'antialias' field to set - * the 'quality'. - * - * XXX: The other option we could pay attention to, but don't - * here is the hint_metrics options. - */ - if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) - f->quality = cairo_win32_get_system_text_quality (); - else { - switch (options->antialias) { - case CAIRO_ANTIALIAS_NONE: - f->quality = NONANTIALIASED_QUALITY; - break; - case CAIRO_ANTIALIAS_GRAY: - f->quality = ANTIALIASED_QUALITY; - break; - case CAIRO_ANTIALIAS_SUBPIXEL: - if (_have_cleartype_quality ()) - f->quality = CLEARTYPE_QUALITY; - else - f->quality = ANTIALIASED_QUALITY; - break; - case CAIRO_ANTIALIAS_DEFAULT: - ASSERT_NOT_REACHED; - } - } - - f->em_square = 0; - f->scaled_hfont = NULL; - f->unscaled_hfont = NULL; - - if (f->quality == logfont->lfQuality || - (logfont->lfQuality == DEFAULT_QUALITY && - options->antialias == CAIRO_ANTIALIAS_DEFAULT)) { - /* If face_hfont is non-NULL, then we can use it to avoid creating our - * own --- because the constraints on face_hfont mentioned above - * guarantee it was created in exactly the same way that - * _win32_scaled_font_get_scaled_hfont would create it. - */ - f->scaled_hfont = face_hfont; - } - /* don't delete the hfont if we're using the one passed in to us */ - f->delete_scaled_hfont = !f->scaled_hfont; - - cairo_matrix_multiply (&scale, font_matrix, ctm); - status = _compute_transform (f, &scale); - if (status) - goto FAIL; - - status = _cairo_scaled_font_init (&f->base, font_face, - font_matrix, ctm, options, - &_cairo_win32_scaled_font_backend); - if (status) - goto FAIL; - - status = _cairo_win32_scaled_font_set_metrics (f); - if (status) { - _cairo_scaled_font_fini (&f->base); - goto FAIL; - } - - *font_out = &f->base; - return CAIRO_STATUS_SUCCESS; - - FAIL: - free (f); - return status; -} - -static cairo_status_t -_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, - HDC hdc) -{ - XFORM xform; - - _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); - - if (!SetWorldTransform (hdc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_win32_scaled_font_set_identity_transform (HDC hdc) -{ - if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, - HFONT *hfont_out) -{ - if (!scaled_font->scaled_hfont) { - LOGFONTW logfont = scaled_font->logfont; - logfont.lfHeight = -scaled_font->logical_size; - logfont.lfWidth = 0; - logfont.lfEscapement = 0; - logfont.lfOrientation = 0; - logfont.lfQuality = scaled_font->quality; - - scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); - if (!scaled_font->scaled_hfont) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); - } - - *hfont_out = scaled_font->scaled_hfont; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, - HDC hdc, - HFONT *hfont_out) -{ - if (scaled_font->unscaled_hfont == NULL) { - OUTLINETEXTMETRIC *otm; - unsigned int otm_size; - HFONT scaled_hfont; - LOGFONTW logfont; - cairo_status_t status; - - status = _win32_scaled_font_get_scaled_hfont (scaled_font, - &scaled_hfont); - if (status) - return status; - - if (! SelectObject (hdc, scaled_hfont)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); - - otm_size = GetOutlineTextMetrics (hdc, 0, NULL); - if (! otm_size) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); - - otm = malloc (otm_size); - if (otm == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { - status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); - free (otm); - return status; - } - - scaled_font->em_square = otm->otmEMSquare; - free (otm); - - logfont = scaled_font->logfont; - logfont.lfHeight = -scaled_font->em_square; - logfont.lfWidth = 0; - logfont.lfEscapement = 0; - logfont.lfOrientation = 0; - logfont.lfQuality = scaled_font->quality; - - scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); - if (! scaled_font->unscaled_hfont) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); - } - - *hfont_out = scaled_font->unscaled_hfont; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, - HDC hdc) -{ - cairo_status_t status; - HFONT hfont; - HFONT old_hfont = NULL; - - status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont); - if (status) - return status; - - old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); - - status = _win32_scaled_font_set_identity_transform (hdc); - if (status) { - SelectObject (hdc, old_hfont); - return status; - } - - SetMapMode (hdc, MM_TEXT); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) -{ - cairo_win32_scaled_font_t *win32_scaled_font; - - win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; - - return win32_scaled_font->is_type1; -} - -cairo_bool_t -_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font) -{ - cairo_win32_scaled_font_t *win32_scaled_font; - - win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; - - return win32_scaled_font->is_bitmap; -} - -static void -_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) -{ -} - -/* implement the font backend interface */ - -static cairo_status_t -_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) -{ - LOGFONTW logfont; - uint16_t *face_name; - int face_name_len; - cairo_status_t status; - - status = _cairo_utf8_to_utf16 (toy_face->family, -1, - &face_name, &face_name_len); - if (status) - return status; - - if (face_name_len > LF_FACESIZE - 1) - face_name_len = LF_FACESIZE - 1; - - memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); - logfont.lfFaceName[face_name_len] = 0; - free (face_name); - - logfont.lfHeight = 0; /* filled in later */ - logfont.lfWidth = 0; /* filled in later */ - logfont.lfEscapement = 0; /* filled in later */ - logfont.lfOrientation = 0; /* filled in later */ - - switch (toy_face->weight) { - case CAIRO_FONT_WEIGHT_NORMAL: - default: - logfont.lfWeight = FW_NORMAL; - break; - case CAIRO_FONT_WEIGHT_BOLD: - logfont.lfWeight = FW_BOLD; - break; - } - - switch (toy_face->slant) { - case CAIRO_FONT_SLANT_NORMAL: - default: - logfont.lfItalic = FALSE; - break; - case CAIRO_FONT_SLANT_ITALIC: - case CAIRO_FONT_SLANT_OBLIQUE: - logfont.lfItalic = TRUE; - break; - } - - logfont.lfUnderline = FALSE; - logfont.lfStrikeOut = FALSE; - /* The docs for LOGFONT discourage using this, since the - * interpretation is locale-specific, but it's not clear what - * would be a better alternative. - */ - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ - logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - - *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_scaled_font_fini (void *abstract_font) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - - if (scaled_font == NULL) - return; - - if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont) - DeleteObject (scaled_font->scaled_hfont); - - if (scaled_font->unscaled_hfont) - DeleteObject (scaled_font->unscaled_hfont); -} - -static cairo_int_status_t -_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font, - double x, - double y, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - uint16_t *utf16; - int n16; - int i; - WORD *glyph_indices = NULL; - cairo_status_t status; - double x_pos, y_pos; - HDC hdc = NULL; - cairo_matrix_t mat; - - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); - if (status) - return status; - - glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD)); - if (!glyph_indices) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL1; - } - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - goto FAIL2; - - if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW"); - goto FAIL3; - } - - *num_glyphs = n16; - *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t)); - if (!*glyphs) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL3; - } - - x_pos = x; - y_pos = y; - - mat = scaled_font->base.ctm; - status = cairo_matrix_invert (&mat); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_scaled_font_freeze_cache (&scaled_font->base); - - for (i = 0; i < n16; i++) { - cairo_scaled_glyph_t *scaled_glyph; - - (*glyphs)[i].index = glyph_indices[i]; - (*glyphs)[i].x = x_pos; - (*glyphs)[i].y = y_pos; - - status = _cairo_scaled_glyph_lookup (&scaled_font->base, - glyph_indices[i], - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (status) { - free (*glyphs); - *glyphs = NULL; - break; - } - - x = scaled_glyph->x_advance; - y = scaled_glyph->y_advance; - cairo_matrix_transform_distance (&mat, &x, &y); - x_pos += x; - y_pos += y; - } - - _cairo_scaled_font_thaw_cache (&scaled_font->base); - -FAIL3: - cairo_win32_scaled_font_done_font (&scaled_font->base); -FAIL2: - free (glyph_indices); -FAIL1: - free (utf16); - - return status; -} - -static cairo_int_status_t -_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, - double x, - double y, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - uint16_t *utf16; - int n16; - GCP_RESULTSW gcp_results; - unsigned int buffer_size, i; - WCHAR *glyph_indices = NULL; - int *dx = NULL; - cairo_status_t status; - double x_pos, y_pos; - double x_incr, y_incr; - HDC hdc = NULL; - - /* GetCharacterPlacement() returns utf16 instead of glyph indices - * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */ - if (scaled_font->is_type1) - return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font, - x, - y, - utf8, - glyphs, - num_glyphs); - - /* Compute a vector in user space along the baseline of length one logical space unit */ - x_incr = 1; - y_incr = 0; - cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr); - x_incr /= scaled_font->logical_scale; - y_incr /= scaled_font->logical_scale; - - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); - if (status) - return status; - - gcp_results.lStructSize = sizeof (GCP_RESULTS); - gcp_results.lpOutString = NULL; - gcp_results.lpOrder = NULL; - gcp_results.lpCaretPos = NULL; - gcp_results.lpClass = NULL; - - buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ - if (buffer_size > INT_MAX) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL1; - } - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - goto FAIL1; - - while (TRUE) { - if (glyph_indices) { - free (glyph_indices); - glyph_indices = NULL; - } - if (dx) { - free (dx); - dx = NULL; - } - - glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR)); - dx = _cairo_malloc_ab (buffer_size, sizeof (int)); - if (!glyph_indices || !dx) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL2; - } - - gcp_results.nGlyphs = buffer_size; - gcp_results.lpDx = dx; - gcp_results.lpGlyphs = glyph_indices; - - if (!GetCharacterPlacementW (hdc, utf16, n16, - 0, - &gcp_results, - GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); - goto FAIL2; - } - - if (gcp_results.lpDx && gcp_results.lpGlyphs) - break; - - /* Too small a buffer, try again */ - - buffer_size += buffer_size / 2; - if (buffer_size > INT_MAX) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL2; - } - } - - *num_glyphs = gcp_results.nGlyphs; - *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t)); - if (!*glyphs) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL2; - } - - x_pos = x; - y_pos = y; - - for (i = 0; i < gcp_results.nGlyphs; i++) { - (*glyphs)[i].index = glyph_indices[i]; - (*glyphs)[i].x = x_pos ; - (*glyphs)[i].y = y_pos; - - x_pos += x_incr * dx[i]; - y_pos += y_incr * dx[i]; - } - - FAIL2: - if (glyph_indices) - free (glyph_indices); - if (dx) - free (dx); - - cairo_win32_scaled_font_done_font (&scaled_font->base); - - FAIL1: - free (utf16); - - return status; -} - -static unsigned long -_cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, - uint32_t ucs4) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - wchar_t unicode[2]; - WORD glyph_index; - HDC hdc = NULL; - cairo_status_t status; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return 0; - - unicode[0] = ucs4; - unicode[1] = 0; - if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { - _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); - glyph_index = 0; - } - - cairo_win32_scaled_font_done_font (&scaled_font->base); - - return glyph_index; -} - -static cairo_status_t -_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) -{ - cairo_status_t status; - cairo_font_extents_t extents; - - TEXTMETRIC metrics; - HDC hdc; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) { - /* For 90-degree rotations (including 0), we get the metrics - * from the GDI in logical space, then convert back to font space - */ - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - GetTextMetrics (hdc, &metrics); - cairo_win32_scaled_font_done_font (&scaled_font->base); - - extents.ascent = metrics.tmAscent / scaled_font->logical_scale; - extents.descent = metrics.tmDescent / scaled_font->logical_scale; - - extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; - extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; - extents.max_y_advance = 0; - - } else { - /* For all other transformations, we use the design metrics - * of the font. The GDI results from GetTextMetrics() on a - * transformed font are inexplicably large and we want to - * avoid them. - */ - status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); - if (status) - return status; - GetTextMetrics (hdc, &metrics); - _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); - - extents.ascent = (double)metrics.tmAscent / scaled_font->em_square; - extents.descent = (double)metrics.tmDescent / scaled_font->em_square; - extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; - extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; - extents.max_y_advance = 0; - - } - - scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR); - - /* Need to determine if this is a Type 1 font for the special - * handling in _text_to_glyphs. Unlike TrueType or OpenType, - * Type1 fonts do not have a "cmap" table (or any other table). - * However GetFontData() will retrieve a Type1 font when - * requesting that GetFontData() retrieve data from the start of - * the file. This is to distinguish Type1 from stroke fonts such - * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant - * but improves performance for the most common fonts. - */ - scaled_font->is_type1 = FALSE; - if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) && - (metrics.tmPitchAndFamily & TMPF_VECTOR)) - { - if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) && - (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR)) - { - scaled_font->is_type1 = TRUE; - } - } - - return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents); -} - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; - GLYPHMETRICS metrics; - cairo_status_t status; - cairo_text_extents_t extents; - HDC hdc; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - if (scaled_font->is_bitmap) { - /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */ - cairo_font_extents_t font_extents; - INT width = 0; - UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph); - - cairo_scaled_font_extents (&scaled_font->base, &font_extents); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - - if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); - width = 0; - } - cairo_win32_scaled_font_done_font (&scaled_font->base); - if (status) - return status; - - extents.x_bearing = 0; - extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale); - extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale); - extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale; - extents.x_advance = extents.width; - extents.y_advance = 0; - } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { - /* If we aren't rotating / skewing the axes, then we get the metrics - * from the GDI in device space and convert to font space. - */ - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - - if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), - GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix) == GDI_ERROR) { - memset (&metrics, 0, sizeof (GLYPHMETRICS)); - } else { - if (metrics.gmBlackBoxX == 1 && metrics.gmBlackBoxY == 1 && - GetGlyphOutlineW (hdc, - _cairo_scaled_glyph_index (scaled_glyph), - GGO_NATIVE | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix) == 0) { - /* Workaround for GetGlyphOutline returning 1x1 bounding box - * for glyph that is in fact empty. - */ - metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; - } - else if (metrics.gmBlackBoxX > 0 && - scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { - /* The bounding box reported by Windows supposedly contains the glyph's "black" area; - * however, antialiasing (especially with ClearType) means that the actual image that - * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side. - * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs, - * for example, or other code that uses glyph extents to determine the area to update, - * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI, - * and two pixels to the right (as tests show some glyphs bleed into this column). - */ - metrics.gmptGlyphOrigin.x -= 1; - metrics.gmBlackBoxX += 3; - } - } - cairo_win32_scaled_font_done_font (&scaled_font->base); - - if (scaled_font->swap_axes) { - extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; - extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; - extents.width = metrics.gmBlackBoxY / scaled_font->y_scale; - extents.height = metrics.gmBlackBoxX / scaled_font->x_scale; - extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale; - extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale; - } else { - extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; - extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; - extents.width = metrics.gmBlackBoxX / scaled_font->x_scale; - extents.height = metrics.gmBlackBoxY / scaled_font->y_scale; - extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale; - extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale; - } - - if (scaled_font->swap_x) { - extents.x_bearing = (- extents.x_bearing - extents.width); - extents.x_advance = - extents.x_advance; - } - - if (scaled_font->swap_y) { - extents.y_bearing = (- extents.y_bearing - extents.height); - extents.y_advance = - extents.y_advance; - } - - } else { - /* For all other transformations, we use the design metrics - * of the font. - */ - status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); - if (status) - return status; - - if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), - GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix) == GDI_ERROR) { - memset (&metrics, 0, sizeof (GLYPHMETRICS)); - } - _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); - - extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; - extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; - extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square; - extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square; - extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; - extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; - } - - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &scaled_font->base, - &extents); - - return CAIRO_STATUS_SUCCESS; -} - -/* Not currently used code, but may be useful in the future if we add - * back the capability to the scaled font backend interface to get the - * actual device space bbox rather than computing it from the - * font-space metrics. - */ -#if 0 -static cairo_status_t -_cairo_win32_scaled_font_glyph_bbox (void *abstract_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; - cairo_win32_scaled_font_t *scaled_font = abstract_font; - int x1 = 0, x2 = 0, y1 = 0, y2 = 0; - - if (num_glyphs > 0) { - HDC hdc; - GLYPHMETRICS metrics; - cairo_status_t status; - int i; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - - for (i = 0; i < num_glyphs; i++) { - int x = _cairo_lround (glyphs[i].x); - int y = _cairo_lround (glyphs[i].y); - - GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix); - - if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) - x1 = x + metrics.gmptGlyphOrigin.x; - if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) - y1 = y - metrics.gmptGlyphOrigin.y; - if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX) - x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX; - if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY) - y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY; - } - - cairo_win32_scaled_font_done_font (&scaled_font->base); - } - - bbox->p1.x = _cairo_fixed_from_int (x1); - bbox->p1.y = _cairo_fixed_from_int (y1); - bbox->p2.x = _cairo_fixed_from_int (x2); - bbox->p2.y = _cairo_fixed_from_int (y2); - - return CAIRO_STATUS_SUCCESS; -} -#endif - -typedef struct { - cairo_win32_scaled_font_t *scaled_font; - HDC hdc; - - cairo_array_t glyphs; - cairo_array_t dx; - - int start_x; - int last_x; - int last_y; -} cairo_glyph_state_t; - -static void -_start_glyphs (cairo_glyph_state_t *state, - cairo_win32_scaled_font_t *scaled_font, - HDC hdc) -{ - state->hdc = hdc; - state->scaled_font = scaled_font; - - _cairo_array_init (&state->glyphs, sizeof (WCHAR)); - _cairo_array_init (&state->dx, sizeof (int)); -} - -static cairo_status_t -_flush_glyphs (cairo_glyph_state_t *state) -{ - cairo_status_t status; - int dx = 0; - WCHAR * elements; - int * dx_elements; - - status = _cairo_array_append (&state->dx, &dx); - if (status) - return status; - - elements = _cairo_array_index (&state->glyphs, 0); - dx_elements = _cairo_array_index (&state->dx, 0); - if (!ExtTextOutW (state->hdc, - state->start_x, state->last_y, - ETO_GLYPH_INDEX, - NULL, - elements, - state->glyphs.num_elements, - dx_elements)) { - return _cairo_win32_print_gdi_error ("_flush_glyphs"); - } - - _cairo_array_truncate (&state->glyphs, 0); - _cairo_array_truncate (&state->dx, 0); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_add_glyph (cairo_glyph_state_t *state, - unsigned long index, - double device_x, - double device_y) -{ - cairo_status_t status; - double user_x = device_x; - double user_y = device_y; - WCHAR glyph_index = index; - int logical_x, logical_y; - - cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); - - logical_x = _cairo_lround (user_x); - logical_y = _cairo_lround (user_y); - - if (state->glyphs.num_elements > 0) { - int dx; - - if (logical_y != state->last_y) { - status = _flush_glyphs (state); - if (status) - return status; - state->start_x = logical_x; - } else { - dx = logical_x - state->last_x; - status = _cairo_array_append (&state->dx, &dx); - if (status) - return status; - } - } else { - state->start_x = logical_x; - } - - state->last_x = logical_x; - state->last_y = logical_y; - - status = _cairo_array_append (&state->glyphs, &glyph_index); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_finish_glyphs (cairo_glyph_state_t *state) -{ - cairo_status_t status; - - status = _flush_glyphs (state); - - _cairo_array_fini (&state->glyphs); - _cairo_array_fini (&state->dx); - - return status; -} - -static cairo_status_t -_draw_glyphs_on_surface (cairo_win32_surface_t *surface, - cairo_win32_scaled_font_t *scaled_font, - COLORREF color, - int x_offset, - int y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_glyph_state_t state; - cairo_status_t status, status2; - int i; - - if (!SaveDC (surface->dc)) - return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); - if (status) - goto FAIL1; - - SetTextColor (surface->dc, color); - SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); - SetBkMode (surface->dc, TRANSPARENT); - - _start_glyphs (&state, scaled_font, surface->dc); - - for (i = 0; i < num_glyphs; i++) { - status = _add_glyph (&state, glyphs[i].index, - glyphs[i].x - x_offset, glyphs[i].y - y_offset); - if (status) - goto FAIL2; - } - - FAIL2: - status2 = _finish_glyphs (&state); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - cairo_win32_scaled_font_done_font (&scaled_font->base); - FAIL1: - RestoreDC (surface->dc, -1); - - return status; -} - -/* Duplicate the green channel of a 4-channel mask in the alpha channel, then - * invert the whole mask. - */ -static void -_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); - p++; - } - } -} - -/* Invert a mask - */ -static void -_invert_argb32_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ *p; - p++; - } - } -} - -/* Compute an alpha-mask from a monochrome RGB24 image - */ -static cairo_surface_t * -_compute_a8_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; - cairo_image_surface_t *image8; - int i, j; - - if (image24->base.status) - return cairo_surface_reference (&image24->base); - - image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, - image24->width, image24->height); - if (image8->base.status) - return &image8->base; - - for (i = 0; i < image24->height; i++) { - uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); - unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); - - for (j = 0; j < image24->width; j++) { - *q = 255 - ((*p & 0x0000ff00) >> 8); - p++; - q++; - } - } - - return &image8->base; -} - -static cairo_int_status_t -_cairo_win32_scaled_font_glyph_init (void *abstract_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - cairo_status_t status; - - if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { - status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph); - if (status) - return status; - } - - if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { - status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph); - if (status) - return status; - } - - if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { - status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph); - if (status) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_scaled_font_show_glyphs (void *abstract_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; - cairo_status_t status; - - if (width == 0 || height == 0) - return CAIRO_STATUS_SUCCESS; - - if (_cairo_surface_is_win32 (generic_surface) && - surface->format == CAIRO_FORMAT_RGB24 && - (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) && - op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_opaque_solid (pattern)) { - - cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; - - /* When compositing OVER on a GDI-understood surface, with a - * solid opaque color, we can just call ExtTextOut directly. - */ - COLORREF new_color; - - status = _cairo_win32_surface_set_clip_region (surface, clip_region); - if (unlikely (status)) - return status; - - new_color = RGB (((int)solid_pattern->color.red_short) >> 8, - ((int)solid_pattern->color.green_short) >> 8, - ((int)solid_pattern->color.blue_short) >> 8); - - return _draw_glyphs_on_surface (surface, scaled_font, new_color, - 0, 0, - glyphs, num_glyphs); - } else { - /* Otherwise, we need to draw using software fallbacks. We create a mask - * surface by drawing the the glyphs onto a DIB, black-on-white then - * inverting. GDI outputs gamma-corrected images so inverted black-on-white - * is very different from white-on-black. We favor the more common - * case where the final output is dark-on-light. - */ - cairo_win32_surface_t *tmp_surface; - cairo_surface_t *mask_surface; - cairo_surface_pattern_t mask; - cairo_bool_t use_subpixel_antialiasing = - scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing; - RECT r; - - tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); - if (tmp_surface->base.status) - return tmp_surface->base.status; - - r.left = 0; - r.top = 0; - r.right = width; - r.bottom = height; - FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); - - status = _draw_glyphs_on_surface (tmp_surface, - scaled_font, RGB (0, 0, 0), - dest_x, dest_y, - glyphs, num_glyphs); - if (status) { - cairo_surface_destroy (&tmp_surface->base); - return status; - } - - if (use_subpixel_antialiasing) { - /* For ClearType, we need a 4-channel mask. If we are compositing on - * a surface with alpha, we need to compute the alpha channel of - * the mask (we just copy the green channel). But for a destination - * surface without alpha the alpha channel of the mask is ignored - */ - - if (surface->format != CAIRO_FORMAT_RGB24) - _compute_argb32_mask_alpha (tmp_surface); - else - _invert_argb32_mask (tmp_surface); - - mask_surface = &tmp_surface->base; - } else { - mask_surface = _compute_a8_mask (tmp_surface); - cairo_surface_destroy (&tmp_surface->base); - status = mask_surface->status; - if (status) - return status; - } - - /* For op == OVER, no-cleartype, a possible optimization here is to - * draw onto an intermediate ARGB32 surface and alpha-blend that with the - * destination - */ - _cairo_pattern_init_for_surface (&mask, mask_surface); - cairo_surface_destroy (mask_surface); - - if (use_subpixel_antialiasing) - mask.base.has_component_alpha = TRUE; - - status = _cairo_surface_composite (op, pattern, - &mask.base, - &surface->base, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height, - clip_region); - - _cairo_pattern_fini (&mask.base); - - return status; - } -} - -static cairo_int_status_t -_cairo_win32_scaled_font_load_truetype_table (void *abstract_font, - unsigned long tag, - long offset, - unsigned char *buffer, - unsigned long *length) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - HDC hdc; - cairo_status_t status; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - - *length = GetFontData (hdc, tag, offset, buffer, *length); - if (*length == GDI_ERROR) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_win32_scaled_font_done_font (&scaled_font->base); - - return status; -} - -static cairo_int_status_t -_cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, - unsigned long index, - uint32_t *ucs4) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - GLYPHSET *glyph_set; - uint16_t *utf16 = NULL; - WORD *glyph_indices = NULL; - HDC hdc = NULL; - int res; - unsigned int i, j, num_glyphs; - cairo_status_t status; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - if (status) - return status; - - res = GetFontUnicodeRanges(hdc, NULL); - if (res == 0) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); - goto exit1; - } - - glyph_set = malloc (res); - if (glyph_set == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto exit1; - } - - res = GetFontUnicodeRanges(hdc, glyph_set); - if (res == 0) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); - goto exit1; - } - - *ucs4 = (uint32_t) -1; - for (i = 0; i < glyph_set->cRanges; i++) { - num_glyphs = glyph_set->ranges[i].cGlyphs; - - utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t)); - if (utf16 == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto exit1; - } - - glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD)); - if (glyph_indices == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto exit2; - } - - for (j = 0; j < num_glyphs; j++) - utf16[j] = glyph_set->ranges[i].wcLow + j; - utf16[j] = 0; - - if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); - goto exit2; - } - - for (j = 0; j < num_glyphs; j++) { - if (glyph_indices[j] == index) { - *ucs4 = utf16[j]; - goto exit2; - } - } - - free (glyph_indices); - glyph_indices = NULL; - free (utf16); - utf16 = NULL; - } - -exit2: - if (glyph_indices) - free (glyph_indices); - if (utf16) - free (utf16); - free (glyph_set); -exit1: - cairo_win32_scaled_font_done_font (&scaled_font->base); - - return status; -} - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_status_t status; - cairo_glyph_t glyph; - cairo_win32_surface_t *surface; - cairo_t *cr; - cairo_surface_t *image; - int width, height; - int x1, y1, x2, y2; - - x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); - y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); - y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); - width = x2 - x1; - height = y2 - y1; - - surface = (cairo_win32_surface_t *) - cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); - - cr = cairo_create (&surface->base); - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy(cr); - if (status) - goto FAIL; - - glyph.index = _cairo_scaled_glyph_index (scaled_glyph); - glyph.x = -x1; - glyph.y = -y1; - status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0), - 0, 0, &glyph, 1); - if (status) - goto FAIL; - - GdiFlush(); - - image = _compute_a8_mask (surface); - status = image->status; - if (status) - goto FAIL; - - cairo_surface_set_device_offset (image, -x1, -y1); - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - (cairo_image_surface_t *) image); - - FAIL: - cairo_surface_destroy (&surface->base); - - return status; -} - -static void -_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix, - FIXED Fx, FIXED Fy, - cairo_fixed_t *fx, cairo_fixed_t *fy) -{ - double x = Fx.value + Fx.fract / 65536.0; - double y = Fy.value + Fy.fract / 65536.0; - cairo_matrix_transform_point (matrix, &x, &y); - *fx = _cairo_fixed_from_double (x); - *fy = _cairo_fixed_from_double (y); -} - -static cairo_status_t -_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; - cairo_status_t status; - GLYPHMETRICS metrics; - HDC hdc; - DWORD bytesGlyph; - unsigned char *buffer, *ptr; - cairo_path_fixed_t *path; - cairo_matrix_t transform; - cairo_fixed_t x, y; - - if (scaled_font->is_bitmap) - return CAIRO_INT_STATUS_UNSUPPORTED; - - hdc = _get_global_font_dc (); - assert (hdc != NULL); - - path = _cairo_path_fixed_create (); - if (!path) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) { - status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); - transform = scaled_font->base.scale; - cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square); - } else { - status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); - cairo_matrix_init_identity(&transform); - } - if (status) - goto CLEANUP_PATH; - - bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), - GGO_NATIVE | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix); - - if (bytesGlyph == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); - goto CLEANUP_FONT; - } - - ptr = buffer = malloc (bytesGlyph); - if (!buffer) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_FONT; - } - - if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), - GGO_NATIVE | GGO_GLYPH_INDEX, - &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); - goto CLEANUP_BUFFER; - } - - while (ptr < buffer + bytesGlyph) { - TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; - unsigned char *endPoly = ptr + header->cb; - - ptr += sizeof (TTPOLYGONHEADER); - - _cairo_win32_transform_FIXED_to_fixed (&transform, - header->pfxStart.x, - header->pfxStart.y, - &x, &y); - status = _cairo_path_fixed_move_to (path, x, y); - if (status) - goto CLEANUP_BUFFER; - - while (ptr < endPoly) { - TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; - POINTFX *points = curve->apfx; - int i; - switch (curve->wType) { - case TT_PRIM_LINE: - for (i = 0; i < curve->cpfx; i++) { - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i].x, - points[i].y, - &x, &y); - status = _cairo_path_fixed_line_to (path, x, y); - if (status) - goto CLEANUP_BUFFER; - } - break; - case TT_PRIM_QSPLINE: - for (i = 0; i < curve->cpfx - 1; i++) { - cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; - if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y)) - goto CLEANUP_BUFFER; - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i].x, - points[i].y, - &cx, &cy); - - if (i + 1 == curve->cpfx - 1) { - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i + 1].x, - points[i + 1].y, - &p2x, &p2y); - } else { - /* records with more than one curve use interpolation for - control points, per http://support.microsoft.com/kb/q87115/ */ - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i + 1].x, - points[i + 1].y, - &x, &y); - p2x = (cx + x) / 2; - p2y = (cy + y) / 2; - } - - c1x = 2 * cx / 3 + p1x / 3; - c1y = 2 * cy / 3 + p1y / 3; - c2x = 2 * cx / 3 + p2x / 3; - c2y = 2 * cy / 3 + p2y / 3; - - status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); - if (status) - goto CLEANUP_BUFFER; - } - break; - case TT_PRIM_CSPLINE: - for (i = 0; i < curve->cpfx - 2; i += 2) { - cairo_fixed_t x1, y1, x2, y2; - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i].x, - points[i].y, - &x, &y); - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i + 1].x, - points[i + 1].y, - &x1, &y1); - _cairo_win32_transform_FIXED_to_fixed (&transform, - points[i + 2].x, - points[i + 2].y, - &x2, &y2); - status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2); - if (status) - goto CLEANUP_BUFFER; - } - break; - } - ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); - } - status = _cairo_path_fixed_close_path (path); - if (status) - goto CLEANUP_BUFFER; - } - - _cairo_scaled_glyph_set_path (scaled_glyph, - &scaled_font->base, - path); - - CLEANUP_BUFFER: - free (buffer); - - CLEANUP_FONT: - if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) - _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); - else - cairo_win32_scaled_font_done_font (&scaled_font->base); - - CLEANUP_PATH: - if (status != CAIRO_STATUS_SUCCESS) - _cairo_path_fixed_destroy (path); - - return status; -} - -const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { - CAIRO_FONT_TYPE_WIN32, - _cairo_win32_scaled_font_fini, - _cairo_win32_scaled_font_glyph_init, - NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ - _cairo_win32_scaled_font_ucs4_to_index, - _cairo_win32_scaled_font_show_glyphs, - _cairo_win32_scaled_font_load_truetype_table, - _cairo_win32_scaled_font_index_to_ucs4, -}; - -/* #cairo_win32_font_face_t */ - -typedef struct _cairo_win32_font_face cairo_win32_font_face_t; - -/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S, - * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must - * all be 0, and hfont is the result of calling CreateFontIndirectW on - * logfont. - */ -struct _cairo_win32_font_face { - cairo_font_face_t base; - LOGFONTW logfont; - HFONT hfont; -}; - -/* implement the platform-specific interface */ - -static void -_cairo_win32_font_face_destroy (void *abstract_face); - -static cairo_bool_t -_is_scale (const cairo_matrix_t *matrix, double scale) -{ - return matrix->xx == scale && matrix->yy == scale && - matrix->xy == 0. && matrix->yx == 0. && - matrix->x0 == 0. && matrix->y0 == 0.; -} - -static cairo_status_t -_cairo_win32_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font) -{ - HFONT hfont = NULL; - - cairo_win32_font_face_t *font_face = abstract_face; - - if (font_face->hfont) { - /* Check whether it's OK to go ahead and use the font-face's HFONT. */ - if (_is_scale (ctm, 1.) && - _is_scale (font_matrix, -font_face->logfont.lfHeight)) { - hfont = font_face->hfont; - } - } - - return _win32_scaled_font_create (&font_face->logfont, - hfont, - &font_face->base, - font_matrix, ctm, options, - font); -} - -const cairo_font_face_backend_t _cairo_win32_font_face_backend = { - CAIRO_FONT_TYPE_WIN32, - _cairo_win32_font_face_create_for_toy, - _cairo_win32_font_face_destroy, - _cairo_win32_font_face_scaled_font_create -}; - -/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t. - * The primary purpose of this mapping is to provide unique - * #cairo_font_face_t values so that our cache and mapping from - * #cairo_font_face_t => #cairo_scaled_font_t works. Once the - * corresponding #cairo_font_face_t objects fall out of downstream - * caches, we don't need them in this hash table anymore. - * - * Modifications to this hash table are protected by - * _cairo_win32_font_face_mutex. - * - * Only #cairo_font_face_t values with null 'hfont' (no - * HFONT preallocated by caller) are stored in this table. We rely - * on callers to manage the lifetime of the HFONT, and they can't - * do that if we share #cairo_font_face_t values with other callers. - */ - -static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; - -static int -_cairo_win32_font_face_keys_equal (const void *key_a, - const void *key_b); - -static void -_cairo_win32_font_face_hash_table_destroy (void) -{ - cairo_hash_table_t *hash_table; - - /* We manually acquire the lock rather than calling - * _cairo_win32_font_face_hash_table_lock simply to avoid creating - * the table only to destroy it again. */ - CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); - hash_table = cairo_win32_font_face_hash_table; - cairo_win32_font_face_hash_table = NULL; - CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); - - if (hash_table != NULL) - _cairo_hash_table_destroy (hash_table); -} - -static cairo_hash_table_t * -_cairo_win32_font_face_hash_table_lock (void) -{ - CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); - - if (unlikely (cairo_win32_font_face_hash_table == NULL)) - { - cairo_win32_font_face_hash_table = - _cairo_hash_table_create (_cairo_win32_font_face_keys_equal); - - if (unlikely (cairo_win32_font_face_hash_table == NULL)) { - CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - } - - return cairo_win32_font_face_hash_table; -} - -static void -_cairo_win32_font_face_hash_table_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); -} - -static void -_cairo_win32_font_face_init_key (cairo_win32_font_face_t *key, - LOGFONTW *logfont, - HFONT font) -{ - unsigned long hash = _CAIRO_HASH_INIT_VALUE; - - key->logfont = *logfont; - key->hfont = font; - - hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName)); - hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight)); - hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic)); - - key->base.hash_entry.hash = hash; -} - -static int -_cairo_win32_font_face_keys_equal (const void *key_a, - const void *key_b) -{ - const cairo_win32_font_face_t *face_a = key_a; - const cairo_win32_font_face_t *face_b = key_b; - - if (face_a->logfont.lfWeight == face_b->logfont.lfWeight && - face_a->logfont.lfItalic == face_b->logfont.lfItalic && - face_a->logfont.lfUnderline == face_b->logfont.lfUnderline && - face_a->logfont.lfStrikeOut == face_b->logfont.lfStrikeOut && - face_a->logfont.lfCharSet == face_b->logfont.lfCharSet && - face_a->logfont.lfOutPrecision == face_b->logfont.lfOutPrecision && - face_a->logfont.lfClipPrecision == face_b->logfont.lfClipPrecision && - face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily && - (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0)) - return TRUE; - else - return FALSE; -} - -static void -_cairo_win32_font_face_destroy (void *abstract_face) -{ - cairo_hash_table_t *hash_table; - cairo_win32_font_face_t *font_face = abstract_face; - - if (!font_face->hfont) { - hash_table = _cairo_win32_font_face_hash_table_lock (); - if (unlikely (hash_table == NULL)) { - return; - } - _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - _cairo_win32_font_face_hash_table_unlock (); - } -} - -/** - * cairo_win32_font_face_create_for_logfontw_hfont: - * @logfont: A #LOGFONTW structure specifying the font to use. - * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement - * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and - * lfEscapement must be zero. - * @font: An #HFONT that can be used when the font matrix is a scale by - * -lfHeight and the CTM is identity. - * - * Creates a new font for the Win32 font backend based on a - * #LOGFONT. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - * The #cairo_scaled_font_t - * returned from cairo_scaled_font_create() is also for the Win32 backend - * and can be used with functions such as cairo_win32_scaled_font_select_font(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - **/ -cairo_font_face_t * -cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) -{ - cairo_win32_font_face_t *font_face, key; - cairo_hash_table_t *hash_table; - cairo_status_t status; - - if (!font) { - hash_table = _cairo_win32_font_face_hash_table_lock (); - if (unlikely (hash_table == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } - - _cairo_win32_font_face_init_key (&key, logfont, font); - - /* Return existing unscaled font if it exists in the hash table. */ - font_face = _cairo_hash_table_lookup (hash_table, - &key.base.hash_entry); - if (font_face != NULL) { - cairo_font_face_reference (&font_face->base); - goto DONE; - } - } - - /* Otherwise create it and insert into hash table. */ - font_face = malloc (sizeof (cairo_win32_font_face_t)); - if (!font_face) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_win32_font_face_init_key (font_face, logfont, font); - _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); - - if (!font) { - assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); - status = _cairo_hash_table_insert (hash_table, - &font_face->base.hash_entry); - if (unlikely (status)) - goto FAIL; - } - -DONE: - if (!font) { - _cairo_win32_font_face_hash_table_unlock (); - } - - return &font_face->base; - -FAIL: - if (!font) { - _cairo_win32_font_face_hash_table_unlock (); - } - - return (cairo_font_face_t *)&_cairo_font_face_nil; -} - -/** - * cairo_win32_font_face_create_for_logfontw: - * @logfont: A #LOGFONTW structure specifying the font to use. - * The lfHeight, lfWidth, lfOrientation and lfEscapement - * fields of this structure are ignored. - * - * Creates a new font for the Win32 font backend based on a - * #LOGFONT. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - * The #cairo_scaled_font_t - * returned from cairo_scaled_font_create() is also for the Win32 backend - * and can be used with functions such as cairo_win32_scaled_font_select_font(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - **/ -cairo_font_face_t * -cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) -{ - return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL); -} - -/** - * cairo_win32_font_face_create_for_hfont: - * @font: An #HFONT structure specifying the font to use. - * - * Creates a new font for the Win32 font backend based on a - * #HFONT. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - * The #cairo_scaled_font_t - * returned from cairo_scaled_font_create() is also for the Win32 backend - * and can be used with functions such as cairo_win32_scaled_font_select_font(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - **/ -cairo_font_face_t * -cairo_win32_font_face_create_for_hfont (HFONT font) -{ - LOGFONTW logfont; - GetObjectW (font, sizeof(logfont), &logfont); - - if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 || - logfont.lfWidth != 0) { - /* We can't use this font because that optimization requires that - * lfEscapement, lfOrientation and lfWidth be zero. */ - font = NULL; - } - - return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font); -} - -static cairo_bool_t -_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) -{ - return scaled_font->backend == &_cairo_win32_scaled_font_backend; -} - -/** - * cairo_win32_scaled_font_select_font: - * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an - * object can be created with cairo_win32_scaled_font_create_for_logfontw(). - * @hdc: a device context - * - * Selects the font into the given device context and changes the - * map mode and world transformation of the device context to match - * that of the font. This function is intended for use when using - * layout APIs such as Uniscribe to do text layout with the - * cairo font. After finishing using the device context, you must call - * cairo_win32_scaled_font_done_font() to release any resources allocated - * by this function. - * - * See cairo_win32_scaled_font_get_metrics_factor() for converting logical - * coordinates from the device context to font space. - * - * Normally, calls to SaveDC() and RestoreDC() would be made around - * the use of this function to preserve the original graphics state. - * - * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. - * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and - * the device context is unchanged. - **/ -cairo_status_t -cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, - HDC hdc) -{ - cairo_status_t status; - HFONT hfont; - HFONT old_hfont = NULL; - int old_mode; - - if (! _cairo_scaled_font_is_win32 (scaled_font)) { - return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); - } - - if (scaled_font->status) - return scaled_font->status; - - status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont); - if (status) - return status; - - old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); - - old_mode = SetGraphicsMode (hdc, GM_ADVANCED); - if (!old_mode) { - status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); - SelectObject (hdc, old_hfont); - return status; - } - - status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); - if (status) { - SetGraphicsMode (hdc, old_mode); - SelectObject (hdc, old_hfont); - return status; - } - - SetMapMode (hdc, MM_TEXT); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_win32_scaled_font_done_font: - * @scaled_font: A scaled font from the Win32 font backend. - * - * Releases any resources allocated by cairo_win32_scaled_font_select_font() - **/ -void -cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) -{ - if (! _cairo_scaled_font_is_win32 (scaled_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - } -} - -/** - * cairo_win32_scaled_font_get_metrics_factor: - * @scaled_font: a scaled font from the Win32 font backend - * - * Gets a scale factor between logical coordinates in the coordinate - * space used by cairo_win32_scaled_font_select_font() (that is, the - * coordinate system used by the Windows functions to return metrics) and - * font space coordinates. - * - * Return value: factor to multiply logical units by to get font space - * coordinates. - **/ -double -cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) -{ - if (! _cairo_scaled_font_is_win32 (scaled_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - return 1.; - } - return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; -} - -/** - * cairo_win32_scaled_font_get_logical_to_device: - * @scaled_font: a scaled font from the Win32 font backend - * @logical_to_device: matrix to return - * - * Gets the transformation mapping the logical space used by @scaled_font - * to device space. - * - * Since: 1.4 - **/ -void -cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *logical_to_device) -{ - cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; - if (! _cairo_scaled_font_is_win32 (scaled_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - cairo_matrix_init_identity (logical_to_device); - return; - } - *logical_to_device = win_font->logical_to_device; -} - -/** - * cairo_win32_scaled_font_get_device_to_logical: - * @scaled_font: a scaled font from the Win32 font backend - * @device_to_logical: matrix to return - * - * Gets the transformation mapping device space to the logical space - * used by @scaled_font. - * - * Since: 1.4 - **/ -void -cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *device_to_logical) -{ - cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; - if (! _cairo_scaled_font_is_win32 (scaled_font)) { - _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); - cairo_matrix_init_identity (device_to_logical); - return; - } - *device_to_logical = win_font->device_to_logical; -} - -void -_cairo_win32_font_reset_static_data (void) -{ - _cairo_win32_font_face_hash_table_destroy (); -} diff --git a/libs/cairo/cairo/src/cairo-win32-printing-surface.c b/libs/cairo/cairo/src/cairo-win32-printing-surface.c deleted file mode 100644 index 56cf3242b..000000000 --- a/libs/cairo/cairo/src/cairo-win32-printing-surface.c +++ /dev/null @@ -1,1956 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -/* We require at least Windows 7 features */ -#if !defined(WINVER) || (WINVER < 0x0601) -# define WINVER 0x0601 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) -# define _WIN32_WINNT 0x0601 -#endif - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-paginated-private.h" - -#include "cairo-clip-private.h" -#include "cairo-win32-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-image-info-private.h" -#include "cairo-surface-clipper-private.h" - -#include - -#if !defined(POSTSCRIPT_IDENTIFY) -# define POSTSCRIPT_IDENTIFY 0x1015 -#endif - -#if !defined(PSIDENT_GDICENTRIC) -# define PSIDENT_GDICENTRIC 0x0000 -#endif - -#if !defined(GET_PS_FEATURESETTING) -# define GET_PS_FEATURESETTING 0x1019 -#endif - -#if !defined(FEATURESETTING_PSLEVEL) -# define FEATURESETTING_PSLEVEL 0x0002 -#endif - -#if !defined(GRADIENT_FILL_RECT_H) -# define GRADIENT_FILL_RECT_H 0x00 -#endif - -#if !defined(CHECKJPEGFORMAT) -# define CHECKJPEGFORMAT 0x1017 -#endif - -#if !defined(CHECKPNGFORMAT) -# define CHECKPNGFORMAT 0x1018 -#endif - -#define PELS_72DPI ((LONG)(72. / 0.0254)) - -static const cairo_surface_backend_t cairo_win32_printing_surface_backend; -static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend; - -static void -_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface) -{ - DWORD word; - INT ps_feature, ps_level; - - word = PSIDENT_GDICENTRIC; - if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) - return; - - ps_feature = FEATURESETTING_PSLEVEL; - if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT), - (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0) - return; - - if (ps_level >= 3) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; -} - -static void -_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface) -{ - DWORD word; - - word = CHECKJPEGFORMAT; - if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; - - word = CHECKPNGFORMAT; - if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; -} - -/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not - * work unless the GDI function GdiInitializeLanguagePack() has been - * called. - * - * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html - * - * The only information I could find on the how to use this - * undocumented function is the use in: - * - * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup - * - * to solve the same problem. The above code first checks if LPK.DLL - * is already loaded. If it is not it calls - * GdiInitializeLanguagePack() using the prototype - * BOOL GdiInitializeLanguagePack (int) - * and argument 0. - */ -static void -_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface) -{ - typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); - gdi_init_lang_pack_func_t gdi_init_lang_pack; - HMODULE module; - - if (GetModuleHandleW (L"LPK.DLL")) - return; - - module = GetModuleHandleW (L"GDI32.DLL"); - if (module) { - gdi_init_lang_pack = (gdi_init_lang_pack_func_t) - GetProcAddress (module, "GdiInitializeLanguagePack"); - if (gdi_init_lang_pack) - gdi_init_lang_pack (0); - } -} - -static cairo_int_status_t -analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_int_status_t status; - cairo_image_transparency_t transparency; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); - if (status) - return status; - - transparency = _cairo_image_analyze_transparency (image); - switch (transparency) { - case CAIRO_IMAGE_UNKNOWN: - ASSERT_NOT_REACHED; - case CAIRO_IMAGE_IS_OPAQUE: - status = CAIRO_STATUS_SUCCESS; - break; - - case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: - case CAIRO_IMAGE_HAS_ALPHA: - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - break; - } - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_bool_t -surface_pattern_supported (const cairo_surface_pattern_t *pattern) -{ - if (_cairo_surface_is_recording (pattern->surface)) - return TRUE; - - if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && - pattern->surface->backend->acquire_source_image == NULL) - { - return FALSE; - } - - return TRUE; -} - -static cairo_bool_t -pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern) -{ - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) - return TRUE; - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) - return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; - - return FALSE; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - if (! pattern_supported (surface, pattern)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (!(op == CAIRO_OPERATOR_SOURCE || - op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_CLEAR)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if ( _cairo_surface_is_recording (surface_pattern->surface)) - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } - - if (op == CAIRO_OPERATOR_SOURCE || - op == CAIRO_OPERATOR_CLEAR) - return CAIRO_STATUS_SUCCESS; - - /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If - * the pattern contains transparency, we return - * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis - * surface. If the analysis surface determines that there is - * anything drawn under this operation, a fallback image will be - * used. Otherwise the operation will be replayed during the - * render stage and we blend the transarency into the white - * background to convert the pattern to opaque. - */ - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - return analyze_surface_pattern_transparency (surface_pattern); - } - - if (_cairo_pattern_is_opaque (pattern, NULL)) - return CAIRO_STATUS_SUCCESS; - else - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; -} - -static cairo_bool_t -_cairo_win32_printing_surface_operation_supported (cairo_win32_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) - return TRUE; - else - return FALSE; -} - -static void -_cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface, - cairo_solid_pattern_t *color) -{ - if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) - _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE); - else - _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK); -} - -static COLORREF -_cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surface, - const cairo_color_t *color) -{ - COLORREF c; - BYTE red, green, blue; - - red = color->red_short >> 8; - green = color->green_short >> 8; - blue = color->blue_short >> 8; - - if (!CAIRO_COLOR_IS_OPAQUE(color)) { - if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { - /* Blend into white */ - uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8); - - red = (color->red_short >> 8) + one_minus_alpha; - green = (color->green_short >> 8) + one_minus_alpha; - blue = (color->blue_short >> 8) + one_minus_alpha; - } else { - /* Blend into black */ - red = (color->red_short >> 8); - green = (color->green_short >> 8); - blue = (color->blue_short >> 8); - } - } - c = RGB (red, green, blue); - - return c; -} - -static cairo_status_t -_cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface, - const cairo_pattern_t *source) -{ - cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; - COLORREF color; - - color = _cairo_win32_printing_surface_flatten_transparency (surface, - &pattern->color); - surface->brush = CreateSolidBrush (color); - if (!surface->brush) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); - surface->old_brush = SelectObject (surface->dc, surface->brush); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface) -{ - if (surface->old_brush) { - SelectObject (surface->dc, surface->old_brush); - DeleteObject (surface->brush); - surface->old_brush = NULL; - } -} - -static cairo_status_t -_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, - RECT *clip) -{ - XFORM xform; - - _cairo_matrix_to_win32_xform (&surface->ctm, &xform); - if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); - GetClipBox (surface->dc, clip); - - _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); - if (!SetWorldTransform (surface->dc, &xform)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface, - const cairo_pattern_t *pattern) -{ - RECT clip; - cairo_status_t status; - - GetClipBox (surface->dc, &clip); - status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); - if (status) - return status; - - FillRect (surface->dc, &clip, surface->brush); - _cairo_win32_printing_surface_done_solid_brush (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - cairo_content_t old_content; - cairo_matrix_t old_ctm; - cairo_bool_t old_has_ctm; - cairo_rectangle_int_t recording_extents; - cairo_status_t status; - cairo_extend_t extend; - cairo_matrix_t p2d; - XFORM xform; - int x_tile, y_tile, left, right, top, bottom; - RECT clip; - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; - cairo_box_t bbox; - - extend = cairo_pattern_get_extend (&pattern->base); - - p2d = pattern->base.matrix; - status = cairo_matrix_invert (&p2d); - /* _cairo_pattern_set_matrix guarantees invertibility */ - assert (status == CAIRO_STATUS_SUCCESS); - - old_ctm = surface->ctm; - old_has_ctm = surface->has_ctm; - cairo_matrix_multiply (&p2d, &p2d, &surface->ctm); - surface->ctm = p2d; - SaveDC (surface->dc); - _cairo_matrix_to_win32_xform (&p2d, &xform); - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (status) - return status; - - _cairo_box_round_to_rectangle (&bbox, &recording_extents); - - status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); - if (status) - return status; - - if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); - right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); - top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); - bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); - } else { - left = 0; - right = 1; - top = 0; - bottom = 1; - } - - old_content = surface->content; - if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { - surface->content = CAIRO_CONTENT_COLOR; - status = _cairo_win32_printing_surface_paint_solid_pattern (surface, - &_cairo_pattern_black.base); - if (status) - return status; - } - - for (y_tile = top; y_tile < bottom; y_tile++) { - for (x_tile = left; x_tile < right; x_tile++) { - cairo_matrix_t m; - double x, y; - - SaveDC (surface->dc); - m = p2d; - cairo_matrix_translate (&m, - x_tile*recording_extents.width, - y_tile*recording_extents.height); - if (extend == CAIRO_EXTEND_REFLECT) { - if (x_tile % 2) { - cairo_matrix_translate (&m, recording_extents.width, 0); - cairo_matrix_scale (&m, -1, 1); - } - if (y_tile % 2) { - cairo_matrix_translate (&m, 0, recording_extents.height); - cairo_matrix_scale (&m, 1, -1); - } - } - surface->ctm = m; - surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); - - /* Set clip path around bbox of the pattern. */ - BeginPath (surface->dc); - - x = 0; - y = 0; - cairo_matrix_transform_point (&surface->ctm, &x, &y); - MoveToEx (surface->dc, (int) x, (int) y, NULL); - - x = recording_extents.width; - y = 0; - cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); - - x = recording_extents.width; - y = recording_extents.height; - cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); - - x = 0; - y = recording_extents.height; - cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); - - CloseFigure (surface->dc); - EndPath (surface->dc); - SelectClipPath (surface->dc, RGN_AND); - - SaveDC (surface->dc); /* Allow clip path to be reset during replay */ - status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - /* Restore both the clip save and our earlier path SaveDC */ - RestoreDC (surface->dc, -2); - - if (status) - return status; - } - } - - surface->content = old_content; - surface->ctm = old_ctm; - surface->has_ctm = old_has_ctm; - RestoreDC (surface->dc, -1); - - return status; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, - cairo_surface_t *source, - const unsigned char **data, - unsigned long *length, - cairo_image_info_t *info) -{ - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_int_status_t status; - DWORD result; - - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); - if (status) - return status; - - result = 0; - if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, - sizeof(result), (char *) &result) <= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (result != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - *data = mime_data; - *length = mime_data_length; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, - cairo_surface_t *source, - const unsigned char **data, - unsigned long *length, - cairo_image_info_t *info) -{ - const unsigned char *mime_data; - unsigned long mime_data_length; - - cairo_int_status_t status; - DWORD result; - - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); - if (status) - return status; - - result = 0; - if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, - sizeof(result), (char *) &result) <= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (result != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - *data = mime_data; - *length = mime_data_length; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - cairo_status_t status; - cairo_extend_t extend; - cairo_image_surface_t *image; - void *image_extra; - cairo_image_surface_t *opaque_image = NULL; - BITMAPINFO bi; - cairo_matrix_t m; - int oldmode; - XFORM xform; - int x_tile, y_tile, left, right, top, bottom; - RECT clip; - const cairo_color_t *background_color; - const unsigned char *mime_data; - unsigned long mime_size; - cairo_image_info_t mime_info; - cairo_bool_t use_mime; - DWORD mime_type; - cairo_bool_t axis_swap; - - /* If we can't use StretchDIBits with this surface, we can't do anything - * here. - */ - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) - background_color = CAIRO_COLOR_WHITE; - else - background_color = CAIRO_COLOR_BLACK; - - extend = cairo_pattern_get_extend (&pattern->base); - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, &image_extra); - if (status) - return status; - - if (image->base.status) { - status = image->base.status; - goto CLEANUP_IMAGE; - } - - if (image->width == 0 || image->height == 0) { - status = CAIRO_STATUS_SUCCESS; - goto CLEANUP_IMAGE; - } - - mime_type = BI_JPEG; - status = _cairo_win32_printing_surface_check_jpeg (surface, - pattern->surface, - &mime_data, - &mime_size, - &mime_info); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - mime_type = BI_PNG; - status = _cairo_win32_printing_surface_check_png (surface, - pattern->surface, - &mime_data, - &mime_size, - &mime_info); - } - if (_cairo_status_is_error (status)) - return status; - - use_mime = (status == CAIRO_STATUS_SUCCESS); - - m = pattern->base.matrix; - status = cairo_matrix_invert (&m); - /* _cairo_pattern_set_matrix guarantees invertibility */ - assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_multiply (&m, &m, &surface->ctm); - cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); - /* Check if the matrix swaps the X and Y axes by checking if the diagonal - * is effectively zero. This can happen, for example, if it was composed - * with a rotation such as a landscape transform. Some printing devices - * don't support such transforms in StretchDIBits. - */ - axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1; - - if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) { - cairo_surface_t *opaque_surface; - cairo_surface_pattern_t image_pattern; - cairo_solid_pattern_t background_pattern; - int width = image->width, height = image->height; - - if (axis_swap) { - width = image->height; - height = image->width; - } - opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - width, - height); - if (opaque_surface->status) { - status = opaque_surface->status; - goto CLEANUP_OPAQUE_IMAGE; - } - - if (image->format != CAIRO_FORMAT_RGB24) { - _cairo_pattern_init_solid (&background_pattern, - background_color); - status = _cairo_surface_paint (opaque_surface, - CAIRO_OPERATOR_SOURCE, - &background_pattern.base, - NULL); - if (status) - goto CLEANUP_OPAQUE_IMAGE; - } - - _cairo_pattern_init_for_surface (&image_pattern, &image->base); - if (axis_swap) { - /* swap the X and Y axes to undo the axis swap in the matrix */ - cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; - cairo_pattern_set_matrix (&image_pattern.base, &swap_xy); - cairo_matrix_multiply (&m, &swap_xy, &m); - } - status = _cairo_surface_paint (opaque_surface, - CAIRO_OPERATOR_OVER, - &image_pattern.base, - NULL); - _cairo_pattern_fini (&image_pattern.base); - if (status) - goto CLEANUP_OPAQUE_IMAGE; - - opaque_image = (cairo_image_surface_t *) opaque_surface; - } else { - opaque_image = image; - } - - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; - bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; - bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; - bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - SaveDC (surface->dc); - _cairo_matrix_to_win32_xform (&m, &xform); - - if (! SetWorldTransform (surface->dc, &xform)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); - goto CLEANUP_OPAQUE_IMAGE; - } - - oldmode = SetStretchBltMode(surface->dc, HALFTONE); - - GetClipBox (surface->dc, &clip); - if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - left = floor ( clip.left / (double) opaque_image->width); - right = ceil (clip.right / (double) opaque_image->width); - top = floor (clip.top / (double) opaque_image->height); - bottom = ceil (clip.bottom / (double) opaque_image->height); - } else { - left = 0; - right = 1; - top = 0; - bottom = 1; - } - - for (y_tile = top; y_tile < bottom; y_tile++) { - for (x_tile = left; x_tile < right; x_tile++) { - if (!StretchDIBits (surface->dc, - x_tile*opaque_image->width, - y_tile*opaque_image->height, - opaque_image->width, - opaque_image->height, - 0, - 0, - use_mime ? mime_info.width : opaque_image->width, - use_mime ? mime_info.height : opaque_image->height, - use_mime ? mime_data : opaque_image->data, - &bi, - DIB_RGB_COLORS, - SRCCOPY)) - { - status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); - goto CLEANUP_OPAQUE_IMAGE; - } - } - } - SetStretchBltMode(surface->dc, oldmode); - RestoreDC (surface->dc, -1); - -CLEANUP_OPAQUE_IMAGE: - if (opaque_image != image) - cairo_surface_destroy (&opaque_image->base); -CLEANUP_IMAGE: - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_status_t -_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - if (_cairo_surface_is_recording (pattern->surface)) { - return _cairo_win32_printing_surface_paint_recording_pattern (surface, - pattern); - } else { - return _cairo_win32_printing_surface_paint_image_pattern (surface, - pattern); - } -} - -static void -vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) -{ - /* MSDN says that the range here is 0x0000 .. 0xff00; - * that may well be a typo, but just chop the low bits - * here. */ - vert->Alpha = 0xff00; - vert->Red = color->red_short & 0xff00; - vert->Green = color->green_short & 0xff00; - vert->Blue = color->blue_short & 0xff00; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surface, - cairo_linear_pattern_t *pattern) -{ - TRIVERTEX *vert; - GRADIENT_RECT *rect; - RECT clip; - XFORM xform; - int i, num_stops; - cairo_matrix_t mat, rot; - double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs; - cairo_extend_t extend; - int range_start, range_stop, num_ranges, num_rects, stop; - int total_verts, total_rects; - cairo_status_t status; - - extend = cairo_pattern_get_extend (&pattern->base.base); - SaveDC (surface->dc); - - mat = pattern->base.base.matrix; - status = cairo_matrix_invert (&mat); - /* _cairo_pattern_set_matrix guarantees invertibility */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&mat, &surface->ctm, &mat); - - p1x = _cairo_fixed_to_double (pattern->p1.x); - p1y = _cairo_fixed_to_double (pattern->p1.y); - p2x = _cairo_fixed_to_double (pattern->p2.x); - p2y = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_translate (&mat, p1x, p1y); - - xd = p2x - p1x; - yd = p2y - p1y; - d = sqrt (xd*xd + yd*yd); - sn = yd/d; - cs = xd/d; - cairo_matrix_init (&rot, - cs, sn, - -sn, cs, - 0, 0); - cairo_matrix_multiply (&mat, &rot, &mat); - - _cairo_matrix_to_win32_xform (&mat, &xform); - - if (!SetWorldTransform (surface->dc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); - - GetClipBox (surface->dc, &clip); - - if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { - range_start = floor (clip.left / d); - range_stop = ceil (clip.right / d); - } else { - range_start = 0; - range_stop = 1; - } - num_ranges = range_stop - range_start; - num_stops = pattern->base.n_stops; - num_rects = num_stops - 1; - - /* Add an extra four points and two rectangles for EXTEND_PAD */ - vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); - rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); - - for (i = 0; i < num_ranges*num_rects; i++) { - vert[i*2].y = (LONG) clip.top; - if (i%num_rects == 0) { - stop = 0; - if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) - stop = num_rects; - vert[i*2].x = (LONG)(d*(range_start + i/num_rects)); - vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color); - } else { - vert[i*2].x = vert[i*2-1].x; - vert[i*2].Red = vert[i*2-1].Red; - vert[i*2].Green = vert[i*2-1].Green; - vert[i*2].Blue = vert[i*2-1].Blue; - vert[i*2].Alpha = vert[i*2-1].Alpha; - } - - stop = i%num_rects + 1; - vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset)); - vert[i*2+1].y = (LONG) clip.bottom; - if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) - stop = num_rects - stop; - vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color); - - rect[i].UpperLeft = i*2; - rect[i].LowerRight = i*2 + 1; - } - total_verts = 2*num_ranges*num_rects; - total_rects = num_ranges*num_rects; - - if (extend == CAIRO_EXTEND_PAD) { - vert[i*2].x = vert[i*2-1].x; - vert[i*2].y = (LONG) clip.top; - vert[i*2].Red = vert[i*2-1].Red; - vert[i*2].Green = vert[i*2-1].Green; - vert[i*2].Blue = vert[i*2-1].Blue; - vert[i*2].Alpha = 0xff00; - vert[i*2+1].x = clip.right; - vert[i*2+1].y = (LONG) clip.bottom; - vert[i*2+1].Red = vert[i*2-1].Red; - vert[i*2+1].Green = vert[i*2-1].Green; - vert[i*2+1].Blue = vert[i*2-1].Blue; - vert[i*2+1].Alpha = 0xff00; - rect[i].UpperLeft = i*2; - rect[i].LowerRight = i*2 + 1; - - i++; - - vert[i*2].x = clip.left; - vert[i*2].y = (LONG) clip.top; - vert[i*2].Red = vert[0].Red; - vert[i*2].Green = vert[0].Green; - vert[i*2].Blue = vert[0].Blue; - vert[i*2].Alpha = 0xff00; - vert[i*2+1].x = vert[0].x; - vert[i*2+1].y = (LONG) clip.bottom; - vert[i*2+1].Red = vert[0].Red; - vert[i*2+1].Green = vert[0].Green; - vert[i*2+1].Blue = vert[0].Blue; - vert[i*2+1].Alpha = 0xff00; - rect[i].UpperLeft = i*2; - rect[i].LowerRight = i*2 + 1; - - total_verts += 4; - total_rects += 2; - } - - if (!GradientFill (surface->dc, - vert, total_verts, - rect, total_rects, - GRADIENT_FILL_RECT_H)) - return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); - - free (rect); - free (vert); - RestoreDC (surface->dc, -1); - - return 0; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, - const cairo_pattern_t *pattern) -{ - cairo_status_t status; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern); - if (status) - return status; - break; - - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_win32_printing_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) pattern); - if (status) - return status; - break; - - case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); - if (status) - return status; - break; - - case CAIRO_PATTERN_TYPE_RADIAL: - return CAIRO_INT_STATUS_UNSUPPORTED; - break; - } - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _win32_print_path_info { - cairo_win32_surface_t *surface; -} win32_path_info_t; - -static cairo_status_t -_cairo_win32_printing_surface_path_move_to (void *closure, - const cairo_point_t *point) -{ - win32_path_info_t *path_info = closure; - - if (path_info->surface->has_ctm) { - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL); - } else { - MoveToEx (path_info->surface->dc, - _cairo_fixed_integer_part (point->x), - _cairo_fixed_integer_part (point->y), - NULL); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_path_line_to (void *closure, - const cairo_point_t *point) -{ - win32_path_info_t *path_info = closure; - - path_info->surface->path_empty = FALSE; - if (path_info->surface->has_ctm) { - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - LineTo (path_info->surface->dc, (int) x, (int) y); - } else { - LineTo (path_info->surface->dc, - _cairo_fixed_integer_part (point->x), - _cairo_fixed_integer_part (point->y)); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_path_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - win32_path_info_t *path_info = closure; - POINT points[3]; - - path_info->surface->path_empty = FALSE; - if (path_info->surface->has_ctm) { - double x, y; - - x = _cairo_fixed_to_double (b->x); - y = _cairo_fixed_to_double (b->y); - cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - points[0].x = (LONG) x; - points[0].y = (LONG) y; - - x = _cairo_fixed_to_double (c->x); - y = _cairo_fixed_to_double (c->y); - cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - points[1].x = (LONG) x; - points[1].y = (LONG) y; - - x = _cairo_fixed_to_double (d->x); - y = _cairo_fixed_to_double (d->y); - cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - points[2].x = (LONG) x; - points[2].y = (LONG) y; - } else { - points[0].x = _cairo_fixed_integer_part (b->x); - points[0].y = _cairo_fixed_integer_part (b->y); - points[1].x = _cairo_fixed_integer_part (c->x); - points[1].y = _cairo_fixed_integer_part (c->y); - points[2].x = _cairo_fixed_integer_part (d->x); - points[2].y = _cairo_fixed_integer_part (d->y); - } - PolyBezierTo (path_info->surface->dc, points, 3); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_path_close_path (void *closure) -{ - win32_path_info_t *path_info = closure; - - CloseFigure (path_info->surface->dc); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, - cairo_path_fixed_t *path) -{ - win32_path_info_t path_info; - - path_info.surface = surface; - return _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_win32_printing_surface_path_move_to, - _cairo_win32_printing_surface_path_line_to, - _cairo_win32_printing_surface_path_curve_to, - _cairo_win32_printing_surface_path_close_path, - &path_info); -} - -static cairo_int_status_t -_cairo_win32_printing_surface_show_page (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - - /* Undo both SaveDC's that we did in start_page */ - RestoreDC (surface->dc, -2); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_win32_surface_t *surface = cairo_container_of (clipper, - cairo_win32_surface_t, - clipper); - cairo_status_t status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - if (path == NULL) { - RestoreDC (surface->dc, -1); - SaveDC (surface->dc); - - return CAIRO_STATUS_SUCCESS; - } - - BeginPath (surface->dc); - status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); - - switch (fill_rule) { - case CAIRO_FILL_RULE_WINDING: - SetPolyFillMode (surface->dc, WINDING); - break; - case CAIRO_FILL_RULE_EVEN_ODD: - SetPolyFillMode (surface->dc, ALTERNATE); - break; - default: - ASSERT_NOT_REACHED; - } - - SelectClipPath (surface->dc, RGN_AND); - - return status; -} - -static void -_cairo_win32_printing_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); - cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); -} - -static cairo_int_status_t -_cairo_win32_printing_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_solid_pattern_t clear; - cairo_status_t status; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; - - if (op == CAIRO_OPERATOR_CLEAR) { - _cairo_win32_printing_surface_init_clear_color (surface, &clear); - source = (cairo_pattern_t*) &clear; - op = CAIRO_OPERATOR_SOURCE; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); - - return _cairo_win32_printing_surface_paint_pattern (surface, source); -} - -static int -_cairo_win32_line_cap (cairo_line_cap_t cap) -{ - switch (cap) { - case CAIRO_LINE_CAP_BUTT: - return PS_ENDCAP_FLAT; - case CAIRO_LINE_CAP_ROUND: - return PS_ENDCAP_ROUND; - case CAIRO_LINE_CAP_SQUARE: - return PS_ENDCAP_SQUARE; - default: - ASSERT_NOT_REACHED; - return 0; - } -} - -static int -_cairo_win32_line_join (cairo_line_join_t join) -{ - switch (join) { - case CAIRO_LINE_JOIN_MITER: - return PS_JOIN_MITER; - case CAIRO_LINE_JOIN_ROUND: - return PS_JOIN_ROUND; - case CAIRO_LINE_JOIN_BEVEL: - return PS_JOIN_BEVEL; - default: - ASSERT_NOT_REACHED; - return 0; - } -} - -static void -_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) -{ - double s; - - s = fabs (m->xx); - if (fabs (m->xy) > s) - s = fabs (m->xy); - if (fabs (m->yx) > s) - s = fabs (m->yx); - if (fabs (m->yy) > s) - s = fabs (m->yy); - *scale = s; - s = 1.0/s; - cairo_matrix_scale (m, s, s); -} - -static cairo_int_status_t -_cairo_win32_printing_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_int_status_t status; - HPEN pen; - LOGBRUSH brush; - COLORREF color; - XFORM xform; - DWORD pen_style; - DWORD pen_width; - DWORD *dash_array; - HGDIOBJ obj; - unsigned int i; - cairo_solid_pattern_t clear; - cairo_matrix_t mat; - double scale; - double scaled_width; - double major, minor; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; - - if (op == CAIRO_OPERATOR_CLEAR) { - _cairo_win32_printing_surface_init_clear_color (surface, &clear); - source = (cairo_pattern_t*) &clear; - op = CAIRO_OPERATOR_SOURCE; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - /* Win32 does not support a dash offset. */ - if (style->num_dashes > 0 && style->dash_offset != 0.0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - } - - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); - assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); - - cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); - _cairo_matrix_factor_out_scale (&mat, &scale); - - pen_style = PS_GEOMETRIC; - dash_array = NULL; - if (style->num_dashes) { - pen_style |= PS_USERSTYLE; - dash_array = calloc (sizeof (DWORD), style->num_dashes); - for (i = 0; i < style->num_dashes; i++) { - DWORD dashes = (DWORD) (scale * style->dash[i]); - /* zero dash-lengths cause ExtCreatePen to fail. Make the dashes - * longer if necessary. - */ - dash_array[i] = MAX(1, dashes); - } - } else { - pen_style |= PS_SOLID; - } - - SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL); - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - - - color = _cairo_win32_printing_surface_flatten_transparency (surface, - &solid->color); - } else { - /* Color not used as the pen will only be used by WidenPath() */ - color = RGB (0,0,0); - } - brush.lbStyle = BS_SOLID; - brush.lbColor = color; - brush.lbHatch = 0; - pen_style |= _cairo_win32_line_cap (style->line_cap); - pen_style |= _cairo_win32_line_join (style->line_join); - scaled_width = scale * style->line_width; - if (scaled_width == 0.0) - return status; - pen_width = (DWORD)scaled_width; - if (pen_width == 0) { - /* ExtCreatePen will fail if passed zero width. We have to choose - * between drawing something too wide, or drawing nothing at all. - * Let's draw something. - */ - pen_width = 1; - } - pen = ExtCreatePen(pen_style, - pen_width, - &brush, - style->num_dashes, - dash_array); - if (pen == NULL) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); - obj = SelectObject (surface->dc, pen); - if (obj == NULL) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); - - BeginPath (surface->dc); - status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); - if (status) - return status; - - /* - * Switch to user space to set line parameters - */ - SaveDC (surface->dc); - - /* Some printers don't handle transformed strokes. Avoid the transform - * if not required for the pen shape. Use the SVD here to find the major - * and minor scales then check if they differ by more than 1 device unit. - * If the difference is smaller, then just treat the scaling as uniform. - * This check ignores rotations as the pen shape is symmetric before - * transformation. - */ - _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor); - if (fabs (major - minor) > 1) { - /* Check if the matrix swaps the X and Y axes such that the diagonal - * is nearly zero. This was observed to cause problems with XPS export. - */ - if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) { - /* swap the X and Y axes to undo the axis swap in the matrix */ - cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; - cairo_matrix_multiply (&mat, &swap_xy, &mat); - } - _cairo_matrix_to_win32_xform (&mat, &xform); - xform.eDx = 0.0f; - xform.eDy = 0.0f; - - if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); - } - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - StrokePath (surface->dc); - } else { - if (!WidenPath (surface->dc)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); - if (!SelectClipPath (surface->dc, RGN_AND)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); - - /* Return to device space to paint the pattern */ - _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); - if (!SetWorldTransform (surface->dc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); - } - RestoreDC (surface->dc, -1); - DeleteObject (pen); - if (dash_array) - free (dash_array); - - return status; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_int_status_t status; - cairo_solid_pattern_t clear; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; - - if (op == CAIRO_OPERATOR_CLEAR) { - _cairo_win32_printing_surface_init_clear_color (surface, &clear); - source = (cairo_pattern_t*) &clear; - op = CAIRO_OPERATOR_SOURCE; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); - - surface->path_empty = TRUE; - BeginPath (surface->dc); - status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); - - switch (fill_rule) { - case CAIRO_FILL_RULE_WINDING: - SetPolyFillMode (surface->dc, WINDING); - break; - case CAIRO_FILL_RULE_EVEN_ODD: - SetPolyFillMode (surface->dc, ALTERNATE); - break; - default: - ASSERT_NOT_REACHED; - } - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_win32_printing_surface_select_solid_brush (surface, source); - if (status) - return status; - - FillPath (surface->dc); - _cairo_win32_printing_surface_done_solid_brush (surface); - } else if (surface->path_empty == FALSE) { - SaveDC (surface->dc); - SelectClipPath (surface->dc, RGN_AND); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); - RestoreDC (surface->dc, -1); - } - - fflush(stderr); - - return status; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_matrix_t ctm; - cairo_glyph_t *unicode_glyphs; - cairo_scaled_font_subsets_glyph_t subset_glyph; - int i, first; - cairo_bool_t sequence_is_unicode; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - /* Where possible reverse the glyph indices back to unicode - * characters. Strings of glyphs that could not be reversed to - * unicode will be printed with ETO_GLYPH_INDEX. - * - * As _cairo_win32_scaled_font_index_to_ucs4() is a slow - * operation, the font subsetting function - * _cairo_scaled_font_subsets_map_glyph() is used to obtain - * the unicode value because it caches the reverse mapping in - * the subsets. - */ - - if (surface->has_ctm) { - for (i = 0; i < num_glyphs; i++) - cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); - cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); - scaled_font = cairo_scaled_font_create (scaled_font->font_face, - &scaled_font->font_matrix, - &ctm, - &scaled_font->options); - } - - unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unicode_glyphs == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, - scaled_font, - glyphs[i].index, - NULL, 0, - &subset_glyph); - if (status) - goto fail; - - unicode_glyphs[i].index = subset_glyph.unicode; - } - - i = 0; - first = 0; - sequence_is_unicode = unicode_glyphs[0].index <= 0xffff; - while (i < num_glyphs) { - if (i == num_glyphs - 1 || - ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode)) - { - status = _cairo_win32_surface_show_glyphs_internal ( - surface, - op, - source, - sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], - i - first + 1, - scaled_font, - clip, - remaining_glyphs, - ! sequence_is_unicode); - first = i + 1; - if (i < num_glyphs - 1) - sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff; - } - i++; - } - -fail: - if (surface->has_ctm) - cairo_scaled_font_destroy (scaled_font); - - free (unicode_glyphs); - - return status; -} - -static cairo_int_status_t -_cairo_win32_printing_surface_show_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph; - cairo_pattern_t *opaque = NULL; - int i; - cairo_matrix_t old_ctm; - cairo_bool_t old_has_ctm; - cairo_solid_pattern_t clear; - cairo_scaled_font_t *local_scaled_font = NULL; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; - - if (op == CAIRO_OPERATOR_CLEAR) { - _cairo_win32_printing_surface_init_clear_color (surface, &clear); - source = (cairo_pattern_t*) &clear; - op = CAIRO_OPERATOR_SOURCE; - } - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - /* When printing bitmap fonts to a printer DC, Windows may - * substitute an outline font for bitmap font. As the win32 - * font backend always uses a screen DC when obtaining the - * font metrics the metrics of the substituted font will not - * match the metrics that the win32 font backend returns. - * - * If we are printing a bitmap font, use fallback images to - * ensure the font is not substituted. - */ -#if CAIRO_HAS_WIN32_FONT - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { - if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - } -#endif - -#if CAIRO_HAS_DWRITE_FONT - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - } -#endif - - /* For non win32 fonts we need to check that each glyph has a - * path available. If a path is not available, - * _cairo_scaled_glyph_lookup() will return - * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be - * used. - */ - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - if (status) - return status; - } - - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - } - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - COLORREF color; - - color = _cairo_win32_printing_surface_flatten_transparency (surface, - &solid->color); - opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, - GetGValue (color) / 255.0, - GetBValue (color) / 255.0); - if (opaque->status) - return opaque->status; - source = opaque; - } - -#if CAIRO_HAS_DWRITE_FONT - /* For a printer, the dwrite path is not desirable as it goes through the - * bitmap-blitting GDI interop route. Better to create a win32 (GDI) font - * so that ExtTextOut can be used, giving the printer driver the chance - * to do the right thing with the text. - */ - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { - status = _cairo_dwrite_scaled_font_create_win32_scaled_font (scaled_font, &local_scaled_font); - if (status == CAIRO_STATUS_SUCCESS) { - scaled_font = local_scaled_font; - } else { - /* Reset status; we'll fall back to drawing glyphs as paths */ - status = CAIRO_STATUS_SUCCESS; - } - } -#endif - -#if CAIRO_HAS_WIN32_FONT - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && - source->type == CAIRO_PATTERN_TYPE_SOLID) - { - status = _cairo_win32_printing_surface_emit_win32_glyphs (surface, - op, - source, - glyphs, - num_glyphs, - scaled_font, - clip, - remaining_glyphs); - goto FINISH; - } -#endif - - SaveDC (surface->dc); - old_ctm = surface->ctm; - old_has_ctm = surface->has_ctm; - surface->has_ctm = TRUE; - surface->path_empty = TRUE; - BeginPath (surface->dc); - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_PATH, - &scaled_glyph); - if (status) - break; - surface->ctm = old_ctm; - cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); - status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); - } - EndPath (surface->dc); - surface->ctm = old_ctm; - surface->has_ctm = old_has_ctm; - if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) { - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_win32_printing_surface_select_solid_brush (surface, source); - if (status) - goto FINISH; - - SetPolyFillMode (surface->dc, WINDING); - FillPath (surface->dc); - _cairo_win32_printing_surface_done_solid_brush (surface); - } else { - SelectClipPath (surface->dc, RGN_AND); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); - } - } - RestoreDC (surface->dc, -1); - - if (opaque) - cairo_pattern_destroy (opaque); - -FINISH: - if (local_scaled_font) - cairo_scaled_font_destroy (local_scaled_font); - - return status; -} - -static cairo_surface_t * -_cairo_win32_printing_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_rectangle_t extents; - - extents.x = extents.y = 0; - extents.width = width; - extents.height = height; - return cairo_recording_surface_create (content, &extents); -} - -static cairo_int_status_t -_cairo_win32_printing_surface_start_page (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - XFORM xform; - double x_res, y_res; - cairo_matrix_t inverse_ctm; - cairo_status_t status; - - SaveDC (surface->dc); /* Save application context first, before doing MWT */ - - /* As the logical coordinates used by GDI functions (eg LineTo) - * are integers we need to do some additional work to prevent - * rounding errors. For example the obvious way to paint a recording - * pattern is to: - * - * SaveDC() - * transform the device context DC by the pattern to device matrix - * replay the recording surface - * RestoreDC() - * - * The problem here is that if the pattern to device matrix is - * [100 0 0 100 0 0], coordinates in the recording pattern such as - * (1.56, 2.23) which correspond to (156, 223) in device space - * will be rounded to (100, 200) due to (1.56, 2.23) being - * truncated to integers. - * - * This is solved by saving the current GDI CTM in surface->ctm, - * switch the GDI CTM to identity, and transforming all - * coordinates by surface->ctm before passing them to GDI. When - * painting a recording pattern, surface->ctm is transformed by the - * pattern to device matrix. - * - * For printing device contexts where 1 unit is 1 dpi, switching - * the GDI CTM to identity maximises the possible resolution of - * coordinates. - * - * If the device context is an EMF file, using an identity - * transform often provides insufficent resolution. The workaround - * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] - * and scale the cairo CTM by [16 0 0 16 0 0]. The - * SetWorldTransform function call to scale the GDI CTM by 1.0/16 - * will be recorded in the EMF followed by all the graphics - * functions by their coordinateds multiplied by 16. - * - * To support allowing the user to set a GDI CTM with scale < 1, - * we avoid switching to an identity CTM if the CTM xx and yy is < 1. - */ - SetGraphicsMode (surface->dc, GM_ADVANCED); - GetWorldTransform(surface->dc, &xform); - if (xform.eM11 < 1 && xform.eM22 < 1) { - cairo_matrix_init_identity (&surface->ctm); - surface->gdi_ctm.xx = xform.eM11; - surface->gdi_ctm.xy = xform.eM21; - surface->gdi_ctm.yx = xform.eM12; - surface->gdi_ctm.yy = xform.eM22; - surface->gdi_ctm.x0 = xform.eDx; - surface->gdi_ctm.y0 = xform.eDy; - } else { - surface->ctm.xx = xform.eM11; - surface->ctm.xy = xform.eM21; - surface->ctm.yx = xform.eM12; - surface->ctm.yy = xform.eM22; - surface->ctm.x0 = xform.eDx; - surface->ctm.y0 = xform.eDy; - cairo_matrix_init_identity (&surface->gdi_ctm); - if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); - } - - surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); - surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); - inverse_ctm = surface->ctm; - status = cairo_matrix_invert (&inverse_ctm); - if (status) - return status; - - x_res = GetDeviceCaps (surface->dc, LOGPIXELSX); - y_res = GetDeviceCaps (surface->dc, LOGPIXELSY); - cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); - _cairo_surface_set_resolution (&surface->base, x_res, y_res); - - SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t paginated_mode) -{ - cairo_win32_surface_t *surface = abstract_surface; - - surface->paginated_mode = paginated_mode; -} - -static cairo_bool_t -_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface) -{ - return TRUE; -} - -/** - * cairo_win32_printing_surface_create: - * @hdc: the DC to create a surface for - * - * Creates a cairo surface that targets the given DC. The DC will be - * queried for its initial clip extents, and this will be used as the - * size of the cairo surface. The DC should be a printing DC; - * antialiasing will be ignored, and GDI will be used as much as - * possible to draw to the surface. - * - * The returned surface will be wrapped using the paginated surface to - * provide correct complex rendering behaviour; show_page() and - * associated methods must be used for correct output. - * - * Return value: the newly created surface - * - * Since: 1.6 - **/ -cairo_surface_t * -cairo_win32_printing_surface_create (HDC hdc) -{ - cairo_win32_surface_t *surface; - cairo_surface_t *paginated; - RECT rect; - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_clipper_init (&surface->clipper, - _cairo_win32_printing_surface_clipper_intersect_clip_path); - - surface->image = NULL; - surface->format = CAIRO_FORMAT_RGB24; - surface->content = CAIRO_CONTENT_COLOR_ALPHA; - surface->d3d9surface = NULL; - - surface->dc = hdc; - surface->bitmap = NULL; - surface->is_dib = FALSE; - surface->saved_dc_bitmap = NULL; - surface->brush = NULL; - surface->old_brush = NULL; - surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); - if (surface->font_subsets == NULL) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - GetClipBox(hdc, &rect); - surface->extents.x = rect.left; - surface->extents.y = rect.top; - surface->extents.width = rect.right - rect.left; - surface->extents.height = rect.bottom - rect.top; - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; - - _cairo_win32_printing_surface_init_ps_mode (surface); - _cairo_win32_printing_surface_init_image_support (surface); - _cairo_win32_printing_surface_init_language_pack (surface); - _cairo_surface_init (&surface->base, - &cairo_win32_printing_surface_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); - - paginated = _cairo_paginated_surface_create (&surface->base, - CAIRO_CONTENT_COLOR_ALPHA, - &cairo_win32_surface_paginated_backend); - - /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); - - return paginated; -} - -cairo_bool_t -_cairo_surface_is_win32_printing (cairo_surface_t *surface) -{ - return surface->backend == &cairo_win32_printing_surface_backend; -} - -static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { - CAIRO_SURFACE_TYPE_WIN32_PRINTING, - _cairo_win32_printing_surface_create_similar, - _cairo_win32_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - _cairo_win32_printing_surface_show_page, - _cairo_win32_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_win32_printing_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_win32_printing_surface_paint, - NULL, /* mask */ - _cairo_win32_printing_surface_stroke, - _cairo_win32_printing_surface_fill, - _cairo_win32_printing_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ -}; - -static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { - _cairo_win32_printing_surface_start_page, - _cairo_win32_printing_surface_set_paginated_mode, - NULL, /* set_bounding_box */ - NULL, /* _cairo_win32_printing_surface_has_fallback_images, */ - _cairo_win32_printing_surface_supports_fine_grained_fallbacks, -}; diff --git a/libs/cairo/cairo/src/cairo-win32-private.h b/libs/cairo/cairo/src/cairo-win32-private.h deleted file mode 100644 index 571b7547c..000000000 --- a/libs/cairo/cairo/src/cairo-win32-private.h +++ /dev/null @@ -1,218 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_WIN32_PRIVATE_H -#define CAIRO_WIN32_PRIVATE_H - -#include "cairo-win32.h" -#include "cairoint.h" -#include "cairo-surface-clipper-private.h" - -#ifndef SHADEBLENDCAPS -#define SHADEBLENDCAPS 120 -#endif -#ifndef SB_NONE -#define SB_NONE 0 -#endif - -#define WIN32_FONT_LOGICAL_SCALE 1 - - -CAIRO_BEGIN_DECLS - -typedef struct _cairo_win32_surface { - cairo_surface_t base; - - cairo_format_t format; - - HDC dc; - - struct IDirect3DSurface9 *d3d9surface; - - /* We create off-screen surfaces as DIBs or DDBs, based on what we created - * originally*/ - HBITMAP bitmap; - cairo_bool_t is_dib; - - /* Used to save the initial 1x1 monochrome bitmap for the DC to - * select back into the DC before deleting the DC and our - * bitmap. For Windows XP, this doesn't seem to be necessary - * ... we can just delete the DC and that automatically unselects - * out bitmap. But it's standard practice so apparently is needed - * on some versions of Windows. - */ - HBITMAP saved_dc_bitmap; - - cairo_surface_t *image; - - cairo_rectangle_int_t extents; - - /* Initial clip bits - * We need these kept around so that we maintain - * whatever clip was set on the original DC at creation - * time when cairo is asked to reset the surface clip. - */ - cairo_rectangle_int_t clip_rect; - HRGN initial_clip_rgn; - cairo_bool_t had_simple_clip; - cairo_region_t *clip_region; - - /* For path clipping to the printing-surface */ - cairo_surface_clipper_t clipper; - - /* Surface DC flags */ - uint32_t flags; - - /* printing surface bits */ - cairo_paginated_mode_t paginated_mode; - cairo_content_t content; - cairo_bool_t path_empty; - cairo_bool_t has_ctm; - cairo_matrix_t ctm; - cairo_bool_t has_gdi_ctm; - cairo_matrix_t gdi_ctm; - HBRUSH brush, old_brush; - cairo_scaled_font_subsets_t *font_subsets; -} cairo_win32_surface_t; - -/* Surface DC flag values */ -enum { - /* If this is a surface created for printing or not */ - CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0), - - /* Whether the DC is a display DC or not */ - CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1), - - /* Whether we can use BitBlt with this surface */ - CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2), - - /* Whether we can use AlphaBlend with this surface */ - CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3), - - /* Whether we can use StretchBlt with this surface */ - CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4), - - /* Whether we can use StretchDIBits with this surface */ - CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5), - - /* Whether we can use GradientFill rectangles with this surface */ - CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), - - /* Whether we can use the CHECKJPEGFORMAT escape function */ - CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), - - /* Whether we can use the CHECKJPEGFORMAT escape function */ - CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), - - /* if this DDB surface can be converted to a DIB if necessary */ - CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<9), -}; - -cairo_status_t -_cairo_win32_print_gdi_error (const char *context); - -cairo_bool_t -_cairo_surface_is_win32 (cairo_surface_t *surface); - -cairo_bool_t -_cairo_surface_is_win32_printing (cairo_surface_t *surface); - -cairo_status_t -_cairo_win32_surface_finish (void *abstract_surface); - -cairo_bool_t -_cairo_win32_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle); - -uint32_t -_cairo_win32_flags_for_dc (HDC dc); - -cairo_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region); - -cairo_int_status_t -_cairo_win32_surface_show_glyphs_internal (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs, - cairo_bool_t glyph_indices); - -cairo_int_status_t -_cairo_win32_surface_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -cairo_surface_t * -_cairo_win32_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height); - -cairo_status_t -_cairo_win32_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_content_t content, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out); - -static inline void -_cairo_matrix_to_win32_xform (const cairo_matrix_t *m, - XFORM *xform) -{ - xform->eM11 = (FLOAT) m->xx; - xform->eM21 = (FLOAT) m->xy; - xform->eM12 = (FLOAT) m->yx; - xform->eM22 = (FLOAT) m->yy; - xform->eDx = (FLOAT) m->x0; - xform->eDy = (FLOAT) m->y0; -} - -cairo_int_status_t -_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface); - -cairo_int_status_t -_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface); - -void -_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); - -cairo_bool_t -_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); - -cairo_bool_t -_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); - -#ifdef CAIRO_HAS_DWRITE_FONT - -cairo_int_status_t -_cairo_dwrite_show_glyphs_on_surface(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); - -cairo_int_status_t -_cairo_dwrite_scaled_font_create_win32_scaled_font(cairo_scaled_font_t *scaled_font, - cairo_scaled_font_t **new_font); - -#endif /* CAIRO_HAS_DWRITE_FONT */ -CAIRO_END_DECLS -#endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-win32-refptr.h b/libs/cairo/cairo/src/cairo-win32-refptr.h deleted file mode 100644 index 8c0ec3338..000000000 --- a/libs/cairo/cairo/src/cairo-win32-refptr.h +++ /dev/null @@ -1,147 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_WIN32_REFPTR_H -#define CAIRO_WIN32_REFPTR_H - -template class TemporaryRef; - -/** - * RefPtr points to a refcounted thing that has AddRef and Release - * methods to increase/decrease the refcount, respectively. After a - * RefPtr is assigned a T*, the T* can be used through the RefPtr - * as if it were a T*. - * - * A RefPtr can forget its underlying T*, which results in the T* - * being wrapped in a temporary object until the T* is either - * re-adopted from or released by the temporary. - */ -template -class RefPtr -{ - // To allow them to use unref() - friend class TemporaryRef; - - struct dontRef {}; - -public: - RefPtr() : ptr(0) { } - RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} - RefPtr(const TemporaryRef& o) : ptr(o.drop()) {} - RefPtr(T* t) : ptr(ref(t)) {} - - template - RefPtr(const RefPtr& o) : ptr(ref(o.get())) {} - - ~RefPtr() { unref(ptr); } - - RefPtr& operator=(const RefPtr& o) { - assign(ref(o.ptr)); - return *this; - } - RefPtr& operator=(const TemporaryRef& o) { - assign(o.drop()); - return *this; - } - RefPtr& operator=(T* t) { - assign(ref(t)); - return *this; - } - - template - RefPtr& operator=(const RefPtr& o) { - assign(ref(o.get())); - return *this; - } - - TemporaryRef forget() { - T* tmp = ptr; - ptr = 0; - return TemporaryRef(tmp, dontRef()); - } - - T* get() const { return ptr; } - operator T*() const { return ptr; } - T* operator->() const { return ptr; } - T& operator*() const { return *ptr; } - template - operator TemporaryRef() { return TemporaryRef(ptr); } - - /** - * WARNING for ease of use, passing a reference will release/clear out ptr! - * We null out the ptr before returning its address so people passing byref - * as input will most likely get functions returning errors rather than accessing - * freed memory. Further more accessing it after this point if it hasn't - * been set will produce a null pointer dereference. - */ - T** operator&() - { - if (ptr) { - ptr->Release(); - ptr = NULL; - } - return &ptr; - } - -private: - void assign(T* t) { - unref(ptr); - ptr = t; - } - - T* ptr; - - static inline T* ref(T* t) { - if (t) { - t->AddRef(); - } - return t; - } - - static inline void unref(T* t) { - if (t) { - t->Release(); - } - } -}; - -/** - * TemporaryRef represents an object that holds a temporary - * reference to a T. TemporaryRef objects can't be manually ref'd or - * unref'd (being temporaries, not lvalues), so can only relinquish - * references to other objects, or unref on destruction. - */ -template -class TemporaryRef -{ - // To allow it to construct TemporaryRef from a bare T* - friend class RefPtr; - - typedef typename RefPtr::dontRef dontRef; - -public: - TemporaryRef(T* t) : ptr(RefPtr::ref(t)) {} - TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} - - template - TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} - - ~TemporaryRef() { RefPtr::unref(ptr); } - - T* drop() const { - T* tmp = ptr; - ptr = 0; - return tmp; - } - -private: - TemporaryRef(T* t, const dontRef&) : ptr(t) {} - - mutable T* ptr; - - TemporaryRef(); - TemporaryRef& operator=(const TemporaryRef&); -}; - -#endif diff --git a/libs/cairo/cairo/src/cairo-win32-surface.c b/libs/cairo/cairo/src/cairo-win32-surface.c deleted file mode 100644 index 2d7395590..000000000 --- a/libs/cairo/cairo/src/cairo-win32-surface.c +++ /dev/null @@ -1,4056 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -/* We require at least Windows 7 features */ -#if !defined(WINVER) || (WINVER < 0x0601) -# define WINVER 0x0601 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) -# define _WIN32_WINNT 0x0601 -#endif - -#include "cairoint.h" - -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-paginated-private.h" -#include "cairo-win32-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-surface-fallback-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-gstate-private.h" -#include "cairo-private.h" -#include -#include -#include - -#if defined(__MINGW32__) && !defined(ETO_PDY) -# define ETO_PDY 0x2000 -#endif - -#undef DEBUG_COMPOSITE - -/* for older SDKs */ -#ifndef SHADEBLENDCAPS -#define SHADEBLENDCAPS 120 -#endif -#ifndef SB_NONE -#define SB_NONE 0x00000000 -#endif - -#define PELS_72DPI ((LONG)(72. / 0.0254)) - -/** - * SECTION:cairo-win32 - * @Title: Win32 Surfaces - * @Short_Description: Microsoft Windows surface support - * @See_Also: #cairo_surface_t - * - * The Microsoft Windows surface is used to render cairo graphics to - * Microsoft Windows windows, bitmaps, and printing device contexts. - * - * The surface returned by cairo_win32_printing_surface_create() is of surface - * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface - * type. - * - * The surface returned by the other win32 constructors is of surface type - * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. - */ - -/** - * CAIRO_HAS_WIN32_SURFACE: - * - * Defined if the Microsoft Windows surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -static const cairo_surface_backend_t cairo_win32_surface_backend; - -/** - * _cairo_win32_print_gdi_error: - * @context: context string to display along with the error - * - * Helper function to dump out a human readable form of the - * current error code. - * - * Return value: A cairo status code for the error code - **/ -cairo_status_t -_cairo_win32_print_gdi_error (const char *context) -{ - void *lpMsgBuf; - DWORD last_error = GetLastError (); - - if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - last_error, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR) &lpMsgBuf, - 0, NULL)) { - fprintf (stderr, "%s: Unknown GDI error", context); - } else { - fwprintf (stderr, L"%s: %S", context, (wchar_t *)lpMsgBuf); - - LocalFree (lpMsgBuf); - } - fflush(stderr); - - /* We should switch off of last_status, but we'd either return - * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there - * is no CAIRO_STATUS_UNKNOWN_ERROR. - */ - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); -} - -uint32_t -_cairo_win32_flags_for_dc (HDC dc) -{ - uint32_t flags = 0; - - if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) { - flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; - - /* These will always be possible, but the actual GetDeviceCaps - * calls will return whether they're accelerated or not. - * We may want to use our own (pixman) routines sometimes - * if they're eventually faster, but for now have GDI do - * everything. - */ - flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; - flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; - } else { - int cap; - - cap = GetDeviceCaps(dc, SHADEBLENDCAPS); - if (cap != SB_NONE) - flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; - - cap = GetDeviceCaps(dc, RASTERCAPS); - if (cap & RC_BITBLT) - flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; - if (cap & RC_STRETCHBLT) - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; - if (cap & RC_STRETCHDIB) - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; - } - - return flags; -} - -static cairo_status_t -_create_dc_and_bitmap (cairo_win32_surface_t *surface, - HDC original_dc, - cairo_format_t format, - int width, - int height, - unsigned char **bits_out, - int *rowstride_out) -{ - cairo_status_t status; - - BITMAPINFO *bitmap_info = NULL; - struct { - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[2]; - } bmi_stack; - void *bits; - - int num_palette = 0; /* Quiet GCC */ - int i; - - surface->dc = NULL; - surface->bitmap = NULL; - surface->is_dib = FALSE; - - switch (format) { - default: - case CAIRO_FORMAT_INVALID: - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - num_palette = 0; - break; - - case CAIRO_FORMAT_A8: - num_palette = 256; - break; - - case CAIRO_FORMAT_A1: - num_palette = 2; - break; - } - - if (num_palette > 2) { - bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); - if (!bitmap_info) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - bitmap_info = (BITMAPINFO *)&bmi_stack; - } - - bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; - bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ - bitmap_info->bmiHeader.biSizeImage = 0; - bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ - bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ - bitmap_info->bmiHeader.biPlanes = 1; - - switch (format) { - /* We can't create real RGB24 bitmaps because something seems to - * break if we do, especially if we don't set up an image - * fallback. It could be a bug with using a 24bpp pixman image - * (and creating one with masks). So treat them like 32bpp. - * Note: This causes problems when using BitBlt/AlphaBlend/etc! - * see end of file. - */ - case CAIRO_FORMAT_RGB24: - case CAIRO_FORMAT_ARGB32: - bitmap_info->bmiHeader.biBitCount = 32; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ - bitmap_info->bmiHeader.biClrImportant = 0; - break; - - case CAIRO_FORMAT_A8: - bitmap_info->bmiHeader.biBitCount = 8; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 256; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 256; i++) { - bitmap_info->bmiColors[i].rgbBlue = i; - bitmap_info->bmiColors[i].rgbGreen = i; - bitmap_info->bmiColors[i].rgbRed = i; - bitmap_info->bmiColors[i].rgbReserved = 0; - } - - break; - - case CAIRO_FORMAT_A1: - bitmap_info->bmiHeader.biBitCount = 1; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 2; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 2; i++) { - bitmap_info->bmiColors[i].rgbBlue = i * 255; - bitmap_info->bmiColors[i].rgbGreen = i * 255; - bitmap_info->bmiColors[i].rgbRed = i * 255; - bitmap_info->bmiColors[i].rgbReserved = 0; - } - - break; - } - - surface->dc = CreateCompatibleDC (original_dc); - if (!surface->dc) - goto FAIL; - - surface->bitmap = CreateDIBSection (surface->dc, - bitmap_info, - DIB_RGB_COLORS, - &bits, - NULL, 0); - if (!surface->bitmap) - goto FAIL; - - surface->is_dib = TRUE; - - GdiFlush(); - - surface->saved_dc_bitmap = SelectObject (surface->dc, - surface->bitmap); - if (!surface->saved_dc_bitmap) - goto FAIL; - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (bits_out) - *bits_out = bits; - - if (rowstride_out) { - /* Windows bitmaps are padded to 32-bit (dword) boundaries */ - switch (format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - *rowstride_out = 4 * width; - break; - - case CAIRO_FORMAT_A8: - *rowstride_out = (width + 3) & ~3; - break; - - case CAIRO_FORMAT_A1: - *rowstride_out = ((width + 31) & ~31) / 8; - break; - } - } - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (surface->saved_dc_bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - surface->saved_dc_bitmap = NULL; - } - - if (surface->bitmap) { - DeleteObject (surface->bitmap); - surface->bitmap = NULL; - } - - if (surface->dc) { - DeleteDC (surface->dc); - surface->dc = NULL; - } - - return status; -} - -static cairo_surface_t * -_cairo_win32_surface_create_for_dc (HDC original_dc, - cairo_format_t format, - int width, - int height) -{ - cairo_status_t status; - cairo_win32_surface_t *surface; - unsigned char *bits; - int rowstride; - - if (! CAIRO_FORMAT_VALID (format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->clip_region = NULL; - - status = _create_dc_and_bitmap (surface, original_dc, format, - width, height, - &bits, &rowstride); - if (status) - goto FAIL; - - surface->image = cairo_image_surface_create_for_data (bits, format, - width, height, rowstride); - status = surface->image->status; - if (status) - goto FAIL; - - surface->format = format; - surface->d3d9surface = NULL; - - surface->clip_rect.x = 0; - surface->clip_rect.y = 0; - surface->clip_rect.width = width; - surface->clip_rect.height = height; - - surface->initial_clip_rgn = NULL; - surface->had_simple_clip = FALSE; - - surface->extents = surface->clip_rect; - surface->font_subsets = NULL; - - _cairo_surface_init (&surface->base, - &cairo_win32_surface_backend, - NULL, /* device */ - _cairo_content_from_format (format)); - - return &surface->base; - - FAIL: - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } - free (surface); - - return _cairo_surface_create_in_error (status); -} - -static cairo_surface_t * -_cairo_win32_surface_create_similar_internal (void *abstract_src, - cairo_content_t content, - int width, - int height, - cairo_bool_t force_dib) -{ - cairo_win32_surface_t *src = abstract_src; - cairo_format_t format = _cairo_format_from_content (content); - cairo_surface_t *new_surf = NULL; - - /* We force a DIB always if: - * - we need alpha; or - * - the parent is a DIB; or - * - the parent is for printing (because we don't care about the bit depth at that point) - * - * We also might end up with a DIB even if a DDB is requested if DDB creation failed - * due to out of memory. - */ - if (src->is_dib || - (content & CAIRO_CONTENT_ALPHA) || - src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) - { - force_dib = TRUE; - } - - if (!force_dib) { - /* try to create a ddb */ - new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height); - - if (new_surf->status != CAIRO_STATUS_SUCCESS) - new_surf = NULL; - } - - if (new_surf == NULL) { - new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height); - } - - return new_surf; -} - -cairo_surface_t * -_cairo_win32_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE); -} - -cairo_status_t -_cairo_win32_surface_finish (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) - cairo_surface_destroy (surface->image); - - /* If we created the Bitmap and DC, destroy them */ - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } else { - _cairo_win32_restore_initial_clip (surface); - } - - if (surface->d3d9surface) { - IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); - IDirect3DSurface9_Release (surface->d3d9surface); - } - - if (surface->initial_clip_rgn) - DeleteObject (surface->initial_clip_rgn); - - if (surface->font_subsets != NULL) - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - - return CAIRO_STATUS_SUCCESS; -} - -static void -get_d3d9_dc_and_clear_clip (cairo_win32_surface_t *surface) -{ - IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc); - // The DC that we get back from the surface will not have - // a clip so clear surface->clip_region so that we don't think we have - // one when we don't. - _cairo_win32_surface_set_clip_region (surface, NULL); -} - -static cairo_status_t -_cairo_win32_surface_d3d9_lock_rect (cairo_win32_surface_t *surface, - int x, - int y, - int width, - int height, - cairo_image_surface_t **local_out) -{ - cairo_image_surface_t *local; - cairo_int_status_t status; - - RECT rectin = { x, y, x+width, y+height }; - D3DLOCKED_RECT rectout; - HRESULT hr; - hr = IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); - hr = IDirect3DSurface9_LockRect (surface->d3d9surface, - &rectout, &rectin, 0); - surface->dc = 0; // Don't use the DC when this is locked! - if (hr) { - get_d3d9_dc_and_clear_clip (surface); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - local = cairo_image_surface_create_for_data (rectout.pBits, - surface->format, - width, height, - rectout.Pitch); - if (local == NULL) { - IDirect3DSurface9_UnlockRect (surface->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - if (local->base.status) { - IDirect3DSurface9_UnlockRect (surface->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - return local->base.status; - } - - *local_out = local; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, - int x, - int y, - int width, - int height, - cairo_win32_surface_t **local_out) -{ - cairo_win32_surface_t *local; - cairo_int_status_t status; - cairo_content_t content = _cairo_content_from_format (surface->format); - - local = - (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal - (surface, content, width, height, TRUE); - if (local == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (local->base.status) - return local->base.status; - - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* Only BitBlt if the source surface supports it. */ - if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) && - BitBlt (local->dc, - 0, 0, - width, height, - surface->dc, - x, y, - SRCCOPY)) - { - status = CAIRO_STATUS_SUCCESS; - } - - if (status) { - /* If we failed here, most likely the source or dest doesn't - * support BitBlt/AlphaBlend (e.g. a printer). - * You can't reliably get bits from a printer DC, so just fill in - * the surface as white (common case for printing). - */ - - RECT r; - r.left = r.top = 0; - r.right = width; - r.bottom = height; - FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); - } - - *local_out = local; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface) -{ - cairo_win32_surface_t *new_surface; - int width = surface->extents.width + surface->extents.x; - int height = surface->extents.height + surface->extents.y; - - BOOL ok; - HBITMAP oldbitmap; - - new_surface = (cairo_win32_surface_t*) - _cairo_win32_surface_create_for_dc (surface->dc, - surface->format, - width, - height); - - if (new_surface->base.status) - return; - - /* DDB can't be 32bpp, so BitBlt is safe */ - ok = BitBlt (new_surface->dc, - 0, 0, width, height, - surface->dc, - 0, 0, SRCCOPY); - - if (!ok) - goto out; - - /* Now swap around new_surface and surface's internal bitmap - * pointers. */ - DeleteDC (new_surface->dc); - new_surface->dc = NULL; - - oldbitmap = SelectObject (surface->dc, new_surface->bitmap); - DeleteObject (oldbitmap); - - surface->image = new_surface->image; - surface->is_dib = new_surface->is_dib; - surface->bitmap = new_surface->bitmap; - - new_surface->bitmap = NULL; - new_surface->image = NULL; - - /* Finally update flags */ - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - out: - cairo_surface_destroy ((cairo_surface_t*)new_surface); -} - -static cairo_status_t -_cairo_win32_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (!surface->image && !surface->is_dib && surface->bitmap && - (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0) - { - /* This is a DDB, and we're being asked to use it as a source for - * something that we couldn't support natively. So turn it into - * a DIB, so that we have an equivalent image surface, as long - * as we're allowed to via flags. - */ - _cairo_win32_convert_ddb_to_dib (surface); - } - - if (surface->image) { - *image_out = (cairo_image_surface_t *)surface->image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (surface->d3d9surface) { - cairo_image_surface_t *local; - status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, 0, 0, - surface->extents.width, - surface->extents.height, &local); - if (status) - return status; - - *image_out = local; - *image_extra = surface; - } else { - cairo_win32_surface_t *local; - status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, - surface->extents.width, - surface->extents.height, &local); - if (status) - return status; - - *image_out = (cairo_image_surface_t *)local->image; - *image_extra = local; - } - // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points - // to the original surface to get back the d3d9surface and properly unlock. - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = image_extra; - - if (local && local->d3d9surface) { - IDirect3DSurface9_UnlockRect (local->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - cairo_surface_destroy ((cairo_surface_t *)image); - } else { - cairo_surface_destroy ((cairo_surface_t *)local); - } -} - -static cairo_status_t -_cairo_win32_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->image) { - GdiFlush(); - - *image_out = (cairo_image_surface_t *) surface->image; - *image_extra = NULL; - *image_rect = surface->extents; - return CAIRO_STATUS_SUCCESS; - } - - if (surface->d3d9surface) { - cairo_image_surface_t *local = NULL; - status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, - interest_rect->x, - interest_rect->y, - interest_rect->width, - interest_rect->height, &local); - - if (status) - return status; - - *image_out = local; - *image_extra = surface; - } else { - cairo_win32_surface_t *local = NULL; - status = _cairo_win32_surface_get_subimage (abstract_surface, - interest_rect->x, - interest_rect->y, - interest_rect->width, - interest_rect->height, &local); - - if (status) - return status; - - *image_out = (cairo_image_surface_t *) local->image; - *image_extra = local; - } - // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points - // to the original surface to get back the d3d9surface and properly unlock. - - *image_rect = *interest_rect; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = image_extra; - - if (!local) - return; - - if (local->d3d9surface) { - IDirect3DSurface9_UnlockRect (local->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - cairo_surface_destroy ((cairo_surface_t *)image); - } else { - - /* clear any clip that's currently set on the surface - so that we can blit uninhibited. */ - _cairo_win32_surface_set_clip_region (surface, NULL); - - if (!BitBlt (surface->dc, - image_rect->x, image_rect->y, - image_rect->width, image_rect->height, - local->dc, - 0, 0, - SRCCOPY)) - _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); - - cairo_surface_destroy ((cairo_surface_t *)local); - } - -} - -cairo_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (surface->clip_region == region) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - /* The semantics we want is that any clip set by cairo combines - * is intersected with the clip on device context that the - * surface was created for. To implement this, we need to - * save the original clip when first setting a clip on surface. - */ - - /* Clear any clip set by cairo, return to the original first */ - status = _cairo_win32_restore_initial_clip (surface); - - /* Then combine any new region with it */ - if (region) { - cairo_rectangle_int_t extents; - int num_rects; - RGNDATA *data; - size_t data_size; - RECT *rects; - int i; - HRGN gdi_region; - - /* Create a GDI region for the cairo region */ - - cairo_region_get_extents (region, &extents); - num_rects = cairo_region_num_rectangles (region); - /* XXX see notes in _cairo_win32_save_initial_clip -- - * this code will interact badly with a HDC which had an initial - * world transform -- we should probably manually transform the - * region rects, because SelectClipRgn takes device units, not - * logical units (unlike IntersectClipRect). - */ - - data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); - data = malloc (data_size); - if (!data) - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - rects = (RECT *)data->Buffer; - - data->rdh.dwSize = sizeof (RGNDATAHEADER); - data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_rects; - data->rdh.nRgnSize = num_rects * sizeof (RECT); - data->rdh.rcBound.left = extents.x; - data->rdh.rcBound.top = extents.y; - data->rdh.rcBound.right = extents.x + extents.width; - data->rdh.rcBound.bottom = extents.y + extents.height; - - for (i = 0; i < num_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].left = rect.x; - rects[i].top = rect.y; - rects[i].right = rect.x + rect.width; - rects[i].bottom = rect.y + rect.height; - } - - gdi_region = ExtCreateRegion (NULL, data_size, data); - free (data); - - if (!gdi_region) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* AND the new region into our DC */ - if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); - - DeleteObject (gdi_region); - } - - return status; -} - -#if !defined(AC_SRC_OVER) -#define AC_SRC_OVER 0x00 -#pragma pack(1) -typedef struct { - BYTE BlendOp; - BYTE BlendFlags; - BYTE SourceConstantAlpha; - BYTE AlphaFormat; -}BLENDFUNCTION; -#pragma pack() -#endif - -/* for compatibility with VC++ 6 */ -#ifndef AC_SRC_ALPHA -#define AC_SRC_ALPHA 0x01 -#endif - -typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest, - int nXOriginDest, - int nYOriginDest, - int nWidthDest, - int hHeightDest, - HDC hdcSrc, - int nXOriginSrc, - int nYOriginSrc, - int nWidthSrc, - int nHeightSrc, - BLENDFUNCTION blendFunction); - -static cairo_int_status_t -_composite_alpha_blend (cairo_win32_surface_t *dst, - cairo_win32_surface_t *src, - int alpha, - int src_x, - int src_y, - int src_w, - int src_h, - int dst_x, - int dst_y, - int dst_w, - int dst_h) -{ - static unsigned alpha_blend_checked = FALSE; - static cairo_alpha_blend_func_t alpha_blend = NULL; - - BLENDFUNCTION blend_function; - - /* Check for AlphaBlend dynamically */ - if (!alpha_blend_checked) { - HMODULE msimg32_dll = LoadLibraryW (L"msimg32"); - - if (msimg32_dll != NULL) - alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll, - "AlphaBlend"); - alpha_blend_checked = TRUE; - } - - if (alpha_blend == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND)) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) - return CAIRO_INT_STATUS_UNSUPPORTED; - - blend_function.BlendOp = AC_SRC_OVER; - blend_function.BlendFlags = 0; - blend_function.SourceConstantAlpha = alpha; - blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; - - if (!alpha_blend (dst->dc, - dst_x, dst_y, - dst_w, dst_h, - src->dc, - src_x, src_y, - src_w, src_h, - blend_function)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)"); - - return CAIRO_STATUS_SUCCESS; -} - -/* makes the alpha channel in a RGB24 surface 0xff */ -static void -make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r) -{ - int x; int y; - for (y = 0; y < src_r.height; y++) { - for (x = 0; x < src_r.width; x++) { - image->data[(src_r.y + y) * image->stride + (src_r.x + x)*4 + 3] = 0xff; - } - } -} - -static cairo_int_status_t -_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src, - cairo_image_surface_t *src_image, - cairo_win32_surface_t *dst, - cairo_rectangle_int_t src_extents, - cairo_rectangle_int_t src_r, - cairo_rectangle_int_t dst_r, - int alpha, - cairo_bool_t needs_alpha, - cairo_bool_t needs_scale) -{ - /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */ - if (src_image) { - if (needs_alpha || needs_scale) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { - BITMAPINFO bi; - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = src_image->width; - bi.bmiHeader.biHeight = - src_image->height; - bi.bmiHeader.biSizeImage = 0; - bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - /* StretchDIBits is broken with top-down dibs; you need to do some - * special munging to make the coordinate space work (basically, - * need to address everything based on the bottom left, instead of top left, - * and need to tell it to flip the resulting image. - * - * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments. - */ - if (!StretchDIBits (dst->dc, - /* dst x,y,w,h */ - dst_r.x, dst_r.y + dst_r.height - 1, - dst_r.width, - (int) dst_r.height, - /* src x,y,w,h */ - src_r.x, src_extents.height - src_r.y + 1, - src_r.width, - (int) src_r.height, - src_image->data, - &bi, - DIB_RGB_COLORS, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)"); - } - } else if (!needs_alpha) { - if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) { - /* Because we store RGB24 & ARGB32 in the same way GDI has no way - * to ignore the alpha channel from a RGB24 source. Therefore, we set - * the alpha channel in our RGB24 source to opaque so that we can treat - * it like ARGB32. */ - GdiFlush(); - make_opaque(src->image, src_r); - } - /* BitBlt or StretchBlt? */ - if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) { - if (!BitBlt (dst->dc, - dst_r.x, dst_r.y, - dst_r.width, dst_r.height, - src->dc, - src_r.x, src_r.y, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)"); - } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { - /* StretchBlt? */ - /* XXX check if we want HALFTONE, based on the src filter */ - BOOL success; - int oldmode = SetStretchBltMode(dst->dc, HALFTONE); - success = StretchBlt(dst->dc, - dst_r.x, dst_r.y, - dst_r.width, dst_r.height, - src->dc, - src_r.x, src_r.y, - src_r.width, src_r.height, - SRCCOPY); - SetStretchBltMode(dst->dc, oldmode); - - if (!success) - return _cairo_win32_print_gdi_error ("StretchBlt"); - } - } else if (needs_alpha && !needs_scale) { - RECT r = {0, 0, 5000, 5000}; - //FillRect(dst->dc, &r, GetStockObject(DKGRAY_BRUSH)); - return _composite_alpha_blend (dst, src, alpha, - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - } - - return CAIRO_STATUS_SUCCESS; -} - -/* from pixman-private.h */ -#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) - -static cairo_int_status_t -_cairo_win32_surface_composite (cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_win32_surface_t *dst = abstract_dst; - cairo_win32_surface_t *src; - cairo_surface_pattern_t *src_surface_pattern; - int alpha; - double scalex, scaley; - cairo_fixed_t x0_fixed, y0_fixed; - cairo_int_status_t status; - - cairo_bool_t needs_alpha, needs_scale, needs_repeat, needs_pad; - cairo_image_surface_t *src_image = NULL; - - cairo_format_t src_format; - cairo_rectangle_int_t src_extents; - - cairo_rectangle_int_t src_r = { src_x, src_y, width, height }; - cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height }; - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n", - op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); -#endif - - /* If the destination can't do any of these, then - * we may as well give up, since this is what we'll - * look to for optimization. - */ - if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT | - CAIRO_WIN32_SURFACE_CAN_ALPHABLEND | - CAIRO_WIN32_SURFACE_CAN_STRETCHBLT | - CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) - == 0) - { - goto UNSUPPORTED; - } - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - goto UNSUPPORTED; - - if (pattern->extend != CAIRO_EXTEND_NONE && - pattern->extend != CAIRO_EXTEND_REPEAT && - pattern->extend != CAIRO_EXTEND_PAD) - goto UNSUPPORTED; - - if (mask_pattern) { - /* FIXME: When we fully support RENDER style 4-channel - * masks we need to check r/g/b != 1.0. - */ - if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; - } else { - alpha = 255; - } - - src_surface_pattern = (cairo_surface_pattern_t *)pattern; - src = (cairo_win32_surface_t *)src_surface_pattern->surface; - - if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE && - dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) - { - /* In some very limited cases, we can use StretchDIBits to draw - * an image surface directly: - * - source is CAIRO_FORMAT_ARGB32 - * - dest is CAIRO_FORMAT_ARGB32 - * - alpha is 255 - * - operator is SOURCE or OVER - * - image stride is 4*width - */ - src_image = (cairo_image_surface_t*) src; - - if (src_image->format != CAIRO_FORMAT_RGB24 || - dst->format != CAIRO_FORMAT_RGB24 || - alpha != 255 || - (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || - src_image->stride != (src_image->width * 4)) - { - goto UNSUPPORTED; - } - - src_format = src_image->format; - src_extents.x = 0; - src_extents.y = 0; - src_extents.width = src_image->width; - src_extents.height = src_image->height; - } else if (src->base.backend != dst->base.backend) { - goto UNSUPPORTED; - } else { - src_format = src->format; - src_extents = src->extents; - } - - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n", - src_extents.width, src_extents.height, - src_x, src_y, - dst_x, dst_y, width, height, - pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy); -#endif - - /* We can only use GDI functions if the source and destination rectangles - * are on integer pixel boundaries. Figure that out here. - */ - x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx); - y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy); - - if (pattern->matrix.yx != 0.0 || - pattern->matrix.xy != 0.0 || - !_cairo_fixed_is_integer(x0_fixed) || - !_cairo_fixed_is_integer(y0_fixed)) - { - goto UNSUPPORTED; - } - - scalex = pattern->matrix.xx; - scaley = pattern->matrix.yy; - - src_r.x += _cairo_fixed_integer_part(x0_fixed); - src_r.y += _cairo_fixed_integer_part(y0_fixed); - - /* Success, right? */ - if (scalex == 0.0 || scaley == 0.0) - return CAIRO_STATUS_SUCCESS; - - if (scalex != 1.0 || scaley != 1.0) - goto UNSUPPORTED; - - /* If the src coordinates are outside of the source surface bounds, - * we have to fix them up, because this is an error for the GDI - * functions. - */ - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n", - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - fflush (stderr); -#endif - - /* If the src rectangle doesn't wholly lie within the src extents, - * fudge things. We really need to do fixup on the unpainted - * region -- e.g. the SOURCE operator is broken for areas outside - * of the extents, because it won't clear that area to transparent - * black. - */ - - needs_pad = FALSE; - if (pattern->extend != CAIRO_EXTEND_REPEAT) { - needs_repeat = FALSE; - - /* If the src rect and the extents of the source image don't overlap at all, - * we can't do anything useful here. - */ - if (src_r.x > src_extents.width || src_r.y > src_extents.height || - (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0) - { - if (op == CAIRO_OPERATOR_OVER) - return CAIRO_STATUS_SUCCESS; - goto UNSUPPORTED; - } - - if (src_r.x < 0) { - src_r.width += src_r.x; - - dst_r.width += src_r.x; - dst_r.x -= src_r.x; - - src_r.x = 0; - needs_pad = TRUE; - } - - if (src_r.y < 0) { - src_r.height += src_r.y; - - dst_r.height += src_r.y; - dst_r.y -= src_r.y; - - src_r.y = 0; - needs_pad = TRUE; - } - - if (src_r.x + src_r.width > src_extents.width) { - src_r.width = src_extents.width - src_r.x; - dst_r.width = src_r.width; - needs_pad = TRUE; - } - - if (src_r.y + src_r.height > src_extents.height) { - src_r.height = src_extents.height - src_r.y; - dst_r.height = src_r.height; - needs_pad = TRUE; - } - } else { - needs_repeat = TRUE; - } - - if (pattern->extend == CAIRO_EXTEND_PAD && needs_pad) { - goto UNSUPPORTED; - } - - /* - * Operations that we can do: - * - * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0) - * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0)) - * This turns into Dst.Alpha = Src.Alpha when SCA = 255. - * (http://msdn.microsoft.com/en-us/library/aa921335.aspx) - * - * RGB OVER RGB -> BitBlt (same as SOURCE) - * RGB OVER ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB - * by setting the alpha values of the source to 255. - * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA - * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte - * - * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA - * RGB OVER ARGB + mask -> Partially supported, We convert this operation into a ARGB OVER ARGB + mask - * by setting the alpha values of the source to 255. - * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA - * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte - * - * RGB SOURCE RGB -> BitBlt - * RGB SOURCE ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB - * by setting the alpha values of the source to 255. - * ARGB SOURCE ARGB -> BitBlt - * ARGB SOURCE RGB -> BitBlt - * - * RGB SOURCE RGB + mask -> unsupported - * RGB SOURCE ARGB + mask -> unsupported - * ARGB SOURCE ARGB + mask -> unsupported - * ARGB SOURCE RGB + mask -> unsupported - */ - - /* - * Figure out what action to take. - */ - if (op == CAIRO_OPERATOR_OVER) { - if (alpha == 0) - return CAIRO_STATUS_SUCCESS; - - if (src_format == dst->format) { - if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) { - needs_alpha = FALSE; - } else { - needs_alpha = TRUE; - } - } else if (src_format == CAIRO_FORMAT_ARGB32 && - dst->format == CAIRO_FORMAT_RGB24) - { - needs_alpha = TRUE; - } else if (src_format == CAIRO_FORMAT_RGB24 && - dst->format == CAIRO_FORMAT_ARGB32 && - src->image) - { - if (alpha == 255) { - needs_alpha = FALSE; - } else { - needs_alpha = TRUE; - } - } else { - goto UNSUPPORTED; - } - } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) { - if ((src_format == dst->format) || - (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24) || - (src_format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32 && src->image)) - { - needs_alpha = FALSE; - } else { - goto UNSUPPORTED; - } - } else { - goto UNSUPPORTED; - } - - if (scalex == 1.0 && scaley == 1.0) { - needs_scale = FALSE; - } else { - /* Should never be reached until we turn StretchBlt back on */ - needs_scale = TRUE; - } - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n", - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - fflush (stderr); -#endif - - status = _cairo_win32_surface_set_clip_region (dst, clip_region); - if (status) - return status; - - /* If we need to repeat, we turn the repeated blit into - * a bunch of piece-by-piece blits. - */ - if (needs_repeat) { - cairo_rectangle_int_t piece_src_r, piece_dst_r; - uint32_t rendered_width = 0, rendered_height = 0; - uint32_t to_render_height, to_render_width; - int32_t piece_x, piece_y; - int32_t src_start_x = MOD(src_r.x, src_extents.width); - int32_t src_start_y = MOD(src_r.y, src_extents.height); - - if (needs_scale) - goto UNSUPPORTED; - - /* If both the src and dest have an image, we may as well fall - * back, because it will be faster than our separate blits. - * Our blit code will be fastest when the src is a DDB and the - * destination is a DDB. - */ - if ((src_image || src->image) && dst->image) - goto UNSUPPORTED; - - /* If the src is not a bitmap but an on-screen (or unknown) - * DC, chances are that fallback will be faster. - */ - if (src->bitmap == NULL) - goto UNSUPPORTED; - - /* If we can use PatBlt, just do so */ - if (!src_image && !needs_alpha) - { - HBRUSH brush; - HGDIOBJ old_brush; - POINT old_brush_origin; - - /* Set up the brush with our bitmap */ - brush = CreatePatternBrush (src->bitmap); - - /* SetBrushOrgEx sets the coordinates in the destination DC of where the - * pattern should start. - */ - SetBrushOrgEx (dst->dc, dst_r.x - src_start_x, - dst_r.y - src_start_y, &old_brush_origin); - - old_brush = SelectObject (dst->dc, brush); - - PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY); - - /* Restore the old brush and pen */ - SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL); - SelectObject (dst->dc, old_brush); - DeleteObject (brush); - - return CAIRO_STATUS_SUCCESS; - } - - /* If we were not able to use PatBlt, then manually expand out the blit */ - - /* Arbitrary factor; we think that going through - * fallback will be faster if we have to do more - * than this amount of blits in either direction. - */ - if (dst_r.width / src_extents.width > 5 || - dst_r.height / src_extents.height > 5) - goto UNSUPPORTED; - - for (rendered_height = 0; - rendered_height < dst_r.height; - rendered_height += to_render_height) - { - piece_y = (src_start_y + rendered_height) % src_extents.height; - to_render_height = src_extents.height - piece_y; - - if (rendered_height + to_render_height > dst_r.height) - to_render_height = dst_r.height - rendered_height; - - for (rendered_width = 0; - rendered_width < dst_r.width; - rendered_width += to_render_width) - { - piece_x = (src_start_x + rendered_width) % src_extents.width; - to_render_width = src_extents.width - piece_x; - - if (rendered_width + to_render_width > dst_r.width) - to_render_width = dst_r.width - rendered_width; - - piece_src_r.x = piece_x; - piece_src_r.y = piece_y; - piece_src_r.width = to_render_width; - piece_src_r.height = to_render_height; - - piece_dst_r.x = dst_r.x + rendered_width; - piece_dst_r.y = dst_r.y + rendered_height; - piece_dst_r.width = to_render_width; - piece_dst_r.height = to_render_height; - - status = _cairo_win32_surface_composite_inner (src, src_image, dst, - src_extents, piece_src_r, piece_dst_r, - alpha, needs_alpha, needs_scale); - if (status != CAIRO_STATUS_SUCCESS) { - /* Uh oh. If something failed, and it's the first - * piece, then we can jump to UNSUPPORTED. - * Otherwise, this is bad times, because part of the - * rendering was already done. */ - if (rendered_width == 0 && - rendered_height == 0) - { - goto UNSUPPORTED; - } - - return status; - } - } - } - } else { - status = _cairo_win32_surface_composite_inner (src, src_image, dst, - src_extents, src_r, dst_r, - alpha, needs_alpha, needs_scale); - } - - if (status == CAIRO_STATUS_SUCCESS) - return status; - -UNSUPPORTED: - /* Fall back to image surface directly, if this is a DIB surface */ - if (dst->image) { - GdiFlush(); - - return dst->image->backend->composite (op, pattern, mask_pattern, - dst->image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region); - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -/* This big function tells us how to optimize operators for the - * case of solid destination and constant-alpha source - * - * Note: This function needs revisiting if we add support for - * super-luminescent colors (a == 0, r,g,b > 0) - */ -static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED } -categorize_solid_dest_operator (cairo_operator_t op, - unsigned short alpha) -{ - enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source; - - if (alpha >= 0xff00) - source = SOURCE_SOLID; - else if (alpha < 0x100) - source = SOURCE_TRANSPARENT; - else - source = SOURCE_OTHER; - - switch (op) { - case CAIRO_OPERATOR_CLEAR: /* 0 0 */ - case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */ - return DO_CLEAR; - break; - - case CAIRO_OPERATOR_SOURCE: /* 1 0 */ - case CAIRO_OPERATOR_IN: /* Ab 0 */ - return DO_SOURCE; - break; - - case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */ - case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */ - if (source == SOURCE_SOLID) - return DO_SOURCE; - else if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */ - case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */ - if (source == SOURCE_SOLID) - return DO_CLEAR; - else if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_DEST: /* 0 1 */ - case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */ - case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */ - return DO_NOTHING; - break; - - case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */ - case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */ - if (source == SOURCE_SOLID) - return DO_NOTHING; - else if (source == SOURCE_TRANSPARENT) - return DO_CLEAR; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_ADD: /* 1 1 */ - if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - } - - ASSERT_NOT_REACHED; - return DO_UNSUPPORTED; -} - -static cairo_status_t -_cairo_win32_surface_fill_rectangles_stretchdib (HDC dc, - const cairo_color_t *color, - cairo_rectangle_int_t *rect, - int num_rects) -{ - BITMAPINFO bi; - int pixel = ((color->alpha_short >> 8) << 24) | - ((color->red_short >> 8) << 16) | - ((color->green_short >> 8) << 8) | - (color->blue_short >> 8); - int i; - - /* Experiments suggest that it's impossible to use FillRect to set the alpha value - of a Win32 HDC for a transparent window. So instead we use StretchDIBits of a single - pixel, which does work. */ - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = 1; - bi.bmiHeader.biHeight = 1; - bi.bmiHeader.biSizeImage = 0; - bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - for (i = 0; i < num_rects; i++) { - if (!StretchDIBits (dc, - /* dst x,y,w,h */ - rect[i].x, rect[i].y, rect[i].width, rect[i].height, - /* src x,y,w,h */ - 0, 0, 1, 1, - &pixel, - &bi, - DIB_RGB_COLORS, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles_stretchdib(StretchDIBits)"); - } - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - COLORREF new_color; - HBRUSH new_brush; - int i; - - status = _cairo_win32_surface_set_clip_region (surface, NULL); - if (status) - return status; - - if (surface->format == CAIRO_FORMAT_ARGB32 && - (surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) && - (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && color->alpha_short >= 0xff00))) { - return _cairo_win32_surface_fill_rectangles_stretchdib (surface->dc, - color, rects, num_rects); - } - if (surface->format != CAIRO_FORMAT_RGB24) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all - * surfaces with alpha.) - */ - switch (categorize_solid_dest_operator (op, color->alpha_short)) { - case DO_CLEAR: - new_color = RGB (0, 0, 0); - break; - case DO_SOURCE: - new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); - break; - case DO_NOTHING: - return CAIRO_STATUS_SUCCESS; - case DO_UNSUPPORTED: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - new_brush = CreateSolidBrush (new_color); - if (!new_brush) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - for (i = 0; i < num_rects; i++) { - RECT rect; - - rect.left = rects[i].x; - rect.top = rects[i].y; - rect.right = rects[i].x + rects[i].width; - rect.bottom = rects[i].y + rects[i].height; - - if (!FillRect (surface->dc, &rect, new_brush)) - goto FAIL; - } - - DeleteObject (new_brush); - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - DeleteObject (new_brush); - - return status; -} - -cairo_bool_t -_cairo_win32_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_win32_surface_t *surface = abstract_surface; - - *rectangle = surface->extents; - return TRUE; -} - -static cairo_status_t -_cairo_win32_surface_flush (void *abstract_surface) -{ - return _cairo_win32_surface_set_clip_region (abstract_surface, NULL); -} - -#define STACK_GLYPH_SIZE 256 - -cairo_int_status_t -_cairo_win32_surface_show_glyphs_internal (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs, - cairo_bool_t glyph_indexing) -{ -#ifdef CAIRO_HAS_WIN32_FONT - if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { -#ifdef CAIRO_HAS_DWRITE_FONT - return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); -#endif - } else { - cairo_win32_surface_t *dst = surface; - - WORD glyph_buf_stack[STACK_GLYPH_SIZE]; - WORD *glyph_buf = glyph_buf_stack; - int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; - int *dxy_buf = dxy_buf_stack; - - BOOL win_result = 0; - int i, j; - - cairo_solid_pattern_t *solid_pattern; - COLORREF color; - - cairo_matrix_t device_to_logical; - - int start_x, start_y; - double user_x, user_y; - int logical_x, logical_y; - unsigned int glyph_index_option; - - /* We can only handle win32 fonts */ - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle opaque solid color sources */ - if (!_cairo_pattern_is_opaque_solid(source)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || - (dst->format != CAIRO_FORMAT_RGB24)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* If we have a fallback mask clip set on the dst, we have - * to go through the fallback path, but only if we're not - * doing this for printing */ - if (clip != NULL) { - if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { - cairo_region_t *clip_region; - cairo_status_t status; - - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - - _cairo_win32_surface_set_clip_region (surface, clip_region); - } - } else { - _cairo_win32_surface_set_clip_region (surface, NULL); - } - - solid_pattern = (cairo_solid_pattern_t *)source; - color = RGB(((int)solid_pattern->color.red_short) >> 8, - ((int)solid_pattern->color.green_short) >> 8, - ((int)solid_pattern->color.blue_short) >> 8); - - cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); - - SaveDC(dst->dc); - - cairo_win32_scaled_font_select_font(scaled_font, dst->dc); - SetTextColor(dst->dc, color); - SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); - SetBkMode(dst->dc, TRANSPARENT); - - if (num_glyphs > STACK_GLYPH_SIZE) { - glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); - dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); - } - - /* It is vital that dx values for dxy_buf are calculated from the delta of - * _logical_ x coordinates (not user x coordinates) or else the sum of all - * previous dx values may start to diverge from the current glyph's x - * coordinate due to accumulated rounding error. As a result strings could - * be painted shorter or longer than expected. */ - - user_x = glyphs[0].x; - user_y = glyphs[0].y; - - cairo_matrix_transform_point(&device_to_logical, - &user_x, &user_y); - - logical_x = _cairo_lround (user_x); - logical_y = _cairo_lround (user_y); - - start_x = logical_x; - start_y = logical_y; - - for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { - glyph_buf[i] = (WORD) glyphs[i].index; - if (i == num_glyphs - 1) { - dxy_buf[j] = 0; - dxy_buf[j+1] = 0; - } else { - double next_user_x = glyphs[i+1].x; - double next_user_y = glyphs[i+1].y; - int next_logical_x, next_logical_y; - - cairo_matrix_transform_point(&device_to_logical, - &next_user_x, &next_user_y); - - next_logical_x = _cairo_lround (next_user_x); - next_logical_y = _cairo_lround (next_user_y); - - dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); - dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y); - /* note that GDI coordinate system is inverted */ - - logical_x = next_logical_x; - logical_y = next_logical_y; - } - } - - if (glyph_indexing) - glyph_index_option = ETO_GLYPH_INDEX; - else - glyph_index_option = 0; - - win_result = ExtTextOutW(dst->dc, - start_x, - start_y, - glyph_index_option | ETO_PDY, - NULL, - glyph_buf, - num_glyphs, - dxy_buf); - if (!win_result) { - _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); - } - - RestoreDC(dst->dc, -1); - - if (glyph_buf != glyph_buf_stack) { - free(glyph_buf); - free(dxy_buf); - } - return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; - } -#else - return CAIRO_INT_STATUS_UNSUPPORTED; -#endif -} - -#undef STACK_GLYPH_SIZE - -cairo_int_status_t -_cairo_win32_surface_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - return _cairo_win32_surface_show_glyphs_internal (surface, - op, - source, - glyphs, - num_glyphs, - scaled_font, - clip, - remaining_glyphs, - TRUE); -} - -static cairo_surface_t * -cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format) -{ - cairo_win32_surface_t *surface; - - RECT rect; - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - surface->clip_region = NULL; - surface->image = NULL; - surface->format = format; - - surface->d3d9surface = NULL; - surface->dc = hdc; - surface->bitmap = NULL; - surface->is_dib = FALSE; - surface->saved_dc_bitmap = NULL; - surface->brush = NULL; - surface->old_brush = NULL; - surface->font_subsets = NULL; - - GetClipBox(hdc, &rect); - surface->extents.x = rect.left; - surface->extents.y = rect.top; - surface->extents.width = rect.right - rect.left; - surface->extents.height = rect.bottom - rect.top; - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - _cairo_surface_init (&surface->base, - &cairo_win32_surface_backend, - NULL, /* device */ - _cairo_content_from_format (format)); - - return &surface->base; -} - -/** - * cairo_win32_surface_create: - * @hdc: the DC to create a surface for - * - * Creates a cairo surface that targets the given DC. The DC will be - * queried for its initial clip extents, and this will be used as the - * size of the cairo surface. The resulting surface will always be of - * format %CAIRO_FORMAT_RGB24; should you need another surface format, - * you will need to create one through - * cairo_win32_surface_create_with_dib() or call - * cairo_win32_surface_create_with_alpha. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_win32_surface_create (HDC hdc) -{ - /* Assume everything comes in as RGB24 */ - return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_RGB24); -} - -/** - * cairo_win32_surface_create_with_alpha: - * @hdc: the DC to create a surface for - * - * Creates a cairo surface that targets the given DC. The DC will be - * queried for its initial clip extents, and this will be used as the - * size of the cairo surface. The resulting surface will always be of - * format %CAIRO_FORMAT_ARGB32; this format is used when drawing into - * transparent windows. - * - * Return value: the newly created surface - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_alpha (HDC hdc) -{ - return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_ARGB32); -} - -/** - * cairo_win32_surface_create_with_dib: - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a device-independent-bitmap surface not associated with - * any particular existing surface or device context. The created - * bitmap will be uninitialized. - * - * Return value: the newly created surface - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_dib (cairo_format_t format, - int width, - int height) -{ - return _cairo_win32_surface_create_for_dc (NULL, format, width, height); -} - -/** - * cairo_win32_surface_create_with_ddb: - * @hdc: the DC to create a surface for - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a device-independent-bitmap surface not associated with - * any particular existing surface or device context. The created - * bitmap will be uninitialized. - * - * Return value: the newly created surface - * - * Since: 1.4 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_ddb (HDC hdc, - cairo_format_t format, - int width, - int height) -{ - cairo_win32_surface_t *new_surf; - HBITMAP ddb; - HDC screen_dc, ddb_dc; - HBITMAP saved_dc_bitmap; - - if (format != CAIRO_FORMAT_RGB24) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); -/* XXX handle these eventually - format != CAIRO_FORMAT_A8 || - format != CAIRO_FORMAT_A1) -*/ - - if (!hdc) { - screen_dc = GetDC (NULL); - hdc = screen_dc; - } else { - screen_dc = NULL; - } - - ddb_dc = CreateCompatibleDC (hdc); - if (ddb_dc == NULL) { - new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto FINISH; - } - - ddb = CreateCompatibleBitmap (hdc, width, height); - if (ddb == NULL) { - DeleteDC (ddb_dc); - - /* Note that if an app actually does hit this out of memory - * condition, it's going to have lots of other issues, as - * video memory is probably exhausted. However, it can often - * continue using DIBs instead of DDBs. - */ - new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto FINISH; - } - - saved_dc_bitmap = SelectObject (ddb_dc, ddb); - - new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc); - new_surf->bitmap = ddb; - new_surf->saved_dc_bitmap = saved_dc_bitmap; - new_surf->is_dib = FALSE; - -FINISH: - if (screen_dc) - ReleaseDC (NULL, screen_dc); - - return (cairo_surface_t*) new_surf; -} - -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_d3dsurface9 (IDirect3DSurface9 *surface) -{ - HDC dc; - cairo_surface_t *win_surface; - - IDirect3DSurface9_AddRef (surface); - IDirect3DSurface9_GetDC (surface, &dc); - win_surface = cairo_win32_surface_create_internal(dc, CAIRO_FORMAT_RGB24); - if (likely(win_surface->status == CAIRO_STATUS_SUCCESS)) { - ((cairo_win32_surface_t*)win_surface)->d3d9surface = surface; - } - return win_surface; - -} -/** - * _cairo_surface_is_win32: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a win32 surface. This will - * return False if this is a win32 printing surface; use - * _cairo_surface_is_win32_printing() to check for that. - * - * Return value: True if the surface is an win32 surface - **/ -int -_cairo_surface_is_win32 (cairo_surface_t *surface) -{ - return surface->backend == &cairo_win32_surface_backend; -} - -/** - * cairo_win32_surface_get_dc - * @surface: a #cairo_surface_t - * - * Returns the HDC associated with this surface, or %NULL if none. - * Also returns %NULL if the surface is not a win32 surface. - * - * Return value: HDC or %NULL if no HDC available. - * - * Since: 1.2 - **/ -HDC -cairo_win32_surface_get_dc (cairo_surface_t *surface) -{ - cairo_win32_surface_t *winsurf; - - if (_cairo_surface_is_win32 (surface)){ - winsurf = (cairo_win32_surface_t *) surface; - - return winsurf->dc; - } - - if (_cairo_surface_is_paginated (surface)) { - cairo_surface_t *target; - - target = _cairo_paginated_surface_get_target (surface); - -#ifndef CAIRO_OMIT_WIN32_PRINTING - if (_cairo_surface_is_win32_printing (target)) { - winsurf = (cairo_win32_surface_t *) target; - return winsurf->dc; - } -#endif - } - - return NULL; -} - - -HDC -cairo_win32_get_dc_with_clip (cairo_t *cr) -{ - cairo_surface_t *surface = cr->gstate->target; - cairo_clip_t clip; - _cairo_clip_init_copy(&clip, &cr->gstate->clip); - - if (_cairo_surface_is_win32 (surface)){ - cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) surface; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip.path) { - status = _cairo_clip_get_region (&clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) { - _cairo_clip_fini(&clip); - return NULL; - } - } - _cairo_win32_surface_set_clip_region (winsurf, clip_region); - - _cairo_clip_fini(&clip); - return winsurf->dc; - } - - if (_cairo_surface_is_paginated (surface)) { - cairo_surface_t *target; - - target = _cairo_paginated_surface_get_target (surface); - -#ifndef CAIRO_OMIT_WIN32_PRINTING - if (_cairo_surface_is_win32_printing (target)) { - cairo_status_t status; - cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) target; - - status = _cairo_surface_clipper_set_clip (&winsurf->clipper, &clip); - - _cairo_clip_fini(&clip); - - if (status) - return NULL; - - return winsurf->dc; - } -#endif - } - - _cairo_clip_fini(&clip); - return NULL; -} - - - -/** - * cairo_win32_surface_get_image - * @surface: a #cairo_surface_t - * - * Returns a #cairo_surface_t image surface that refers to the same bits - * as the DIB of the Win32 surface. If the passed-in win32 surface - * is not a DIB surface, %NULL is returned. - * - * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), - * or %NULL if the win32 surface is not a DIB. - * - * Since: 1.4 - */ -cairo_surface_t * -cairo_win32_surface_get_image (cairo_surface_t *surface) -{ - if (!_cairo_surface_is_win32(surface)) - return NULL; - - return ((cairo_win32_surface_t*)surface)->image; -} - -static cairo_bool_t -_cairo_win32_surface_is_similar (void *surface_a, - void *surface_b) -{ - cairo_win32_surface_t *a = surface_a; - cairo_win32_surface_t *b = surface_b; - - return a->dc == b->dc; -} - -typedef struct _cairo_win32_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_operator_t op; - const cairo_pattern_t *pattern; - cairo_antialias_t antialias; - - uint8_t *mask_data; - uint32_t mask_stride; - - cairo_image_surface_t *mask; - cairo_win32_surface_t *dst; - cairo_region_t *clip_region; - - cairo_composite_rectangles_t composite_rectangles; -} cairo_win32_surface_span_renderer_t; - -static cairo_status_t -_cairo_win32_surface_span_renderer_render_rows ( - void *abstract_renderer, - int y, - int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - while (height--) - _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_surface_span_renderer_destroy (void *abstract_renderer) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - if (!renderer) return; - - if (renderer->mask != NULL) - cairo_surface_destroy (&renderer->mask->base); - - free (renderer); -} - -static cairo_status_t -_cairo_win32_surface_span_renderer_finish (void *abstract_renderer) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (renderer->pattern == NULL || renderer->mask == NULL) - return CAIRO_STATUS_SUCCESS; - - status = cairo_surface_status (&renderer->mask->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; - cairo_win32_surface_t *dst = renderer->dst; - cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&renderer->mask->base); - /* composite onto the image surface directly if we can */ - if (dst->image) { - GdiFlush(); /* XXX: I'm not sure if this needed or not */ - - status = dst->image->backend->composite (renderer->op, - renderer->pattern, mask_pattern, dst->image, - rects->bounded.x, rects->bounded.y, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height, - renderer->clip_region); - } else { - /* otherwise go through the fallback_composite path which - * will do the appropriate surface acquisition */ - status = _cairo_surface_fallback_composite ( - renderer->op, - renderer->pattern, mask_pattern, &dst->base, - rects->bounded.x, rects->bounded.y, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height, - renderer->clip_region); - } - cairo_pattern_destroy (mask_pattern); - - } - if (status != CAIRO_STATUS_SUCCESS) - return _cairo_span_renderer_set_error (abstract_renderer, - status); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_paint (surface->image, op, source, clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_mask (surface->image, op, source, mask, clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_stroke (surface->image, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_fill (surface->image, op, source, - path, fill_rule, - tolerance, antialias, - clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-spans-private.h" -#include "cairo-surface-fallback-private.h" - -typedef struct { - cairo_surface_t *dst; - cairo_rectangle_int_t extents; - cairo_image_surface_t *image; - cairo_rectangle_int_t image_rect; - void *image_extra; -} fallback_state_t; - -/** - * _fallback_init: - * - * Acquire destination image surface needed for an image-based - * fallback. - * - * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not - * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all - * went well, or some error status otherwise. - **/ -static cairo_int_status_t -_fallback_init (fallback_state_t *state, - cairo_surface_t *dst, - int x, - int y, - int width, - int height) -{ - cairo_status_t status; - - state->extents.x = x; - state->extents.y = y; - state->extents.width = width; - state->extents.height = height; - - state->dst = dst; - - status = _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, - &state->image_extra); - if (unlikely (status)) - return status; - - - /* XXX: This NULL value tucked away in state->image is a rather - * ugly interface. Cleaner would be to push the - * CAIRO_INT_STATUS_NOTHING_TO_DO value down into - * _cairo_surface_acquire_dest_image and its backend - * counterparts. */ - assert (state->image != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_fallback_fini (fallback_state_t *state) -{ - _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, - state->image_extra); -} - -typedef cairo_status_t -(*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static cairo_status_t -_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, - cairo_clip_t *clip, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *mask; - cairo_region_t *clip_region = NULL, *fallback_region = NULL; - cairo_status_t status; - cairo_bool_t clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with - * a mask (as called via _cairo_surface_mask) triggers assertion failures. - */ - mask = _cairo_surface_create_similar_solid (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - if (unlikely (mask->status)) - return mask->status; - - if (clip_region && (extents->x || extents->y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto CLEANUP_SURFACE; - - cairo_region_translate (fallback_region, - -extents->x, - -extents->y); - clip_region = fallback_region; - } - - status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, mask, - extents->x, extents->y, - extents, - clip_region); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - if (clip_surface) - status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); - - _cairo_pattern_init_for_surface (mask_pattern, mask); - - CLEANUP_SURFACE: - if (fallback_region) - cairo_region_destroy (fallback_region); - cairo_surface_destroy (mask); - - return status; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_status_t status; - - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - - _cairo_pattern_fini (&mask_pattern.base); - } - - return status; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *intermediate; - cairo_surface_pattern_t pattern; - cairo_surface_pattern_t clip_pattern; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - /* We'd be better off here creating a surface identical in format - * to dst, but we have no way of getting that information. Instead - * we ask the backend to create a similar surface of identical content, - * in the belief that the backend will do something useful - like use - * an identical format. For example, the xlib backend will endeavor to - * use a compatible depth to enable core protocol routines. - */ - intermediate = - _cairo_surface_create_similar_scratch (dst, dst->content, - extents->width, - extents->height); - if (intermediate == NULL) { - intermediate = - _cairo_image_surface_create_with_content (dst->content, - extents->width, - extents->width); - } - if (unlikely (intermediate->status)) - return intermediate->status; - - /* Initialize the intermediate surface from the destination surface */ - _cairo_pattern_init_for_surface (&pattern, dst); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL, intermediate, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - status = (*draw_func) (draw_closure, op, - src, intermediate, - extents->x, extents->y, - extents, - NULL); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); - - /* Combine that with the clip */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, - &clip_pattern.base, NULL, intermediate, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Punch the clip out of the destination */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &clip_pattern.base, NULL, dst, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Now add the two results together */ - _cairo_pattern_init_for_surface (&pattern, intermediate); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - - CLEANUP_CLIP: - _cairo_pattern_fini (&clip_pattern.base); - CLEANUP_SURFACE: - cairo_surface_destroy (intermediate); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - } - - /* Create a surface that is mask IN clip */ - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (unlikely (status)) - return status; - - /* Compute dest' = dest OUT (mask IN clip) */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &mask_pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - if (unlikely (status)) - goto CLEANUP_MASK_PATTERN; - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - CLEANUP_MASK_PATTERN: - _cairo_pattern_fini (&mask_pattern.base); - return status; -} - -static int -_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -/** - * _clip_and_composite: - * @clip: a #cairo_clip_t - * @op: the operator to draw with - * @src: source pattern - * @draw_func: function that can be called to draw with the mask onto a surface. - * @draw_closure: data to pass to @draw_func. - * @dst: destination surface - * @extents: rectangle holding a bounding box for the operation; this - * rectangle will be used as the size for the temporary - * surface. - * - * When there is a surface clip, we typically need to create an intermediate - * surface. This function handles the logic of creating a temporary surface - * drawing to it, then compositing the result onto the target surface. - * - * @draw_func is to called to draw the mask; it will be called no more - * than once. - * - * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. - **/ -static cairo_status_t -_clip_and_composite (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - - if (_cairo_rectangle_empty (extents)) - /* Nothing to do */ - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, - src, - draw_func, draw_closure, - dst, extents); - } else { - cairo_bool_t clip_surface = FALSE; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_surface) { - if (_cairo_operator_bounded_by_mask (op)) { - status = _clip_and_composite_with_mask (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } else { - status = _clip_and_composite_combine (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } - } else { - status = draw_func (draw_closure, op, - src, dst, - 0, 0, - extents, - clip_region); - } - } - - return status; -} - -/* Composites a region representing a set of trapezoids. - */ -static cairo_status_t -_composite_trap_region (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_surface_pattern_t mask_pattern; - cairo_pattern_t *mask = NULL; - int mask_x = 0, mask_y =0; - - if (clip != NULL) { - cairo_surface_t *clip_surface = NULL; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); - mask_x = extents->x - clip_x; - mask_y = extents->y - clip_y; - mask = &mask_pattern.base; - } - - status = _cairo_surface_composite (op, src, mask, dst, - extents->x, extents->y, - mask_x, mask_y, - extents->x, extents->y, - extents->width, extents->height, - trap_region); - - if (mask != NULL) - _cairo_pattern_fini (mask); - - return status; -} - -typedef struct { - cairo_traps_t *traps; - cairo_antialias_t antialias; -} cairo_composite_traps_info_t; - -static cairo_status_t -_composite_traps_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_traps_info_t *info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (dst_x != 0 || dst_y != 0) - _cairo_traps_translate (info->traps, - dst_x, - dst_y); - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - status = _cairo_surface_composite_trapezoids (op, - src, dst, info->antialias, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - info->traps->traps, - info->traps->num_traps, - clip_region); - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -enum { - HAS_CLEAR_REGION = 0x1, -}; - -static cairo_status_t -_clip_and_composite_region (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_region_t clear_region; - unsigned int has_region = 0; - cairo_status_t status; - - if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { - /* If we optimize drawing with an unbounded operator to - * _cairo_surface_fill_rectangles() or to drawing with a - * clip region, then we have an additional region to clear. - */ - _cairo_region_init_rectangle (&clear_region, extents); - status = cairo_region_subtract (&clear_region, trap_region); - if (unlikely (status)) - return status; - - if (! cairo_region_is_empty (&clear_region)) - has_region |= HAS_CLEAR_REGION; - } - - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && - clip == NULL) - { - const cairo_color_t *color; - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); - } else { - /* For a simple rectangle, we can just use composite(), for more - * rectangles, we have to set a clip region. The cost of rasterizing - * trapezoids is pretty high for most backends currently, so it's - * worthwhile even if a region is needed. - * - * If we have a clip surface, we set it as the mask; this only works - * for bounded operators other than SOURCE; for unbounded operators, - * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in its handling - * of the mask then what we want. - * - * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has - * more than rectangle and the destination doesn't support clip - * regions. In that case, we fall through. - */ - status = _composite_trap_region (clip, src, op, dst, - trap_region, extents); - } - - if (has_region & HAS_CLEAR_REGION) { - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_surface_fill_region (dst, - CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - } - _cairo_region_fini (&clear_region); - } - - return status; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_rectangles (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - const cairo_color_t *color; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: convert clip region to geometric boxes? */ - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: fallback for the region_subtract() operation */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->has_intersections) { - if (traps->is_rectangular) { - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - } - if (unlikely (status)) - return status; - } - - for (i = 0; i < traps->num_traps; i++) { - if (! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (traps->num_traps, - sizeof (cairo_rectangle_int_t)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - - rects[i].x = x1; - rects[i].y = y1; - rects[i].width = x2 - x1; - rects[i].height = y2 - y1; - } - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); - - if (rects != stack_rects) - free (rects); - - return status; -} - -/* fast-path for very common composite of a single rectangle */ -static cairo_status_t -_composite_rectangle (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - cairo_rectangle_int_t rect; - - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_fixed_is_integer (traps->traps[0].top) || - ! _cairo_fixed_is_integer (traps->traps[0].bottom) || - ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); - rect.y = _cairo_fixed_integer_part (traps->traps[0].top); - rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; - rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; - - return _cairo_surface_composite (op, src, NULL, dst, - rect.x, rect.y, - 0, 0, - rect.x, rect.y, - rect.width, rect.height, - NULL); -} - -/* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_clip_and_composite_trapezoids (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_composite_traps_info_t traps_info; - cairo_region_t *clip_region = NULL; - cairo_bool_t clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (! clip_surface || - (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) - { - cairo_region_t *trap_region = NULL; - - if (_cairo_operator_bounded_by_source (op)) { - status = _fill_rectangles (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _composite_rectangle (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_traps_extract_region (traps, &trap_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (trap_region != NULL) { - status = cairo_region_intersect_rectangle (trap_region, extents); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - - if (clip_region != NULL) { - status = cairo_region_intersect (trap_region, clip_region); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - } - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t trap_extents; - - cairo_region_get_extents (trap_region, &trap_extents); - if (! _cairo_rectangle_intersect (extents, &trap_extents)) { - cairo_region_destroy (trap_region); - return CAIRO_STATUS_SUCCESS; - } - } - - status = _clip_and_composite_region (src, op, dst, - trap_region, - clip_surface ? clip : NULL, - extents); - cairo_region_destroy (trap_region); - - if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) - return status; - } - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - traps_info.traps = traps; - traps_info.antialias = antialias; - - return _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, extents); -} - -typedef struct { - cairo_polygon_t *polygon; - cairo_fill_rule_t fill_rule; - cairo_antialias_t antialias; -} cairo_composite_spans_info_t; - -static cairo_status_t -_composite_spans_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_rectangles_t rects; - cairo_composite_spans_info_t *info = closure; - - rects.source = *extents; - rects.mask = *extents; - rects.bounded = *extents; - /* The incoming dst_x/y are where we're pretending the origin of - * the dst surface is -- *not* the offset of a rectangle where - * we'd like to place the result. */ - rects.bounded.x -= dst_x; - rects.bounded.y -= dst_y; - rects.unbounded = rects.bounded; - rects.is_bounded = _cairo_operator_bounded_by_either (op); - - return _cairo_surface_composite_polygon (dst, op, src, - info->fill_rule, - info->antialias, - &rects, - info->polygon, - clip_region); -} - -static cairo_status_t -_cairo_win32_surface_span_renderer_composite - (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_image_surface_t *mask, - cairo_win32_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (src == NULL || mask == NULL) - return CAIRO_STATUS_SUCCESS; - - status = cairo_surface_status (&mask->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&mask->base); - /* composite onto the image surface directly if we can */ - if (dst->image) { - GdiFlush(); /* XXX: I'm not sure if this needed or not */ - - status = dst->image->backend->composite (op, - src, mask_pattern, dst->image, - extents->x, extents->y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - /* otherwise go through the fallback_composite path which - * will do the appropriate surface acquisition */ - status = _cairo_surface_fallback_composite ( - op, - src, mask_pattern, &dst->base, - extents->x, extents->y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - cairo_pattern_destroy (mask_pattern); - - } - return status; -} - -typedef struct _cairo_image_surface_span_renderer { - cairo_span_renderer_t base; - - uint8_t *mask_data; - uint32_t mask_stride; -} cairo_image_surface_span_renderer_t; - -cairo_status_t -_cairo_image_surface_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans); - -static cairo_status_t -_composite_spans (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_spans_info_t *info = closure; - cairo_image_surface_span_renderer_t renderer; - cairo_scan_converter_t *converter; - cairo_image_surface_t *mask; - cairo_status_t status; - - converter = _cairo_tor_scan_converter_create (extents->x, extents->y, - extents->x + extents->width, - extents->y + extents->height, - info->fill_rule); - status = converter->add_polygon (converter, info->polygon); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - - { - mask = cairo_image_surface_create (CAIRO_FORMAT_A8, - extents->width, - extents->height); - - if (cairo_surface_status(&mask->base)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_CONVERTER; - } - } - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = cairo_image_surface_get_stride (mask); - renderer.mask_data = cairo_image_surface_get_data (mask); - renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; - - status = converter->generate (converter, &renderer.base); - if (unlikely (status)) - goto CLEANUP_RENDERER; - - _cairo_win32_surface_span_renderer_composite - (closure, - op, - src, - mask, - (cairo_win32_surface_t*)dst, // ewwww - dst_x, - dst_y, - extents, - clip_region); -#if 0 - { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_RENDERER; - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - pixman_image_unref (src); - } -#endif - CLEANUP_RENDERER: - cairo_surface_destroy (mask); - CLEANUP_CONVERTER: - converter->destroy (converter); - return status; -} - - - -cairo_status_t -_cairo_win32_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_clip_path_t *clip_path = clip ? clip->path : NULL; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_boxes_t boxes; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - cairo_traps_t traps; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && clip_path->prev == NULL && - _cairo_operator_bounded_by_mask (op)) - { - return _cairo_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - - /* meh, surface-fallback is dying anyway... */ - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _cairo_traps_init_boxes (&traps, &boxes); - if (unlikely (status)) - goto CLEANUP_BOXES; - - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, CAIRO_ANTIALIAS_DEFAULT, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - _cairo_traps_fini (&traps); - -CLEANUP_BOXES: - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -static cairo_status_t -_cairo_surface_mask_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_pattern_t *mask = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - if (src) { - status = _cairo_surface_composite (op, - src, mask, dst, - extents->x, extents->y, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - status = _cairo_surface_composite (op, - mask, NULL, dst, - extents->x, extents->y, - 0, 0, /* unused */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_win32_surface_fallback_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - return _clip_and_composite (clip, op, source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); -} - -cairo_status_t -_cairo_win32_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, stroke_style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - cairo_composite_spans_info_t info; - - info.polygon = &polygon; - info.fill_rule = CAIRO_FILL_RULE_WINDING; - info.antialias = antialias; - - status = _clip_and_composite (clip, op, source, - _composite_spans, - &info, surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -cairo_status_t -_cairo_win32_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_bool_t is_rectilinear; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - if (_cairo_path_fixed_fill_is_empty (path)) - goto DO_TRAPS; - - is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); - if (is_rectilinear) { - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - } - - - if (antialias != CAIRO_ANTIALIAS_NONE) { - cairo_composite_spans_info_t info; - - info.polygon = &polygon; - info.fill_rule = fill_rule; - info.antialias = antialias; - - status = _clip_and_composite (clip, op, source, - _composite_spans, - &info, surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - fill_rule); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -static const cairo_surface_backend_t cairo_win32_surface_backend = { - CAIRO_SURFACE_TYPE_WIN32, - _cairo_win32_surface_create_similar, - _cairo_win32_surface_finish, - _cairo_win32_surface_acquire_source_image, - _cairo_win32_surface_release_source_image, - _cairo_win32_surface_acquire_dest_image, - _cairo_win32_surface_release_dest_image, - NULL, /* clone similar */ - _cairo_win32_surface_composite, - _cairo_win32_surface_fill_rectangles, - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_win32_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - _cairo_win32_surface_flush, - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_win32_surface_fallback_paint, - _cairo_win32_surface_fallback_mask, - _cairo_win32_surface_fallback_stroke, - _cairo_win32_surface_fallback_fill, - _cairo_win32_surface_show_glyphs, - - NULL, /* snapshot */ - _cairo_win32_surface_is_similar, -}; - -/* Notes: - * - * Win32 alpha-understanding functions - * - * BitBlt - will copy full 32 bits from a 32bpp DIB to result - * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) - * (but not safe going RGB24->ARGB32, if RGB24 is also represented - * as a 32bpp DIB, since the alpha isn't discarded!) - * - * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, - * it will still copy over the src alpha, because the SCA value (255) will be - * multiplied by all the src components. - */ - - -cairo_int_status_t -_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface) -{ - RECT rect; - int clipBoxType; - int gm; - XFORM saved_xform; - - /* GetClipBox/GetClipRgn and friends interact badly with a world transform - * set. GetClipBox returns values in logical (transformed) coordinates; - * it's unclear what GetClipRgn returns, because the region is empty in the - * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. - * Similarily, IntersectClipRect works in logical units, whereas SelectClipRgn - * works in device units. - * - * So, avoid the whole mess and get rid of the world transform - * while we store our initial data and when we restore initial coordinates. - * - * XXX we may need to modify x/y by the ViewportOrg or WindowOrg - * here in GM_COMPATIBLE; unclear. - */ - gm = GetGraphicsMode (hdc); - if (gm == GM_ADVANCED) { - GetWorldTransform (hdc, &saved_xform); - ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); - } - - clipBoxType = GetClipBox (hdc, &rect); - if (clipBoxType == ERROR) { - _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); - SetGraphicsMode (hdc, gm); - /* XXX: Can we make a more reasonable guess at the error cause here? */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - surface->clip_rect.x = rect.left; - surface->clip_rect.y = rect.top; - surface->clip_rect.width = rect.right - rect.left; - surface->clip_rect.height = rect.bottom - rect.top; - - surface->initial_clip_rgn = NULL; - surface->had_simple_clip = FALSE; - - if (clipBoxType == COMPLEXREGION) { - surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); - if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { - DeleteObject(surface->initial_clip_rgn); - surface->initial_clip_rgn = NULL; - } - } else if (clipBoxType == SIMPLEREGION) { - surface->had_simple_clip = TRUE; - } - - if (gm == GM_ADVANCED) - SetWorldTransform (hdc, &saved_xform); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - XFORM saved_xform; - int gm = GetGraphicsMode (surface->dc); - if (gm == GM_ADVANCED) { - GetWorldTransform (surface->dc, &saved_xform); - ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY); - } - - /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ - SelectClipRgn (surface->dc, surface->initial_clip_rgn); - - if (surface->had_simple_clip) { - /* then if we had a simple clip, intersect */ - IntersectClipRect (surface->dc, - surface->clip_rect.x, - surface->clip_rect.y, - surface->clip_rect.x + surface->clip_rect.width, - surface->clip_rect.y + surface->clip_rect.height); - } - - if (gm == GM_ADVANCED) - SetWorldTransform (surface->dc, &saved_xform); - - return status; -} - -void -_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) -{ - RGNDATA *rd; - unsigned int z; - - if (header) - fprintf (stderr, "%s\n", header); - - if (rgn == NULL) { - fprintf (stderr, " NULL\n"); - } - - z = GetRegionData(rgn, 0, NULL); - rd = (RGNDATA*) malloc(z); - z = GetRegionData(rgn, z, rd); - - fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", - rd->rdh.nCount, - rd->rdh.rcBound.left, - rd->rdh.rcBound.top, - rd->rdh.rcBound.right - rd->rdh.rcBound.left, - rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); - - for (z = 0; z < rd->rdh.nCount; z++) { - RECT r = ((RECT*)rd->Buffer)[z]; - fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", - z, r.left, r.top, r.right - r.left, r.bottom - r.top); - } - - free(rd); - fflush (stderr); -} - -/** - * cairo_win32_surface_set_can_convert_to_dib - * @surface: a #cairo_surface_t - * @can_convert: a #cairo_bool_t indicating whether this surface can - * be coverted to a DIB if necessary - * - * A DDB surface with this flag set can be converted to a DIB if it's - * used as a source in a way that GDI can't natively handle; for - * example, drawing a RGB24 DDB onto an ARGB32 DIB. Doing this - * conversion results in a significant speed optimization, because we - * can call on pixman to perform the operation natively, instead of - * reading the data from the DC each time. - * - * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully - * changed, or an error otherwise. - * - */ -cairo_status_t -cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - if (surface->bitmap) { - if (can_convert) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; - else - surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_win32_surface_get_can_convert_to_dib - * @surface: a #cairo_surface_t - * @can_convert: a #cairo_bool_t* that receives the return value - * - * Returns the value of the flag indicating whether the surface can be - * converted to a DIB if necessary, as set by - * cairo_win32_surface_set_can_convert_to_dib. - * - * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully - * retreived, or an error otherwise. - * - */ -cairo_status_t -cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0); - return CAIRO_STATUS_SUCCESS; -} - -int -cairo_win32_surface_get_width (cairo_surface_t *asurface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - return surface->extents.width; -} - -int -cairo_win32_surface_get_height (cairo_surface_t *asurface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - return surface->extents.height; -} diff --git a/libs/cairo/cairo/src/cairo-win32.h b/libs/cairo/cairo/src/cairo-win32.h deleted file mode 100644 index a1d45c19a..000000000 --- a/libs/cairo/cairo/src/cairo-win32.h +++ /dev/null @@ -1,307 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _CAIRO_WIN32_H_ -#define _CAIRO_WIN32_H_ - -#include "cairo.h" - -#if CAIRO_HAS_WIN32_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_win32_surface_create (HDC hdc); - -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_alpha (HDC hdc); - -cairo_public cairo_surface_t * -cairo_win32_printing_surface_create (HDC hdc); - -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_ddb (HDC hdc, - cairo_format_t format, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_dib (cairo_format_t format, - int width, - int height); -cairo_public HDC -cairo_win32_surface_get_dc (cairo_surface_t *surface); - -cairo_public HDC -cairo_win32_get_dc_with_clip (cairo_t *cr); - -cairo_public cairo_surface_t * -cairo_win32_surface_get_image (cairo_surface_t *surface); - -cairo_public cairo_status_t -cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert); - -cairo_public cairo_status_t -cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert); - -BYTE cairo_win32_get_system_text_quality (void); - -cairo_public int -cairo_win32_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_win32_surface_get_height (cairo_surface_t *surface); - -#if CAIRO_HAS_WIN32_FONT - -/* - * Win32 font support - */ - -cairo_public cairo_font_face_t * -cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); - -cairo_public cairo_font_face_t * -cairo_win32_font_face_create_for_hfont (HFONT font); - -cairo_public cairo_font_face_t * -cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font); - -cairo_public cairo_status_t -cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, - HDC hdc); - -cairo_public void -cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font); - -cairo_public double -cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font); - -cairo_public void -cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *logical_to_device); - -cairo_public void -cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *device_to_logical); - -#endif /* CAIRO_HAS_WIN32_FONT */ - -#if CAIRO_HAS_DWRITE_FONT - -/* - * Win32 DirectWrite font support - */ -cairo_public cairo_font_face_t * -cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrite_font_face); - -void -cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed); - -void -cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force); - -cairo_bool_t -cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font); - -void -cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode); - -int -cairo_dwrite_get_cleartype_rendering_mode(); - -#endif /* CAIRO_HAS_DWRITE_FONT */ - -struct IDirect3DSurface9; -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_d3dsurface9 (struct IDirect3DSurface9 *surface); - - -#if CAIRO_HAS_D2D_SURFACE - -struct _cairo_device -{ - int type; - int refcount; -}; - -/** - * Create a D2D device - * - * \return New D2D device, NULL if creation failed. - */ -cairo_device_t * -cairo_d2d_create_device(); - -cairo_device_t * -cairo_d2d_create_device_from_d3d10device(struct ID3D10Device1 *device); - -/** - * Releases a D2D device. - * - * \return References left to the device - */ -int -cairo_release_device(cairo_device_t *device); - -/** - * Addrefs a D2D device. - * - * \return References to the device - */ -int -cairo_addref_device(cairo_device_t *device); - -/** - * Flushes a D3D device. In most cases the surface backend will do this - * internally, but when using a surfaces created from a shared handle this - * should be executed manually when a different device is going to be accessing - * the same surface data. This will also block until the device is finished - * processing all work. - */ -void -cairo_d2d_finish_device(cairo_device_t *device); - -/** - * Gets the D3D10 device used by a certain cairo_device_t. - */ -struct ID3D10Device1* -cairo_d2d_device_get_device(cairo_device_t *device); - -/** - * Create a D2D surface for an HWND - * - * \param device Device used to create the surface - * \param wnd Handle for the window - * \param content Content of the window, should be COLOR_ALPHA for transparent windows - * \return New cairo surface - */ -cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content); - -/** - * Create a D2D surface of a certain size. - * - * \param device Device used to create the surface - * \param format Cairo format of the surface - * \param width Width of the surface - * \param height Height of the surface - * \return New cairo surface - */ -cairo_public cairo_surface_t * -cairo_d2d_surface_create(cairo_device_t *device, - cairo_format_t format, - int width, - int height); - -/** - * Create a D3D surface from a Texture SharedHandle, this is obtained from a - * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format - * or an A8 format, the treatment of the alpha channel can be indicated using - * the content parameter. - * - * \param device Device used to create the surface - * \param handle Shared handle to the texture we want to wrap - * \param content Content of the texture, COLOR_ALPHA for ARGB - * \return New cairo surface - */ -cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content); - -/** - * Create a D3D surface from an ID3D10Texture2D texture, this is obtained from a - * CreateTexture2D call on a D3D10 device. This has to be an A8R8G8B8 format - * or an A8 format, the treatment of the alpha channel can be indicated using - * the content parameter. - * - * \param device Device used to create the surface - * \param texture Texture that we want to wrap - * \param content Content of the texture - * \return New cairo surface - */ -cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_texture(cairo_device_t *device, - struct ID3D10Texture2D *texture, - cairo_content_t content); - -/** - * Get the ID3D10Texture2D used for a surface. - */ -cairo_public struct ID3D10Texture2D *cairo_d2d_surface_get_texture(cairo_surface_t *surf); - -/** - * Present the backbuffer for a surface create for an HWND. This needs - * to be called when the owner of the original window surface wants to - * actually present the executed drawing operations to the screen. - * - * \param surface D2D surface. - */ -void cairo_d2d_present_backbuffer(cairo_surface_t *surface); - -/** - * Scroll the surface, this only moves the surface graphics, it does not - * actually scroll child windows or anything like that. Nor does it invalidate - * that area of the window. - * - * \param surface The d2d surface this operation should apply to. - * \param x The x delta for the movement - * \param y The y delta for the movement - * \param clip The clip rectangle, the is the 'part' of the surface that needs - * scrolling. - */ -void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); - -/** - * Get a DC for the current render target. When selecting the retention option this - * call can be relatively slow, since it may require reading back contents from the - * hardware surface. - * - * \note This must be matched by a call to ReleaseDC! - * - * \param retain_contents If true the current contents of the RT is copied to the DC, - * otherwise the DC is initialized to transparent black. - */ -HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents); - -/** - * Release the DC acquired through GetDC(). Optionally an update region may be specified - * - * \param updated_rect The area of the DC that was updated, if null the entire dc will - * be updated. - */ -void cairo_d2d_release_dc(cairo_surface_t *surcace, const cairo_rectangle_int_t *updated_rect); - -/** - * Get an estimate of the amount of (video) RAM which is currently in use by the D2D - * internal image surface cache. - */ -int cairo_d2d_get_image_surface_cache_usage(); - -/** - * Get an estimate of the amount of VRAM which is currently used by the d2d - * surfaces for a device. This does -not- include the internal image surface - * cache. - */ -int cairo_d2d_get_surface_vram_usage(cairo_device_t *device); - -/** - * Get the width of the surface. - */ -int cairo_d2d_surface_get_width(cairo_surface_t *surface); - -/** - * Get the height of the surface. - */ -int cairo_d2d_surface_get_height(cairo_surface_t *surface); -#endif - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_WIN32_SURFACE */ -# error Cairo was not compiled with support for the win32 backend -#endif /* CAIRO_HAS_WIN32_SURFACE */ - -#endif /* _CAIRO_WIN32_H_ */ diff --git a/libs/cairo/cairo/src/cairo-xcb-surface.c b/libs/cairo/cairo/src/cairo-xcb-surface.c deleted file mode 100644 index 7f53e630f..000000000 --- a/libs/cairo/cairo/src/cairo-xcb-surface.c +++ /dev/null @@ -1,1332 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-xcb.h" -#include "cairo-xcb-private.h" - -#if CAIRO_HAS_XCB_DRM_FUNCTIONS -#include -#endif - -#define AllPlanes ((unsigned) -1) -#define CAIRO_ASSUME_PIXMAP 20 -#define XLIB_COORD_MAX 32767 - -#if CAIRO_HAS_XLIB_XCB_FUNCTIONS -slim_hidden_proto (cairo_xcb_surface_create); -slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); -slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); -#endif - -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS -#include "drm/cairo-drm-private.h" -#endif - -/** - * SECTION:cairo-xcb - * @Title: XCB Surfaces - * @Short_Description: X Window System rendering using the XCB library - * @See_Also: #cairo_surface_t - * - * The XCB surface is used to render cairo graphics to X Window System - * windows and pixmaps using the XCB library. - * - * Note that the XCB surface automatically takes advantage of the X render - * extension if it is available. - */ - -#if CAIRO_HAS_XCB_SHM_FUNCTIONS -static cairo_status_t -_cairo_xcb_surface_create_similar_shm (cairo_xcb_surface_t *other, - pixman_format_code_t pixman_format, - int width, int height, - cairo_surface_t **out) -{ - size_t size, stride; - cairo_xcb_shm_info_t *shm_info; - cairo_status_t status; - cairo_surface_t *image; - - stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); - size = stride * height; - if (size < CAIRO_XCB_SHM_SMALL_IMAGE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_xcb_connection_allocate_shm_info (other->connection, - size, &shm_info); - if (unlikely (status)) - return status; - - image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, - pixman_format, - width, height, - stride); - status = image->status; - if (unlikely (status)) { - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - status = _cairo_user_data_array_set_data (&image->user_data, - (const cairo_user_data_key_t *) other->connection, - shm_info, - (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); - if (unlikely (status)) { - cairo_surface_destroy (image); - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - *out = image; - return CAIRO_STATUS_SUCCESS; -} -#endif - -cairo_surface_t * -_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other, - cairo_content_t content, - int width, int height) -{ - cairo_surface_t *image = NULL; - pixman_format_code_t pixman_format; - - /* XXX choose pixman_format from connection->image_formats */ - switch (content) { - case CAIRO_CONTENT_ALPHA: - pixman_format = PIXMAN_a8; - break; - case CAIRO_CONTENT_COLOR: - pixman_format = PIXMAN_x8r8g8b8; - break; - default: - ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: - pixman_format = PIXMAN_a8r8g8b8; - break; - } - -#if CAIRO_HAS_XCB_SHM_FUNCTIONS - if (other->flags & CAIRO_XCB_HAS_SHM) { - cairo_status_t status; - - status = _cairo_xcb_surface_create_similar_shm (other, - pixman_format, - width, height, - &image); - if (_cairo_status_is_error (status)) - return _cairo_surface_create_in_error (status); - } -#endif - - if (image == NULL) { - image = _cairo_image_surface_create_with_pixman_format (NULL, - pixman_format, - width, height, - 0); - } - - return image; -} - -cairo_surface_t * -_cairo_xcb_surface_create_similar (void *abstract_other, - cairo_content_t content, - int width, - int height) -{ - cairo_xcb_surface_t *other = abstract_other; - cairo_xcb_surface_t *surface; - cairo_xcb_connection_t *connection; - xcb_pixmap_t pixmap; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; - - if (width <= 0 || height <= 0) - return NULL; - -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS - if (other->drm != NULL) { - cairo_surface_t *drm; - - drm = _cairo_drm_surface_create_similar (other->drm, content, width, height); - if (drm != NULL) - return drm; - } -#endif - - if ((other->flags & CAIRO_XCB_HAS_RENDER) == 0) - return _cairo_xcb_surface_create_similar_image (other, content, width, height); - - connection = other->connection; - status = _cairo_xcb_connection_acquire (connection); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - status =_cairo_xcb_connection_take_socket (connection); - if (unlikely (status)) { - _cairo_xcb_connection_release (connection); - return _cairo_surface_create_in_error (status); - } - - if (content == other->base.content) { - pixmap = _cairo_xcb_connection_create_pixmap (connection, - other->depth, - other->drawable, - width, height); - - surface = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_internal (other->screen, - pixmap, TRUE, - other->pixman_format, - other->xrender_format, - width, height); - } else { - cairo_format_t format; - pixman_format_code_t pixman_format; - - /* XXX find a compatible xrender format */ - switch (content) { - case CAIRO_CONTENT_ALPHA: - pixman_format = PIXMAN_a8; - format = CAIRO_FORMAT_A8; - break; - case CAIRO_CONTENT_COLOR: - pixman_format = PIXMAN_x8r8g8b8; - format = CAIRO_FORMAT_RGB24; - break; - default: - ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: - pixman_format = PIXMAN_a8r8g8b8; - format = CAIRO_FORMAT_ARGB32; - break; - } - - pixmap = _cairo_xcb_connection_create_pixmap (connection, - PIXMAN_FORMAT_DEPTH (pixman_format), - other->drawable, - width, height); - - surface = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_internal (other->screen, - pixmap, TRUE, - pixman_format, - connection->standard_formats[format], - width, height); - } - - if (unlikely (surface->base.status)) - _cairo_xcb_connection_free_pixmap (connection, pixmap); - - _cairo_xcb_connection_release (connection); - - return &surface->base; -} - -static cairo_status_t -_cairo_xcb_surface_finish (void *abstract_surface) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->fallback != NULL) { - cairo_surface_finish (surface->fallback); - cairo_surface_destroy (surface->fallback); - } - - cairo_list_del (&surface->link); - -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS - if (surface->drm != NULL) { - cairo_surface_finish (surface->drm); - cairo_surface_destroy (surface->drm); - - xcb_dri2_destroy_drawable (surface->connection->xcb_connection, - surface->drawable); - } -#endif - - status = _cairo_xcb_connection_acquire (surface->connection); - if (status == CAIRO_STATUS_SUCCESS) { - if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { - if (surface->picture != XCB_NONE) { - _cairo_xcb_connection_render_free_picture (surface->connection, - surface->picture); - } - - if (surface->owns_pixmap) - _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); - } - _cairo_xcb_connection_release (surface->connection); - } - - _cairo_xcb_connection_destroy (surface->connection); - - return status; -} - -static void -_destroy_image (pixman_image_t *image, void *data) -{ - free (data); -} - -#if CAIRO_HAS_XCB_SHM_FUNCTIONS -static cairo_int_status_t -_cairo_xcb_surface_create_shm_image (cairo_xcb_surface_t *target, - cairo_image_surface_t **image_out, - cairo_xcb_shm_info_t **shm_info_out) -{ - cairo_image_surface_t *image; - cairo_xcb_shm_info_t *shm_info; - cairo_status_t status; - size_t size, stride; - - if ((target->flags & CAIRO_XCB_HAS_SHM) == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - stride = CAIRO_STRIDE_FOR_WIDTH_BPP (target->width, - PIXMAN_FORMAT_BPP (target->pixman_format)); - size = stride * target->height; - if (size < CAIRO_XCB_SHM_SMALL_IMAGE) { - target->flags &= ~CAIRO_XCB_HAS_SHM; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_xcb_connection_allocate_shm_info (target->screen->connection, - size, &shm_info); - if (unlikely (status)) - return status; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (shm_info->mem, - target->pixman_format, - target->width, - target->height, - stride); - status = image->base.status; - if (unlikely (status)) { - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - status = _cairo_user_data_array_set_data (&image->base.user_data, - (const cairo_user_data_key_t *) target->connection, - shm_info, - (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); - if (unlikely (status)) { - cairo_surface_destroy (&image->base); - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - *image_out = image; - *shm_info_out = shm_info; - return CAIRO_STATUS_SUCCESS; -} -#endif - -static cairo_status_t -_get_shm_image (cairo_xcb_surface_t *surface, - cairo_image_surface_t **image_out) -{ -#if CAIRO_HAS_XCB_SHM_FUNCTIONS - cairo_image_surface_t *image; - cairo_xcb_shm_info_t *shm_info; - cairo_status_t status; - - status = _cairo_xcb_surface_create_shm_image (surface, &image, &shm_info); - if (unlikely (status)) - return status; - - if (! surface->base.is_clear) { - status = _cairo_xcb_connection_shm_get_image (surface->connection, - surface->drawable, - 0, 0, - surface->width, - surface->height, - shm_info->shm, - shm_info->offset); - if (unlikely (status)) - return status; - } else { - memset (image->data, 0, image->stride * image->height); - } - - *image_out = image; - return CAIRO_STATUS_SUCCESS; -#else - return CAIRO_INT_STATUS_UNSUPPORTED; -#endif -} - -static cairo_status_t -_get_image (cairo_xcb_surface_t *surface, - cairo_bool_t use_shm, - cairo_image_surface_t **image_out) -{ - cairo_image_surface_t *image; - cairo_xcb_connection_t *connection; - xcb_get_image_reply_t *reply; - cairo_status_t status; - - if (surface->base.is_clear || surface->deferred_clear) { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - surface->pixman_format, - surface->width, - surface->height, - 0); - *image_out = image; - return image->base.status; - } - - connection = surface->connection; - - status = _cairo_xcb_connection_acquire (connection); - if (unlikely (status)) - return status; - - status = _cairo_xcb_connection_take_socket (connection); - if (unlikely (status)) - goto FAIL; - - if (use_shm) { - status = _get_shm_image (surface, image_out); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FAIL; - } - - if (surface->use_pixmap == 0) { - status = _cairo_xcb_connection_get_image (connection, - surface->drawable, - 0, 0, - surface->width, - surface->height, - &reply); - if (unlikely (status)) - goto FAIL; - } else { - surface->use_pixmap--; - reply = NULL; - } - - if (reply == NULL && ! surface->owns_pixmap) { - /* xcb_get_image_t from a window is dangerous because it can - * produce errors if the window is unmapped or partially - * outside the screen. We could check for errors and - * retry, but to keep things simple, we just create a - * temporary pixmap - */ - xcb_pixmap_t pixmap; - xcb_gcontext_t gc; - - gc = _cairo_xcb_screen_get_gc (surface->screen, - surface->drawable, - surface->depth); - pixmap = _cairo_xcb_connection_create_pixmap (connection, - surface->depth, - surface->drawable, - surface->width, - surface->height); - - /* XXX IncludeInferiors? */ - _cairo_xcb_connection_copy_area (connection, - surface->drawable, - pixmap, gc, - 0, 0, - 0, 0, - surface->width, - surface->height); - - _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); - - status = _cairo_xcb_connection_get_image (connection, - pixmap, - 0, 0, - surface->width, - surface->height, - &reply); - _cairo_xcb_connection_free_pixmap (connection, pixmap); - - if (unlikely (status)) - goto FAIL; - } - - if (unlikely (reply == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - /* XXX byte swap */ - /* XXX format conversion */ - assert (reply->depth == surface->depth); - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format - (xcb_get_image_data (reply), - surface->pixman_format, - surface->width, - surface->height, - CAIRO_STRIDE_FOR_WIDTH_BPP (surface->width, - PIXMAN_FORMAT_BPP (surface->pixman_format))); - status = image->base.status; - if (unlikely (status)) { - free (reply); - goto FAIL; - } - - assert (xcb_get_image_data_length (reply) == image->height * image->stride); - - pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply); - - _cairo_xcb_connection_release (connection); - - /* synchronisation point */ - surface->marked_dirty = FALSE; - - *image_out = image; - return CAIRO_STATUS_SUCCESS; - -FAIL: - _cairo_xcb_connection_release (connection); - return status; -} - -static cairo_status_t -_cairo_xcb_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_acquire_source_image (surface->drm, - image_out, image_extra); - } - - if (surface->fallback != NULL) { - image = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback); - goto DONE; - } - - image = (cairo_image_surface_t *) - _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); - if (image != NULL) { - image = (cairo_image_surface_t *) cairo_surface_reference (&image->base); - goto DONE; - } - - status = _get_image (surface, FALSE, &image); - if (unlikely (status)) - return status; - - cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); - -DONE: - *image_out = image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xcb_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_release_source_image (surface->drm, - image, image_extra); - } - - cairo_surface_destroy (&image->base); -} - -static cairo_bool_t -_cairo_xcb_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_xcb_surface_t *surface = abstract_surface; - - extents->x = extents->y = 0; - extents->width = surface->width; - extents->height = surface->height; - return TRUE; -} - -static void -_cairo_xcb_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - /* XXX copy from xlib */ - _cairo_font_options_init_default (options); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); -} - -static cairo_status_t -_put_shm_image (cairo_xcb_surface_t *surface, - xcb_gcontext_t gc, - cairo_image_surface_t *image) -{ -#if CAIRO_HAS_XCB_SHM_FUNCTIONS - cairo_xcb_shm_info_t *shm_info; - - shm_info = _cairo_user_data_array_get_data (&image->base.user_data, - (const cairo_user_data_key_t *) surface->connection); - if (shm_info == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - shm_info->seqno = - _cairo_xcb_connection_shm_put_image (surface->connection, - surface->drawable, - gc, - surface->width, surface->height, - 0, 0, - image->width, image->height, - 0, 0, - image->depth, - shm_info->shm, - shm_info->offset); - - return CAIRO_STATUS_SUCCESS; -#else - return CAIRO_INT_STATUS_UNSUPPORTED; -#endif -} - -static cairo_status_t -_put_image (cairo_xcb_surface_t *surface, - cairo_image_surface_t *image) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - /* XXX track damaged region? */ - - status = _cairo_xcb_connection_acquire (surface->connection); - if (unlikely (status)) - return status; - - status = _cairo_xcb_connection_take_socket (surface->connection); - if (unlikely (status)) { - _cairo_xcb_connection_release (surface->connection); - return status; - } - - if (image->pixman_format == surface->pixman_format) { - xcb_gcontext_t gc; - - assert (image->width == surface->width); - assert (image->height == surface->height); - assert (image->depth == surface->depth); - assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); - - gc = _cairo_xcb_screen_get_gc (surface->screen, - surface->drawable, - surface->depth); - - status = _put_shm_image (surface, gc, image); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - _cairo_xcb_connection_put_image (surface->connection, - surface->drawable, gc, - image->width, image->height, - 0, 0, - image->depth, - image->stride, - image->data); - status = CAIRO_STATUS_SUCCESS; - } - - _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); - } else { - ASSERT_NOT_REACHED; - } - - _cairo_xcb_connection_release (surface->connection); - return status; -} - -static cairo_status_t -_cairo_xcb_surface_flush (void *abstract_surface) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) - return surface->drm->backend->flush (surface->drm); - - if (likely (surface->fallback == NULL)) { - status = CAIRO_STATUS_SUCCESS; - if (! surface->base.finished && surface->deferred_clear) - status = _cairo_xcb_surface_clear (surface); - - return status; - } - - status = surface->base.status; - if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) { - status = cairo_surface_status (surface->fallback); - - if (status == CAIRO_STATUS_SUCCESS) { - status = _put_image (surface, - (cairo_image_surface_t *) surface->fallback); - } - - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_attach_snapshot (&surface->base, - surface->fallback, - cairo_surface_finish); - } - } - - cairo_surface_destroy (surface->fallback); - surface->fallback = NULL; - - return status; -} - -static cairo_status_t -_cairo_xcb_surface_mark_dirty (void *abstract_surface, - int x, int y, - int width, int height) -{ - cairo_xcb_surface_t *surface = abstract_surface; - surface->marked_dirty = TRUE; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_xcb_surface_map_to_image (cairo_xcb_surface_t *surface) -{ - cairo_status_t status; - cairo_image_surface_t *image; - - status = _get_image (surface, TRUE, &image); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - return &image->base; -} - -static cairo_int_status_t -_cairo_xcb_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) - return _cairo_surface_paint (surface->drm, op, source, clip); - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_paint (surface->fallback, op, source, clip); -} - -static cairo_int_status_t -_cairo_xcb_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) - return _cairo_surface_mask (surface->drm, op, source, mask, clip); - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_mask (surface, - op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_mask (surface, - op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_mask (surface->fallback, - op, source, mask, - clip); -} - -static cairo_int_status_t -_cairo_xcb_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_stroke (surface->drm, - op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_stroke (surface, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_stroke (surface, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_stroke (surface->fallback, - op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); -} - -static cairo_int_status_t -_cairo_xcb_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_fill (surface->drm, - op, source, - path, fill_rule, - tolerance, antialias, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_fill (surface->fallback, - op, source, - path, fill_rule, - tolerance, antialias, - clip); -} - -static cairo_int_status_t -_cairo_xcb_surface_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *num_remaining) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - *num_remaining = 0; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_show_text_glyphs (surface->drm, - op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_glyphs (surface, - op, source, - scaled_font, glyphs, num_glyphs, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_glyphs (surface, - op, source, - scaled_font, glyphs, num_glyphs, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_show_text_glyphs (surface->fallback, - op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, - clip); -} - -const cairo_surface_backend_t _cairo_xcb_surface_backend = { - CAIRO_SURFACE_TYPE_XCB, - - _cairo_xcb_surface_create_similar, - _cairo_xcb_surface_finish, - _cairo_xcb_surface_acquire_source_image, - _cairo_xcb_surface_release_source_image, - NULL, NULL, NULL, /* dest acquire/release/clone */ - - NULL, /* composite */ - NULL, /* fill */ - NULL, /* trapezoids */ - NULL, /* span */ - NULL, /* check-span */ - - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_xcb_surface_get_extents, - NULL, /* old-glyphs */ - _cairo_xcb_surface_get_font_options, - - _cairo_xcb_surface_flush, - _cairo_xcb_surface_mark_dirty, - _cairo_xcb_surface_scaled_font_fini, - _cairo_xcb_surface_scaled_glyph_fini, - - _cairo_xcb_surface_paint, - _cairo_xcb_surface_mask, - _cairo_xcb_surface_stroke, - _cairo_xcb_surface_fill, - _cairo_xcb_surface_glyphs, -}; - -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS -static cairo_surface_t * -_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, - cairo_xcb_screen_t *screen, - xcb_drawable_t drawable, - pixman_format_code_t pixman_format, - int width, int height) -{ - uint32_t attachments[] = { XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT }; - xcb_dri2_get_buffers_reply_t *buffers; - xcb_dri2_dri2_buffer_t *buffer; - cairo_surface_t *surface; - - if (! _cairo_drm_size_is_valid (screen->device, width, height)) - return NULL; - - xcb_dri2_create_drawable (connection->xcb_connection, - drawable); - - buffers = xcb_dri2_get_buffers_reply (connection->xcb_connection, - xcb_dri2_get_buffers (connection->xcb_connection, - drawable, 1, - ARRAY_LENGTH (attachments), - attachments), - 0); - if (buffers == NULL) { - xcb_dri2_destroy_drawable (connection->xcb_connection, - drawable); - return NULL; - } - - /* If the drawable is a window, we expect to receive an extra fake front, - * which would involve copying on each flush - contrary to the user - * expectations. But that is likely to be preferable to mixing drm/xcb - * operations. - */ - buffer = xcb_dri2_get_buffers_buffers (buffers); - if (buffers->count == 1 && buffer[0].attachment == XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT) { - assert (buffer[0].cpp == PIXMAN_FORMAT_BPP (pixman_format) / 8); - surface = cairo_drm_surface_create_for_name (screen->device, - buffer[0].name, - _cairo_format_from_pixman_format (pixman_format), - width, height, - buffer[0].pitch); - } else { - xcb_dri2_destroy_drawable (connection->xcb_connection, - drawable); - surface = NULL; - } - free (buffers); - - return surface; -} - -#else - -static cairo_surface_t * -_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, - cairo_xcb_screen_t *screen, - xcb_drawable_t drawable, - pixman_format_code_t pixman_format, - int width, int height) -{ - return NULL; -} - -#endif - -cairo_surface_t * -_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, - xcb_drawable_t drawable, - cairo_bool_t owns_pixmap, - pixman_format_code_t pixman_format, - xcb_render_pictformat_t xrender_format, - int width, - int height) -{ - cairo_xcb_surface_t *surface; - - surface = malloc (sizeof (cairo_xcb_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_xcb_surface_backend, - &screen->connection->device, - _cairo_content_from_pixman_format (pixman_format)); - - surface->connection = _cairo_xcb_connection_reference (screen->connection); - surface->screen = screen; - cairo_list_add (&surface->link, &screen->surfaces); - - surface->fallback = NULL; - - surface->drawable = drawable; - surface->owns_pixmap = owns_pixmap; - surface->use_pixmap = 0; - - surface->deferred_clear = FALSE; - - surface->width = width; - surface->height = height; - surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); - - surface->picture = XCB_NONE; - - surface->pixman_format = pixman_format; - surface->xrender_format = xrender_format; - - surface->flags = screen->connection->flags; - - surface->marked_dirty = FALSE; - surface->drm = NULL; - if (screen->device != NULL) { - surface->drm = _xcb_drm_create_surface_for_drawable (surface->connection, - surface->screen, - drawable, - pixman_format, - width, height); - } - - return &surface->base; -} - -static xcb_screen_t * -_cairo_xcb_screen_from_visual (xcb_connection_t *connection, - xcb_visualtype_t *visual, - int *depth) -{ - xcb_depth_iterator_t d; - xcb_screen_iterator_t s; - - s = xcb_setup_roots_iterator (xcb_get_setup (connection)); - for (; s.rem; xcb_screen_next (&s)) { - if (s.data->root_visual == visual->visual_id) { - *depth = s.data->root_depth; - return s.data; - } - - d = xcb_screen_allowed_depths_iterator(s.data); - for (; d.rem; xcb_depth_next (&d)) { - xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); - - for (; v.rem; xcb_visualtype_next (&v)) { - if (v.data->visual_id == visual->visual_id) { - *depth = d.data->depth; - return s.data; - } - } - } - } - - return NULL; -} - -cairo_surface_t * -cairo_xcb_surface_create (xcb_connection_t *xcb_connection, - xcb_drawable_t drawable, - xcb_visualtype_t *visual, - int width, - int height) -{ - cairo_xcb_screen_t *screen; - xcb_screen_t *xcb_screen; - cairo_format_masks_t image_masks; - pixman_format_code_t pixman_format; - xcb_render_pictformat_t xrender_format; - int depth; - - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); - - if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - - xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth); - if (unlikely (xcb_screen == NULL)) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_VISUAL); - - image_masks.alpha_mask = 0; - image_masks.red_mask = visual->red_mask; - image_masks.green_mask = visual->green_mask; - image_masks.blue_mask = visual->blue_mask; - if (depth > 16) - image_masks.bpp = 32; - else if (depth > 8) - image_masks.bpp = 16; - else if (depth > 1) - image_masks.bpp = 8; - else - image_masks.bpp = 1; - - if (! _pixman_format_from_masks (&image_masks, &pixman_format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); - if (unlikely (screen == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - xrender_format = - _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection, - visual->visual_id); - - return _cairo_xcb_surface_create_internal (screen, drawable, FALSE, - pixman_format, - xrender_format, - width, height); -} -#if CAIRO_HAS_XLIB_XCB_FUNCTIONS -slim_hidden_def (cairo_xcb_surface_create); -#endif - -cairo_surface_t * -cairo_xcb_surface_create_for_bitmap (xcb_connection_t *xcb_connection, - xcb_screen_t *xcb_screen, - xcb_pixmap_t bitmap, - int width, - int height) -{ - cairo_xcb_screen_t *screen; - - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); - if (unlikely (screen == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE, - PIXMAN_a1, - screen->connection->standard_formats[CAIRO_FORMAT_A1], - width, height); -} -#if CAIRO_HAS_XLIB_XCB_FUNCTIONS -slim_hidden_def (cairo_xcb_surface_create_for_bitmap); -#endif - -/** - * cairo_xcb_surface_create_with_xrender_format: - * @connection: an XCB connection - * @drawable: an XCB drawable - * @screen: the XCB screen associated with @drawable - * @format: the picture format to use for drawing to @drawable. The - * depth of @format mush match the depth of the drawable. - * @width: the current width of @drawable - * @height: the current height of @drawable - * - * Creates an XCB surface that draws to the given drawable. - * The way that colors are represented in the drawable is specified - * by the provided picture format. - * - * Note: If @drawable is a Window, then the function - * cairo_xcb_surface_set_size() must be called whenever the size of the - * window changes. - * - * Return value: the newly created surface. - **/ -cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connection, - xcb_screen_t *xcb_screen, - xcb_drawable_t drawable, - xcb_render_pictforminfo_t *format, - int width, - int height) -{ - cairo_xcb_screen_t *screen; - cairo_format_masks_t image_masks; - pixman_format_code_t pixman_format; - - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - - image_masks.alpha_mask = - (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift; - image_masks.red_mask = - (unsigned long) format->direct.red_mask << format->direct.red_shift; - image_masks.green_mask = - (unsigned long) format->direct.green_mask << format->direct.green_shift; - image_masks.blue_mask = - (unsigned long) format->direct.blue_mask << format->direct.blue_shift; -#if 0 - image_masks.bpp = format->depth; -#else - if (format->depth > 16) - image_masks.bpp = 32; - else if (format->depth > 8) - image_masks.bpp = 16; - else if (format->depth > 1) - image_masks.bpp = 8; - else - image_masks.bpp = 1; -#endif - - if (! _pixman_format_from_masks (&image_masks, &pixman_format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); - if (unlikely (screen == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - return _cairo_xcb_surface_create_internal (screen, - drawable, - FALSE, - pixman_format, - format->id, - width, height); -} -#if CAIRO_HAS_XLIB_XCB_FUNCTIONS -slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); -#endif - -/** - * cairo_xcb_surface_set_size: - * @surface: a #cairo_surface_t for the XCB backend - * @width: the new width of the surface - * @height: the new height of the surface - * - * Informs cairo of the new size of the XCB drawable underlying the - * surface. For a surface created for a window (rather than a pixmap), - * this function must be called each time the size of the window - * changes. (For a subwindow, you are normally resizing the window - * yourself, but for a toplevel window, it is necessary to listen for - * ConfigureNotify events.) - * - * A pixmap can never change size, so it is never necessary to call - * this function on a surface created for a pixmap. - **/ -void -cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, - int width, - int height) -{ - cairo_xcb_surface_t *surface; - cairo_status_t status_ignored; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - - if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); - return; - } - - surface = (cairo_xcb_surface_t *) abstract_surface; - surface->width = width; - surface->height = height; -} -#if CAIRO_HAS_XLIB_XCB_FUNCTIONS -slim_hidden_def (cairo_xcb_surface_set_size); -#endif diff --git a/libs/cairo/cairo/src/cairo-xcb-xrender.h b/libs/cairo/cairo/src/cairo-xcb-xrender.h deleted file mode 100644 index ff81706c4..000000000 --- a/libs/cairo/cairo/src/cairo-xcb-xrender.h +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XCB_XRENDER_H -#define CAIRO_XCB_XRENDER_H - -#include "cairo.h" - -#if CAIRO_HAS_XCB_SURFACE - -#include -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c, - xcb_drawable_t drawable, - xcb_screen_t *screen, - xcb_render_pictforminfo_t *format, - int width, - int height); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_XCB_SURFACE */ -# error Cairo was not compiled with support for the xcb backend -#endif /* CAIRO_HAS_XCB_SURFACE */ - -#endif /* CAIRO_XCB_XRENDER_H */ diff --git a/libs/cairo/cairo/src/cairo-xcb.h b/libs/cairo/cairo/src/cairo-xcb.h deleted file mode 100644 index de72b4f04..000000000 --- a/libs/cairo/cairo/src/cairo-xcb.h +++ /dev/null @@ -1,62 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XCB_H -#define CAIRO_XCB_H - -#include "cairo.h" - -#if CAIRO_HAS_XCB_SURFACE - -#include -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xcb_surface_create (xcb_connection_t *connection, - xcb_drawable_t drawable, - xcb_visualtype_t *visual, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, - xcb_screen_t *screen, - xcb_pixmap_t bitmap, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, - xcb_screen_t *screen, - xcb_drawable_t drawable, - xcb_render_pictforminfo_t *format, - int width, - int height); - -cairo_public void -cairo_xcb_surface_set_size (cairo_surface_t *surface, - int width, - int height); - -/* debug interface */ - -cairo_public void -cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, - int major_version, - int minor_version); - -cairo_public void -cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, - int major_version, - int minor_version); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_XCB_SURFACE */ -# error Cairo was not compiled with support for the xcb backend -#endif /* CAIRO_HAS_XCB_SURFACE */ - -#endif /* CAIRO_XCB_H */ diff --git a/libs/cairo/cairo/src/cairo-xlib-display.c b/libs/cairo/cairo/src/cairo-xlib-display.c deleted file mode 100644 index 7967d6027..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-display.c +++ /dev/null @@ -1,638 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-xlib-private.h" -#include "cairo-xlib-xrender-private.h" -#include "cairo-freelist-private.h" -#include "cairo-error-private.h" - -#include /* For XESetCloseDisplay */ - -typedef int (*cairo_xlib_error_func_t) (Display *display, - XErrorEvent *event); - -struct _cairo_xlib_job { - cairo_xlib_job_t *next; - enum { - RESOURCE, - WORK - } type; - union { - struct { - cairo_xlib_notify_resource_func notify; - XID xid; - } resource; - struct { - cairo_xlib_notify_func notify; - void *data; - void (*destroy) (void *); - } work; - } func; -}; - -static cairo_xlib_display_t *_cairo_xlib_display_list; - -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook); - -static void -_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) -{ - cairo_xlib_screen_t *screen; - cairo_xlib_hook_t *hook; - - cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link) - _cairo_xlib_screen_close_display (display, screen); - - while (TRUE) { - hook = display->close_display_hooks; - if (hook == NULL) - break; - - _cairo_xlib_remove_close_display_hook_internal (display, hook); - - hook->func (display, hook); - } - display->closed = TRUE; -} - -static void -_cairo_xlib_display_finish (void *abstract_display) -{ - cairo_xlib_display_t *display = abstract_display; - - display->display = NULL; -} - -static void -_cairo_xlib_display_destroy (void *abstract_display) -{ - cairo_xlib_display_t *display = abstract_display; - - /* destroy all outstanding notifies */ - while (display->workqueue != NULL) { - cairo_xlib_job_t *job = display->workqueue; - display->workqueue = job->next; - - if (job->type == WORK && job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - - _cairo_freelist_free (&display->wq_freelist, job); - } - _cairo_freelist_fini (&display->wq_freelist); - - while (! cairo_list_is_empty (&display->screens)) { - _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens, - cairo_xlib_screen_t, - link)); - } - - free (display); -} - -static int -_noop_error_handler (Display *display, - XErrorEvent *event) -{ - return False; /* return value is ignored */ -} - -static void -_cairo_xlib_display_notify (cairo_xlib_display_t *display) -{ - cairo_xlib_job_t *jobs, *job, *freelist; - Display *dpy = display->display; - - /* Optimistic atomic pointer read -- don't care if it is wrong due to - * contention as we will check again very shortly. - */ - if (display->workqueue == NULL) - return; - - jobs = display->workqueue; - while (jobs != NULL) { - display->workqueue = NULL; - - /* reverse the list to obtain FIFO order */ - job = NULL; - do { - cairo_xlib_job_t *next = jobs->next; - jobs->next = job; - job = jobs; - jobs = next; - } while (jobs != NULL); - freelist = jobs = job; - - do { - job = jobs; - jobs = job->next; - - switch (job->type){ - case WORK: - job->func.work.notify (dpy, job->func.work.data); - if (job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - break; - - case RESOURCE: - job->func.resource.notify (dpy, job->func.resource.xid); - break; - } - } while (jobs != NULL); - - do { - job = freelist; - freelist = job->next; - _cairo_freelist_free (&display->wq_freelist, job); - } while (freelist != NULL); - - jobs = display->workqueue; - } -} - -static int -_cairo_xlib_close_display (Display *dpy, XExtCodes *codes) -{ - cairo_xlib_display_t *display, **prev, *next; - cairo_xlib_error_func_t old_handler; - - CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); - for (display = _cairo_xlib_display_list; display; display = display->next) - if (display->display == dpy) - break; - CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); - if (display == NULL) - return 0; - - if (! cairo_device_acquire (&display->base)) { - /* protect the notifies from triggering XErrors */ - XSync (dpy, False); - old_handler = XSetErrorHandler (_noop_error_handler); - - _cairo_xlib_display_notify (display); - _cairo_xlib_call_close_display_hooks (display); - - /* catch any that arrived before marking the display as closed */ - _cairo_xlib_display_notify (display); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - - cairo_device_release (&display->base); - } - - /* - * Unhook from the global list - */ - CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); - prev = &_cairo_xlib_display_list; - for (display = _cairo_xlib_display_list; display; display = next) { - next = display->next; - if (display->display == dpy) { - *prev = next; - break; - } else - prev = &display->next; - } - CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); - - assert (display != NULL); - - cairo_device_finish (&display->base); - cairo_device_destroy (&display->base); - - /* Return value in accordance with requirements of - * XESetCloseDisplay */ - return 0; -} - -static const cairo_device_backend_t _cairo_xlib_device_backend = { - CAIRO_DEVICE_TYPE_XLIB, - - NULL, - NULL, - - NULL, /* flush */ - _cairo_xlib_display_finish, - _cairo_xlib_display_destroy, -}; - -/** - * cairo_xlib_device_create: - * @dpy: the display to create the device for - * - * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. - * - * Returns: the device belonging to @dpy - **/ -cairo_device_t * -_cairo_xlib_device_create (Display *dpy) -{ - cairo_xlib_display_t *display; - cairo_xlib_display_t **prev; - cairo_device_t *device; - XExtCodes *codes; - const char *env; - - static int buggy_repeat_force = -1; - - CAIRO_MUTEX_INITIALIZE (); - - /* There is an apparent deadlock between this mutex and the - * mutex for the display, but it's actually safe. For the - * app to call XCloseDisplay() while any other thread is - * inside this function would be an error in the logic - * app, and the CloseDisplay hook is the only other place we - * acquire this mutex. - */ - CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); - - for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next) - { - if (display->display == dpy) { - /* - * MRU the list - */ - if (prev != &_cairo_xlib_display_list) { - *prev = display->next; - display->next = _cairo_xlib_display_list; - _cairo_xlib_display_list = display; - } - device = cairo_device_reference (&display->base); - goto UNLOCK; - } - } - - display = malloc (sizeof (cairo_xlib_display_t)); - if (unlikely (display == NULL)) { - device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); - goto UNLOCK; - } - - /* Xlib calls out to the extension close_display hooks in LIFO - * order. So we have to ensure that all extensions that we depend - * on in our close_display hook are properly initialized before we - * add our hook. For now, that means Render, so we call into its - * QueryVersion function to ensure it gets initialized. - */ - display->render_major = display->render_minor = -1; - XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); - env = getenv ("CAIRO_DEBUG"); - if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) { - int max_render_major, max_render_minor; - - env += sizeof ("xrender-version=") - 1; - if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) - max_render_major = max_render_minor = -1; - - if (max_render_major < display->render_major || - (max_render_major == display->render_major && - max_render_minor < display->render_minor)) - { - display->render_major = max_render_major; - display->render_minor = max_render_minor; - } - } - - codes = XAddExtension (dpy); - if (unlikely (codes == NULL)) { - device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); - free (display); - goto UNLOCK; - } - - _cairo_device_init (&display->base, &_cairo_xlib_device_backend); - - XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); - - _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t)); - - cairo_device_reference (&display->base); /* add one for the CloseDisplay */ - display->display = dpy; - cairo_list_init (&display->screens); - display->workqueue = NULL; - display->close_display_hooks = NULL; - display->closed = FALSE; - - memset (display->cached_xrender_formats, 0, - sizeof (display->cached_xrender_formats)); - - /* Prior to Render 0.10, there is no protocol support for gradients and - * we call function stubs instead, which would silently consume the drawing. - */ -#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 - display->buggy_gradients = TRUE; -#else - display->buggy_gradients = FALSE; -#endif - display->buggy_pad_reflect = FALSE; - display->buggy_repeat = FALSE; - - /* This buggy_repeat condition is very complicated because there - * are multiple X server code bases (with multiple versioning - * schemes within a code base), and multiple bugs. - * - * The X servers: - * - * 1. The Vendor=="XFree86" code base with release numbers such - * as 4.7.0 (VendorRelease==40700000). - * - * 2. The Vendor=="X.Org" code base (a descendant of the - * XFree86 code base). It originally had things like - * VendorRelease==60700000 for release 6.7.0 but then changed - * its versioning scheme so that, for example, - * VendorRelease==10400000 for the 1.4.0 X server within the - * X.Org 7.3 release. - * - * The bugs: - * - * 1. The original bug that led to the buggy_repeat - * workaround. This was a bug that Owen Taylor investigated, - * understood well, and characterized against carious X - * servers. Confirmed X servers with this bug include: - * - * "XFree86" <= 40500000 - * "X.Org" <= 60802000 (only with old numbering >= 60700000) - * - * 2. A separate bug resulting in a crash of the X server when - * using cairo's extend-reflect test case, (which, surprisingly - * enough was not passing RepeatReflect to the X server, but - * instead using RepeatNormal in a workaround). Nobody to date - * has understood the bug well, but it appears to be gone as of - * the X.Org 1.4.0 server. This bug is coincidentally avoided - * by using the same buggy_repeat workaround. Confirmed X - * servers with this bug include: - * - * "X.org" == 60900000 (old versioning scheme) - * "X.org" < 10400000 (new numbering scheme) - * - * For the old-versioning-scheme X servers we don't know - * exactly when second the bug started, but since bug 1 is - * present through 6.8.2 and bug 2 is present in 6.9.0 it seems - * safest to just blacklist all old-versioning-scheme X servers, - * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. - */ - if (strstr (ServerVendor (dpy), "X.Org") != NULL) { - if (VendorRelease (dpy) >= 60700000) { - if (VendorRelease (dpy) < 70000000) - display->buggy_repeat = TRUE; - - /* We know that gradients simply do not work in early Xorg servers */ - if (VendorRelease (dpy) < 70200000) - display->buggy_gradients = TRUE; - - /* And the extended repeat modes were not fixed until much later */ - display->buggy_pad_reflect = TRUE; - } else { - if (VendorRelease (dpy) < 10400000) - display->buggy_repeat = TRUE; - - /* Too many bugs in the early drivers */ - if (VendorRelease (dpy) < 10699000) - display->buggy_pad_reflect = TRUE; - } - } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { - if (VendorRelease (dpy) <= 40500000) - display->buggy_repeat = TRUE; - - display->buggy_gradients = TRUE; - display->buggy_pad_reflect = TRUE; - } - - /* gradients don't seem to work */ - display->buggy_gradients = TRUE; - - - /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ - /* If buggy_repeat_force == -1, then initialize. - * - set to -2, meaning "nothing was specified", and we trust the above detection. - * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off - * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on - */ - if (buggy_repeat_force == -1) { - const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT"); - - buggy_repeat_force = -2; - - if (flag && flag[0] == '0') - buggy_repeat_force = 0; - else if (flag && flag[0] == '1') - buggy_repeat_force = 1; - } - - if (buggy_repeat_force != -2) - display->buggy_repeat = (buggy_repeat_force == 1); - - display->next = _cairo_xlib_display_list; - _cairo_xlib_display_list = display; - - device = &display->base; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); - return device; -} - -void -_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - hook->prev = NULL; - hook->next = display->close_display_hooks; - if (hook->next != NULL) - hook->next->prev = hook; - display->close_display_hooks = hook; -} - -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - if (display->close_display_hooks == hook) - display->close_display_hooks = hook->next; - else if (hook->prev != NULL) - hook->prev->next = hook->next; - - if (hook->next != NULL) - hook->next->prev = hook->prev; - - hook->prev = NULL; - hook->next = NULL; -} - -void -_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - _cairo_xlib_remove_close_display_hook_internal (display, hook); -} - -cairo_status_t -_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, - cairo_xlib_notify_resource_func notify, - XID xid) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = RESOURCE; - job->func.resource.xid = xid; - job->func.resource.notify = notify; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - -cairo_status_t -_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, - cairo_xlib_notify_func notify, - void *data, - void (*destroy) (void *)) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = WORK; - job->func.work.data = data; - job->func.work.notify = notify; - job->func.work.destroy = destroy; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - -cairo_status_t -_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) -{ - cairo_status_t status; - - status = cairo_device_acquire (device); - if (status) - return status; - - *display = (cairo_xlib_display_t *) device; - _cairo_xlib_display_notify (*display); - return status; -} - -XRenderPictFormat * -_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, - cairo_format_t format) -{ - XRenderPictFormat *xrender_format; - -#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER - xrender_format = display->cached_xrender_formats[format]; - if (likely (xrender_format != NULL)) - return xrender_format; -#endif - - xrender_format = display->cached_xrender_formats[format]; - if (xrender_format == NULL) { - int pict_format; - - switch (format) { - case CAIRO_FORMAT_A1: - pict_format = PictStandardA1; break; - case CAIRO_FORMAT_A8: - pict_format = PictStandardA8; break; - case CAIRO_FORMAT_RGB24: - pict_format = PictStandardRGB24; break; - case CAIRO_FORMAT_RGB16_565: { - Visual *visual = NULL; - Screen *screen = DefaultScreenOfDisplay(display->display); - int j; - for (j = 0; j < screen->ndepths; j++) { - Depth *d = &screen->depths[j]; - if (d->depth == 16 && d->nvisuals && &d->visuals[0]) { - if (d->visuals[0].red_mask == 0xf800 && - d->visuals[0].green_mask == 0x7e0 && - d->visuals[0].blue_mask == 0x1f) - visual = &d->visuals[0]; - break; - } - } - if (!visual) - return NULL; - xrender_format = XRenderFindVisualFormat(display->display, visual); - break; - } - case CAIRO_FORMAT_INVALID: - default: - ASSERT_NOT_REACHED; - case CAIRO_FORMAT_ARGB32: - pict_format = PictStandardARGB32; break; - } - if (!xrender_format) - xrender_format = XRenderFindStandardFormat (display->display, - pict_format); - display->cached_xrender_formats[format] = xrender_format; - } - - return xrender_format; -} - -cairo_xlib_screen_t * -_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, - Screen *screen) -{ - cairo_xlib_screen_t *info; - - cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) { - if (info->screen == screen) { - if (display->screens.next != &info->link) - cairo_list_move (&info->link, &display->screens); - return info; - } - } - - return NULL; -} - -void -_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, - int *major, int *minor) -{ - *major = display->render_major; - *minor = display->render_minor; -} - -cairo_bool_t -_cairo_xlib_display_has_repeat (cairo_device_t *device) -{ - return ! ((cairo_xlib_display_t *) device)->buggy_repeat; -} - -cairo_bool_t -_cairo_xlib_display_has_reflect (cairo_device_t *device) -{ - return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect; -} - -cairo_bool_t -_cairo_xlib_display_has_gradients (cairo_device_t *device) -{ - return ! ((cairo_xlib_display_t *) device)->buggy_gradients; -} diff --git a/libs/cairo/cairo/src/cairo-xlib-private.h b/libs/cairo/cairo/src/cairo-xlib-private.h deleted file mode 100644 index e575b2704..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-private.h +++ /dev/null @@ -1,168 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XLIB_PRIVATE_H -#define CAIRO_XLIB_PRIVATE_H - -#include "cairo-xlib.h" -#include "cairo-xlib-xrender-private.h" - -#include "cairo-compiler-private.h" -#include "cairo-device-private.h" -#include "cairo-freelist-type-private.h" -#include "cairo-list-private.h" -#include "cairo-reference-count-private.h" -#include "cairo-types-private.h" - -typedef struct _cairo_xlib_display cairo_xlib_display_t; -typedef struct _cairo_xlib_screen cairo_xlib_screen_t; - -typedef struct _cairo_xlib_hook cairo_xlib_hook_t; -typedef struct _cairo_xlib_job cairo_xlib_job_t; -typedef void (*cairo_xlib_notify_func) (Display *, void *); -typedef void (*cairo_xlib_notify_resource_func) (Display *, XID); - -struct _cairo_xlib_hook { - cairo_xlib_hook_t *prev, *next; /* private */ - void (*func) (cairo_xlib_display_t *display, void *data); -}; - -/* size of color cube */ -#define CUBE_SIZE 6 -/* size of gray ramp */ -#define RAMP_SIZE 16 - -struct _cairo_xlib_display { - cairo_device_t base; - - cairo_xlib_display_t *next; - - Display *display; - cairo_list_t screens; - - int render_major; - int render_minor; - XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1]; - - cairo_xlib_job_t *workqueue; - cairo_freelist_t wq_freelist; - - cairo_xlib_hook_t *close_display_hooks; - unsigned int buggy_gradients :1; - unsigned int buggy_pad_reflect :1; - unsigned int buggy_repeat :1; - unsigned int closed :1; -}; - -typedef struct _cairo_xlib_visual_info { - cairo_list_t link; - VisualID visualid; - struct { uint8_t a, r, g, b; } colors[256]; - uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE]; - uint8_t field8_to_cube[256]; - int8_t dither8_to_cube[256]; - uint8_t gray8_to_pseudocolor[256]; -} cairo_xlib_visual_info_t; - -struct _cairo_xlib_screen { - cairo_list_t link; - - cairo_device_t *device; - Screen *screen; - - cairo_bool_t has_font_options; - cairo_font_options_t font_options; - - GC gc[4]; - cairo_atomic_int_t gc_depths; /* 4 x uint8_t */ - - cairo_list_t visuals; -}; - -cairo_private cairo_device_t * -_cairo_xlib_device_create (Display *display); - -cairo_private cairo_xlib_screen_t * -_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, - Screen *screen); - -cairo_private void -_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); - -cairo_private void -_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); - -cairo_private cairo_status_t -_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, - cairo_xlib_notify_func notify, - void *data, - void (*destroy)(void *)); -cairo_private cairo_status_t -_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, - cairo_xlib_notify_resource_func notify, - XID resource); -cairo_private cairo_status_t -_cairo_xlib_display_acquire (cairo_device_t *device, - cairo_xlib_display_t **display); - -cairo_private void -_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, - int *major, int *minor); - -cairo_private cairo_bool_t -_cairo_xlib_display_has_repeat (cairo_device_t *device); - -cairo_private cairo_bool_t -_cairo_xlib_display_has_reflect (cairo_device_t *device); - -cairo_private cairo_bool_t -_cairo_xlib_display_has_gradients (cairo_device_t *device); - -cairo_private XRenderPictFormat * -_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, - cairo_format_t format); - -cairo_private cairo_status_t -_cairo_xlib_screen_get (Display *dpy, - Screen *screen, - cairo_xlib_screen_t **out); - -cairo_private void -_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info); - -cairo_private void -_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info); - -cairo_private GC -_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - int depth, - Drawable drawable); - -cairo_private void -_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - int depth, - GC gc); - -cairo_private cairo_font_options_t * -_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info); - -cairo_private cairo_status_t -_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - Visual *visual, - cairo_xlib_visual_info_t **out); - -cairo_private cairo_status_t -_cairo_xlib_visual_info_create (Display *dpy, - int screen, - VisualID visualid, - cairo_xlib_visual_info_t **out); - -cairo_private void -_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info); - -#endif /* CAIRO_XLIB_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-xlib-screen.c b/libs/cairo/cairo/src/cairo-xlib-screen.c deleted file mode 100644 index 9663ddb7b..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-screen.c +++ /dev/null @@ -1,439 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Partially based on code from xftdpy.c, original code licensed under: - * - * Copyright © 2000 Keith Packard - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Keith Packard makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "cairoint.h" - -#include "cairo-xlib-private.h" -#include "cairo-xlib-xrender-private.h" - -#include "cairo-xlib-surface-private.h" -#include "cairo-error-private.h" - -#include "cairo-fontconfig-private.h" - -static int -parse_boolean (const char *v) -{ - char c0, c1; - - c0 = *v; - if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') - return 1; - if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') - return 0; - if (c0 == 'o') - { - c1 = v[1]; - if (c1 == 'n' || c1 == 'N') - return 1; - if (c1 == 'f' || c1 == 'F') - return 0; - } - - return -1; -} - -static cairo_bool_t -get_boolean_default (Display *dpy, - const char *option, - cairo_bool_t *value) -{ - char *v; - int i; - - v = XGetDefault (dpy, "Xft", option); - if (v) { - i = parse_boolean (v); - if (i >= 0) { - *value = i; - return TRUE; - } - } - - return FALSE; -} - -static cairo_bool_t -get_integer_default (Display *dpy, - const char *option, - int *value) -{ - char *v, *e; - - v = XGetDefault (dpy, "Xft", option); - if (v) { -#if CAIRO_HAS_FC_FONT - if (FcNameConstant ((FcChar8 *) v, value)) - return TRUE; -#endif - - *value = strtol (v, &e, 0); - if (e != v) - return TRUE; - } - - return FALSE; -} - -static void -_cairo_xlib_init_screen_font_options (Display *dpy, - cairo_xlib_screen_t *info) -{ - cairo_bool_t xft_hinting; - cairo_bool_t xft_antialias; - int xft_hintstyle; - int xft_rgba; - int xft_lcdfilter; - cairo_antialias_t antialias; - cairo_subpixel_order_t subpixel_order; - cairo_lcd_filter_t lcd_filter; - cairo_hint_style_t hint_style; - - if (!get_boolean_default (dpy, "antialias", &xft_antialias)) - xft_antialias = TRUE; - - if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) { - /* -1 is an non-existant Fontconfig constant used to differentiate - * the case when no lcdfilter property is available. - */ - xft_lcdfilter = -1; - } - - if (!get_boolean_default (dpy, "hinting", &xft_hinting)) - xft_hinting = TRUE; - - if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle)) - xft_hintstyle = FC_HINT_FULL; - - if (!get_integer_default (dpy, "rgba", &xft_rgba)) - { - cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device; - - xft_rgba = FC_RGBA_UNKNOWN; - -#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6 - if (display->render_major > 0 || display->render_minor >= 6) { - int render_order = XRenderQuerySubpixelOrder (dpy, - XScreenNumberOfScreen (info->screen)); - - switch (render_order) { - default: - case SubPixelUnknown: - xft_rgba = FC_RGBA_UNKNOWN; - break; - case SubPixelHorizontalRGB: - xft_rgba = FC_RGBA_RGB; - break; - case SubPixelHorizontalBGR: - xft_rgba = FC_RGBA_BGR; - break; - case SubPixelVerticalRGB: - xft_rgba = FC_RGBA_VRGB; - break; - case SubPixelVerticalBGR: - xft_rgba = FC_RGBA_VBGR; - break; - case SubPixelNone: - xft_rgba = FC_RGBA_NONE; - break; - } - } -#endif - } - - if (xft_hinting) { - switch (xft_hintstyle) { - case FC_HINT_NONE: - hint_style = CAIRO_HINT_STYLE_NONE; - break; - case FC_HINT_SLIGHT: - hint_style = CAIRO_HINT_STYLE_SLIGHT; - break; - case FC_HINT_MEDIUM: - hint_style = CAIRO_HINT_STYLE_MEDIUM; - break; - case FC_HINT_FULL: - hint_style = CAIRO_HINT_STYLE_FULL; - break; - default: - hint_style = CAIRO_HINT_STYLE_DEFAULT; - } - } else { - hint_style = CAIRO_HINT_STYLE_NONE; - } - - switch (xft_rgba) { - case FC_RGBA_RGB: - subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; - break; - case FC_RGBA_BGR: - subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; - break; - case FC_RGBA_VRGB: - subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; - break; - case FC_RGBA_VBGR: - subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; - break; - case FC_RGBA_UNKNOWN: - case FC_RGBA_NONE: - default: - subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - } - - switch (xft_lcdfilter) { - case FC_LCD_NONE: - lcd_filter = CAIRO_LCD_FILTER_NONE; - break; - case FC_LCD_DEFAULT: - lcd_filter = CAIRO_LCD_FILTER_FIR5; - break; - case FC_LCD_LIGHT: - lcd_filter = CAIRO_LCD_FILTER_FIR3; - break; - case FC_LCD_LEGACY: - lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; - break; - default: - lcd_filter = CAIRO_LCD_FILTER_DEFAULT; - break; - } - - if (xft_antialias) { - if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) - antialias = CAIRO_ANTIALIAS_GRAY; - else - antialias = CAIRO_ANTIALIAS_SUBPIXEL; - } else { - antialias = CAIRO_ANTIALIAS_NONE; - } - - cairo_font_options_set_hint_style (&info->font_options, hint_style); - cairo_font_options_set_antialias (&info->font_options, antialias); - cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order); - _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter); - cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); -} - -void -_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info) -{ - Display *dpy; - int i; - - dpy = display->display; - - for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if ((info->gc_depths >> (8*i)) & 0xff) - XFreeGC (dpy, info->gc[i]); - } - info->gc_depths = 0; -} - -void -_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) -{ - while (! cairo_list_is_empty (&info->visuals)) { - _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, - cairo_xlib_visual_info_t, - link)); - } - - cairo_list_del (&info->link); - - free (info); -} - -cairo_status_t -_cairo_xlib_screen_get (Display *dpy, - Screen *screen, - cairo_xlib_screen_t **out) -{ - cairo_xlib_display_t *display; - cairo_device_t *device; - cairo_xlib_screen_t *info; - cairo_status_t status; - - device = _cairo_xlib_device_create (dpy); - status = device->status; - if (unlikely (status)) - goto CLEANUP_DEVICE; - - status = _cairo_xlib_display_acquire (device, &display); - if (unlikely (status)) - goto CLEANUP_DEVICE; - - info = _cairo_xlib_display_get_screen (display, screen); - if (info != NULL) { - *out = info; - goto CLEANUP_DISPLAY; - } - - info = malloc (sizeof (cairo_xlib_screen_t)); - if (unlikely (info == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_DISPLAY; - } - - info->device = device; - info->screen = screen; - info->has_font_options = FALSE; - info->gc_depths = 0; - memset (info->gc, 0, sizeof (info->gc)); - - cairo_list_init (&info->visuals); - cairo_list_add (&info->link, &display->screens); - - *out = info; - - CLEANUP_DISPLAY: - cairo_device_release (&display->base); - - CLEANUP_DEVICE: - cairo_device_destroy (device); - return status; -} - -GC -_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - int depth, - Drawable drawable) -{ - GC gc = NULL; - int i; - - for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (((info->gc_depths >> (8*i)) & 0xff) == depth) { - info->gc_depths &= ~(0xff << (8*i)); - gc = info->gc[i]; - break; - } - } - - if (gc == NULL) { - XGCValues gcv; - - gcv.graphics_exposures = False; - gcv.fill_style = FillTiled; - gc = XCreateGC (display->display, - drawable, - GCGraphicsExposures | GCFillStyle, &gcv); - } - - return gc; -} - -void -_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - int depth, - GC gc) -{ - int i; - - for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (((info->gc_depths >> (8*i)) & 0xff) == 0) - break; - } - - if (i == ARRAY_LENGTH (info->gc)) { - cairo_status_t status; - - /* perform random substitution to ensure fair caching over depths */ - i = rand () % ARRAY_LENGTH (info->gc); - status = - _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) XFreeGC, - info->gc[i], - NULL); - if (unlikely (status)) { - /* leak the server side resource... */ - XFree ((char *) info->gc[i]); - } - } - - info->gc[i] = gc; - info->gc_depths &= ~(0xff << (8*i)); - info->gc_depths |= depth << (8*i); -} - -cairo_status_t -_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, - Visual *v, - cairo_xlib_visual_info_t **out) -{ - cairo_xlib_visual_info_t *visual; - cairo_status_t status; - - cairo_list_foreach_entry (visual, - cairo_xlib_visual_info_t, - &info->visuals, - link) - { - if (visual->visualid == v->visualid) { - *out = visual; - return CAIRO_STATUS_SUCCESS; - } - } - - status = _cairo_xlib_visual_info_create (display->display, - XScreenNumberOfScreen (info->screen), - v->visualid, - &visual); - if (unlikely (status)) - return status; - - cairo_list_add (&visual->link, &info->visuals); - *out = visual; - return CAIRO_STATUS_SUCCESS; -} - -cairo_font_options_t * -_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) -{ - if (! info->has_font_options) { - _cairo_font_options_init_default (&info->font_options); - _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON); - - if (info->screen != NULL) { - cairo_xlib_display_t *display; - - if (! _cairo_xlib_display_acquire (info->device, &display)) { - _cairo_xlib_init_screen_font_options (display->display, - info); - cairo_device_release (&display->base); - } - } - - info->has_font_options = TRUE; - } - - return &info->font_options; -} diff --git a/libs/cairo/cairo/src/cairo-xlib-surface-private.h b/libs/cairo/cairo/src/cairo-xlib-surface-private.h deleted file mode 100644 index cd7e79d69..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-surface-private.h +++ /dev/null @@ -1,84 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XLIB_SURFACE_PRIVATE_H -#define CAIRO_XLIB_SURFACE_PRIVATE_H - -#include "cairo-xlib.h" -#include "cairo-xlib-private.h" -#include "cairo-xlib-xrender-private.h" - -#include "cairo-surface-private.h" - -typedef struct _cairo_xlib_surface cairo_xlib_surface_t; - -struct _cairo_xlib_surface { - cairo_surface_t base; - - cairo_xlib_screen_t *screen; - cairo_xlib_hook_t close_display_hook; - - Drawable drawable; - cairo_bool_t owns_pixmap; - Visual *visual; - - int use_pixmap; - - int render_major; - int render_minor; - - /* TRUE if the server has a bug with repeating pictures - * - * https://bugs.freedesktop.org/show_bug.cgi?id=3566 - * - * We can't test for this because it depends on whether the - * picture is in video memory or not. - * - * We also use this variable as a guard against a second - * independent bug with transformed repeating pictures: - * - * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html - * - * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so - * we can reuse the test for now. - */ - unsigned int buggy_gradients : 1; - unsigned int buggy_pad_reflect : 1; - unsigned int buggy_repeat : 1; -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1 -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1 -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1 - - int width; - int height; - int depth; - - Picture dst_picture, src_picture; - - unsigned int clip_dirty; - XRectangle embedded_clip_rects[8]; - XRectangle *clip_rects; - int num_clip_rects; - cairo_region_t *clip_region; - - XRenderPictFormat *xrender_format; - cairo_filter_t filter; - cairo_extend_t extend; - cairo_bool_t has_component_alpha; - int precision; - XTransform xtransform; - - uint32_t a_mask; - uint32_t r_mask; - uint32_t g_mask; - uint32_t b_mask; -}; - -enum { - CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC = 0x01, - CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02, - CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL = 0x03 -}; - -#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-xlib-surface.c b/libs/cairo/cairo/src/cairo-xlib-surface.c deleted file mode 100644 index 36b696eaa..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-surface.c +++ /dev/null @@ -1,4896 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Heed well the words of Owen Taylor: - * "Any patch that works around a render bug, or claims to, without a - * specific reference to the bug filed in bugzilla.freedesktop.org will - * never pass approval." - */ - -#include "cairoint.h" - -#include "cairo-xlib-private.h" -#include "cairo-xlib-surface-private.h" -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-scaled-font-private.h" -#include "cairo-surface-snapshot-private.h" -#include "cairo-surface-subsurface-private.h" -#include "cairo-region-private.h" -#include "cairo-xlib-xrender-private.h" - -#include /* for XDestroyImage */ -#include /* for access to XDisplay's innards */ - -#define XLIB_COORD_MAX 32767 - -#define DEBUG 0 - -#if DEBUG -#define UNSUPPORTED(reason) \ - fprintf (stderr, \ - "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ - __FUNCTION__, __LINE__, reason), \ - CAIRO_INT_STATUS_UNSUPPORTED -#else -#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED -#endif - -#if DEBUG -static void CAIRO_PRINTF_FORMAT (2, 3) -_x_bread_crumb (Display *dpy, - const char *fmt, - ...) -{ - xReq *req; - char buf[2048]; - unsigned int len, len_dwords; - va_list ap; - - va_start (ap, fmt); - len = vsnprintf (buf, sizeof (buf), fmt, ap); - va_end (ap); - - buf[len++] = '\0'; - while (len & 3) - buf[len++] = '\0'; - - LockDisplay (dpy); - GetEmptyReq (NoOperation, req); - - len_dwords = len >> 2; - SetReqLen (req, len_dwords, len_dwords); - Data (dpy, buf, len); - - UnlockDisplay (dpy); - SyncHandle (); -} -#define X_DEBUG(x) _x_bread_crumb x -#else -#define X_DEBUG(x) -#endif - -/** - * SECTION:cairo-xlib - * @Title: XLib Surfaces - * @Short_Description: X Window System rendering using XLib - * @See_Also: #cairo_surface_t - * - * The XLib surface is used to render cairo graphics to X Window System - * windows and pixmaps using the XLib library. - * - * Note that the XLib surface automatically takes advantage of X render extension - * if it is available. - */ - -/** - * CAIRO_HAS_XLIB_SURFACE: - * - * Defined if the Xlib surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/** - * SECTION:cairo-xlib-xrender - * @Title: XLib/XRender Backend - * @Short_Description: X Window System rendering using XLib and the X Render extension - * @See_Also: #cairo_surface_t - * - * The XLib surface is used to render cairo graphics to X Window System - * windows and pixmaps using the XLib and Xrender libraries. - * - * Note that the XLib surface automatically takes advantage of X Render extension - * if it is available. - */ - -/** - * CAIRO_HAS_XLIB_XRENDER_SURFACE: - * - * Defined if the XLib/XRender surface functions are available. - * This macro can be used to conditionally compile backend-specific code. - */ - -/* Xlib doesn't define a typedef, so define one ourselves */ -typedef int (*cairo_xlib_error_func_t) (Display *display, - XErrorEvent *event); - -static cairo_surface_t * -_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, - Drawable drawable, - Visual *visual, - XRenderPictFormat *xrender_format, - int width, - int height, - int depth); - -static cairo_bool_t -_cairo_surface_is_xlib (cairo_surface_t *surface); - -static cairo_bool_t -_native_byte_order_lsb (void); - -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -/* - * Instead of taking two round trips for each blending request, - * assume that if a particular drawable fails GetImage that it will - * fail for a "while"; use temporary pixmaps to avoid the errors - */ - -#define CAIRO_ASSUME_PIXMAP 20 - -static const XTransform identity = { { - { 1 << 16, 0x00000, 0x00000 }, - { 0x00000, 1 << 16, 0x00000 }, - { 0x00000, 0x00000, 1 << 16 }, -} }; - -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) - -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) - -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) - -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) - -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) - -#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) -#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) - -#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) - -#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ - ((op) <= CAIRO_OPERATOR_SATURATE || \ - (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ - (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) - -static Visual * -_visual_for_xrender_format(Screen *screen, - XRenderPictFormat *xrender_format) -{ - int d, v; - - /* XXX Consider searching through the list of known cairo_visual_t for - * the reverse mapping. - */ - - for (d = 0; d < screen->ndepths; d++) { - Depth *d_info = &screen->depths[d]; - - if (d_info->depth != xrender_format->depth) - continue; - - for (v = 0; v < d_info->nvisuals; v++) { - Visual *visual = &d_info->visuals[v]; - - switch (visual->class) { - case TrueColor: - if (xrender_format->type != PictTypeDirect) - continue; - break; - - case DirectColor: - /* Prefer TrueColor to DirectColor. - * (XRenderFindVisualFormat considers both TrueColor and DirectColor - * Visuals to match the same PictFormat.) - */ - continue; - - case StaticGray: - case GrayScale: - case StaticColor: - case PseudoColor: - if (xrender_format->type != PictTypeIndexed) - continue; - break; - } - - if (xrender_format == - XRenderFindVisualFormat (DisplayOfScreen(screen), visual)) - return visual; - } - } - - return NULL; -} - -static cairo_status_t -_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, - cairo_region_t *region) -{ - cairo_bool_t had_clip_rects = surface->clip_region != NULL; - - if (had_clip_rects == FALSE && region == NULL) - return CAIRO_STATUS_SUCCESS; - - if (surface->clip_region == region) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_equal (surface->clip_region, region)) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (surface->clip_rects != surface->embedded_clip_rects) { - free (surface->clip_rects); - surface->clip_rects = surface->embedded_clip_rects; - } - surface->num_clip_rects = 0; - - if (region != NULL) { - XRectangle *rects = NULL; - int n_rects, i; - - n_rects = cairo_region_num_rectangles (region); - if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - rects = surface->embedded_clip_rects; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - } - - surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_content_t -_xrender_format_to_content (XRenderPictFormat *xrender_format) -{ - cairo_bool_t xrender_format_has_alpha; - cairo_bool_t xrender_format_has_color; - - /* This only happens when using a non-Render server. Let's punt - * and say there's no alpha here. */ - if (xrender_format == NULL) - return CAIRO_CONTENT_COLOR; - - xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0); - xrender_format_has_color = (xrender_format->direct.redMask != 0 || - xrender_format->direct.greenMask != 0 || - xrender_format->direct.blueMask != 0); - - if (xrender_format_has_alpha) - if (xrender_format_has_color) - return CAIRO_CONTENT_COLOR_ALPHA; - else - return CAIRO_CONTENT_ALPHA; - else - return CAIRO_CONTENT_COLOR; -} - -static cairo_surface_t * -_cairo_xlib_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_xlib_surface_t *src = abstract_src; - XRenderPictFormat *xrender_format; - cairo_xlib_surface_t *surface; - cairo_xlib_display_t *display; - Pixmap pix; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; - - if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) - return NULL; - - if (_cairo_xlib_display_acquire (src->base.device, &display)) - return NULL; - - /* If we never found an XRenderFormat or if it isn't compatible - * with the content being requested, then we fallback to just - * constructing a cairo_format_t instead, (which will fairly - * arbitrarily pick a visual/depth for the similar surface. - */ - xrender_format = src->xrender_format; - if ((xrender_format != NULL && - _xrender_format_to_content (xrender_format) == content) || - (xrender_format = - _cairo_xlib_display_get_xrender_format (display, - _cairo_format_from_content (content)))) - { - Visual *visual; - - /* We've got a compatible XRenderFormat now, which means the - * similar surface will match the existing surface as closely in - * visual/depth etc. as possible. */ - pix = XCreatePixmap (display->display, src->drawable, - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); - - if (xrender_format == src->xrender_format) - visual = src->visual; - else - visual = _visual_for_xrender_format(src->screen->screen, - xrender_format); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (src->screen, pix, - visual, - xrender_format, - width, height, - xrender_format->depth); - } - else - { -#ifdef DEBUG_FORCE_FALLBACKS - Screen *screen = src->screen->screen; - int depth; - - /* No compatabile XRenderFormat, see if we can make an ordinary pixmap, - * so that we can still accelerate blits with XCopyArea(). */ - if (content != CAIRO_CONTENT_COLOR) { - cairo_device_release (&display->base); - return NULL; - } - - depth = DefaultDepthOfScreen (screen); - - pix = XCreatePixmap (display->display, RootWindowOfScreen (screen), - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - depth); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (src->screen, pix, - DefaultVisualOfScreen (screen), - NULL, - width, height, depth); -#else - /* No compatabile XRenderFormat, just say no. */ - cairo_device_release (&display->base); - return NULL; -#endif - } - - if (unlikely (surface->base.status)) { - XFreePixmap (display->display, pix); - cairo_device_release (&display->base); - return &surface->base; - } - - surface->owns_pixmap = TRUE; - - cairo_device_release (&display->base); - - return &surface->base; -} - -static cairo_status_t -_cairo_xlib_surface_finish (void *abstract_surface) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_xlib_display_t *display; - - X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - if (surface->owns_pixmap) { - cairo_status_t status2; - - if (surface->dst_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->dst_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - if (surface->src_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->src_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - status2 = _cairo_xlib_display_queue_resource (display, - (cairo_xlib_notify_resource_func) XFreePixmap, - surface->drawable); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } else { - if (surface->dst_picture != None) - XRenderFreePicture (display->display, surface->dst_picture); - - if (surface->src_picture != None) - XRenderFreePicture (display->display, surface->src_picture); - } - - if (surface->clip_rects != surface->embedded_clip_rects) - free (surface->clip_rects); - - if (display->display != NULL) - _cairo_xlib_remove_close_display_hook (display, - &surface->close_display_hook); - - cairo_device_release (&display->base); - - cairo_region_destroy (surface->clip_region); - - return status; -} - -static cairo_status_t -_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - GC *gc) -{ - *gc = _cairo_xlib_screen_get_gc (display, - surface->screen, - surface->depth, - surface->drawable); - if (unlikely (*gc == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - GC gc) -{ - _cairo_xlib_screen_put_gc (display, - surface->screen, - surface->depth, - gc); -} - -static int -_noop_error_handler (Display *display, - XErrorEvent *event) -{ - return False; /* return value is ignored */ -} - -static void -_swap_ximage_2bytes (XImage *ximage) -{ - int i, j; - char *line = ximage->data; - - for (j = ximage->height; j; j--) { - uint16_t *p = (uint16_t *) line; - for (i = ximage->width; i; i--) { - *p = bswap_16 (*p); - p++; - } - - line += ximage->bytes_per_line; - } -} - -static void -_swap_ximage_3bytes (XImage *ximage) -{ - int i, j; - char *line = ximage->data; - - for (j = ximage->height; j; j--) { - uint8_t *p = (uint8_t *) line; - for (i = ximage->width; i; i--) { - uint8_t tmp; - tmp = p[2]; - p[2] = p[0]; - p[0] = tmp; - p += 3; - } - - line += ximage->bytes_per_line; - } -} - -static void -_swap_ximage_4bytes (XImage *ximage) -{ - int i, j; - char *line = ximage->data; - - for (j = ximage->height; j; j--) { - uint32_t *p = (uint32_t *) line; - for (i = ximage->width; i; i--) { - *p = bswap_32 (*p); - p++; - } - - line += ximage->bytes_per_line; - } -} - -static void -_swap_ximage_nibbles (XImage *ximage) -{ - int i, j; - char *line = ximage->data; - - for (j = ximage->height; j; j--) { - uint8_t *p = (uint8_t *) line; - for (i = (ximage->width + 1) / 2; i; i--) { - *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf); - p++; - } - - line += ximage->bytes_per_line; - } -} - -static void -_swap_ximage_bits (XImage *ximage) -{ - int i, j; - char *line = ximage->data; - int unit = ximage->bitmap_unit; - int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8; - - for (j = ximage->height; j; j--) { - char *p = line; - - for (i = line_bytes; i; i--) { - char b = *p; - b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); - b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); - b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); - *p = b; - - p++; - } - - line += ximage->bytes_per_line; - } -} - -static void -_swap_ximage_to_native (XImage *ximage) -{ - int unit_bytes = 0; - int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; - - if (ximage->bits_per_pixel == 1 && - ximage->bitmap_bit_order != native_byte_order) - { - _swap_ximage_bits (ximage); - if (ximage->bitmap_bit_order == ximage->byte_order) - return; - } - - if (ximage->byte_order == native_byte_order) - return; - - switch (ximage->bits_per_pixel) { - case 1: - unit_bytes = ximage->bitmap_unit / 8; - break; - case 4: - _swap_ximage_nibbles (ximage); - /* fall-through */ - case 8: - case 16: - case 20: - case 24: - case 28: - case 30: - case 32: - unit_bytes = (ximage->bits_per_pixel + 7) / 8; - break; - default: - /* This could be hit on some rare but possible cases. */ - ASSERT_NOT_REACHED; - } - - switch (unit_bytes) { - case 1: - break; - case 2: - _swap_ximage_2bytes (ximage); - break; - case 3: - _swap_ximage_3bytes (ximage); - break; - case 4: - _swap_ximage_4bytes (ximage); - break; - default: - ASSERT_NOT_REACHED; - } -} - - -/* Given a mask, (with a single sequence of contiguous 1 bits), return - * the number of 1 bits in 'width' and the number of 0 bits to its - * right in 'shift'. */ -static void -_characterize_field (uint32_t mask, int *width, int *shift) -{ - *width = _cairo_popcount (mask); - /* The final '& 31' is to force a 0 mask to result in 0 shift. */ - *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; -} - - -/* Convert a field of 'width' bits to 'new_width' bits with correct - * rounding. */ -static inline uint32_t -_resize_field (uint32_t field, int width, int new_width) -{ - if (width == 0) - return 0; - - if (width >= new_width) { - return field >> (width - new_width); - } else { - uint32_t result = field << (new_width - width); - - while (width < new_width) { - result |= result >> width; - width <<= 1; - } - return result; - } -} - -static inline uint32_t -_adjust_field (uint32_t field, int adjustment) -{ - return MIN (255, MAX(0, (int)field + adjustment)); -} - -/* Given a shifted field value, (described by 'width' and 'shift), - * resize it 8-bits and return that value. - * - * Note that the original field value must not have any non-field bits - * set. - */ -static inline uint32_t -_field_to_8 (uint32_t field, int width, int shift) -{ - return _resize_field (field >> shift, width, 8); -} - -static inline uint32_t -_field_to_8_undither (uint32_t field, int width, int shift, - int dither_adjustment) -{ - return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width); -} - -/* Given an 8-bit value, convert it to a field of 'width', shift it up - * to 'shift, and return it. */ -static inline uint32_t -_field_from_8 (uint32_t field, int width, int shift) -{ - return _resize_field (field, 8, width) << shift; -} - -static inline uint32_t -_field_from_8_dither (uint32_t field, int width, int shift, - int8_t dither_adjustment) -{ - return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift); -} - -static inline uint32_t -_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info, - uint32_t r, uint32_t g, uint32_t b, - int8_t dither_adjustment) -{ - if (r == g && g == b) { - dither_adjustment /= RAMP_SIZE; - return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)]; - } else { - dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128]; - return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]] - [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]] - [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]]; - } -} - -static inline uint32_t -_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, - uint32_t pixel) -{ - uint32_t r, g, b; - pixel &= 0xff; - r = visual_info->colors[pixel].r; - g = visual_info->colors[pixel].g; - b = visual_info->colors[pixel].b; - return (r << 16) | - (g << 8) | - (b ); -} - - -/* should range from -128 to 127 */ -#define X 16 -static const int8_t dither_pattern[4][4] = { - {-8*X, +0*X, -6*X, +2*X}, - {+4*X, -4*X, +6*X, -2*X}, - {-5*X, +4*X, -7*X, +1*X}, - {+7*X, -1*X, +5*X, -3*X} -}; -#undef X - - -static cairo_status_t -_get_image_surface (cairo_xlib_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect) -{ - cairo_int_status_t status; - cairo_image_surface_t *image = NULL; - XImage *ximage; - cairo_rectangle_int_t extents; - pixman_format_code_t pixman_format; - cairo_format_masks_t xlib_masks; - cairo_xlib_display_t *display; - - extents.x = 0; - extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; - - if (interest_rect) { - if (! _cairo_rectangle_intersect (&extents, interest_rect)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (status) - return status; - - if (image_rect) - *image_rect = extents; - - /* XXX: This should try to use the XShm extension if available */ - - if (surface->use_pixmap == 0) - { - cairo_xlib_error_func_t old_handler; - - old_handler = XSetErrorHandler (_noop_error_handler); - - ximage = XGetImage (display->display, - surface->drawable, - extents.x, extents.y, - extents.width, extents.height, - AllPlanes, ZPixmap); - - XSetErrorHandler (old_handler); - - /* If we get an error, the surface must have been a window, - * so retry with the safe code path. - */ - if (!ximage) - surface->use_pixmap = CAIRO_ASSUME_PIXMAP; - } - else - { - surface->use_pixmap--; - ximage = NULL; - } - - if (ximage == NULL) { - /* XGetImage from a window is dangerous because it can - * produce errors if the window is unmapped or partially - * outside the screen. We could check for errors and - * retry, but to keep things simple, we just create a - * temporary pixmap - */ - Pixmap pixmap; - GC gc; - - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - goto BAIL; - - pixmap = XCreatePixmap (display->display, - surface->drawable, - extents.width <= 0 ? 1 : extents.width, - extents.height <= 0 ? 1 : extents.height, - surface->depth); - if (pixmap) { - XCopyArea (display->display, surface->drawable, pixmap, gc, - extents.x, extents.y, - extents.width, extents.height, - 0, 0); - - ximage = XGetImage (display->display, - pixmap, - 0, 0, - extents.width <= 0 ? 1 : extents.width, - extents.height <= 0 ? 1 : extents.height, - AllPlanes, ZPixmap); - - XFreePixmap (display->display, pixmap); - } - - _cairo_xlib_surface_put_gc (display, surface, gc); - - if (ximage == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - _swap_ximage_to_native (ximage); - - xlib_masks.bpp = ximage->bits_per_pixel; - xlib_masks.alpha_mask = surface->a_mask; - xlib_masks.red_mask = surface->r_mask; - xlib_masks.green_mask = surface->g_mask; - xlib_masks.blue_mask = surface->b_mask; - - /* We can't use pixman to simply write to image if: - * (a) the pixels are not appropriately aligned, - * (b) pixman does not the pixel format, or - * (c) if the image is palettized and we need to convert. - */ - if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && - _pixman_format_from_masks (&xlib_masks, &pixman_format) && - (surface->visual == NULL || surface->visual->class == TrueColor)) - { - image = (cairo_image_surface_t*) - _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, - pixman_format, - ximage->width, - ximage->height, - ximage->bytes_per_line); - status = image->base.status; - if (unlikely (status)) - goto BAIL; - - /* Let the surface take ownership of the data */ - _cairo_image_surface_assume_ownership_of_data (image); - ximage->data = NULL; - } else { - /* The visual we are dealing with is not supported by the - * standard pixman formats. So we must first convert the data - * to a supported format. */ - - cairo_format_t format; - unsigned char *data; - uint32_t *row; - uint32_t in_pixel, out_pixel; - unsigned int rowstride; - uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0; - int a_width=0, r_width=0, g_width=0, b_width=0; - int a_shift=0, r_shift=0, g_shift=0, b_shift=0; - int x, y, x0, y0, x_off, y_off; - cairo_xlib_visual_info_t *visual_info = NULL; - - if (surface->visual == NULL || surface->visual->class == TrueColor) { - cairo_bool_t has_alpha; - cairo_bool_t has_color; - - has_alpha = surface->a_mask; - has_color = (surface->r_mask || - surface->g_mask || - surface->b_mask); - - if (has_color) { - if (has_alpha) { - format = CAIRO_FORMAT_ARGB32; - } else { - format = CAIRO_FORMAT_RGB24; - } - } else { - /* XXX: Using CAIRO_FORMAT_A8 here would be more - * efficient, but would require slightly different code in - * the image conversion to put the alpha channel values - * into the right place. */ - format = CAIRO_FORMAT_ARGB32; - } - - a_mask = surface->a_mask; - r_mask = surface->r_mask; - g_mask = surface->g_mask; - b_mask = surface->b_mask; - - _characterize_field (a_mask, &a_width, &a_shift); - _characterize_field (r_mask, &r_width, &r_shift); - _characterize_field (g_mask, &g_width, &g_shift); - _characterize_field (b_mask, &b_width, &b_shift); - - } else { - format = CAIRO_FORMAT_RGB24; - - status = _cairo_xlib_screen_get_visual_info (display, - surface->screen, - surface->visual, - &visual_info); - if (unlikely (status)) - goto BAIL; - } - - image = (cairo_image_surface_t *) cairo_image_surface_create - (format, ximage->width, ximage->height); - status = image->base.status; - if (unlikely (status)) - goto BAIL; - - data = cairo_image_surface_get_data (&image->base); - rowstride = cairo_image_surface_get_stride (&image->base) >> 2; - row = (uint32_t *) data; - x0 = extents.x + surface->base.device_transform.x0; - y0 = extents.y + surface->base.device_transform.y0; - for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); - y < ximage->height; - y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { - const int8_t *dither_row = dither_pattern[y_off]; - for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); - x < ximage->width; - x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { - int dither_adjustment = dither_row[x_off]; - - in_pixel = XGetPixel (ximage, x, y); - if (visual_info == NULL) { - out_pixel = ( - _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | - _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | - _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 | - _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment)); - } else { - /* Undithering pseudocolor does not look better */ - out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel); - } - row[x] = out_pixel; - } - row += rowstride; - } - cairo_surface_mark_dirty (&image->base); - } - - BAIL: - if (ximage) - XDestroyImage (ximage); - - cairo_device_release (&display->base); - - if (unlikely (status)) { - if (image) { - cairo_surface_destroy (&image->base); - image = NULL; - } - } - *image_out = image; - return status; -} - -static void -_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (!surface->src_picture) { - XRenderPictureAttributes pa; - int mask = 0; - - pa.subwindow_mode = IncludeInferiors; - mask |= CPSubwindowMode; - - surface->src_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - mask, &pa); - } -} - -static void -_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (surface->clip_region != NULL) { - XRenderSetPictureClipRectangles (display->display, surface->dst_picture, - 0, 0, - surface->clip_rects, - surface->num_clip_rects); - } else { - XRenderPictureAttributes pa; - pa.clip_mask = None; - XRenderChangePicture (display->display, surface->dst_picture, - CPClipMask, &pa); - } - - surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; -} - -static void -_cairo_xlib_surface_set_precision (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - cairo_antialias_t antialias) -{ - int precision; - - switch (antialias) { - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_GRAY: - precision = PolyModeImprecise; - break; - case CAIRO_ANTIALIAS_NONE: - case CAIRO_ANTIALIAS_SUBPIXEL: - precision = PolyModePrecise; - break; - } - - /* NVidia's driver version 190.42 is much slower when using PolyModeInprecise */ - precision = PolyModePrecise; - - if (surface->precision != precision) { - XRenderPictureAttributes pa; - - pa.poly_mode = precision; - XRenderChangePicture (display->display, surface->dst_picture, - CPPolyMode, &pa); - - surface->precision = precision; - } -} - -static void -_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (!surface->dst_picture) { - surface->dst_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - 0, NULL); - } - - if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) - _cairo_xlib_surface_set_picture_clip_rects (display, surface); -} - -static cairo_status_t -_draw_image_surface (cairo_xlib_surface_t *surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int dst_x, - int dst_y) -{ - cairo_xlib_display_t *display; - XImage ximage; - cairo_format_masks_t image_masks; - int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; - pixman_image_t *pixman_image = NULL; - cairo_status_t status; - cairo_bool_t own_data; - GC gc; - - ximage.width = image->width; - ximage.height = image->height; - ximage.format = ZPixmap; - ximage.byte_order = native_byte_order; - ximage.bitmap_unit = 32; /* always for libpixman */ - ximage.bitmap_bit_order = native_byte_order; - ximage.bitmap_pad = 32; /* always for libpixman */ - ximage.depth = surface->depth; - ximage.red_mask = surface->r_mask; - ximage.green_mask = surface->g_mask; - ximage.blue_mask = surface->b_mask; - ximage.xoffset = 0; - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) - { - pixman_format_code_t intermediate_format; - int ret; - - image_masks.alpha_mask = surface->a_mask; - image_masks.red_mask = surface->r_mask; - image_masks.green_mask = surface->g_mask; - image_masks.blue_mask = surface->b_mask; - image_masks.bpp = surface->depth; - ret = _pixman_format_from_masks (&image_masks, &intermediate_format); - assert (ret); - - own_data = FALSE; - - pixman_image = pixman_image_create_bits (intermediate_format, - image->width, - image->height, - NULL, - 0); - if (pixman_image == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - image->pixman_image, - NULL, - pixman_image, - 0, 0, - 0, 0, - 0, 0, - image->width, image->height); - - ximage.bits_per_pixel = image_masks.bpp; - ximage.data = (char *) pixman_image_get_data (pixman_image); - ximage.bytes_per_line = pixman_image_get_stride (pixman_image); - - ret = XInitImage (&ximage); - assert (ret != 0); - } - else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && - (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && - (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && - (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) - { - int ret; - - ximage.bits_per_pixel = image_masks.bpp; - ximage.bytes_per_line = image->stride; - ximage.data = (char *)image->data; - own_data = FALSE; - - ret = XInitImage (&ximage); - assert (ret != 0); - } - else - { - unsigned int stride, rowstride; - int x, y, x0, y0, x_off, y_off; - uint32_t in_pixel, out_pixel, *row; - int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0; - int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0; - int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0; - int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0; - cairo_xlib_visual_info_t *visual_info = NULL; - cairo_bool_t true_color; - int ret; - - if (surface->depth > 16) - ximage.bits_per_pixel = 32; - else if (surface->depth > 8) - ximage.bits_per_pixel = 16; - else if (surface->depth > 1) - ximage.bits_per_pixel = 8; - else - ximage.bits_per_pixel = 1; - stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, - ximage.bits_per_pixel); - ximage.bytes_per_line = stride; - ximage.data = _cairo_malloc_ab (stride, ximage.height); - if (unlikely (ximage.data == NULL)) { - own_data = FALSE; - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - own_data = TRUE; - - ret = XInitImage (&ximage); - assert (ret != 0); - - _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift); - _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift); - _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift); - _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift); - - true_color = surface->visual == NULL || - surface->visual->class == TrueColor; - if (true_color) { - _characterize_field (surface->a_mask, &o_a_width, &o_a_shift); - _characterize_field (surface->r_mask, &o_r_width, &o_r_shift); - _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); - _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); - } else { - status = _cairo_xlib_screen_get_visual_info (display, - surface->screen, - surface->visual, - &visual_info); - if (unlikely (status)) - goto BAIL; - } - - rowstride = image->stride >> 2; - row = (uint32_t *) image->data; - x0 = dst_x + surface->base.device_transform.x0; - y0 = dst_y + surface->base.device_transform.y0; - for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); - y < ximage.height; - y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) - { - const int8_t *dither_row = dither_pattern[y_off]; - - for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); - x < ximage.width; - x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) - { - int dither_adjustment = dither_row[x_off]; - int a, r, g, b; - - if (image_masks.bpp == 1) - in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); - else if (image_masks.bpp <= 8) - in_pixel = ((uint8_t*)row)[x]; - else if (image_masks.bpp <= 16) - in_pixel = ((uint16_t*)row)[x]; - else if (image_masks.bpp <= 24) -#ifdef WORDS_BIGENDIAN - in_pixel = ((uint8_t*)row)[3 * x] << 16 | - ((uint8_t*)row)[3 * x + 1] << 8 | - ((uint8_t*)row)[3 * x + 2]; -#else - in_pixel = ((uint8_t*)row)[3 * x] | - ((uint8_t*)row)[3 * x + 1] << 8 | - ((uint8_t*)row)[3 * x + 2] << 16; -#endif - else - in_pixel = row[x]; - - /* If the incoming image has no alpha channel, then the input - * is opaque and the output should have the maximum alpha value. - * For all other channels, their absence implies 0. - */ - if (image_masks.alpha_mask == 0x0) - a = 0xff; - else - a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift); - r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift); - g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift); - b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift); - - if (true_color) { - out_pixel = _field_from_8 (a, o_a_width, o_a_shift) | - _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) | - _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) | - _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment); - } else { - out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment); - } - - XPutPixel (&ximage, x, y, out_pixel); - } - - row += rowstride; - } - } - - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - goto BAIL; - - XPutImage (display->display, surface->drawable, gc, - &ximage, src_x, src_y, dst_x, dst_y, - width, height); - - _cairo_xlib_surface_put_gc (display, surface, gc); - - BAIL: - - cairo_device_release (&display->base); - - if (own_data) - free (ximage.data); - if (pixman_image) - pixman_image_unref (pixman_image); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, NULL, &image, NULL); - if (unlikely (status)) - return status; - - *image_out = image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_xlib_surface_snapshot (void *abstract_surface) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, NULL, &image, NULL); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - return &image->base; -} - -static void -_cairo_xlib_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (unlikely (status)) - return status; - - *image_out = image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _draw_image_surface (surface, image, - 0, 0, image->width, image->height, - image_rect->x, image_rect->y); - status = _cairo_surface_set_error (&surface->base, status); - - cairo_surface_destroy (&image->base); -} - -/* - * Return whether two xlib surfaces share the same - * screen. Both core and Render drawing require this - * when using multiple drawables in an operation. - */ -static inline cairo_bool_t -_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, - cairo_xlib_surface_t *src) -{ - return dst->screen == src->screen; -} - -static cairo_status_t -_cairo_xlib_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_surface_t *clone; - cairo_status_t status; - - if (src->backend == surface->base.backend ) { - cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; - - if (_cairo_xlib_surface_same_screen (surface, xlib_src)) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return UNSUPPORTED ("roi too large for xlib"); - - clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (surface, - image_src->base.content, - width, height); - if (clone == NULL) - return UNSUPPORTED ("unhandled image format, no similar surface"); - - if (unlikely (clone->base.status)) - return clone->base.status; - - status = _draw_image_surface (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (unlikely (status)) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_surface_t * -_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - /* This function's only responsibility is to create a proper surface - * for when XRender is not available. The proper surface is a xlib - * surface (as opposed to image surface which is what create_similar - * returns in those cases) and the size of the dithering pattern, not - * 1x1. This surface can then be used in - * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid" - * fills using core protocol */ - - cairo_xlib_surface_t *other = abstract_surface; - cairo_image_surface_t *image; - cairo_xlib_surface_t *surface = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_display_t *display; - - int width = ARRAY_LENGTH (dither_pattern[0]); - int height = ARRAY_LENGTH (dither_pattern); - - Pixmap pixmap = None; - - if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) - return NULL; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (_cairo_color_get_content (&solid_pattern->color), - width, height); - status = image->base.status; - if (unlikely (status)) - goto BAIL; - - status = _cairo_xlib_display_acquire (other->base.device, &display); - if (unlikely (status)) - goto BAIL; - - pixmap = XCreatePixmap (display->display, - other->drawable, - width, height, - other->depth); - cairo_device_release (&display->base); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->screen, - pixmap, - other->visual, - other->xrender_format, - width, height, - other->depth); - status = surface->base.status; - if (unlikely (status)) - goto BAIL; - - status = _cairo_surface_paint (&image->base, - CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, - NULL); - if (unlikely (status)) - goto BAIL; - - status = _draw_image_surface (surface, image, - 0, 0, - width, height, - 0, 0); - if (unlikely (status)) - goto BAIL; - - BAIL: - cairo_surface_destroy (&image->base); - - if (status) { - if (pixmap != None) { - if (!_cairo_xlib_display_acquire (other->base.device, &display)) { - XFreePixmap (display->display, pixmap); - cairo_device_release (&display->base); - } - } - cairo_surface_destroy (&surface->base); - - return _cairo_surface_create_in_error (status); - } - - surface->owns_pixmap = TRUE; - return &surface->base; -} - -static cairo_bool_t -_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - cairo_xlib_surface_t *other = abstract_surface; - return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); -} - -static cairo_status_t -_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - const cairo_matrix_t *matrix, - double xc, - double yc) -{ - XTransform xtransform; - - /* Casting between pixman_transform_t and XTransform is safe because - * they happen to be the exact same type. - */ - _cairo_matrix_to_pixman_matrix (matrix, - (pixman_transform_t *) &xtransform, - xc, yc); - - if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) - return CAIRO_STATUS_SUCCESS; - - if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - return UNSUPPORTED ("XRender does not support picture transforms"); - - XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform); - surface->xtransform = xtransform; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - cairo_filter_t filter) -{ - const char *render_filter; - - if (surface->filter == filter) - return CAIRO_STATUS_SUCCESS; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; - - return UNSUPPORTED ("XRender does not support filter"); - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = FilterFast; - break; - case CAIRO_FILTER_GOOD: - render_filter = FilterGood; - break; - case CAIRO_FILTER_BEST: - render_filter = FilterBest; - break; - case CAIRO_FILTER_NEAREST: - render_filter = FilterNearest; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = FilterBilinear; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - render_filter = FilterBest; - break; - } - - XRenderSetPictureFilter (display->display, surface->src_picture, - (char *) render_filter, NULL, 0); - surface->filter = filter; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, - cairo_extend_t extend, - unsigned long *mask, - XRenderPictureAttributes *pa) -{ - int repeat; - - if (surface->extend == extend) - return CAIRO_STATUS_SUCCESS; - - switch (extend) { - case CAIRO_EXTEND_NONE: - repeat = RepeatNone; - break; - case CAIRO_EXTEND_REPEAT: - repeat = RepeatNormal; - break; - case CAIRO_EXTEND_REFLECT: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy reflect"); - - repeat = RepeatReflect; - break; - case CAIRO_EXTEND_PAD: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy pad"); - - repeat = RepeatPad; - break; - default: - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *mask |= CPRepeat; - pa->repeat = repeat; - - surface->extend = extend; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, - cairo_bool_t ca, - unsigned long *mask, - XRenderPictureAttributes *pa) -{ - if (surface->has_component_alpha == ca) - return CAIRO_STATUS_SUCCESS; - - *mask |= CPComponentAlpha; - pa->component_alpha = ca; - - surface->has_component_alpha = ca; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - const cairo_surface_attributes_t *attributes, - double xc, - double yc) -{ - cairo_int_status_t status; - XRenderPictureAttributes pa; - unsigned long mask = 0; - - _cairo_xlib_surface_ensure_src_picture (display, surface); - - status = _cairo_xlib_surface_set_matrix (display, surface, - &attributes->matrix, xc, yc); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_set_repeat (surface, attributes->extend, - &mask, &pa); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_set_component_alpha (surface, - attributes->has_component_alpha, - &mask, &pa); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_set_filter (display, surface, attributes->filter); - if (unlikely (status)) - return status; - - if (mask) - XRenderChangePicture (display->display, surface->src_picture, mask, &pa); - - return CAIRO_STATUS_SUCCESS; -} - -/* Checks whether we can can directly draw from src to dst with - * the core protocol: either with CopyArea or using src as a - * a tile in a GC. - */ -static cairo_bool_t -_surfaces_compatible (cairo_xlib_surface_t *dst, - cairo_xlib_surface_t *src) -{ - /* same screen */ - if (! _cairo_xlib_surface_same_screen (dst, src)) - return FALSE; - - /* same depth (for core) */ - if (src->depth != dst->depth) - return FALSE; - - /* if Render is supported, match picture formats */ - if (src->xrender_format != dst->xrender_format) - return FALSE; - else if (src->xrender_format != NULL) - return TRUE; - - /* Without Render, match visuals instead */ - if (src->visual == dst->visual) - return TRUE; - - return FALSE; -} - -static cairo_bool_t -_surface_has_alpha (cairo_xlib_surface_t *surface) -{ - if (surface->xrender_format) { - if (surface->xrender_format->type == PictTypeDirect && - surface->xrender_format->direct.alphaMask != 0) - return TRUE; - else - return FALSE; - } else { - /* In the no-render case, we never have alpha */ - return FALSE; - } -} - -/* Returns true if the given operator and alpha combination requires alpha - * compositing to complete on source and destination surfaces with the same - * format. i.e. if a simple bitwise copy is not appropriate. - */ -static cairo_bool_t -_operator_needs_alpha_composite (cairo_operator_t op, - cairo_bool_t surfaces_have_alpha) -{ - if (op == CAIRO_OPERATOR_SOURCE) - return FALSE; - - if (op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_IN || - op == CAIRO_OPERATOR_ATOP) - return surfaces_have_alpha; - - return TRUE; -} - -/* There is a bug in most older X servers with compositing using a - * untransformed repeating source pattern when the source is in off-screen - * video memory, and another with repeated transformed images using a - * general transform matrix. When these bugs could be triggered, we need a - * fallback: in the common case where we have no transformation and the - * source and destination have the same format/visual, we can do the - * operation using the core protocol for the first bug, otherwise, we need - * a software fallback. - * - * We can also often optimize a compositing operation by calling XCopyArea - * for some common cases where there is no alpha compositing to be done. - * We figure that out here as well. - */ -typedef enum { - DO_RENDER, /* use render */ - DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ - DO_XTILE, /* core protocol XSetTile optimization/fallback */ - DO_UNSUPPORTED /* software fallback */ -} composite_operation_t; - -/* Initial check for the render bugs; we need to recheck for the - * offscreen-memory bug after we turn patterns into surfaces, since that - * may introduce a repeating pattern for gradient patterns. We don't need - * to check for the repeat+transform bug because gradient surfaces aren't - * transformed. - * - * All we do here is reject cases where we *know* are going to - * hit the bug and won't be able to use a core protocol fallback. - */ -static composite_operation_t -_categorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) - -{ - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) - return DO_UNSUPPORTED; - - if (! dst->buggy_repeat) - return DO_RENDER; - - if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - { - /* Check for the bug with repeat patterns nad general transforms. */ - if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, - NULL, NULL)) - { - return DO_UNSUPPORTED; - } - - if (have_mask || - !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - { - return DO_UNSUPPORTED; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; - - /* This is the case where we have the bug involving - * untransformed repeating source patterns with off-screen - * video memory; reject some cases where a core protocol - * fallback is impossible. - */ - if (_cairo_surface_is_xlib (surface_pattern->surface)) { - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; - - if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) - return DO_UNSUPPORTED; - - /* If these are on the same screen but otherwise incompatible, - * make a copy as core drawing can't cross depths and doesn't - * work right across visuals of the same depth - */ - if (_cairo_xlib_surface_same_screen (dst, src) && - !_surfaces_compatible (dst, src)) - { - return DO_UNSUPPORTED; - } - } - } - } - - return DO_RENDER; -} - -/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces - * If we end up returning DO_UNSUPPORTED here, we're throwing away work we - * did to turn gradients into a pattern, but most of the time we can handle - * that case with core protocol fallback. - * - * Also check here if we can just use XCopyArea, instead of going through - * Render. - */ -static composite_operation_t -_recategorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *src_attr, - cairo_bool_t have_mask) -{ - /* Can we use the core protocol? */ - if (! have_mask && - _surfaces_compatible (src, dst) && - _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - ! _operator_needs_alpha_composite (op, _surface_has_alpha (dst))) - { - if (src_attr->extend == CAIRO_EXTEND_NONE) - return DO_XCOPYAREA; - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) - return DO_XTILE; - } - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT && - (src->width != 1 || src->height != 1)) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return DO_UNSUPPORTED; - - return DO_RENDER; -} - -static int -_render_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return PictOpClear; - - case CAIRO_OPERATOR_SOURCE: - return PictOpSrc; - case CAIRO_OPERATOR_OVER: - return PictOpOver; - case CAIRO_OPERATOR_IN: - return PictOpIn; - case CAIRO_OPERATOR_OUT: - return PictOpOut; - case CAIRO_OPERATOR_ATOP: - return PictOpAtop; - - case CAIRO_OPERATOR_DEST: - return PictOpDst; - case CAIRO_OPERATOR_DEST_OVER: - return PictOpOverReverse; - case CAIRO_OPERATOR_DEST_IN: - return PictOpInReverse; - case CAIRO_OPERATOR_DEST_OUT: - return PictOpOutReverse; - case CAIRO_OPERATOR_DEST_ATOP: - return PictOpAtopReverse; - - case CAIRO_OPERATOR_XOR: - return PictOpXor; - case CAIRO_OPERATOR_ADD: - return PictOpAdd; - case CAIRO_OPERATOR_SATURATE: - return PictOpSaturate; - - case CAIRO_OPERATOR_MULTIPLY: - return PictOpMultiply; - case CAIRO_OPERATOR_SCREEN: - return PictOpScreen; - case CAIRO_OPERATOR_OVERLAY: - return PictOpOverlay; - case CAIRO_OPERATOR_DARKEN: - return PictOpDarken; - case CAIRO_OPERATOR_LIGHTEN: - return PictOpLighten; - case CAIRO_OPERATOR_COLOR_DODGE: - return PictOpColorDodge; - case CAIRO_OPERATOR_COLOR_BURN: - return PictOpColorBurn; - case CAIRO_OPERATOR_HARD_LIGHT: - return PictOpHardLight; - case CAIRO_OPERATOR_SOFT_LIGHT: - return PictOpSoftLight; - case CAIRO_OPERATOR_DIFFERENCE: - return PictOpDifference; - case CAIRO_OPERATOR_EXCLUSION: - return PictOpExclusion; - case CAIRO_OPERATOR_HSL_HUE: - return PictOpHSLHue; - case CAIRO_OPERATOR_HSL_SATURATION: - return PictOpHSLSaturation; - case CAIRO_OPERATOR_HSL_COLOR: - return PictOpHSLColor; - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return PictOpHSLLuminosity; - - default: - ASSERT_NOT_REACHED; - return PictOpOver; - } -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *pattern, - int x, int y, - int width, int height, - cairo_xlib_surface_t **surface_out, - cairo_surface_attributes_t *attributes) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - cairo_matrix_t matrix = pattern->matrix; - cairo_xlib_surface_t *surface; - char buf[CAIRO_STACK_BUFFER_SIZE]; - XFixed *stops; - XRenderColor *colors; - XRenderPictFormat *format; - Picture picture; - unsigned int i; - - if (dst->buggy_gradients) - break; - - if (gradient->n_stops < 2) /* becomes a solid */ - break; - - if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) - { - stops = (XFixed *) buf; - } - else - { - stops = - _cairo_malloc_ab (gradient->n_stops, - sizeof (XFixed) + sizeof (XRenderColor)); - if (unlikely (stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - colors = (XRenderColor *) (stops + gradient->n_stops); - for (i = 0; i < gradient->n_stops; i++) { - stops[i] = - _cairo_fixed_16_16_from_double (gradient->stops[i].offset); - - colors[i].red = gradient->stops[i].color.red_short; - colors[i].green = gradient->stops[i].color.green_short; - colors[i].blue = gradient->stops[i].color.blue_short; - colors[i].alpha = gradient->stops[i].color.alpha_short; - } - -#if 0 - /* For some weird reason the X server is sometimes getting - * CreateGradient requests with bad length. So far I've only seen - * XRenderCreateLinearGradient request with 4 stops sometime end up - * with length field matching 0 stops at the server side. I've - * looked at the libXrender code and I can't see anything that - * could cause this behavior. However, for some reason having a - * XSync call here seems to avoid the issue so I'll keep it here - * until it's solved. - */ - XSync (display->display, False); -#endif - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - XLinearGradient grad; - - cairo_fixed_t xdim, ydim; - - xdim = linear->p2.x - linear->p1.x; - ydim = linear->p2.y - linear->p1.y; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * XXX: Consider converting out-of-range co-ordinates and transforms. - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ - if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || - _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) - { - double sf; - - if (xdim > ydim) - sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); - else - sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); - - grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - cairo_matrix_scale (&matrix, sf, sf); - } - else - { - grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - picture = XRenderCreateLinearGradient (display->display, &grad, - stops, colors, - gradient->n_stops); - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - XRadialGradient grad; - - grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x); - grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y); - grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1); - - grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x); - grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y); - grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2); - - picture = XRenderCreateRadialGradient (display->display, &grad, - stops, colors, - gradient->n_stops); - - } - - if (stops != (XFixed *) buf) - free (stops); - - if (unlikely (picture == None)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* Wrap the remote Picture in an xlib surface. */ - format = _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_ARGB32); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (dst->screen, None, - NULL, format, - /* what could possibly go wrong? */ - XLIB_COORD_MAX, XLIB_COORD_MAX, 32); - if (unlikely (surface->base.status)) { - XRenderFreePicture (display->display, picture); - return surface->base.status; - } - - surface->src_picture = picture; - - attributes->matrix = matrix; - attributes->extend = pattern->extend; - attributes->filter = CAIRO_FILTER_NEAREST; - attributes->x_offset = 0; - attributes->y_offset = 0; - attributes->has_component_alpha = FALSE; - - *surface_out = surface; - return CAIRO_STATUS_SUCCESS; - } - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_SURFACE: - break; - } - - return _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) surface_out, - attributes); -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_xlib_surface_t **src_out, - cairo_xlib_surface_t **mask_out, - cairo_surface_attributes_t *src_attr, - cairo_surface_attributes_t *mask_attr) -{ - if (! dst->buggy_gradients && - (src->type == CAIRO_PATTERN_TYPE_LINEAR || - src->type == CAIRO_PATTERN_TYPE_RADIAL || - (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || - mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) - { - cairo_int_status_t status; - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src, - src_x, src_y, - width, height, - src_out, - src_attr); - if (unlikely (status)) - return status; - - if (mask) { - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, mask, - mask_x, - mask_y, - width, - height, - mask_out, - mask_attr); - if (unlikely (status)) { - _cairo_pattern_release_surface (src, &(*src_out)->base, - src_attr); - return status; - } - } else { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pattern_acquire_surfaces (src, mask, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) src_out, - (cairo_surface_t **) mask_out, - src_attr, mask_attr); -} - -static cairo_int_status_t -_cairo_xlib_surface_upload(cairo_xlib_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *image; - cairo_rectangle_int_t extents; - cairo_status_t status; - int tx, ty; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) pattern)->surface; - if (image->base.type != CAIRO_SURFACE_TYPE_IMAGE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && - (image->base.content & CAIRO_CONTENT_ALPHA) == 0))) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) { - if (image->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { - image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target; - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } else if (image->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) image; - image = (cairo_image_surface_t *) sub->target; - src_x += sub->extents.x; - src_y += sub->extents.y; - extents = sub->extents; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } - - if (image->format == CAIRO_FORMAT_INVALID) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (image->depth != surface->depth) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - src_x += tx; - src_y += ty; - - /* XXX for EXTEND_NONE perform unbounded fixups? */ - if (src_x < extents.x || - src_y < extents.y || - src_x + width > (unsigned) extents.width || - src_y + height > (unsigned) extents.height) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - if (clip_region != NULL) { - int n, num_rect; - - src_x -= dst_x; - src_y -= dst_y; - - num_rect = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rect; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - status = _draw_image_surface (surface, image, - rect.x + src_x, rect.y + src_y, - rect.width, rect.height, - rect.x, rect.y); - if (unlikely (status)) - break; - } - } else { - status = _draw_image_surface (surface, image, - src_x, src_y, - width, height, - dst_x, dst_y); - } - - cairo_device_release (surface->base.device); - - return status; -} - -static cairo_int_status_t -_cairo_xlib_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_surface_t *mask; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int itx, ity; - cairo_bool_t is_integer_translation; - GC gc; - - if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return UNSUPPORTED ("no support for masks"); - - operation = _categorize_composite_operation (dst, op, src_pattern, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); - - if (mask_pattern == NULL) { - /* Can we do a simple upload in-place? */ - status = _cairo_xlib_surface_upload(dst, op, src_pattern, - src_x, src_y, - dst_x, dst_y, - width, height, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_xlib_display_acquire (dst-> base.device, &display); - if (unlikely (status)) - return status; - - status = - _cairo_xlib_surface_acquire_pattern_surfaces (display, dst, - src_pattern, mask_pattern, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (unlikely (status)) - goto BAIL0; - - /* check for fallback surfaces that we cannot handle ... */ - assert (_cairo_surface_is_xlib (&src->base)); - assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); - - if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { - status = UNSUPPORTED ("unsupported mask"); - goto BAIL; - } - - operation = _recategorize_composite_operation (dst, op, src, &src_attr, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - switch (operation) - { - case DO_RENDER: - status = _cairo_xlib_surface_set_attributes (display, - src, &src_attr, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - if (mask) { - status = _cairo_xlib_surface_set_attributes (display, - mask, &mask_attr, - dst_x + width / 2., - dst_y + height/ 2.); - if (unlikely (status)) - goto BAIL; - - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - mask->src_picture, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } else { - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - 0, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - break; - - case DO_XCOPYAREA: - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XCOPYAREA. */ - assert (is_integer_translation); - - if (clip_region == NULL) { - XCopyArea (display->display, src->drawable, dst->drawable, gc, - src_x + src_attr.x_offset + itx, - src_y + src_attr.y_offset + ity, - width, height, - dst_x, dst_y); - } else { - int n, num_rects, x, y; - - x = src_x + src_attr.x_offset + itx - dst_x; - y = src_y + src_attr.y_offset + ity - dst_y; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XCopyArea (display->display, src->drawable, dst->drawable, gc, - rect.x + x, rect.y + y, - rect.width, rect.height, - rect.x, rect.y); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_XTILE: - /* This case is only used for bug fallbacks, though we also use it for - * the case where we don't have the RENDER extension, by forcing - * buggy_repeat to TRUE. - * - * We've checked that we have a repeating unscaled source in - * _recategorize_composite_operation. - */ - - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XTILE. */ - assert (is_integer_translation); - - XSetTSOrigin (display->display, gc, - - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); - XSetTile (display->display, gc, src->drawable); - - if (clip_region == NULL) { - XFillRectangle (display->display, dst->drawable, gc, - dst_x, dst_y, width, height); - } else { - int n, num_rects; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XFillRectangle (display->display, dst->drawable, gc, - rect.x, rect.y, rect.width, rect.height); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_UNSUPPORTED: - default: - ASSERT_NOT_REACHED; - } - - if (!_cairo_operator_bounded_by_source (op)) - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr, src->width, src->height, - mask ? &mask_attr : NULL, - mask ? mask->width : 0, - mask ? mask->height : 0, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - - BAIL: - if (mask) - _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - - BAIL0: - cairo_device_release (&display->base); - - return status; -} - -/* XXX move this out of core and into acquire_pattern_surface() above. */ -static cairo_int_status_t -_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_status_t status; - cairo_solid_pattern_t solid; - cairo_surface_t *solid_surface = NULL; - cairo_surface_attributes_t attrs; - cairo_xlib_display_t *display; - GC gc; - int i; - - _cairo_pattern_init_solid (&solid, color); - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); - - status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, - 0, 0, - ARRAY_LENGTH (dither_pattern[0]), - ARRAY_LENGTH (dither_pattern), - CAIRO_PATTERN_ACQUIRE_NONE, - &solid_surface, - &attrs); - if (unlikely (status)) { - _cairo_xlib_surface_put_gc (display, surface, gc); - cairo_device_release (&display->base); - return status; - } - - assert (_cairo_surface_is_xlib (solid_surface)); - - XSetTSOrigin (display->display, gc, - - (surface->base.device_transform.x0 + attrs.x_offset), - - (surface->base.device_transform.y0 + attrs.y_offset)); - XSetTile (display->display, gc, - ((cairo_xlib_surface_t *) solid_surface)->drawable); - - for (i = 0; i < num_rects; i++) { - XFillRectangle (display->display, surface->drawable, gc, - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - } - - _cairo_xlib_surface_put_gc (display, surface, gc); - - _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); - - cairo_device_release (&display->base); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xlib_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_display_t *display; - XRenderColor render_color; - cairo_status_t status; - int i; - - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - if (op == CAIRO_OPERATOR_CLEAR || - ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && - CAIRO_COLOR_IS_OPAQUE (color))) - { - return _cairo_xlib_surface_solid_fill_rectangles (surface, color, - rects, num_rects); - } - - return UNSUPPORTED ("no support for FillRectangles with this op"); - } - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); - - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; - - status = _cairo_xlib_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_xlib_surface_ensure_dst_picture (display, surface); - if (num_rects == 1) { - /* Take advantage of the protocol compaction that libXrender performs - * to amalgamate sequences of XRenderFillRectangle(). - */ - XRenderFillRectangle (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, - rects->x, - rects->y, - rects->width, - rects->height); - } else { - XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; - XRectangle *xrects = static_xrects; - - if (num_rects > ARRAY_LENGTH (static_xrects)) { - xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); - if (unlikely (xrects == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_rects; i++) { - xrects[i].x = rects[i].x; - xrects[i].y = rects[i].y; - xrects[i].width = rects[i].width; - xrects[i].height = rects[i].height; - } - - XRenderFillRectangles (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, xrects, num_rects); - - if (xrects != static_xrects) - free (xrects); - } - -BAIL: - cairo_device_release (&display->base); - return status; -} - -#define CAIRO_FIXED_16_16_MIN -32768 -#define CAIRO_FIXED_16_16_MAX 32767 - -static cairo_bool_t -_line_exceeds_16_16 (const cairo_line_t *line) -{ - return - line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); -} - -static void -_project_line_x_onto_16_16 (const cairo_line_t *line, - cairo_fixed_t top, - cairo_fixed_t bottom, - XLineFixed *out) -{ - cairo_point_double_t p1, p2; - double m; - - p1.x = _cairo_fixed_to_double (line->p1.x); - p1.y = _cairo_fixed_to_double (line->p1.y); - - p2.x = _cairo_fixed_to_double (line->p2.x); - p2.y = _cairo_fixed_to_double (line->p2.y); - - m = (p2.x - p1.x) / (p2.y - p1.y); - out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); - out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); -} - -static cairo_int_status_t -_cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - XRenderPictFormat *pict_format; - XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; - XTrapezoid *xtraps = xtraps_stack; - int i; - - if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); - - operation = _categorize_composite_operation (dst, op, pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, - pattern, - src_x, src_y, - width, height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - switch (antialias) { - case CAIRO_ANTIALIAS_NONE: - pict_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A1); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_DEFAULT: - default: - pict_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A8); - break; - } - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - _cairo_xlib_surface_set_precision (display, dst, antialias); - - status = _cairo_xlib_surface_set_attributes (display, - src, &attributes, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - if (num_traps > ARRAY_LENGTH (xtraps_stack)) { - xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (unlikely (xtraps == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_traps; i++) { - /* top/bottom will be clamped to surface bounds */ - xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); - xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - - /* However, all the other coordinates will have been left untouched so - * as not to introduce numerical error. Recompute them if they - * exceed the 16.16 limits. - */ - if (unlikely (_line_exceeds_16_16 (&traps[i].left))) { - _project_line_x_onto_16_16 (&traps[i].left, - traps[i].top, - traps[i].bottom, - &xtraps[i].left); - xtraps[i].left.p1.y = xtraps[i].top; - xtraps[i].left.p2.y = xtraps[i].bottom; - } else { - xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); - xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); - xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); - xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); - } - - if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { - _project_line_x_onto_16_16 (&traps[i].right, - traps[i].top, - traps[i].bottom, - &xtraps[i].right); - xtraps[i].right.p1.y = xtraps[i].top; - xtraps[i].right.p2.y = xtraps[i].bottom; - } else { - xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); - xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); - xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); - xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); - } - } - - if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - XRenderCompositeTrapezoids (display->display, - _render_operator (op), - src->src_picture, dst->dst_picture, - pict_format, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - xtraps, num_traps); - - if (xtraps != xtraps_stack) - free (xtraps); - - if (! _cairo_operator_bounded_by_mask (op)) { - cairo_traps_t _traps; - cairo_box_t box; - cairo_rectangle_int_t extents; - - /* XRenderCompositeTrapezoids() creates a mask only large enough for the - * trapezoids themselves, but if the operator is unbounded, then we need - * to actually composite all the way out to the bounds. - */ - /* XXX: update the interface to pass composite rects */ - _traps.traps = traps; - _traps.num_traps = num_traps; - _cairo_traps_extents (&_traps, &box); - _cairo_box_round_to_rectangle (&box, &extents); - - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, - src->width, src->height, - extents.width, extents.height, - src_x, src_y, - -extents.x + dst_x, -extents.y + dst_y, - dst_x, dst_y, - width, height, - clip_region); - } - - BAIL: - _cairo_pattern_release_surface (pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); - - return status; -} - -static cairo_bool_t -_cairo_xlib_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_xlib_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static void -_cairo_xlib_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - cairo_xlib_surface_t *surface = abstract_surface; - - *options = *_cairo_xlib_screen_get_font_options (surface->screen); -} - -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - -static cairo_bool_t -_cairo_xlib_surface_is_similar (void *surface_a, - void *surface_b) -{ - return _cairo_xlib_surface_same_screen (surface_a, surface_b); -} - -static const cairo_surface_backend_t cairo_xlib_surface_backend = { - CAIRO_SURFACE_TYPE_XLIB, - _cairo_xlib_surface_create_similar, - _cairo_xlib_surface_finish, - _cairo_xlib_surface_acquire_source_image, - _cairo_xlib_surface_release_source_image, - _cairo_xlib_surface_acquire_dest_image, - _cairo_xlib_surface_release_dest_image, - _cairo_xlib_surface_clone_similar, - _cairo_xlib_surface_composite, - _cairo_xlib_surface_fill_rectangles, - _cairo_xlib_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_xlib_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_xlib_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - _cairo_xlib_surface_scaled_font_fini, - _cairo_xlib_surface_scaled_glyph_fini, - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - _cairo_xlib_surface_show_glyphs, - - _cairo_xlib_surface_snapshot, - _cairo_xlib_surface_is_similar, - - NULL, /* fill_stroke */ - - _cairo_xlib_surface_create_solid_pattern_surface, - _cairo_xlib_surface_can_repaint_solid_pattern_surface -}; - -/** - * _cairo_surface_is_xlib: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_xlib_surface_t - * - * Return value: True if the surface is an xlib surface - **/ -static cairo_bool_t -_cairo_surface_is_xlib (cairo_surface_t *surface) -{ - return surface->backend == &cairo_xlib_surface_backend; -} - -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) -{ - cairo_xlib_surface_t *surface = cairo_container_of (data, - cairo_xlib_surface_t, - close_display_hook); - Display *dpy; - - dpy = display->display; - - X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); - - if (surface->dst_picture != None) { - XRenderFreePicture (dpy, surface->dst_picture); - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - XRenderFreePicture (dpy, surface->src_picture); - surface->src_picture = None; - } - - if (surface->owns_pixmap) { - XFreePixmap (dpy, surface->drawable); - surface->drawable = None; - surface->owns_pixmap = FALSE; - } -} - -static cairo_surface_t * -_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, - Drawable drawable, - Visual *visual, - XRenderPictFormat *xrender_format, - int width, - int height, - int depth) -{ - cairo_xlib_surface_t *surface; - cairo_xlib_display_t *display; - cairo_status_t status; - - if (depth == 0) { - if (xrender_format) { - depth = xrender_format->depth; - - /* XXX find matching visual for core/dithering fallbacks? */ - } else if (visual) { - Screen *scr = screen->screen; - - if (visual == DefaultVisualOfScreen (scr)) { - depth = DefaultDepthOfScreen (scr); - } else { - int j, k; - - /* This is ugly, but we have to walk over all visuals - * for the display to find the correct depth. - */ - depth = 0; - for (j = 0; j < scr->ndepths; j++) { - Depth *d = &scr->depths[j]; - for (k = 0; k < d->nvisuals; k++) { - if (&d->visuals[k] == visual) { - depth = d->depth; - goto found; - } - } - } - } - } - - if (depth == 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - -found: - ; - } - - surface = malloc (sizeof (cairo_xlib_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - status = _cairo_xlib_display_acquire (screen->device, &display); - if (unlikely (status)) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (status)); - } - - _cairo_xlib_display_get_xrender_version (display, - &surface->render_major, - &surface->render_minor); - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - if (!xrender_format) { - if (visual) { - xrender_format = XRenderFindVisualFormat (display->display, visual); - } else if (depth == 1) { - xrender_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A1); - } - } - } else { - /* we cannot use XRender for this surface, so ensure we don't try */ - surface->render_major = -1; - surface->render_minor = -1; - } - - /* initialize and hook into the CloseDisplay callback */ - surface->close_display_hook.func = _cairo_xlib_surface_detach_display; - _cairo_xlib_add_close_display_hook (display, - &surface->close_display_hook); - - cairo_device_release (&display->base); - - _cairo_surface_init (&surface->base, - &cairo_xlib_surface_backend, - screen->device, - _xrender_format_to_content (xrender_format)); - - surface->screen = screen; - - surface->drawable = drawable; - surface->owns_pixmap = FALSE; - surface->use_pixmap = 0; - surface->width = width; - surface->height = height; - - surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - /* so we can use the XTile fallback */ - surface->buggy_repeat = TRUE; - } - - surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) - surface->buggy_pad_reflect = TRUE; - - surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) - surface->buggy_gradients = TRUE; - - surface->dst_picture = None; - surface->src_picture = None; - - surface->visual = visual; - surface->xrender_format = xrender_format; - surface->depth = depth; - surface->filter = CAIRO_FILTER_NEAREST; - surface->extend = CAIRO_EXTEND_NONE; - surface->has_component_alpha = FALSE; - surface->precision = PolyModePrecise; - surface->xtransform = identity; - - surface->clip_region = NULL; - surface->clip_rects = surface->embedded_clip_rects; - surface->num_clip_rects = 0; - surface->clip_dirty = 0; - - /* - * Compute the pixel format masks from either a XrenderFormat or - * else from a visual; failing that we assume the drawable is an - * alpha-only pixmap as it could only have been created that way - * through the cairo_xlib_surface_create_for_bitmap function. - */ - if (xrender_format) { - surface->a_mask = (unsigned long) - surface->xrender_format->direct.alphaMask - << surface->xrender_format->direct.alpha; - surface->r_mask = (unsigned long) - surface->xrender_format->direct.redMask - << surface->xrender_format->direct.red; - surface->g_mask = (unsigned long) - surface->xrender_format->direct.greenMask - << surface->xrender_format->direct.green; - surface->b_mask = (unsigned long) - surface->xrender_format->direct.blueMask - << surface->xrender_format->direct.blue; - } else if (visual) { - surface->a_mask = 0; - surface->r_mask = visual->red_mask; - surface->g_mask = visual->green_mask; - surface->b_mask = visual->blue_mask; - } else { - if (depth < 32) - surface->a_mask = (1 << depth) - 1; - else - surface->a_mask = 0xffffffff; - surface->r_mask = 0; - surface->g_mask = 0; - surface->b_mask = 0; - } - - return &surface->base; -} - -static Screen * -_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) -{ - int s, d, v; - - for (s = 0; s < ScreenCount (dpy); s++) { - Screen *screen; - - screen = ScreenOfDisplay (dpy, s); - if (visual == DefaultVisualOfScreen (screen)) - return screen; - - for (d = 0; d < screen->ndepths; d++) { - Depth *depth; - - depth = &screen->depths[d]; - for (v = 0; v < depth->nvisuals; v++) - if (visual == &depth->visuals[v]) - return screen; - } - } - - return NULL; -} - -/** - * cairo_xlib_surface_create: - * @dpy: an X Display - * @drawable: an X Drawable, (a Pixmap or a Window) - * @visual: the visual to use for drawing to @drawable. The depth - * of the visual must match the depth of the drawable. - * Currently, only TrueColor visuals are fully supported. - * @width: the current width of @drawable. - * @height: the current height of @drawable. - * - * Creates an Xlib surface that draws to the given drawable. - * The way that colors are represented in the drawable is specified - * by the provided visual. - * - * Note: If @drawable is a Window, then the function - * cairo_xlib_surface_set_size() must be called whenever the size of the - * window changes. - * - * When @drawable is a Window containing child windows then drawing to - * the created surface will be clipped by those child windows. When - * the created surface is used as a source, the contents of the - * children will be included. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - int width, - int height) -{ - Screen *scr; - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - /* you're lying, and you know it! */ - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - } - - scr = _cairo_xlib_screen_from_visual (dpy, visual); - if (scr == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - - status = _cairo_xlib_screen_get (dpy, scr, &screen); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); - - return _cairo_xlib_surface_create_internal (screen, drawable, - visual, NULL, - width, height, 0); -} - -/** - * cairo_xlib_surface_create_for_bitmap: - * @dpy: an X Display - * @bitmap: an X Drawable, (a depth-1 Pixmap) - * @screen: the X Screen associated with @bitmap - * @width: the current width of @bitmap. - * @height: the current height of @bitmap. - * - * Creates an Xlib surface that draws to the given bitmap. - * This will be drawn to as a %CAIRO_FORMAT_A1 object. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_xlib_surface_create_for_bitmap (Display *dpy, - Pixmap bitmap, - Screen *scr, - int width, - int height) -{ - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - - status = _cairo_xlib_screen_get (dpy, scr, &screen); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); - - return _cairo_xlib_surface_create_internal (screen, bitmap, - NULL, NULL, - width, height, 1); -} - -#if CAIRO_HAS_XLIB_XRENDER_SURFACE -/** - * cairo_xlib_surface_create_with_xrender_format: - * @dpy: an X Display - * @drawable: an X Drawable, (a Pixmap or a Window) - * @screen: the X Screen associated with @drawable - * @format: the picture format to use for drawing to @drawable. The depth - * of @format must match the depth of the drawable. - * @width: the current width of @drawable. - * @height: the current height of @drawable. - * - * Creates an Xlib surface that draws to the given drawable. - * The way that colors are represented in the drawable is specified - * by the provided picture format. - * - * Note: If @drawable is a Window, then the function - * cairo_xlib_surface_set_size() must be called whenever the size of the - * window changes. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_xlib_surface_create_with_xrender_format (Display *dpy, - Drawable drawable, - Screen *scr, - XRenderPictFormat *format, - int width, - int height) -{ - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); - - status = _cairo_xlib_screen_get (dpy, scr, &screen); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); - - return _cairo_xlib_surface_create_internal (screen, drawable, - _visual_for_xrender_format (scr, format), - format, width, height, 0); -} - -/** - * cairo_xlib_surface_get_xrender_format: - * @surface: an xlib surface - * - * Gets the X Render picture format that @surface uses for rendering with the - * X Render extension. If the surface was created by - * cairo_xlib_surface_create_with_xrender_format() originally, the return - * value is the format passed to that constructor. - * - * Return value: the XRenderPictFormat* associated with @surface, - * or %NULL if the surface is not an xlib surface - * or if the X Render extension is not available. - * - * Since: 1.6 - **/ -XRenderPictFormat * -cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) -{ - cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; - - /* Throw an error for a non-xlib surface */ - if (! _cairo_surface_is_xlib (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return xlib_surface->xrender_format; -} -#endif - -/** - * cairo_xlib_surface_set_size: - * @surface: a #cairo_surface_t for the XLib backend - * @width: the new width of the surface - * @height: the new height of the surface - * - * Informs cairo of the new size of the X Drawable underlying the - * surface. For a surface created for a Window (rather than a Pixmap), - * this function must be called each time the size of the window - * changes. (For a subwindow, you are normally resizing the window - * yourself, but for a toplevel window, it is necessary to listen for - * ConfigureNotify events.) - * - * A Pixmap can never change size, so it is never necessary to call - * this function on a surface created for a Pixmap. - **/ -void -cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, - int width, - int height) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_xlib (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); - return; - } - - surface->width = width; - surface->height = height; -} -/** - * cairo_xlib_surface_set_drawable: - * @surface: a #cairo_surface_t for the XLib backend - * @drawable: the new drawable for the surface - * @width: the width of the new drawable - * @height: the height of the new drawable - * - * Informs cairo of a new X Drawable underlying the - * surface. The drawable must match the display, screen - * and format of the existing drawable or the application - * will get X protocol errors and will probably terminate. - * No checks are done by this function to ensure this - * compatibility. - **/ -void -cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, - Drawable drawable, - int width, - int height) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_xlib (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); - return; - } - - /* XXX: and what about this case? */ - if (surface->owns_pixmap) - return; - - if (surface->drawable != drawable) { - cairo_xlib_display_t *display; - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return; - - X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); - - if (surface->dst_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->dst_picture); - if (unlikely (status)) { - status = _cairo_surface_set_error (&surface->base, status); - return; - } - - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->src_picture); - if (unlikely (status)) { - status = _cairo_surface_set_error (&surface->base, status); - return; - } - - surface->src_picture = None; - } - - cairo_device_release (&display->base); - - surface->drawable = drawable; - } - surface->width = width; - surface->height = height; -} - -/** - * cairo_xlib_surface_get_display: - * @surface: a #cairo_xlib_surface_t - * - * Get the X Display for the underlying X Drawable. - * - * Return value: the display. - * - * Since: 1.2 - **/ -Display * -cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) -{ - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return ((cairo_xlib_display_t *) abstract_surface->device)->display; -} - -/** - * cairo_xlib_surface_get_drawable: - * @surface: a #cairo_xlib_surface_t - * - * Get the underlying X Drawable used for the surface. - * - * Return value: the drawable. - * - * Since: 1.2 - **/ -Drawable -cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->drawable; -} - -/** - * cairo_xlib_surface_get_screen: - * @surface: a #cairo_xlib_surface_t - * - * Get the X Screen for the underlying X Drawable. - * - * Return value: the screen. - * - * Since: 1.2 - **/ -Screen * -cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return surface->screen->screen; -} - -/** - * cairo_xlib_surface_get_visual: - * @surface: a #cairo_xlib_surface_t - * - * Gets the X Visual associated with @surface, suitable for use with the - * underlying X Drawable. If @surface was created by - * cairo_xlib_surface_create(), the return value is the Visual passed to that - * constructor. - * - * Return value: the Visual or %NULL if there is no appropriate Visual for - * @surface. - * - * Since: 1.2 - **/ -Visual * -cairo_xlib_surface_get_visual (cairo_surface_t *surface) -{ - cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; - - if (! _cairo_surface_is_xlib (surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return xlib_surface->visual; -} - -/** - * cairo_xlib_surface_get_depth: - * @surface: a #cairo_xlib_surface_t - * - * Get the number of bits used to represent each pixel value. - * - * Return value: the depth of the surface in bits. - * - * Since: 1.2 - **/ -int -cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->depth; -} - -/** - * cairo_xlib_surface_get_width: - * @surface: a #cairo_xlib_surface_t - * - * Get the width of the X Drawable underlying the surface in pixels. - * - * Return value: the width of the surface in pixels. - * - * Since: 1.2 - **/ -int -cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->width; -} - -/** - * cairo_xlib_surface_get_height: - * @surface: a #cairo_xlib_surface_t - * - * Get the height of the X Drawable underlying the surface in pixels. - * - * Return value: the height of the surface in pixels. - * - * Since: 1.2 - **/ -int -cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->height; -} - -enum { - GLYPHSET_INDEX_ARGB32, - GLYPHSET_INDEX_A8, - GLYPHSET_INDEX_A1, - NUM_GLYPHSETS -}; - -typedef struct _cairo_xlib_font_glyphset_free_glyphs { - GlyphSet glyphset; - int glyph_count; - unsigned long glyph_indices[128]; -} cairo_xlib_font_glyphset_free_glyphs_t; - -typedef struct _cairo_xlib_font_glyphset_info { - GlyphSet glyphset; - cairo_format_t format; - XRenderPictFormat *xrender_format; - cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; -} cairo_xlib_font_glyphset_info_t; - -typedef struct _cairo_xlib_surface_font_private { - cairo_scaled_font_t *scaled_font; - cairo_scaled_font_t *grayscale_font; - cairo_xlib_hook_t close_display_hook; - cairo_device_t *device; - cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; -} cairo_xlib_surface_font_private_t; - -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, - void *data) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_scaled_font_t *scaled_font; - - font_private = cairo_container_of (data, - cairo_xlib_surface_font_private_t, - close_display_hook); - scaled_font = font_private->scaled_font; - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - font_private = scaled_font->surface_private; - scaled_font->surface_private = NULL; - - _cairo_scaled_font_reset_cache (scaled_font); - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (font_private != NULL) { - int i; - - if (font_private->grayscale_font) { - cairo_scaled_font_destroy (font_private->grayscale_font); - } - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - if (glyphset_info->glyphset) - XRenderFreeGlyphSet (display->display, glyphset_info->glyphset); - - if (glyphset_info->pending_free_glyphs != NULL) - free (glyphset_info->pending_free_glyphs); - } - - cairo_device_destroy (font_private->device); - free (font_private); - } -} - -static cairo_status_t -_cairo_xlib_surface_font_init (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); - if (unlikely (font_private == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->scaled_font = scaled_font; - font_private->grayscale_font = NULL; - font_private->device = cairo_device_reference (&display->base); - - /* initialize and hook into the CloseDisplay callback */ - font_private->close_display_hook.func = - _cairo_xlib_surface_remove_scaled_font; - _cairo_xlib_add_close_display_hook (display, - &font_private->close_display_hook); - - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; - switch (i) { - case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; - case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; - case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; - default: ASSERT_NOT_REACHED; break; - } - glyphset_info->xrender_format = NULL; - glyphset_info->glyphset = None; - glyphset_info->pending_free_glyphs = NULL; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &cairo_xlib_surface_backend; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_status_t status; - - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_xlib_display_t *display; - int i; - - if (font_private->grayscale_font) { - cairo_scaled_font_destroy (font_private->grayscale_font); - } - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status) - goto BAIL; - - _cairo_xlib_remove_close_display_hook (display, - &font_private->close_display_hook); - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - - if (glyphset_info->pending_free_glyphs != NULL) - free (glyphset_info->pending_free_glyphs); - - if (glyphset_info->glyphset) { - status = _cairo_xlib_display_queue_resource (display, - XRenderFreeGlyphSet, - glyphset_info->glyphset); - (void) status; /* XXX cannot propagate failure */ - } - } - - cairo_device_release (&display->base); -BAIL: - cairo_device_destroy (&display->base); - free (font_private); - } -} - -static void -_cairo_xlib_render_free_glyphs (Display *dpy, - cairo_xlib_font_glyphset_free_glyphs_t *to_free) -{ - XRenderFreeGlyphs (dpy, - to_free->glyphset, - to_free->glyph_indices, - to_free->glyph_count); -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) -{ - return scaled_glyph->surface_private; -} - -static void -_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, - cairo_xlib_font_glyphset_info_t *glyphset_info) -{ - scaled_glyph->surface_private = glyphset_info; -} - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - if (scaled_font->finished) - return; - - font_private = scaled_font->surface_private; - glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (font_private != NULL && glyphset_info != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - cairo_status_t status; - - to_free = glyphset_info->pending_free_glyphs; - if (to_free != NULL && - to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) - { - cairo_xlib_display_t *display; - - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, - to_free, - free); - cairo_device_release (&display->base); - } - /* XXX cannot propagate failure */ - if (unlikely (status)) - free (to_free); - - to_free = glyphset_info->pending_free_glyphs = NULL; - } - - if (to_free == NULL) { - to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); - if (unlikely (to_free == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return; /* XXX cannot propagate failure */ - } - - to_free->glyphset = glyphset_info->glyphset; - to_free->glyph_count = 0; - glyphset_info->pending_free_glyphs = to_free; - } - - to_free->glyph_indices[to_free->glyph_count++] = - _cairo_scaled_glyph_index (scaled_glyph); - } -} - -static cairo_bool_t -_native_byte_order_lsb (void) -{ - int x = 1; - - return *((char *) &x) == 1; -} - -static int -_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) -{ - if (format == CAIRO_FORMAT_A8) - return GLYPHSET_INDEX_A8; - if (format == CAIRO_FORMAT_A1) - return GLYPHSET_INDEX_A1; - - assert (format == CAIRO_FORMAT_ARGB32); - return GLYPHSET_INDEX_ARGB32; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - int glyphset_index; - - glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); - font_private = scaled_font->surface_private; - glyphset_info = &font_private->glyphset_info[glyphset_index]; - if (glyphset_info->glyphset == None) { - cairo_xlib_display_t *display; - - if (_cairo_xlib_display_acquire (font_private->device, &display)) - return NULL; - - glyphset_info->xrender_format = - _cairo_xlib_display_get_xrender_format (display, - glyphset_info->format); - glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, - glyphset_info->xrender_format); - - cairo_device_release (&display->base); - } - - return glyphset_info; -} - -static cairo_bool_t -_cairo_xlib_glyphset_info_has_pending_free_glyph ( - cairo_xlib_font_glyphset_info_t *glyphset_info, - unsigned long glyph_index) -{ - if (glyphset_info->pending_free_glyphs != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - int i; - - to_free = glyphset_info->pending_free_glyphs; - for (i = 0; i < to_free->glyph_count; i++) { - if (to_free->glyph_indices[i] == glyph_index) { - to_free->glyph_count--; - memmove (&to_free->glyph_indices[i], - &to_free->glyph_indices[i+1], - (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); - return TRUE; - } - } - } - - return FALSE; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph ( - cairo_scaled_font_t *scaled_font, - unsigned long glyph_index, - cairo_image_surface_t *surface) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = scaled_font->surface_private; - if (font_private == NULL) - return NULL; - - if (surface != NULL) { - i = _cairo_xlib_get_glyphset_index_for_format (surface->format); - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } else { - for (i = 0; i < NUM_GLYPHSETS; i++) { - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } - } - - return NULL; -} - -static cairo_status_t -_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t **pscaled_glyph) -{ - XGlyphInfo glyph_info; - unsigned long glyph_index; - unsigned char *data; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_bool_t already_had_glyph_surface; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - - /* check to see if we have a pending XRenderFreeGlyph for this glyph */ - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); - if (glyphset_info != NULL) { - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - return CAIRO_STATUS_SUCCESS; - } - - if (!glyph_surface) { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_SURFACE, - pscaled_glyph); - if (unlikely (status)) - return status; - - scaled_glyph = *pscaled_glyph; - glyph_surface = scaled_glyph->surface; - already_had_glyph_surface = FALSE; - } else { - already_had_glyph_surface = TRUE; - } - - if (scaled_font->surface_private == NULL) { - status = _cairo_xlib_surface_font_init (display, scaled_font); - if (unlikely (status)) - return status; - } - - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, - glyph_surface->format); - - /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */ - { - int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height; - int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) - : XMaxRequestSize (display->display)) * 4 - - sz_xRenderAddGlyphsReq - - sz_xGlyphInfo - - 8; - if (len >= max_request_size) - return UNSUPPORTED ("glyph too large for XRequest"); - } - - /* If the glyph surface has zero height or width, we create - * a clear 1x1 surface, to avoid various X server bugs. - */ - if (glyph_surface->width == 0 || glyph_surface->height == 0) { - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - } - - /* If the glyph format does not match the font format, then we - * create a temporary surface for the glyph image with the font's - * format. - */ - if (glyph_surface->format != glyphset_info->format) { - cairo_surface_pattern_t pattern; - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, - glyph_surface->width, - glyph_surface->height); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); - status = _cairo_surface_paint (tmp_surface, - CAIRO_OPERATOR_SOURCE, &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - - if (unlikely (status)) - goto BAIL; - } - - /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ - glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); - glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); - glyph_info.width = glyph_surface->width; - glyph_info.height = glyph_surface->height; - glyph_info.xOff = scaled_glyph->x_advance; - glyph_info.yOff = scaled_glyph->y_advance; - - data = glyph_surface->data; - - /* flip formats around */ - switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) { - case GLYPHSET_INDEX_A1: - /* local bitmaps are always stored with bit == byte */ - if (_native_byte_order_lsb() != (BitmapBitOrder (display->display) == LSBFirst)) { - int c = glyph_surface->stride * glyph_surface->height; - unsigned char *d; - unsigned char *new, *n; - - new = malloc (c); - if (!new) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = data; - do { - char b = *d++; - b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); - b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); - b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); - *n++ = b; - } while (--c); - data = new; - } - break; - case GLYPHSET_INDEX_A8: - break; - case GLYPHSET_INDEX_ARGB32: - if (_native_byte_order_lsb() != (ImageByteOrder (display->display) == LSBFirst)) { - unsigned int c = glyph_surface->stride * glyph_surface->height / 4; - const uint32_t *d; - uint32_t *new, *n; - - new = malloc (4 * c); - if (unlikely (new == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = (uint32_t *) data; - do { - *n++ = bswap_32 (*d); - d++; - } while (--c); - data = (uint8_t *) new; - } - break; - default: - ASSERT_NOT_REACHED; - break; - } - /* XXX assume X server wants pixman padding. Xft assumes this as well */ - - struct _XDisplay *dpy = (struct _XDisplay *) display->display; - int req_length = sz_xRenderAddGlyphsReq + 4; - if (req_length & 3) - req_length += 4 - (req_length & 3); - if (dpy->bufptr + req_length > dpy->bufmax) - XFlush (display->display); - - XRenderAddGlyphs (display->display, glyphset_info->glyphset, - &glyph_index, &glyph_info, 1, - (char *) data, - glyph_surface->stride * glyph_surface->height); - - if (data != glyph_surface->data) - free (data); - - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - - BAIL: - if (glyph_surface != scaled_glyph->surface) - cairo_surface_destroy (&glyph_surface->base); - - /* if the scaled glyph didn't already have a surface attached - * to it, release the created surface now that we have it - * uploaded to the X server. If the surface has already been - * there (eg. because image backend requested it), leave it in - * the cache - */ - if (!already_had_glyph_surface) - _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); - - return status; -} - -typedef void (*cairo_xrender_composite_text_func_t) - (Display *dpy, - int op, - Picture src, - Picture dst, - _Xconst XRenderPictFormat *maskFormat, - int xSrc, - int ySrc, - int xDst, - int yDst, - _Xconst XGlyphElt8 *elts, - int nelt); - -/* Build a struct of the same size of #cairo_glyph_t that can be used both as - * an input glyph with double coordinates, and as "working" glyph with - * integer from-current-point offsets. */ -typedef union { - cairo_glyph_t d; - unsigned long index; - struct { - unsigned long index; - int x; - int y; - } i; -} cairo_xlib_glyph_t; - -/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ -COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); - -/* Start a new element for the first glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs - * (Xrender limits each element to 252 glyphs, we limit them to 128) - * - * These same conditions need to be mirrored between - * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks - */ -#define _start_new_glyph_elt(count, glyph) \ - (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) - -static cairo_status_t -_emit_glyphs_chunk (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - /* info for this chunk */ - int num_elts, - int width, - cairo_xlib_font_glyphset_info_t *glyphset_info) -{ - /* Which XRenderCompositeText function to use */ - cairo_xrender_composite_text_func_t composite_text_func; - int size; - - /* Element buffer stuff */ - XGlyphElt8 *elts; - XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; - - /* Reuse the input glyph array for output char generation */ - char *char8 = (char *) glyphs; - unsigned short *char16 = (unsigned short *) glyphs; - unsigned int *char32 = (unsigned int *) glyphs; - - int i; - int nelt; /* Element index */ - int n; /* Num output glyphs in current element */ - int j; /* Num output glyphs so far */ - - switch (width) { - case 1: - /* don't cast the 8-variant, to catch possible mismatches */ - composite_text_func = XRenderCompositeText8; - size = sizeof (char); - break; - case 2: - composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; - size = sizeof (unsigned short); - break; - default: - case 4: - composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; - size = sizeof (unsigned int); - } - - /* Allocate element array */ - if (num_elts <= ARRAY_LENGTH (stack_elts)) { - elts = stack_elts; - } else { - elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); - if (unlikely (elts == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* Fill them in */ - nelt = 0; - n = 0; - j = 0; - for (i = 0; i < num_glyphs; i++) { - - /* Start a new element for first output glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() - */ - if (_start_new_glyph_elt (j, &glyphs[i])) { - if (j) { - elts[nelt].nchars = n; - nelt++; - n = 0; - } - elts[nelt].chars = char8 + size * j; - elts[nelt].glyphset = glyphset_info->glyphset; - elts[nelt].xOff = glyphs[i].i.x; - elts[nelt].yOff = glyphs[i].i.y; - } - - switch (width) { - case 1: char8 [j] = (char) glyphs[i].index; break; - case 2: char16[j] = (unsigned short) glyphs[i].index; break; - default: - case 4: char32[j] = (unsigned int) glyphs[i].index; break; - } - - n++; - j++; - } - - if (n) { - elts[nelt].nchars = n; - nelt++; - } - - /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the - * expected number of xGlyphElts. */ - assert (nelt == num_elts); - - composite_text_func (display->display, - _render_operator (op), - src->src_picture, - dst->dst_picture, - glyphset_info->xrender_format, - attributes->x_offset + elts[0].xOff, - attributes->y_offset + elts[0].yOff, - elts[0].xOff, elts[0].yOff, - (XGlyphElt8 *) elts, nelt); - - if (elts != stack_elts) - free (elts); - - return CAIRO_STATUS_SUCCESS; -} - - -/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have - * enough room for padding */ -#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) - -static cairo_status_t -_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - int *remaining_glyphs) -{ - int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph; - cairo_fixed_t x = 0, y = 0; - cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; - - unsigned long max_index = 0; - int width = 1; - int num_elts = 0; - int num_out_glyphs = 0; - - int max_request_size = XMaxRequestSize (display->display) * 4 - - MAX (sz_xRenderCompositeGlyphs8Req, - MAX(sz_xRenderCompositeGlyphs16Req, - sz_xRenderCompositeGlyphs32Req)); - int request_size = 0; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - - for (i = 0; i < num_glyphs; i++) { - int this_x, this_y; - int old_width; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - return status; - - this_x = _cairo_lround (glyphs[i].d.x); - this_y = _cairo_lround (glyphs[i].d.y); - - /* Glyph skipping: - * - * We skip any glyphs that have troublesome coordinates. We want - * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in - * a signed 16bit integer, otherwise it will overflow in the render - * protocol. - * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in - * a signed 15bit integer. The trivial option would be to allow - * coordinates -8192..8192, but that's kinda dull. It probably will - * take a decade or so to get monitors 8192x4096 or something. A - * negative value of -8192 on the other hand, is absolutely useless. - * Note that we do want to allow some negative positions. The glyph - * may start off the screen but part of it make it to the screen. - * Anyway, we will allow positions in the range -4096..122887. That - * will buy us a few more years before this stops working. - * - * Update: upon seeing weird glyphs, we just return and let fallback - * code do the job. - */ - if (((this_x+4096)|(this_y+4096))&~0x3fffu) - break; - - /* Send unsent glyphs to the server */ - if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { - status = _cairo_xlib_surface_add_glyph (display, - scaled_font, - &scaled_glyph); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - /* Break so we flush glyphs so far and let fallback code - * handle the rest */ - break; - - return status; - } - } - - this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (!glyphset_info) - glyphset_info = this_glyphset_info; - - /* The invariant here is that we can always flush the glyphs - * accumulated before this one, using old_width, and they - * would fit in the request. - */ - old_width = width; - - /* Update max glyph index */ - if (glyphs[i].index > max_index) { - max_index = glyphs[i].index; - if (max_index >= 65536) - width = 4; - else if (max_index >= 256) - width = 2; - if (width != old_width) - request_size += (width - old_width) * num_out_glyphs; - } - - /* If we will pass the max request size by adding this glyph, - * flush current glyphs. Note that we account for a - * possible element being added below. - * - * Also flush if changing glyphsets, as Xrender limits one mask - * format per request, so we can either break up, or use a - * wide-enough mask format. We do the former. One reason to - * prefer the latter is the fact that Xserver ADDs all glyphs - * to the mask first, and then composes that to final surface, - * though it's not a big deal. - */ - if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || - (this_glyphset_info != glyphset_info)) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, old_width, glyphset_info); - if (unlikely (status)) - return status; - - glyphs += i; - num_glyphs -= i; - i = 0; - max_index = glyphs[i].index; - width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; - request_size = 0; - num_elts = 0; - num_out_glyphs = 0; - x = y = 0; - glyphset_info = this_glyphset_info; - } - - /* Convert absolute glyph position to relative-to-current-point - * position */ - glyphs[i].i.x = this_x - x; - glyphs[i].i.y = this_y - y; - - /* Start a new element for the first glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _emit_glyphs_chunk(). - */ - if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { - num_elts++; - request_size += _cairo_sz_xGlyphElt; - } - - /* adjust current-position */ - x = this_x + scaled_glyph->x_advance; - y = this_y + scaled_glyph->y_advance; - - num_out_glyphs++; - request_size += width; - } - - if (num_elts) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, width, glyphset_info); - } - - *remaining_glyphs = num_glyphs - i; - if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - return status; -} - -static cairo_bool_t -_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &cairo_xlib_surface_backend) || - (font_private != NULL && font_private->device != dst->base.device)) - { - return FALSE; - } - - return TRUE; -} - -/* Gets a grayscale version of scaled_font. The grayscale version is cached - * in our surface_private data. - */ -static cairo_scaled_font_t * -_cairo_xlib_get_grayscale_font (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; - cairo_bool_t needs_font; - - if (font_private == NULL) { - cairo_status_t status = _cairo_xlib_surface_font_init (display, scaled_font); - if (unlikely (status)) - return _cairo_scaled_font_create_in_error (status); - font_private = scaled_font->surface_private; - } - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - needs_font = !font_private->grayscale_font; - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (needs_font) { - cairo_font_options_t options; - cairo_scaled_font_t *new_font; - - options = scaled_font->options; - options.antialias = CAIRO_ANTIALIAS_GRAY; - new_font = cairo_scaled_font_create (scaled_font->font_face, - &scaled_font->font_matrix, - &scaled_font->ctm, &options); - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - if (!font_private->grayscale_font) { - font_private->grayscale_font = new_font; - new_font = NULL; - } - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (new_font) { - cairo_scaled_font_destroy (new_font); - } - } - - return font_private->grayscale_font; -} - -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; - composite_operation_t operation; - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *src = NULL; - cairo_region_t *clip_region = NULL; - cairo_xlib_display_t *display; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) - return UNSUPPORTED ("XRender does not support CompositeText"); - - /* Just let unbounded operators go through the fallback code - * instead of trying to do the fixups here */ - if (! _cairo_operator_bounded_by_mask (op)) - return UNSUPPORTED ("unsupported unbounded op"); - - /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- - * the solid source seems to be multiplied by the glyph mask, and - * then the entire thing is copied to the destination surface, - * including the fully transparent "background" of the rectangular - * glyph surface. */ - if (op == CAIRO_OPERATOR_SOURCE && - ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) - { - return UNSUPPORTED ("known bug in Render"); - } - - /* We can only use our code if we either have no clip or - * have a real native clip region set. If we're using - * fallback clip masking, we have to go through the full - * fallback path. - */ - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - } - - operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported op"); - - if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("unowned font"); - - - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; - - if (!dst->base.permit_subpixel_antialiasing && - scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { - scaled_font = _cairo_xlib_get_grayscale_font (display, scaled_font); - } - - X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); - - if (clip_region != NULL && - cairo_region_num_rectangles (clip_region) == 1) - { - cairo_rectangle_int_t glyph_extents; - cairo_rectangle_int_t clip_extents; - - /* Can we do without the clip? - * Around 50% of the time the clip is redundant (firefox). - */ - _cairo_scaled_font_glyph_approximate_extents (scaled_font, - glyphs, num_glyphs, - &glyph_extents); - - cairo_region_get_extents(clip_region, &clip_extents); - if (clip_extents.x <= glyph_extents.x && - clip_extents.y <= glyph_extents.y && - clip_extents.x + clip_extents.width >= glyph_extents.x + glyph_extents.width && - clip_extents.y + clip_extents.height >= glyph_extents.y + glyph_extents.height) - { - clip_region = NULL; - } - } - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL0; - - /* After passing all those tests, we're now committed to rendering - * these glyphs or to fail trying. We first upload any glyphs to - * the X server that it doesn't have already, then we draw - * them. - */ - - /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore - * the mask (the glyphs). This code below was executed as a side effect - * of going through the _clip_and_composite fallback code for old_show_glyphs, - * so PictOpClear was never used with CompositeText before. - */ - if (op == CAIRO_OPERATOR_CLEAR) { - src_pattern = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - 0, 0, 1, 1, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); - if (unlikely (status)) - goto BAIL0; - } else { - cairo_rectangle_int_t glyph_extents; - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - goto BAIL0; - - if (clip != NULL) { - if (! _cairo_rectangle_intersect (&glyph_extents, - _cairo_clip_get_extents (clip))) - { - goto BAIL0; - } - } - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src_pattern, - glyph_extents.x, - glyph_extents.y, - glyph_extents.width, - glyph_extents.height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - } - - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported op"); - goto BAIL1; - } - - status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0); - if (unlikely (status)) - goto BAIL1; - - _cairo_scaled_font_freeze_cache (scaled_font); - if (_cairo_xlib_surface_owns_font (dst, scaled_font)) { - status = _cairo_xlib_surface_emit_glyphs (display, - dst, - (cairo_xlib_glyph_t *) glyphs, - num_glyphs, - scaled_font, - op, - src, - &attributes, - remaining_glyphs); - } else { - status = UNSUPPORTED ("unowned font"); - } - _cairo_scaled_font_thaw_cache (scaled_font); - - BAIL1: - if (src) - _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); - - return status; -} diff --git a/libs/cairo/cairo/src/cairo-xlib-visual.c b/libs/cairo/cairo/src/cairo-xlib-visual.c deleted file mode 100644 index f70db3a77..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-visual.c +++ /dev/null @@ -1,156 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" - -#include "cairo-xlib-private.h" - -#include "cairo-error-private.h" - -/* A perceptual distance metric between two colors. No sqrt needed - * since the square of the distance is still a valid metric. */ - -/* XXX: This is currently using linear distance in RGB space which is - * decidedly not perceptually linear. If someone cared a lot about the - * quality, they might choose something else here. Then again, they - * might also choose not to use a PseudoColor visual... */ -static inline int -_color_distance (unsigned short r1, unsigned short g1, unsigned short b1, - unsigned short r2, unsigned short g2, unsigned short b2) -{ - r1 >>= 8; g1 >>= 8; b1 >>= 8; - r2 >>= 8; g2 >>= 8; b2 >>= 8; - - return ((r2 - r1) * (r2 - r1) + - (g2 - g1) * (g2 - g1) + - (b2 - b1) * (b2 - b1)); -} - -cairo_status_t -_cairo_xlib_visual_info_create (Display *dpy, - int screen, - VisualID visualid, - cairo_xlib_visual_info_t **out) -{ - cairo_xlib_visual_info_t *info; - Colormap colormap = DefaultColormap (dpy, screen); - XColor color; - int gray, red, green, blue; - int i, j, distance, min_distance = 0; - XColor colors[256]; - unsigned short cube_index_to_short[CUBE_SIZE]; - unsigned short ramp_index_to_short[RAMP_SIZE]; - unsigned char gray_to_pseudocolor[RAMP_SIZE]; - - for (i = 0; i < CUBE_SIZE; i++) - cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1); - for (i = 0; i < RAMP_SIZE; i++) - ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); - - info = malloc (sizeof (cairo_xlib_visual_info_t)); - if (unlikely (info == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - info->visualid = visualid; - - /* Allocate a gray ramp and a color cube. - * Give up as soon as failures start. */ - - for (gray = 0; gray < RAMP_SIZE; gray++) { - color.red = color.green = color.blue = ramp_index_to_short[gray]; - if (! XAllocColor (dpy, colormap, &color)) - goto DONE_ALLOCATE; - } - - /* XXX: Could do this in a more clever order to have the best - * possible results from early failure. Could also choose a cube - * uniformly distributed in a better space than RGB. */ - for (red = 0; red < CUBE_SIZE; red++) { - for (green = 0; green < CUBE_SIZE; green++) { - for (blue = 0; blue < CUBE_SIZE; blue++) { - color.red = cube_index_to_short[red]; - color.green = cube_index_to_short[green]; - color.blue = cube_index_to_short[blue]; - color.pixel = 0; - color.flags = 0; - color.pad = 0; - if (! XAllocColor (dpy, colormap, &color)) - goto DONE_ALLOCATE; - } - } - } - DONE_ALLOCATE: - - for (i = 0; i < ARRAY_LENGTH (colors); i++) - colors[i].pixel = i; - XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors)); - - /* Search for nearest colors within allocated colormap. */ - for (gray = 0; gray < RAMP_SIZE; gray++) { - for (i = 0; i < 256; i++) { - distance = _color_distance (ramp_index_to_short[gray], - ramp_index_to_short[gray], - ramp_index_to_short[gray], - colors[i].red, - colors[i].green, - colors[i].blue); - if (i == 0 || distance < min_distance) { - gray_to_pseudocolor[gray] = colors[i].pixel; - min_distance = distance; - if (!min_distance) - break; - } - } - } - for (red = 0; red < CUBE_SIZE; red++) { - for (green = 0; green < CUBE_SIZE; green++) { - for (blue = 0; blue < CUBE_SIZE; blue++) { - for (i = 0; i < 256; i++) { - distance = _color_distance (cube_index_to_short[red], - cube_index_to_short[green], - cube_index_to_short[blue], - colors[i].red, - colors[i].green, - colors[i].blue); - if (i == 0 || distance < min_distance) { - info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel; - min_distance = distance; - if (!min_distance) - break; - } - } - } - } - } - - for (i = 0, j = 0; i < 256; i++) { - if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i))) - j++; - info->field8_to_cube[i] = j; - - info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1); - } - for (i = 0, j = 0; i < 256; i++) { - if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i))) - j++; - info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j]; - } - - for (i = 0; i < 256; i++) { - info->colors[i].a = 0xff; - info->colors[i].r = colors[i].red >> 8; - info->colors[i].g = colors[i].green >> 8; - info->colors[i].b = colors[i].blue >> 8; - } - - *out = info; - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info) -{ - /* No need for XFreeColors() whilst using DefaultColormap */ - free (info); -} diff --git a/libs/cairo/cairo/src/cairo-xlib-xrender-private.h b/libs/cairo/cairo/src/cairo-xlib-xrender-private.h deleted file mode 100644 index 6372787a0..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-xrender-private.h +++ /dev/null @@ -1,1138 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Part of this file is subject to a different license. See below. */ - -#ifndef CAIRO_XLIB_XRENDER_PRIVATE_H -#define CAIRO_XLIB_XRENDER_PRIVATE_H - -#include "cairo-features.h" -#include "cairo-compiler-private.h" - -#include - -/* These prototypes are used when defining interfaces missing from the - * render headers. As it happens, it is the case that all libxrender - * functions take a pointer as first argument. */ - -__attribute__((__unused__)) static void _void_consume (void *p, ...) { } -__attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } -__attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } -__attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } - - -#if CAIRO_HAS_XLIB_XRENDER_SURFACE - -#include "cairo-xlib-xrender.h" - -#include -#include - -/* We require Render >= 0.6. The following defines were only added in - * 0.10. Make sure they are defined. - */ - -/* Filters included in 0.10 */ -#ifndef FilterConvolution -#define FilterConvolution "convolution" -#endif - -/* Extended repeat attributes included in 0.10 */ -#ifndef RepeatNone -#define RepeatNone 0 -#define RepeatNormal 1 -#define RepeatPad 2 -#define RepeatReflect 3 -#endif - - -#ifndef PictOptBlendMinimum -/* - * Operators only available in version 0.11 - */ -#define PictOpBlendMinimum 0x30 -#define PictOpMultiply 0x30 -#define PictOpScreen 0x31 -#define PictOpOverlay 0x32 -#define PictOpDarken 0x33 -#define PictOpLighten 0x34 -#define PictOpColorDodge 0x35 -#define PictOpColorBurn 0x36 -#define PictOpHardLight 0x37 -#define PictOpSoftLight 0x38 -#define PictOpDifference 0x39 -#define PictOpExclusion 0x3a -#define PictOpHSLHue 0x3b -#define PictOpHSLSaturation 0x3c -#define PictOpHSLColor 0x3d -#define PictOpHSLLuminosity 0x3e -#define PictOpBlendMaximum 0x3e -#endif - -/* There doesn't appear to be a simple #define that we can conditionalize - * on. Instead, use the version; gradients were introdiced in 0.10. */ -#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 -#define XRenderCreateLinearGradient _int_consume -#define XRenderCreateRadialGradient _int_consume -#define XRenderCreateConicalGradient _int_consume -typedef struct _XCircle { - XFixed x; - XFixed y; - XFixed radius; -} XCircle; -typedef struct _XLinearGradient { - XPointFixed p1; - XPointFixed p2; -} XLinearGradient; - -typedef struct _XRadialGradient { - XCircle inner; - XCircle outer; -} XRadialGradient; - -typedef struct _XConicalGradient { - XPointFixed center; - XFixed angle; /* in degrees */ -} XConicalGradient; -#endif - - -#else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ - -/* Provide dummy symbols and macros to get it compile and take the fallback - * route, just like as if Xrender is not available in the server at run-time. */ - - -/* Functions */ - -#define XRenderQueryExtension _int_consume -#define XRenderQueryVersion _int_consume -#define XRenderQueryFormats _int_consume -#define XRenderQuerySubpixelOrder _int_consume -#define XRenderSetSubpixelOrder _int_consume -#define XRenderFindVisualFormat _voidp_consume -#define XRenderFindFormat _voidp_consume -#define XRenderFindStandardFormat _voidp_consume -#define XRenderQueryPictIndexValues _voidp_consume -#define XRenderCreatePicture _int_consume -#define XRenderChangePicture _void_consume -#define XRenderSetPictureClipRectangles _void_consume -#define XRenderSetPictureClipRegion _void_consume -#define XRenderSetPictureTransform _void_consume -#define XRenderFreePicture _void_consume_free -#define XRenderComposite _void_consume -#define XRenderCreateGlyphSet _int_consume -#define XRenderReferenceGlyphSet _int_consume -#define XRenderFreeGlyphSet _void_consume_free -#define XRenderAddGlyphs _void_consume -#define XRenderFreeGlyphs _void_consume -#define XRenderCompositeString8 _void_consume -#define XRenderCompositeString16 _void_consume -#define XRenderCompositeString32 _void_consume -#define XRenderCompositeText8 (cairo_xrender_composite_text_func_t) _void_consume -#define XRenderCompositeText16 _void_consume -#define XRenderCompositeText32 _void_consume -#define XRenderFillRectangle _void_consume -#define XRenderFillRectangles _void_consume -#define XRenderCompositeTrapezoids _void_consume -#define XRenderCompositeTriangles _void_consume -#define XRenderCompositeTriStrip _void_consume -#define XRenderCompositeTriFan _void_consume -#define XRenderCompositeDoublePoly _void_consume -#define XRenderParseColor _int_consume -#define XRenderCreateCursor _int_consume -#define XRenderQueryFilters _voidp_consume -#define XRenderSetPictureFilter _void_consume -#define XRenderCreateAnimCursor _int_consume -#define XRenderAddTraps _void_consume -#define XRenderCreateSolidFill _int_consume -#define XRenderCreateLinearGradient _int_consume -#define XRenderCreateRadialGradient _int_consume -#define XRenderCreateConicalGradient _int_consume - -#define cairo_xlib_surface_create_with_xrender_format _voidp_consume - - - -/* The rest of this file is copied from various Xrender header files, with - * the following copyright/license information: - * - * Copyright (C) 2000 SuSE, Inc. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of SuSE not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. SuSE makes no representations about the - * suitability of this software for any purpose. It is provided "as is" - * without express or implied warranty. - * - * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: Keith Packard, SuSE, Inc. - */ - - -/* Copied from X11/extensions/render.h */ - -typedef unsigned long Glyph; -typedef unsigned long GlyphSet; -typedef unsigned long Picture; -typedef unsigned long PictFormat; - -#define BadPictFormat 0 -#define BadPicture 1 -#define BadPictOp 2 -#define BadGlyphSet 3 -#define BadGlyph 4 -#define RenderNumberErrors (BadGlyph+1) - -#define PictTypeIndexed 0 -#define PictTypeDirect 1 - -#define PictOpMinimum 0 -#define PictOpClear 0 -#define PictOpSrc 1 -#define PictOpDst 2 -#define PictOpOver 3 -#define PictOpOverReverse 4 -#define PictOpIn 5 -#define PictOpInReverse 6 -#define PictOpOut 7 -#define PictOpOutReverse 8 -#define PictOpAtop 9 -#define PictOpAtopReverse 10 -#define PictOpXor 11 -#define PictOpAdd 12 -#define PictOpSaturate 13 -#define PictOpMaximum 13 - -/* - * Operators only available in version 0.2 - */ -#define PictOpDisjointMinimum 0x10 -#define PictOpDisjointClear 0x10 -#define PictOpDisjointSrc 0x11 -#define PictOpDisjointDst 0x12 -#define PictOpDisjointOver 0x13 -#define PictOpDisjointOverReverse 0x14 -#define PictOpDisjointIn 0x15 -#define PictOpDisjointInReverse 0x16 -#define PictOpDisjointOut 0x17 -#define PictOpDisjointOutReverse 0x18 -#define PictOpDisjointAtop 0x19 -#define PictOpDisjointAtopReverse 0x1a -#define PictOpDisjointXor 0x1b -#define PictOpDisjointMaximum 0x1b - -#define PictOpConjointMinimum 0x20 -#define PictOpConjointClear 0x20 -#define PictOpConjointSrc 0x21 -#define PictOpConjointDst 0x22 -#define PictOpConjointOver 0x23 -#define PictOpConjointOverReverse 0x24 -#define PictOpConjointIn 0x25 -#define PictOpConjointInReverse 0x26 -#define PictOpConjointOut 0x27 -#define PictOpConjointOutReverse 0x28 -#define PictOpConjointAtop 0x29 -#define PictOpConjointAtopReverse 0x2a -#define PictOpConjointXor 0x2b -#define PictOpConjointMaximum 0x2b - -/* - * Operators only available in version 0.11 - */ -#define PictOpBlendMinimum 0x30 -#define PictOpMultiply 0x30 -#define PictOpScreen 0x31 -#define PictOpOverlay 0x32 -#define PictOpDarken 0x33 -#define PictOpLighten 0x34 -#define PictOpColorDodge 0x35 -#define PictOpColorBurn 0x36 -#define PictOpHardLight 0x37 -#define PictOpSoftLight 0x38 -#define PictOpDifference 0x39 -#define PictOpExclusion 0x3a -#define PictOpHSLHue 0x3b -#define PictOpHSLSaturation 0x3c -#define PictOpHSLColor 0x3d -#define PictOpHSLLuminosity 0x3e -#define PictOpBlendMaximum 0x3e - -#define PolyEdgeSharp 0 -#define PolyEdgeSmooth 1 - -#define PolyModePrecise 0 -#define PolyModeImprecise 1 - -#define CPRepeat (1 << 0) -#define CPAlphaMap (1 << 1) -#define CPAlphaXOrigin (1 << 2) -#define CPAlphaYOrigin (1 << 3) -#define CPClipXOrigin (1 << 4) -#define CPClipYOrigin (1 << 5) -#define CPClipMask (1 << 6) -#define CPGraphicsExposure (1 << 7) -#define CPSubwindowMode (1 << 8) -#define CPPolyEdge (1 << 9) -#define CPPolyMode (1 << 10) -#define CPDither (1 << 11) -#define CPComponentAlpha (1 << 12) -#define CPLastBit 12 - -/* Filters included in 0.6 */ -#define FilterNearest "nearest" -#define FilterBilinear "bilinear" -/* Filters included in 0.10 */ -#define FilterConvolution "convolution" - -#define FilterFast "fast" -#define FilterGood "good" -#define FilterBest "best" - -#define FilterAliasNone -1 - -/* Subpixel orders included in 0.6 */ -#define SubPixelUnknown 0 -#define SubPixelHorizontalRGB 1 -#define SubPixelHorizontalBGR 2 -#define SubPixelVerticalRGB 3 -#define SubPixelVerticalBGR 4 -#define SubPixelNone 5 - -/* Extended repeat attributes included in 0.10 */ -#define RepeatNone 0 -#define RepeatNormal 1 -#define RepeatPad 2 -#define RepeatReflect 3 - - - -/* Copied from X11/extensions/Xrender.h */ - -typedef struct { - short red; - short redMask; - short green; - short greenMask; - short blue; - short blueMask; - short alpha; - short alphaMask; -} XRenderDirectFormat; - -typedef struct { - PictFormat id; - int type; - int depth; - XRenderDirectFormat direct; - Colormap colormap; -} XRenderPictFormat; - -#define PictFormatID (1 << 0) -#define PictFormatType (1 << 1) -#define PictFormatDepth (1 << 2) -#define PictFormatRed (1 << 3) -#define PictFormatRedMask (1 << 4) -#define PictFormatGreen (1 << 5) -#define PictFormatGreenMask (1 << 6) -#define PictFormatBlue (1 << 7) -#define PictFormatBlueMask (1 << 8) -#define PictFormatAlpha (1 << 9) -#define PictFormatAlphaMask (1 << 10) -#define PictFormatColormap (1 << 11) - -typedef struct _XRenderPictureAttributes { - int repeat; - Picture alpha_map; - int alpha_x_origin; - int alpha_y_origin; - int clip_x_origin; - int clip_y_origin; - Pixmap clip_mask; - Bool graphics_exposures; - int subwindow_mode; - int poly_edge; - int poly_mode; - Atom dither; - Bool component_alpha; -} XRenderPictureAttributes; - -typedef struct { - unsigned short red; - unsigned short green; - unsigned short blue; - unsigned short alpha; -} XRenderColor; - -typedef struct _XGlyphInfo { - unsigned short width; - unsigned short height; - short x; - short y; - short xOff; - short yOff; -} XGlyphInfo; - -typedef struct _XGlyphElt8 { - GlyphSet glyphset; - _Xconst char *chars; - int nchars; - int xOff; - int yOff; -} XGlyphElt8; - -typedef struct _XGlyphElt16 { - GlyphSet glyphset; - _Xconst unsigned short *chars; - int nchars; - int xOff; - int yOff; -} XGlyphElt16; - -typedef struct _XGlyphElt32 { - GlyphSet glyphset; - _Xconst unsigned int *chars; - int nchars; - int xOff; - int yOff; -} XGlyphElt32; - -typedef double XDouble; - -typedef struct _XPointDouble { - XDouble x, y; -} XPointDouble; - -#define XDoubleToFixed(f) ((XFixed) ((f) * 65536)) -#define XFixedToDouble(f) (((XDouble) (f)) / 65536) - -typedef int XFixed; - -typedef struct _XPointFixed { - XFixed x, y; -} XPointFixed; - -typedef struct _XLineFixed { - XPointFixed p1, p2; -} XLineFixed; - -typedef struct _XTriangle { - XPointFixed p1, p2, p3; -} XTriangle; - -typedef struct _XCircle { - XFixed x; - XFixed y; - XFixed radius; -} XCircle; - -typedef struct _XTrapezoid { - XFixed top, bottom; - XLineFixed left, right; -} XTrapezoid; - -typedef struct _XTransform { - XFixed matrix[3][3]; -} XTransform; - -typedef struct _XFilters { - int nfilter; - char **filter; - int nalias; - short *alias; -} XFilters; - -typedef struct _XIndexValue { - unsigned long pixel; - unsigned short red, green, blue, alpha; -} XIndexValue; - -typedef struct _XAnimCursor { - Cursor cursor; - unsigned long delay; -} XAnimCursor; - -typedef struct _XSpanFix { - XFixed left, right, y; -} XSpanFix; - -typedef struct _XTrap { - XSpanFix top, bottom; -} XTrap; - -typedef struct _XLinearGradient { - XPointFixed p1; - XPointFixed p2; -} XLinearGradient; - -typedef struct _XRadialGradient { - XCircle inner; - XCircle outer; -} XRadialGradient; - -typedef struct _XConicalGradient { - XPointFixed center; - XFixed angle; /* in degrees */ -} XConicalGradient; - -#define PictStandardARGB32 0 -#define PictStandardRGB24 1 -#define PictStandardA8 2 -#define PictStandardA4 3 -#define PictStandardA1 4 -#define PictStandardNUM 5 - - - -/* Copied from X11/extensions/renderproto.h */ - -#include - -#define Window CARD32 -#define Drawable CARD32 -#define Font CARD32 -#define Pixmap CARD32 -#define Cursor CARD32 -#define Colormap CARD32 -#define GContext CARD32 -#define Atom CARD32 -#define VisualID CARD32 -#define Time CARD32 -#define KeyCode CARD8 -#define KeySym CARD32 - -#define Picture CARD32 -#define PictFormat CARD32 -#define Fixed INT32 -#define Glyphset CARD32 -#define Glyph CARD32 - -/* - * data structures - */ - -typedef struct { - CARD16 red B16; - CARD16 redMask B16; - CARD16 green B16; - CARD16 greenMask B16; - CARD16 blue B16; - CARD16 blueMask B16; - CARD16 alpha B16; - CARD16 alphaMask B16; -} xDirectFormat; - -#define sz_xDirectFormat 16 - -typedef struct { - PictFormat id B32; - CARD8 type; - CARD8 depth; - CARD16 pad1 B16; - xDirectFormat direct; - Colormap colormap; -} xPictFormInfo; - -#define sz_xPictFormInfo 28 - -typedef struct { - VisualID visual; - PictFormat format; -} xPictVisual; - -#define sz_xPictVisual 8 - -typedef struct { - CARD8 depth; - CARD8 pad1; - CARD16 nPictVisuals B16; - CARD32 pad2 B32; -} xPictDepth; - -#define sz_xPictDepth 8 - -typedef struct { - CARD32 nDepth B32; - PictFormat fallback B32; -} xPictScreen; - -#define sz_xPictScreen 8 - -typedef struct { - CARD32 pixel B32; - CARD16 red B16; - CARD16 green B16; - CARD16 blue B16; - CARD16 alpha B16; -} xIndexValue; - -#define sz_xIndexValue 12 - -typedef struct { - CARD16 red B16; - CARD16 green B16; - CARD16 blue B16; - CARD16 alpha B16; -} xRenderColor; - -#define sz_xRenderColor 8 - -typedef struct { - Fixed x B32; - Fixed y B32; -} xPointFixed; - -#define sz_xPointFixed 8 - -typedef struct { - xPointFixed p1; - xPointFixed p2; -} xLineFixed; - -#define sz_xLineFixed 16 - -typedef struct { - xPointFixed p1, p2, p3; -} xTriangle; - -#define sz_xTriangle 24 - -typedef struct { - Fixed top B32; - Fixed bottom B32; - xLineFixed left; - xLineFixed right; -} xTrapezoid; - -#define sz_xTrapezoid 40 - -typedef struct { - CARD16 width B16; - CARD16 height B16; - INT16 x B16; - INT16 y B16; - INT16 xOff B16; - INT16 yOff B16; -} xGlyphInfo; - -#define sz_xGlyphInfo 12 - -typedef struct { - CARD8 len; - CARD8 pad1; - CARD16 pad2; - INT16 deltax; - INT16 deltay; -} xGlyphElt; - -#define sz_xGlyphElt 8 - -typedef struct { - Fixed l, r, y; -} xSpanFix; - -#define sz_xSpanFix 12 - -typedef struct { - xSpanFix top, bot; -} xTrap; - -#define sz_xTrap 24 - -/* - * requests and replies - */ -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD32 majorVersion B32; - CARD32 minorVersion B32; -} xRenderQueryVersionReq; - -#define sz_xRenderQueryVersionReq 12 - -typedef struct { - BYTE type; /* X_Reply */ - BYTE pad1; - CARD16 sequenceNumber B16; - CARD32 length B32; - CARD32 majorVersion B32; - CARD32 minorVersion B32; - CARD32 pad2 B32; - CARD32 pad3 B32; - CARD32 pad4 B32; - CARD32 pad5 B32; -} xRenderQueryVersionReply; - -#define sz_xRenderQueryVersionReply 32 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; -} xRenderQueryPictFormatsReq; - -#define sz_xRenderQueryPictFormatsReq 4 - -typedef struct { - BYTE type; /* X_Reply */ - BYTE pad1; - CARD16 sequenceNumber B16; - CARD32 length B32; - CARD32 numFormats B32; - CARD32 numScreens B32; - CARD32 numDepths B32; - CARD32 numVisuals B32; - CARD32 numSubpixel B32; /* Version 0.6 */ - CARD32 pad5 B32; -} xRenderQueryPictFormatsReply; - -#define sz_xRenderQueryPictFormatsReply 32 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - PictFormat format B32; -} xRenderQueryPictIndexValuesReq; - -#define sz_xRenderQueryPictIndexValuesReq 8 - -typedef struct { - BYTE type; /* X_Reply */ - BYTE pad1; - CARD16 sequenceNumber B16; - CARD32 length B32; - CARD32 numIndexValues; - CARD32 pad2 B32; - CARD32 pad3 B32; - CARD32 pad4 B32; - CARD32 pad5 B32; - CARD32 pad6 B32; -} xRenderQueryPictIndexValuesReply; - -#define sz_xRenderQueryPictIndexValuesReply 32 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture pid B32; - Drawable drawable B32; - PictFormat format B32; - CARD32 mask B32; -} xRenderCreatePictureReq; - -#define sz_xRenderCreatePictureReq 20 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture B32; - CARD32 mask B32; -} xRenderChangePictureReq; - -#define sz_xRenderChangePictureReq 12 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture B32; - INT16 xOrigin B16; - INT16 yOrigin B16; -} xRenderSetPictureClipRectanglesReq; - -#define sz_xRenderSetPictureClipRectanglesReq 12 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture B32; -} xRenderFreePictureReq; - -#define sz_xRenderFreePictureReq 8 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture mask B32; - Picture dst B32; - INT16 xSrc B16; - INT16 ySrc B16; - INT16 xMask B16; - INT16 yMask B16; - INT16 xDst B16; - INT16 yDst B16; - CARD16 width B16; - CARD16 height B16; -} xRenderCompositeReq; - -#define sz_xRenderCompositeReq 36 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture src B32; - Picture dst B32; - CARD32 colorScale B32; - CARD32 alphaScale B32; - INT16 xSrc B16; - INT16 ySrc B16; - INT16 xDst B16; - INT16 yDst B16; - CARD16 width B16; - CARD16 height B16; -} xRenderScaleReq; - -#define sz_xRenderScaleReq 32 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture dst B32; - PictFormat maskFormat B32; - INT16 xSrc B16; - INT16 ySrc B16; -} xRenderTrapezoidsReq; - -#define sz_xRenderTrapezoidsReq 24 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture dst B32; - PictFormat maskFormat B32; - INT16 xSrc B16; - INT16 ySrc B16; -} xRenderTrianglesReq; - -#define sz_xRenderTrianglesReq 24 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture dst B32; - PictFormat maskFormat B32; - INT16 xSrc B16; - INT16 ySrc B16; -} xRenderTriStripReq; - -#define sz_xRenderTriStripReq 24 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture dst B32; - PictFormat maskFormat B32; - INT16 xSrc B16; - INT16 ySrc B16; -} xRenderTriFanReq; - -#define sz_xRenderTriFanReq 24 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Glyphset gsid B32; - PictFormat format B32; -} xRenderCreateGlyphSetReq; - -#define sz_xRenderCreateGlyphSetReq 12 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Glyphset gsid B32; - Glyphset existing B32; -} xRenderReferenceGlyphSetReq; - -#define sz_xRenderReferenceGlyphSetReq 24 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Glyphset glyphset B32; -} xRenderFreeGlyphSetReq; - -#define sz_xRenderFreeGlyphSetReq 8 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Glyphset glyphset B32; - CARD32 nglyphs; -} xRenderAddGlyphsReq; - -#define sz_xRenderAddGlyphsReq 12 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Glyphset glyphset B32; -} xRenderFreeGlyphsReq; - -#define sz_xRenderFreeGlyphsReq 8 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture src B32; - Picture dst B32; - PictFormat maskFormat B32; - Glyphset glyphset B32; - INT16 xSrc B16; - INT16 ySrc B16; -} xRenderCompositeGlyphsReq, xRenderCompositeGlyphs8Req, -xRenderCompositeGlyphs16Req, xRenderCompositeGlyphs32Req; - -#define sz_xRenderCompositeGlyphs8Req 28 -#define sz_xRenderCompositeGlyphs16Req 28 -#define sz_xRenderCompositeGlyphs32Req 28 - -/* 0.1 and higher */ - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - CARD8 op; - CARD8 pad1; - CARD16 pad2 B16; - Picture dst B32; - xRenderColor color; -} xRenderFillRectanglesReq; - -#define sz_xRenderFillRectanglesReq 20 - -/* 0.5 and higher */ - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Cursor cid B32; - Picture src B32; - CARD16 x B16; - CARD16 y B16; -} xRenderCreateCursorReq; - -#define sz_xRenderCreateCursorReq 16 - -/* 0.6 and higher */ - -/* - * This can't use an array because 32-bit values may be in bitfields - */ -typedef struct { - Fixed matrix11 B32; - Fixed matrix12 B32; - Fixed matrix13 B32; - Fixed matrix21 B32; - Fixed matrix22 B32; - Fixed matrix23 B32; - Fixed matrix31 B32; - Fixed matrix32 B32; - Fixed matrix33 B32; -} xRenderTransform; - -#define sz_xRenderTransform 36 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture B32; - xRenderTransform transform; -} xRenderSetPictureTransformReq; - -#define sz_xRenderSetPictureTransformReq 44 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Drawable drawable B32; -} xRenderQueryFiltersReq; - -#define sz_xRenderQueryFiltersReq 8 - -typedef struct { - BYTE type; /* X_Reply */ - BYTE pad1; - CARD16 sequenceNumber B16; - CARD32 length B32; - CARD32 numAliases B32; /* LISTofCARD16 */ - CARD32 numFilters B32; /* LISTofSTRING8 */ - CARD32 pad2 B32; - CARD32 pad3 B32; - CARD32 pad4 B32; - CARD32 pad5 B32; -} xRenderQueryFiltersReply; - -#define sz_xRenderQueryFiltersReply 32 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture B32; - CARD16 nbytes B16; /* number of bytes in name */ - CARD16 pad B16; -} xRenderSetPictureFilterReq; - -#define sz_xRenderSetPictureFilterReq 12 - -/* 0.8 and higher */ - -typedef struct { - Cursor cursor B32; - CARD32 delay B32; -} xAnimCursorElt; - -#define sz_xAnimCursorElt 8 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Cursor cid B32; -} xRenderCreateAnimCursorReq; - -#define sz_xRenderCreateAnimCursorReq 8 - -/* 0.9 and higher */ - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture picture; - INT16 xOff B16; - INT16 yOff B16; -} xRenderAddTrapsReq; - -#define sz_xRenderAddTrapsReq 12 - -/* 0.10 and higher */ - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture pid B32; - xRenderColor color; -} xRenderCreateSolidFillReq; - -#define sz_xRenderCreateSolidFillReq 16 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture pid B32; - xPointFixed p1; - xPointFixed p2; - CARD32 nStops; -} xRenderCreateLinearGradientReq; - -#define sz_xRenderCreateLinearGradientReq 28 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture pid B32; - xPointFixed inner; - xPointFixed outer; - Fixed inner_radius; - Fixed outer_radius; - CARD32 nStops; -} xRenderCreateRadialGradientReq; - -#define sz_xRenderCreateRadialGradientReq 36 - -typedef struct { - CARD8 reqType; - CARD8 renderReqType; - CARD16 length B16; - Picture pid B32; - xPointFixed center; - Fixed angle; /* in degrees */ - CARD32 nStops; -} xRenderCreateConicalGradientReq; - -#define sz_xRenderCreateConicalGradientReq 24 - -#undef Window -#undef Drawable -#undef Font -#undef Pixmap -#undef Cursor -#undef Colormap -#undef GContext -#undef Atom -#undef VisualID -#undef Time -#undef KeyCode -#undef KeySym - -#undef Picture -#undef PictFormat -#undef Fixed -#undef Glyphset -#undef Glyph - - -#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ - -#endif /* CAIRO_XLIB_XRENDER_PRIVATE_H */ diff --git a/libs/cairo/cairo/src/cairo-xlib-xrender.h b/libs/cairo/cairo/src/cairo-xlib-xrender.h deleted file mode 100644 index 996983e4e..000000000 --- a/libs/cairo/cairo/src/cairo-xlib-xrender.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XLIB_XRENDER_H -#define CAIRO_XLIB_XRENDER_H - -#include "cairo.h" - -#if CAIRO_HAS_XLIB_XRENDER_SURFACE - -#include -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xlib_surface_create_with_xrender_format (Display *dpy, - Drawable drawable, - Screen *screen, - XRenderPictFormat *format, - int width, - int height); - -cairo_public XRenderPictFormat * -cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ -# error Cairo was not compiled with support for the xlib XRender backend -#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ - -#endif /* CAIRO_XLIB_XRENDER_H */ diff --git a/libs/cairo/cairo/src/cairo-xlib.h b/libs/cairo/cairo/src/cairo-xlib.h deleted file mode 100644 index fdcff769c..000000000 --- a/libs/cairo/cairo/src/cairo-xlib.h +++ /dev/null @@ -1,68 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_XLIB_H -#define CAIRO_XLIB_H - -#include "cairo.h" - -#if CAIRO_HAS_XLIB_SURFACE - -#include - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_xlib_surface_create_for_bitmap (Display *dpy, - Pixmap bitmap, - Screen *screen, - int width, - int height); - -cairo_public void -cairo_xlib_surface_set_size (cairo_surface_t *surface, - int width, - int height); - -cairo_public void -cairo_xlib_surface_set_drawable (cairo_surface_t *surface, - Drawable drawable, - int width, - int height); - -cairo_public Display * -cairo_xlib_surface_get_display (cairo_surface_t *surface); - -cairo_public Drawable -cairo_xlib_surface_get_drawable (cairo_surface_t *surface); - -cairo_public Screen * -cairo_xlib_surface_get_screen (cairo_surface_t *surface); - -cairo_public Visual * -cairo_xlib_surface_get_visual (cairo_surface_t *surface); - -cairo_public int -cairo_xlib_surface_get_depth (cairo_surface_t *surface); - -cairo_public int -cairo_xlib_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_xlib_surface_get_height (cairo_surface_t *surface); - -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/libs/cairo/cairo/src/cairo-xml-surface.c b/libs/cairo/cairo/src/cairo-xml-surface.c deleted file mode 100644 index f1c3c3ed6..000000000 --- a/libs/cairo/cairo/src/cairo-xml-surface.c +++ /dev/null @@ -1,1120 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This surface is intended to produce a verbose, hierarchical, DAG XML file - * representing a single surface. It is intended to be used by debuggers, - * such as cairo-sphinx, or by application test-suites that what a log of - * operations. - */ - -#include "cairoint.h" - -#include "cairo-xml.h" - -#include "cairo-clip-private.h" -#include "cairo-device-private.h" -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-recording-surface-private.h" - -#define static cairo_warn static - -typedef struct _cairo_xml_surface cairo_xml_surface_t; - -typedef struct _cairo_xml { - cairo_device_t base; - - cairo_output_stream_t *stream; - int indent; -} cairo_xml_t; - -struct _cairo_xml_surface { - cairo_surface_t base; - - double width, height; -}; - -slim_hidden_proto (cairo_xml_for_recording_surface); - -static const cairo_surface_backend_t _cairo_xml_surface_backend; - -static const char * -_operator_to_string (cairo_operator_t op) -{ - static const char *names[] = { - "CLEAR", /* CAIRO_OPERATOR_CLEAR */ - - "SOURCE", /* CAIRO_OPERATOR_SOURCE */ - "OVER", /* CAIRO_OPERATOR_OVER */ - "IN", /* CAIRO_OPERATOR_IN */ - "OUT", /* CAIRO_OPERATOR_OUT */ - "ATOP", /* CAIRO_OPERATOR_ATOP */ - - "DEST", /* CAIRO_OPERATOR_DEST */ - "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ - "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ - "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ - "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ - - "XOR", /* CAIRO_OPERATOR_XOR */ - "ADD", /* CAIRO_OPERATOR_ADD */ - "SATURATE", /* CAIRO_OPERATOR_SATURATE */ - - "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ - "SCREEN", /* CAIRO_OPERATOR_SCREEN */ - "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ - "DARKEN", /* CAIRO_OPERATOR_DARKEN */ - "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ - "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ - "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ - "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ - "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ - "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ - "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ - "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ - "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ - "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ - "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ - }; - assert (op < ARRAY_LENGTH (names)); - return names[op]; -} - -static const char * -_extend_to_string (cairo_extend_t extend) -{ - static const char *names[] = { - "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ - "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ - "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ - "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ - }; - assert (extend < ARRAY_LENGTH (names)); - return names[extend]; -} - -static const char * -_filter_to_string (cairo_filter_t filter) -{ - static const char *names[] = { - "FILTER_FAST", /* CAIRO_FILTER_FAST */ - "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ - "FILTER_BEST", /* CAIRO_FILTER_BEST */ - "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ - "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ - "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ - }; - assert (filter < ARRAY_LENGTH (names)); - return names[filter]; -} - -static const char * -_fill_rule_to_string (cairo_fill_rule_t rule) -{ - static const char *names[] = { - "WINDING", /* CAIRO_FILL_RULE_WINDING */ - "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ - }; - assert (rule < ARRAY_LENGTH (names)); - return names[rule]; -} - -static const char * -_antialias_to_string (cairo_antialias_t antialias) -{ - static const char *names[] = { - "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ - "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ - "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ - "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ - }; - assert (antialias < ARRAY_LENGTH (names)); - return names[antialias]; -} - -static const char * -_line_cap_to_string (cairo_line_cap_t line_cap) -{ - static const char *names[] = { - "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ - "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ - "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ - }; - assert (line_cap < ARRAY_LENGTH (names)); - return names[line_cap]; -} - -static const char * -_line_join_to_string (cairo_line_join_t line_join) -{ - static const char *names[] = { - "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ - "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ - "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ - }; - assert (line_join < ARRAY_LENGTH (names)); - return names[line_join]; -} - -static const char * -_content_to_string (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_ALPHA: return "ALPHA"; - case CAIRO_CONTENT_COLOR: return "COLOR"; - default: - case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; - } -} - -static const char * -_format_to_string (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: return "ARGB32"; - case CAIRO_FORMAT_RGB24: return "RGB24"; - case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; - case CAIRO_FORMAT_A8: return "A8"; - case CAIRO_FORMAT_A1: return "A1"; - case CAIRO_FORMAT_INVALID: return "INVALID"; - } - ASSERT_NOT_REACHED; - return "INVALID"; -} - -static cairo_status_t -_device_flush (void *abstract_device) -{ - cairo_xml_t *xml = abstract_device; - cairo_status_t status; - - status = _cairo_output_stream_flush (xml->stream); - - return status; -} - -static void -_device_destroy (void *abstract_device) -{ - cairo_xml_t *xml = abstract_device; - cairo_status_t status; - - status = _cairo_output_stream_destroy (xml->stream); - - free (xml); -} - -static const cairo_device_backend_t _cairo_xml_device_backend = { - CAIRO_DEVICE_TYPE_XML, - - NULL, NULL, /* lock, unlock */ - - _device_flush, - NULL, /* finish */ - _device_destroy -}; - -static cairo_device_t * -_cairo_xml_create_internal (cairo_output_stream_t *stream) -{ - cairo_xml_t *xml; - - xml = malloc (sizeof (cairo_xml_t)); - if (unlikely (xml == NULL)) - return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); - - memset (xml, 0, sizeof (cairo_xml_t)); - - _cairo_device_init (&xml->base, &_cairo_xml_device_backend); - - xml->indent = 0; - xml->stream = stream; - - return &xml->base; -} - -static void -_cairo_xml_indent (cairo_xml_t *xml, int indent) -{ - xml->indent += indent; - assert (xml->indent >= 0); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) -{ - va_list ap; - char indent[80]; - int len; - - len = MIN (xml->indent, ARRAY_LENGTH (indent)); - memset (indent, ' ', len); - _cairo_output_stream_write (xml->stream, indent, len); - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - - _cairo_output_stream_write (xml->stream, "\n", 1); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) -{ - char indent[80]; - int len; - - len = MIN (xml->indent, ARRAY_LENGTH (indent)); - memset (indent, ' ', len); - _cairo_output_stream_write (xml->stream, indent, len); - - if (fmt != NULL) { - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - } -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) -{ - if (fmt != NULL) { - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - } - - _cairo_output_stream_write (xml->stream, "\n", 1); -} - -static cairo_surface_t * -_cairo_xml_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_rectangle_t extents; - - extents.x = extents.y = 0; - extents.width = width; - extents.height = height; - - return cairo_recording_surface_create (content, &extents); -} - -static cairo_bool_t -_cairo_xml_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_xml_surface_t *surface = abstract_surface; - - if (surface->width < 0 || surface->height < 0) - return FALSE; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static cairo_status_t -_cairo_xml_move_to (void *closure, - const cairo_point_t *p1) -{ - _cairo_xml_printf_continue (closure, " %f %f m", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_line_to (void *closure, - const cairo_point_t *p1) -{ - _cairo_xml_printf_continue (closure, " %f %f l", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y), - _cairo_fixed_to_double (p2->x), - _cairo_fixed_to_double (p2->y), - _cairo_fixed_to_double (p3->x), - _cairo_fixed_to_double (p3->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_close_path (void *closure) -{ - _cairo_xml_printf_continue (closure, " h"); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xml_emit_path (cairo_xml_t *xml, - cairo_path_fixed_t *path) -{ - cairo_status_t status; - - _cairo_xml_printf_start (xml, ""); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_xml_move_to, - _cairo_xml_line_to, - _cairo_xml_curve_to, - _cairo_xml_close_path, - xml); - assert (status == CAIRO_STATUS_SUCCESS); - _cairo_xml_printf_end (xml, ""); -} - -static void -_cairo_xml_emit_string (cairo_xml_t *xml, - const char *node, - const char *data) -{ - _cairo_xml_printf (xml, "<%s>%s", node, data, node); -} - -static void -_cairo_xml_emit_double (cairo_xml_t *xml, - const char *node, - double data) -{ - _cairo_xml_printf (xml, "<%s>%f", node, data, node); -} - -static cairo_xml_t * -to_xml (cairo_xml_surface_t *surface) -{ - return (cairo_xml_t *) surface->base.device; -} - -static cairo_status_t -_cairo_xml_surface_emit_clip_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, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_path (xml, &clip_path->path); - _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); - _cairo_xml_emit_string (xml, "antialias", - _antialias_to_string (clip_path->antialias)); - _cairo_xml_emit_string (xml, "fill-rule", - _fill_rule_to_string (clip_path->fill_rule)); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf_end (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, - 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, "%f %f %f %f", - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xml_emit_matrix (cairo_xml_t *xml, - const cairo_matrix_t *matrix) -{ - if (! _cairo_matrix_is_identity (matrix)) { - _cairo_xml_printf (xml, "%f %f %f %f %f %f", - matrix->xx, matrix->yx, - matrix->xy, matrix->yy, - matrix->x0, matrix->y0); - } -} - -static void -_cairo_xml_emit_gradient (cairo_xml_t *xml, - const cairo_gradient_pattern_t *gradient) -{ - unsigned int i; - - for (i = 0; i < gradient->n_stops; i++) { - _cairo_xml_printf (xml, - "%f %f %f %f %f", - gradient->stops[i].offset, - gradient->stops[i].color.red, - gradient->stops[i].color.green, - gradient->stops[i].color.blue, - gradient->stops[i].color.alpha); - } -} - -static cairo_status_t -_cairo_xml_emit_linear (cairo_xml_t *xml, - const cairo_linear_pattern_t *linear) -{ - _cairo_xml_printf (xml, - "", - _cairo_fixed_to_double (linear->p1.x), - _cairo_fixed_to_double (linear->p1.y), - _cairo_fixed_to_double (linear->p2.x), - _cairo_fixed_to_double (linear->p2.y)); - _cairo_xml_indent (xml, 2); - _cairo_xml_emit_gradient (xml, &linear->base); - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - 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, - "", - _cairo_fixed_to_double (radial->c1.x), - _cairo_fixed_to_double (radial->c1.y), - _cairo_fixed_to_double (radial->r1), - _cairo_fixed_to_double (radial->c2.x), - _cairo_fixed_to_double (radial->c2.y), - _cairo_fixed_to_double (radial->r2)); - _cairo_xml_indent (xml, 2); - _cairo_xml_emit_gradient (xml, &radial->base); - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_write_func (void *closure, const unsigned char *data, unsigned len) -{ - _cairo_output_stream_write (closure, data, len); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_image (cairo_xml_t *xml, - cairo_image_surface_t *image) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - _cairo_xml_printf_start (xml, - "", - image->width, image->height, - _format_to_string (image->format)); - - stream = _cairo_base64_stream_create (xml->stream); - status = cairo_surface_write_to_png_stream (&image->base, - _write_func, stream); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_output_stream_destroy (stream); - if (unlikely (status)) - return status; - - _cairo_xml_printf_end (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_surface (cairo_xml_t *xml, - const cairo_surface_pattern_t *pattern) -{ - cairo_surface_t *source = pattern->surface; - cairo_status_t status; - - if (_cairo_surface_is_recording (source)) { - status = cairo_xml_for_recording_surface (&xml->base, source); - } else { - cairo_image_surface_t *image; - void *image_extra; - - status = _cairo_surface_acquire_source_image (source, - &image, &image_extra); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_image (xml, image); - - _cairo_surface_release_source_image (source, image, image_extra); - } - - return status; -} - -static cairo_status_t -_cairo_xml_emit_pattern (cairo_xml_t *xml, - const char *source_or_mask, - const cairo_pattern_t *pattern) -{ - cairo_status_t status; - - _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); - _cairo_xml_indent (xml, 2); - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); - break; - default: - ASSERT_NOT_REACHED; - status = CAIRO_INT_STATUS_UNSUPPORTED; - break; - } - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - _cairo_xml_emit_matrix (xml, &pattern->matrix); - _cairo_xml_printf (xml, - "%s", - _extend_to_string (pattern->extend)); - _cairo_xml_printf (xml, - "%s", - _filter_to_string (pattern->filter)); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "", source_or_mask); - - return status; -} - -static cairo_int_status_t -_cairo_xml_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "mask", mask); - if (unlikely (status)) - return status; - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - _cairo_xml_emit_double (xml, "line-width", style->line_width); - _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); - _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); - _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - if (style->num_dashes) { - unsigned int i; - - _cairo_xml_printf_start (xml, "", - style->dash_offset); - for (i = 0; i < style->num_dashes; i++) - _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); - - _cairo_xml_printf_end (xml, ""); - } - - _cairo_xml_emit_path (xml, path); - _cairo_xml_emit_double (xml, "tolerance", tolerance); - _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); - - _cairo_xml_emit_matrix (xml, ctm); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - _cairo_xml_emit_path (xml, path); - _cairo_xml_emit_double (xml, "tolerance", tolerance); - _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); - _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -#if CAIRO_HAS_FT_FONT -#include "cairo-ft-private.h" -static cairo_status_t -_cairo_xml_emit_type42_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font) -{ - const cairo_scaled_font_backend_t *backend; - cairo_output_stream_t *base64_stream; - cairo_output_stream_t *zlib_stream; - cairo_status_t status, status2; - unsigned long size; - uint32_t len; - uint8_t *buf; - - backend = scaled_font->backend; - if (backend->load_truetype_table == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - size = 0; - status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); - if (unlikely (status)) - return status; - - buf = malloc (size); - if (unlikely (buf == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); - if (unlikely (status)) { - free (buf); - return status; - } - - _cairo_xml_printf_start (xml, "", - _cairo_ft_scaled_font_get_load_flags (scaled_font)); - - - base64_stream = _cairo_base64_stream_create (xml->stream); - len = size; - _cairo_output_stream_write (base64_stream, &len, sizeof (len)); - - zlib_stream = _cairo_deflate_stream_create (base64_stream); - - _cairo_output_stream_write (zlib_stream, buf, size); - free (buf); - - status2 = _cairo_output_stream_destroy (zlib_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - status2 = _cairo_output_stream_destroy (base64_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_xml_printf_end (xml, ""); - - return status; -} -#else -static cairo_status_t -_cairo_xml_emit_type42_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} -#endif - -static cairo_status_t -_cairo_xml_emit_type3_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - _cairo_xml_printf_start (xml, ""); - _cairo_xml_printf_end (xml, ""); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_scaled_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_status_t status; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - status = _cairo_xml_emit_type42_font (xml, scaled_font); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_xml_emit_type3_font (xml, scaled_font, - glyphs, num_glyphs); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return status; -} - -static cairo_int_status_t -_cairo_xml_surface_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - int i; - - _cairo_xml_printf (xml, ""); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); - if (unlikely (status)) - return status; - - for (i = 0; i < num_glyphs; i++) { - _cairo_xml_printf (xml, "%f %f", - glyphs[i].index, - glyphs[i].x, - glyphs[i].y); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - *remaining_glyphs = 0; - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t -_cairo_xml_surface_backend = { - CAIRO_SURFACE_TYPE_XML, - _cairo_xml_surface_create_similar, - NULL, - NULL, NULL, /* source image */ - NULL, NULL, /* dst image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, NULL, /* copy/show page */ - _cairo_xml_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* font fini */ - NULL, /* scaled_glyph_fini */ - - /* The 5 high level operations */ - _cairo_xml_surface_paint, - _cairo_xml_surface_mask, - _cairo_xml_surface_stroke, - _cairo_xml_surface_fill, - _cairo_xml_surface_glyphs, - - NULL, /* snapshot */ - - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - /* The alternate high-level text operation */ - NULL, NULL, /* has, show_text_glyphs */ -}; - -static cairo_surface_t * -_cairo_xml_surface_create_internal (cairo_device_t *device, - cairo_content_t content, - double width, - double height) -{ - cairo_xml_surface_t *surface; - - surface = malloc (sizeof (cairo_xml_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_xml_surface_backend, - device, - content); - - surface->width = width; - surface->height = height; - - return &surface->base; -} - -cairo_device_t * -cairo_xml_create (const char *filename) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create_for_filename (filename); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_xml_create_internal (stream); -} - -cairo_device_t * -cairo_xml_create_for_stream (cairo_write_func_t write_func, - void *closure) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create (write_func, NULL, closure); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_xml_create_internal (stream); -} - -cairo_surface_t * -cairo_xml_surface_create (cairo_device_t *device, - cairo_content_t content, - double width, double height) -{ - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) - return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - return _cairo_xml_surface_create_internal (device, content, width, height); -} - -cairo_status_t -cairo_xml_for_recording_surface (cairo_device_t *device, - cairo_surface_t *recording_surface) -{ - cairo_box_t bbox; - cairo_rectangle_int_t extents; - cairo_surface_t *surface; - cairo_xml_t *xml; - cairo_status_t status; - - if (unlikely (device->status)) - return device->status; - - if (unlikely (recording_surface->status)) - return recording_surface->status; - - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) - return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (! _cairo_surface_is_recording (recording_surface))) - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - - status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - surface = _cairo_xml_surface_create_internal (device, - recording_surface->content, - extents.width, - extents.height); - if (unlikely (surface->status)) - return surface->status; - - xml = (cairo_xml_t *) device; - - _cairo_xml_printf (xml, - "", - _content_to_string (recording_surface->content), - extents.width, extents.height); - _cairo_xml_indent (xml, 2); - - cairo_surface_set_device_offset (surface, -extents.x, -extents.y); - status = _cairo_recording_surface_replay (recording_surface, surface); - cairo_surface_destroy (surface); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); - - return status; -} -slim_hidden_def (cairo_xml_for_recording_surface); diff --git a/libs/cairo/cairo/src/cairo-xml.h b/libs/cairo/cairo/src/cairo-xml.h deleted file mode 100644 index 0367076a3..000000000 --- a/libs/cairo/cairo/src/cairo-xml.h +++ /dev/null @@ -1,36 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/cairo/src/cairo.c b/libs/cairo/cairo/src/cairo.c deleted file mode 100644 index e4a90b57f..000000000 --- a/libs/cairo/cairo/src/cairo.c +++ /dev/null @@ -1,4167 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "cairoint.h" -#include "cairo-private.h" - -#include "cairo-arc-private.h" -#include "cairo-error-private.h" -#include "cairo-path-private.h" - -/** - * SECTION:cairo - * @Title: cairo_t - * @Short_Description: The cairo drawing context - * @See_Also: #cairo_surface_t - * - * #cairo_t is the main object used when drawing with cairo. To - * draw with cairo, you create a #cairo_t, set the target surface, - * and drawing options for the #cairo_t, create shapes with - * functions like cairo_move_to() and cairo_line_to(), and then - * draw shapes with cairo_stroke() or cairo_fill(). - * - * #cairo_t's can be pushed to a stack via cairo_save(). - * They may then safely be changed, without loosing the current state. - * Use cairo_restore() to restore to the saved state. - */ - -/** - * SECTION:cairo-text - * @Title: text - * @Short_Description: Rendering text and glyphs - * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(), - * cairo_glyph_path() - * - * The functions with text in their name form cairo's - * toy text API. The toy API takes UTF-8 encoded - * text and is limited in its functionality to rendering simple - * left-to-right text with no advanced features. That means for example - * that most complex scripts like Hebrew, Arabic, and Indic scripts are - * out of question. No kerning or correct positioning of diacritical marks - * either. The font selection is pretty limited too and doesn't handle the - * case that the selected font does not cover the characters in the text. - * This set of functions are really that, a toy text API, for testing and - * demonstration purposes. Any serious application should avoid them. - * - * The functions with glyphs in their name form cairo's - * low-level text API. The low-level API relies on - * the user to convert text to a set of glyph indexes and positions. This - * is a very hard problem and is best handled by external libraries, like - * the pangocairo that is part of the Pango text layout and rendering library. - * Pango is available from http://www.pango.org/. - */ - -/** - * SECTION:cairo-transforms - * @Title: Transformations - * @Short_Description: Manipulating the current transformation matrix - * @See_Also: #cairo_matrix_t - * - * The current transformation matrix, ctm, is a - * two-dimensional affine transformation that maps all coordinates and other - * drawing instruments from the user space into the - * surface's canonical coordinate system, also known as the device - * space. - */ - -#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) - -#if !defined(INFINITY) -#define INFINITY HUGE_VAL -#endif - -static const cairo_t _cairo_nil = { - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NO_MEMORY, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - NULL, /* gstate */ - {{ 0 }, { 0 }}, /* gstate_tail */ - NULL, /* gstate_freelist */ - {{ /* path */ - { 0, 0 }, /* last_move_point */ - { 0, 0 }, /* current point */ - FALSE, /* has_current_point */ - FALSE, /* has_last_move_point */ - FALSE, /* has_curve_to */ - FALSE, /* is_box */ - FALSE, /* maybe_fill_region */ - TRUE, /* is_empty_fill */ - { {0, 0}, {0, 0}}, /* extents */ - {{{NULL,NULL}}} /* link */ - }} -}; - -static const cairo_t _cairo_nil__null_pointer = { - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NULL_POINTER, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - NULL, /* gstate */ - {{ 0 }, { 0 }}, /* gstate_tail */ - NULL, /* gstate_freelist */ - {{ /* path */ - { 0, 0 }, /* last_move_point */ - { 0, 0 }, /* current point */ - FALSE, /* has_current_point */ - FALSE, /* has_last_move_point */ - FALSE, /* has_curve_to */ - FALSE, /* is_box */ - FALSE, /* maybe_fill_region */ - TRUE, /* is_empty_fill */ - { {0, 0}, {0, 0}}, /* extents */ - {{{NULL,NULL}}} /* link */ - }} -}; -#include - -/** - * _cairo_error: - * @status: a status value indicating an error, (eg. not - * %CAIRO_STATUS_SUCCESS) - * - * Checks that status is an error status, but does nothing else. - * - * All assignments of an error status to any user-visible object - * within the cairo application should result in a call to - * _cairo_error(). - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - * - * Return value: the error status. - **/ -cairo_status_t -_cairo_error (cairo_status_t status) -{ - CAIRO_ENSURE_UNIQUE; - assert (_cairo_status_is_error (status)); - -#ifdef MOZILLA_VERSION - static int abort_on_error = -1; - if (abort_on_error < 0) { - abort_on_error = (getenv("MOZ_CAIRO_ERROR_ABORT") != NULL) ? 1 : 0; - } - if (abort_on_error) { - abort(); - } -#endif - return status; -} - -/** - * _cairo_set_error: - * @cr: a cairo context - * @status: a status value indicating an error - * - * Atomically sets cr->status to @status and calls _cairo_error; - * Does nothing if status is %CAIRO_STATUS_SUCCESS. - * - * All assignments of an error status to cr->status should happen - * through _cairo_set_error(). Note that due to the nature of the atomic - * operation, it is not safe to call this function on the nil objects. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - **/ -static void -_cairo_set_error (cairo_t *cr, cairo_status_t status) -{ - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ - _cairo_status_set_error (&cr->status, _cairo_error (status)); -} - -/* We keep a small stash of contexts to reduce malloc pressure */ -#define CAIRO_STASH_SIZE 4 -#if CAIRO_NO_MUTEX -static struct { - cairo_t pool[CAIRO_STASH_SIZE]; - int occupied; -} _context_stash; - -static cairo_t * -_context_get (void) -{ - int avail; - - avail = ffs (~_context_stash.occupied) - 1; - if (avail >= CAIRO_STASH_SIZE) - return malloc (sizeof (cairo_t)); - - _context_stash.occupied |= 1 << avail; - return &_context_stash.pool[avail]; -} - -static void -_context_put (cairo_t *cr) -{ - if (cr < &_context_stash.pool[0] || - cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) - { - free (cr); - return; - } - - _context_stash.occupied &= ~(1 << (cr - &_context_stash.pool[0])); -} -#elif HAS_ATOMIC_OPS -static struct { - cairo_t pool[CAIRO_STASH_SIZE]; - cairo_atomic_int_t occupied; -} _context_stash; - -static cairo_t * -_context_get (void) -{ - cairo_atomic_int_t avail, old, new; - - do { - old = _cairo_atomic_int_get (&_context_stash.occupied); - avail = ffs (~old) - 1; - if (avail >= CAIRO_STASH_SIZE) - return malloc (sizeof (cairo_t)); - - new = old | (1 << avail); - } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); - - return &_context_stash.pool[avail]; -} - -static void -_context_put (cairo_t *cr) -{ - cairo_atomic_int_t old, new, avail; - - if (cr < &_context_stash.pool[0] || - cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) - { - free (cr); - return; - } - - avail = ~(1 << (cr - &_context_stash.pool[0])); - do { - old = _cairo_atomic_int_get (&_context_stash.occupied); - new = old & avail; - } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); -} -#else -#define _context_get() malloc (sizeof (cairo_t)) -#define _context_put(cr) free (cr) -#endif - -/* XXX This should disappear in favour of a common pool of error objects. */ -static cairo_t *_cairo_nil__objects[CAIRO_STATUS_LAST_STATUS + 1]; - -static cairo_t * -_cairo_create_in_error (cairo_status_t status) -{ - cairo_t *cr; - - assert (status != CAIRO_STATUS_SUCCESS); - - /* Sanity check */ - if (status < 0 || status > CAIRO_STATUS_LAST_STATUS) { - abort(); - } - - /* special case OOM in order to avoid another allocation */ - switch ((int) status) { - case CAIRO_STATUS_NO_MEMORY: - return (cairo_t *) &_cairo_nil; - case CAIRO_STATUS_NULL_POINTER: - return (cairo_t *) &_cairo_nil__null_pointer; - } - - CAIRO_MUTEX_LOCK (_cairo_error_mutex); - cr = _cairo_nil__objects[status]; - if (cr == NULL) { - cr = malloc (sizeof (cairo_t)); - if (unlikely (cr == NULL)) { - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_t *) &_cairo_nil; - } - - *cr = _cairo_nil; - cr->status = status; - _cairo_nil__objects[status] = cr; - } - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); - - return cr; -} - -void -_cairo_reset_static_data (void) -{ - int status; - - CAIRO_MUTEX_LOCK (_cairo_error_mutex); - for (status = CAIRO_STATUS_SUCCESS; - status <= CAIRO_STATUS_LAST_STATUS; - status++) - { - if (_cairo_nil__objects[status] != NULL) { - free (_cairo_nil__objects[status]); - _cairo_nil__objects[status] = NULL; - } - } - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); -} - -/** - * cairo_create: - * @target: target surface for the context - * - * Creates a new #cairo_t with all graphics state parameters set to - * default values and with @target as a target surface. The target - * surface should be constructed with a backend-specific function such - * as cairo_image_surface_create() (or any other - * cairo_backend_surface_create() variant). - * - * This function references @target, so you can immediately - * call cairo_surface_destroy() on it if you don't need to - * maintain a separate reference to it. - * - * Return value: a newly allocated #cairo_t with a reference - * count of 1. The initial reference count should be released - * with cairo_destroy() when you are done using the #cairo_t. - * This function never returns %NULL. If memory cannot be - * allocated, a special #cairo_t object will be returned on - * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. - * You can use this object normally, but no drawing will - * be done. - **/ -cairo_t * -cairo_create (cairo_surface_t *target) -{ - cairo_t *cr; - cairo_status_t status; - - if (unlikely (target == NULL)) - return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - if (unlikely (target->status)) - return _cairo_create_in_error (target->status); - - cr = _context_get (); - if (unlikely (cr == NULL)) - return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1); - - cr->status = CAIRO_STATUS_SUCCESS; - - _cairo_user_data_array_init (&cr->user_data); - _cairo_path_fixed_init (cr->path); - - cr->gstate = &cr->gstate_tail[0]; - cr->gstate_freelist = &cr->gstate_tail[1]; - cr->gstate_tail[1].next = NULL; - - status = _cairo_gstate_init (cr->gstate, target); - if (unlikely (status)) { - _context_put (cr); - cr = _cairo_create_in_error (status); - } - - return cr; -} -slim_hidden_def (cairo_create); - -/** - * cairo_reference: - * @cr: a #cairo_t - * - * Increases the reference count on @cr by one. This prevents - * @cr from being destroyed until a matching call to cairo_destroy() - * is made. - * - * The number of references to a #cairo_t can be get using - * cairo_get_reference_count(). - * - * Return value: the referenced #cairo_t. - **/ -cairo_t * -cairo_reference (cairo_t *cr) -{ - if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) - return cr; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); - - _cairo_reference_count_inc (&cr->ref_count); - - return cr; -} - -/** - * cairo_destroy: - * @cr: a #cairo_t - * - * Decreases the reference count on @cr by one. If the result - * is zero, then @cr and all associated resources are freed. - * See cairo_reference(). - **/ -void -cairo_destroy (cairo_t *cr) -{ - cairo_surface_t *surface; - - if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) - return; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) - return; - - while (cr->gstate != &cr->gstate_tail[0]) { - if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) - break; - } - - /* The context is expected (>99% of all use cases) to be held for the - * duration of a single expose event/sequence of graphic operations. - * Therefore, on destroy we explicitly flush the Cairo pipeline of any - * pending operations. - */ - surface = _cairo_gstate_get_original_target (cr->gstate); - if (surface != NULL) - cairo_surface_flush (surface); - - _cairo_gstate_fini (cr->gstate); - cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ - while (cr->gstate_freelist != NULL) { - cairo_gstate_t *gstate = cr->gstate_freelist; - cr->gstate_freelist = gstate->next; - free (gstate); - } - - _cairo_path_fixed_fini (cr->path); - - _cairo_user_data_array_fini (&cr->user_data); - - /* mark the context as invalid to protect against misuse */ - cr->status = CAIRO_STATUS_NULL_POINTER; - - _context_put (cr); -} -slim_hidden_def (cairo_destroy); - -/** - * cairo_get_user_data: - * @cr: a #cairo_t - * @key: the address of the #cairo_user_data_key_t the user data was - * attached to - * - * Return user data previously attached to @cr using the specified - * key. If no user data has been attached with the given key this - * function returns %NULL. - * - * Return value: the user data previously attached or %NULL. - * - * Since: 1.4 - **/ -void * -cairo_get_user_data (cairo_t *cr, - const cairo_user_data_key_t *key) -{ - return _cairo_user_data_array_get_data (&cr->user_data, - key); -} - -/** - * cairo_set_user_data: - * @cr: a #cairo_t - * @key: the address of a #cairo_user_data_key_t to attach the user data to - * @user_data: the user data to attach to the #cairo_t - * @destroy: a #cairo_destroy_func_t which will be called when the - * #cairo_t is destroyed or when new user data is attached using the - * same key. - * - * Attach user data to @cr. To remove user data from a surface, - * call this function with the key that was used to set it and %NULL - * for @data. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a - * slot could not be allocated for the user data. - * - * Since: 1.4 - **/ -cairo_status_t -cairo_set_user_data (cairo_t *cr, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy) -{ - if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) - return cr->status; - - return _cairo_user_data_array_set_data (&cr->user_data, - key, user_data, destroy); -} - -/** - * cairo_get_reference_count: - * @cr: a #cairo_t - * - * Returns the current reference count of @cr. - * - * Return value: the current reference count of @cr. If the - * object is a nil object, 0 will be returned. - * - * Since: 1.4 - **/ -unsigned int -cairo_get_reference_count (cairo_t *cr) -{ - if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) - return 0; - - return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count); -} - -/** - * cairo_save: - * @cr: a #cairo_t - * - * Makes a copy of the current state of @cr and saves it - * on an internal stack of saved states for @cr. When - * cairo_restore() is called, @cr will be restored to - * the saved state. Multiple calls to cairo_save() and - * cairo_restore() can be nested; each call to cairo_restore() - * restores the state from the matching paired cairo_save(). - * - * It isn't necessary to clear all saved states before - * a #cairo_t is freed. If the reference count of a #cairo_t - * drops to zero in response to a call to cairo_destroy(), - * any saved states will be freed along with the #cairo_t. - **/ -void -cairo_save (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_save); - -/** - * cairo_restore: - * @cr: a #cairo_t - * - * Restores @cr to the state saved by a preceding call to - * cairo_save() and removes that state from the stack of - * saved states. - **/ -void -cairo_restore (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_restore); - -/** - * cairo_push_group: - * @cr: a cairo context - * - * Temporarily redirects drawing to an intermediate surface known as a - * group. The redirection lasts until the group is completed by a call - * to cairo_pop_group() or cairo_pop_group_to_source(). These calls - * provide the result of any drawing to the group as a pattern, - * (either as an explicit object, or set as the source pattern). - * - * This group functionality can be convenient for performing - * intermediate compositing. One common use of a group is to render - * objects as opaque within the group, (so that they occlude each - * other), and then blend the result with translucence onto the - * destination. - * - * Groups can be nested arbitrarily deep by making balanced calls to - * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new - * target group onto/from a stack. - * - * The cairo_push_group() function calls cairo_save() so that any - * changes to the graphics state will not be visible outside the - * group, (the pop_group functions call cairo_restore()). - * - * By default the intermediate group will have a content type of - * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for - * the group by using cairo_push_group_with_content() instead. - * - * As an example, here is how one might fill and stroke a path with - * translucence, but without any portion of the fill being visible - * under the stroke: - * - * - * cairo_push_group (cr); - * cairo_set_source (cr, fill_pattern); - * cairo_fill_preserve (cr); - * cairo_set_source (cr, stroke_pattern); - * cairo_stroke (cr); - * cairo_pop_group_to_source (cr); - * cairo_paint_with_alpha (cr, alpha); - * - * - * Since: 1.2 - */ -void -cairo_push_group (cairo_t *cr) -{ - cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); -} - -/** - * cairo_push_group_with_content: - * @cr: a cairo context - * @content: a #cairo_content_t indicating the type of group that - * will be created - * - * Temporarily redirects drawing to an intermediate surface known as a - * group. The redirection lasts until the group is completed by a call - * to cairo_pop_group() or cairo_pop_group_to_source(). These calls - * provide the result of any drawing to the group as a pattern, - * (either as an explicit object, or set as the source pattern). - * - * The group will have a content type of @content. The ability to - * control this content type is the only distinction between this - * function and cairo_push_group() which you should see for a more - * detailed description of group rendering. - * - * Since: 1.2 - */ -void -cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) -{ - cairo_surface_t *group_surface; - cairo_clip_t *clip; - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - clip = _cairo_gstate_get_clip (cr->gstate); - if (clip->all_clipped) { - group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - status = group_surface->status; - if (unlikely (status)) - goto bail; - } else { - cairo_surface_t *parent_surface; - const cairo_rectangle_int_t *clip_extents; - cairo_rectangle_int_t extents; - cairo_matrix_t matrix; - cairo_bool_t is_empty; - - parent_surface = _cairo_gstate_get_target (cr->gstate); - - /* Get the extents that we'll use in creating our new group surface */ - is_empty = _cairo_surface_get_extents (parent_surface, &extents); - clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate)); - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (&extents, clip_extents); - - group_surface = _cairo_surface_create_similar_solid (parent_surface, - content, - extents.width, - extents.height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - status = group_surface->status; - if (unlikely (status)) - goto bail; - - /* Set device offsets on the new surface so that logically it appears at - * the same location on the parent surface -- when we pop_group this, - * the source pattern will get fixed up for the appropriate target surface - * device offsets, so we want to set our own surface offsets from /that/, - * and not from the device origin. */ - cairo_surface_set_device_offset (group_surface, - parent_surface->device_transform.x0 - extents.x, - parent_surface->device_transform.y0 - extents.y); - - /* If we have a current path, we need to adjust it to compensate for - * the device offset just applied. */ - cairo_matrix_init_translate (&matrix, -extents.x, -extents.y); - _cairo_path_fixed_transform (cr->path, &matrix); - } - - /* create a new gstate for the redirect */ - cairo_save (cr); - if (unlikely (cr->status)) - goto bail; - - status = _cairo_gstate_redirect_target (cr->gstate, group_surface); - -bail: - cairo_surface_destroy (group_surface); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_push_group_with_content); - -/** - * cairo_pop_group: - * @cr: a cairo context - * - * Terminates the redirection begun by a call to cairo_push_group() or - * cairo_push_group_with_content() and returns a new pattern - * containing the results of all drawing operations performed to the - * group. - * - * The cairo_pop_group() function calls cairo_restore(), (balancing a - * call to cairo_save() by the push_group function), so that any - * changes to the graphics state will not be visible outside the - * group. - * - * Return value: a newly created (surface) pattern containing the - * results of all drawing operations performed to the group. The - * caller owns the returned object and should call - * cairo_pattern_destroy() when finished with it. - * - * Since: 1.2 - **/ -cairo_pattern_t * -cairo_pop_group (cairo_t *cr) -{ - cairo_surface_t *group_surface, *parent_target; - cairo_pattern_t *group_pattern; - cairo_matrix_t group_matrix, device_transform_matrix; - cairo_status_t status; - - if (unlikely (cr->status)) - return _cairo_pattern_create_in_error (cr->status); - - /* Grab the active surfaces */ - group_surface = _cairo_gstate_get_target (cr->gstate); - parent_target = _cairo_gstate_get_parent_target (cr->gstate); - - /* Verify that we are at the right nesting level */ - if (parent_target == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_INVALID_POP_GROUP); - return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP); - } - - /* We need to save group_surface before we restore; we don't need - * to reference parent_target and original_target, since the - * gstate will still hold refs to them once we restore. */ - group_surface = cairo_surface_reference (group_surface); - - cairo_restore (cr); - - if (unlikely (cr->status)) { - group_pattern = _cairo_pattern_create_in_error (cr->status); - goto done; - } - - group_pattern = cairo_pattern_create_for_surface (group_surface); - status = group_pattern->status; - if (unlikely (status)) { - _cairo_set_error (cr, status); - goto done; - } - - _cairo_gstate_get_matrix (cr->gstate, &group_matrix); - /* Transform by group_matrix centered around device_transform so that when - * we call _cairo_gstate_copy_transformed_pattern the result is a pattern - * with a matrix equivalent to the device_transform of group_surface. */ - if (_cairo_surface_has_device_transform (group_surface)) { - cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); - _cairo_pattern_transform (group_pattern, &group_matrix); - _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); - } else { - cairo_pattern_set_matrix (group_pattern, &group_matrix); - } - - /* If we have a current path, we need to adjust it to compensate for - * the device offset just removed. */ - cairo_matrix_multiply (&device_transform_matrix, - &_cairo_gstate_get_target (cr->gstate)->device_transform, - &group_surface->device_transform_inverse); - _cairo_path_fixed_transform (cr->path, &device_transform_matrix); - -done: - cairo_surface_destroy (group_surface); - - return group_pattern; -} -slim_hidden_def(cairo_pop_group); - -/** - * cairo_pop_group_to_source: - * @cr: a cairo context - * - * Terminates the redirection begun by a call to cairo_push_group() or - * cairo_push_group_with_content() and installs the resulting pattern - * as the source pattern in the given cairo context. - * - * The behavior of this function is equivalent to the sequence of - * operations: - * - * - * #cairo_pattern_t *group = cairo_pop_group (cr); - * cairo_set_source (cr, group); - * cairo_pattern_destroy (group); - * - * - * but is more convenient as their is no need for a variable to store - * the short-lived pointer to the pattern. - * - * The cairo_pop_group() function calls cairo_restore(), (balancing a - * call to cairo_save() by the push_group function), so that any - * changes to the graphics state will not be visible outside the - * group. - * - * Since: 1.2 - **/ -void -cairo_pop_group_to_source (cairo_t *cr) -{ - cairo_pattern_t *group_pattern; - - group_pattern = cairo_pop_group (cr); - cairo_set_source (cr, group_pattern); - cairo_pattern_destroy (group_pattern); -} - -/** - * cairo_set_operator: - * @cr: a #cairo_t - * @op: a compositing operator, specified as a #cairo_operator_t - * - * Sets the compositing operator to be used for all drawing - * operations. See #cairo_operator_t for details on the semantics of - * each available compositing operator. - * - * The default operator is %CAIRO_OPERATOR_OVER. - **/ -void -cairo_set_operator (cairo_t *cr, cairo_operator_t op) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_operator (cr->gstate, op); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_operator); - - -static cairo_bool_t -_current_source_matches_solid (cairo_t *cr, - double red, - double green, - double blue, - double alpha) -{ - const cairo_pattern_t *current; - cairo_color_t color; - - current = cr->gstate->source; - if (current->type != CAIRO_PATTERN_TYPE_SOLID) - return FALSE; - - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - alpha = _cairo_restrict_value (alpha, 0.0, 1.0); - - _cairo_color_init_rgba (&color, red, green, blue, alpha); - return _cairo_color_equal (&color, - &((cairo_solid_pattern_t *) current)->color); -} -/** - * cairo_set_source_rgb - * @cr: a cairo context - * @red: red component of color - * @green: green component of color - * @blue: blue component of color - * - * Sets the source pattern within @cr to an opaque color. This opaque - * color will then be used for any subsequent drawing operation until - * a new source pattern is set. - * - * The color components are floating point numbers in the range 0 to - * 1. If the values passed in are outside that range, they will be - * clamped. - * - * The default source pattern is opaque black, (that is, it is - * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)). - **/ -void -cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) -{ - cairo_pattern_t *pattern; - - if (unlikely (cr->status)) - return; - - if (_current_source_matches_solid (cr, red, green, blue, 1.)) - return; - - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); - - pattern = cairo_pattern_create_rgb (red, green, blue); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); -} -slim_hidden_def (cairo_set_source_rgb); - -/** - * cairo_set_source_rgba: - * @cr: a cairo context - * @red: red component of color - * @green: green component of color - * @blue: blue component of color - * @alpha: alpha component of color - * - * Sets the source pattern within @cr to a translucent color. This - * color will then be used for any subsequent drawing operation until - * a new source pattern is set. - * - * The color and alpha components are floating point numbers in the - * range 0 to 1. If the values passed in are outside that range, they - * will be clamped. - * - * The default source pattern is opaque black, (that is, it is - * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)). - **/ -void -cairo_set_source_rgba (cairo_t *cr, - double red, double green, double blue, - double alpha) -{ - cairo_pattern_t *pattern; - - if (unlikely (cr->status)) - return; - - if (_current_source_matches_solid (cr, red, green, blue, alpha)) - return; - - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); - - pattern = cairo_pattern_create_rgba (red, green, blue, alpha); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); -} - -/** - * cairo_set_source_surface: - * @cr: a cairo context - * @surface: a surface to be used to set the source pattern - * @x: User-space X coordinate for surface origin - * @y: User-space Y coordinate for surface origin - * - * This is a convenience function for creating a pattern from @surface - * and setting it as the source in @cr with cairo_set_source(). - * - * The @x and @y parameters give the user-space coordinate at which - * the surface origin should appear. (The surface origin is its - * upper-left corner before any transformation has been applied.) The - * @x and @y parameters are negated and then set as translation values - * in the pattern matrix. - * - * Other than the initial translation pattern matrix, as described - * above, all other pattern attributes, (such as its extend mode), are - * set to the default values as in cairo_pattern_create_for_surface(). - * The resulting pattern can be queried with cairo_get_source() so - * that these attributes can be modified if desired, (eg. to create a - * repeating pattern with cairo_pattern_set_extend()). - **/ -void -cairo_set_source_surface (cairo_t *cr, - cairo_surface_t *surface, - double x, - double y) -{ - cairo_pattern_t *pattern; - cairo_matrix_t matrix; - - if (unlikely (cr->status)) - return; - - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); - - pattern = cairo_pattern_create_for_surface (surface); - - cairo_matrix_init_translate (&matrix, -x, -y); - cairo_pattern_set_matrix (pattern, &matrix); - - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); -} -slim_hidden_def (cairo_set_source_surface); - -/** - * cairo_set_source - * @cr: a cairo context - * @source: a #cairo_pattern_t to be used as the source for - * subsequent drawing operations. - * - * Sets the source pattern within @cr to @source. This pattern - * will then be used for any subsequent drawing operation until a new - * source pattern is set. - * - * Note: The pattern's transformation matrix will be locked to the - * user space in effect at the time of cairo_set_source(). This means - * that further modifications of the current transformation matrix - * will not affect the source pattern. See cairo_pattern_set_matrix(). - * - * The default source pattern is a solid pattern that is opaque black, - * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, - * 0.0)). - **/ -void -cairo_set_source (cairo_t *cr, cairo_pattern_t *source) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (source == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - if (source->status) { - _cairo_set_error (cr, source->status); - return; - } - - status = _cairo_gstate_set_source (cr->gstate, source); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_source); - -/** - * cairo_get_source: - * @cr: a cairo context - * - * Gets the current source pattern for @cr. - * - * Return value: the current source pattern. This object is owned by - * cairo. To keep a reference to it, you must call - * cairo_pattern_reference(). - **/ -cairo_pattern_t * -cairo_get_source (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_pattern_create_in_error (cr->status); - - return _cairo_gstate_get_source (cr->gstate); -} - -/** - * cairo_set_tolerance: - * @cr: a #cairo_t - * @tolerance: the tolerance, in device units (typically pixels) - * - * Sets the tolerance used when converting paths into trapezoids. - * Curved segments of the path will be subdivided until the maximum - * deviation between the original path and the polygonal approximation - * is less than @tolerance. The default value is 0.1. A larger - * value will give better performance, a smaller value, better - * appearance. (Reducing the value from the default value of 0.1 - * is unlikely to improve appearance significantly.) The accuracy of paths - * within Cairo is limited by the precision of its internal arithmetic, and - * the prescribed @tolerance is restricted to the smallest - * representable internal value. - **/ -void -cairo_set_tolerance (cairo_t *cr, double tolerance) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (tolerance < CAIRO_TOLERANCE_MINIMUM) - tolerance = CAIRO_TOLERANCE_MINIMUM; - - status = _cairo_gstate_set_tolerance (cr->gstate, tolerance); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_tolerance); - -/** - * cairo_set_antialias: - * @cr: a #cairo_t - * @antialias: the new antialiasing mode - * - * Set the antialiasing mode of the rasterizer used for drawing shapes. - * This value is a hint, and a particular backend may or may not support - * a particular value. At the current time, no backend supports - * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes. - * - * Note that this option does not affect text rendering, instead see - * cairo_font_options_set_antialias(). - **/ -void -cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_antialias (cr->gstate, antialias); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_set_fill_rule: - * @cr: a #cairo_t - * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t - * - * Set the current fill rule within the cairo context. The fill rule - * is used to determine which regions are inside or outside a complex - * (potentially self-intersecting) path. The current fill rule affects - * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details - * on the semantics of each available fill rule. - * - * The default fill rule is %CAIRO_FILL_RULE_WINDING. - **/ -void -cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_set_line_width: - * @cr: a #cairo_t - * @width: a line width - * - * Sets the current line width within the cairo context. The line - * width value specifies the diameter of a pen that is circular in - * user space, (though device-space pen may be an ellipse in general - * due to scaling/shear/rotation of the CTM). - * - * Note: When the description above refers to user space and CTM it - * refers to the user space and CTM in effect at the time of the - * stroking operation, not the user space and CTM in effect at the - * time of the call to cairo_set_line_width(). The simplest usage - * makes both of these spaces identical. That is, if there is no - * change to the CTM between a call to cairo_set_line_width() and the - * stroking operation, then one can just pass user-space values to - * cairo_set_line_width() and ignore this note. - * - * As with the other stroke parameters, the current line width is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. - * - * The default line width value is 2.0. - **/ -void -cairo_set_line_width (cairo_t *cr, double width) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (width < 0.) - width = 0.; - - status = _cairo_gstate_set_line_width (cr->gstate, width); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_line_width); - -/** - * cairo_set_line_cap: - * @cr: a cairo context - * @line_cap: a line cap style - * - * Sets the current line cap style within the cairo context. See - * #cairo_line_cap_t for details about how the available line cap - * styles are drawn. - * - * As with the other stroke parameters, the current line cap style is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. - * - * The default line cap style is %CAIRO_LINE_CAP_BUTT. - **/ -void -cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_line_cap (cr->gstate, line_cap); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_line_cap); - -/** - * cairo_set_line_join: - * @cr: a cairo context - * @line_join: a line join style - * - * Sets the current line join style within the cairo context. See - * #cairo_line_join_t for details about how the available line join - * styles are drawn. - * - * As with the other stroke parameters, the current line join style is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. - * - * The default line join style is %CAIRO_LINE_JOIN_MITER. - **/ -void -cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_line_join (cr->gstate, line_join); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_line_join); - -/** - * cairo_set_dash: - * @cr: a cairo context - * @dashes: an array specifying alternate lengths of on and off stroke portions - * @num_dashes: the length of the dashes array - * @offset: an offset into the dash pattern at which the stroke should start - * - * Sets the dash pattern to be used by cairo_stroke(). A dash pattern - * is specified by @dashes, an array of positive values. Each value - * provides the length of alternate "on" and "off" portions of the - * stroke. The @offset specifies an offset into the pattern at which - * the stroke begins. - * - * Each "on" segment will have caps applied as if the segment were a - * separate sub-path. In particular, it is valid to use an "on" length - * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order - * to distributed dots or squares along a path. - * - * Note: The length values are in user-space units as evaluated at the - * time of stroking. This is not necessarily the same as the user - * space at the time of cairo_set_dash(). - * - * If @num_dashes is 0 dashing is disabled. - * - * If @num_dashes is 1 a symmetric pattern is assumed with alternating - * on and off portions of the size specified by the single value in - * @dashes. - * - * If any value in @dashes is negative, or if all values are 0, then - * @cr will be put into an error state with a status of - * %CAIRO_STATUS_INVALID_DASH. - **/ -void -cairo_set_dash (cairo_t *cr, - const double *dashes, - int num_dashes, - double offset) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_dash (cr->gstate, - dashes, num_dashes, offset); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_get_dash_count: - * @cr: a #cairo_t - * - * This function returns the length of the dash array in @cr (0 if dashing - * is not currently in effect). - * - * See also cairo_set_dash() and cairo_get_dash(). - * - * Return value: the length of the dash array, or 0 if no dash array set. - * - * Since: 1.4 - */ -int -cairo_get_dash_count (cairo_t *cr) -{ - int num_dashes; - - if (unlikely (cr->status)) - return 0; - - _cairo_gstate_get_dash (cr->gstate, NULL, &num_dashes, NULL); - - return num_dashes; -} - -/** - * cairo_get_dash: - * @cr: a #cairo_t - * @dashes: return value for the dash array, or %NULL - * @offset: return value for the current dash offset, or %NULL - * - * Gets the current dash array. If not %NULL, @dashes should be big - * enough to hold at least the number of values returned by - * cairo_get_dash_count(). - * - * Since: 1.4 - **/ -void -cairo_get_dash (cairo_t *cr, - double *dashes, - double *offset) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_get_dash (cr->gstate, dashes, NULL, offset); -} - -/** - * cairo_set_miter_limit: - * @cr: a cairo context - * @limit: miter limit to set - * - * Sets the current miter limit within the cairo context. - * - * If the current line join style is set to %CAIRO_LINE_JOIN_MITER - * (see cairo_set_line_join()), the miter limit is used to determine - * whether the lines should be joined with a bevel instead of a miter. - * Cairo divides the length of the miter by the line width. - * If the result is greater than the miter limit, the style is - * converted to a bevel. - * - * As with the other stroke parameters, the current line miter limit is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. - * - * The default miter limit value is 10.0, which will convert joins - * with interior angles less than 11 degrees to bevels instead of - * miters. For reference, a miter limit of 2.0 makes the miter cutoff - * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 - * degrees. - * - * A miter limit for a desired angle can be computed as: miter limit = - * 1/sin(angle/2) - **/ -void -cairo_set_miter_limit (cairo_t *cr, double limit) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_miter_limit (cr->gstate, limit); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_translate: - * @cr: a cairo context - * @tx: amount to translate in the X direction - * @ty: amount to translate in the Y direction - * - * Modifies the current transformation matrix (CTM) by translating the - * user-space origin by (@tx, @ty). This offset is interpreted as a - * user-space coordinate according to the CTM in place before the new - * call to cairo_translate(). In other words, the translation of the - * user-space origin takes place after any existing transformation. - **/ -void -cairo_translate (cairo_t *cr, double tx, double ty) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_translate (cr->gstate, tx, ty); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_translate); - -/** - * cairo_scale: - * @cr: a cairo context - * @sx: scale factor for the X dimension - * @sy: scale factor for the Y dimension - * - * Modifies the current transformation matrix (CTM) by scaling the X - * and Y user-space axes by @sx and @sy respectively. The scaling of - * the axes takes place after any existing transformation of user - * space. - **/ -void -cairo_scale (cairo_t *cr, double sx, double sy) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_scale (cr->gstate, sx, sy); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_scale); - -/** - * cairo_rotate: - * @cr: a cairo context - * @angle: angle (in radians) by which the user-space axes will be - * rotated - * - * Modifies the current transformation matrix (CTM) by rotating the - * user-space axes by @angle radians. The rotation of the axes takes - * places after any existing transformation of user space. The - * rotation direction for positive angles is from the positive X axis - * toward the positive Y axis. - **/ -void -cairo_rotate (cairo_t *cr, double angle) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_rotate (cr->gstate, angle); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_transform: - * @cr: a cairo context - * @matrix: a transformation to be applied to the user-space axes - * - * Modifies the current transformation matrix (CTM) by applying - * @matrix as an additional transformation. The new transformation of - * user space takes place after any existing transformation. - **/ -void -cairo_transform (cairo_t *cr, - const cairo_matrix_t *matrix) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_transform (cr->gstate, matrix); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_transform); - -/** - * cairo_set_matrix: - * @cr: a cairo context - * @matrix: a transformation matrix from user space to device space - * - * Modifies the current transformation matrix (CTM) by setting it - * equal to @matrix. - **/ -void -cairo_set_matrix (cairo_t *cr, - const cairo_matrix_t *matrix) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_matrix (cr->gstate, matrix); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_matrix); - -/** - * cairo_identity_matrix: - * @cr: a cairo context - * - * Resets the current transformation matrix (CTM) by setting it equal - * to the identity matrix. That is, the user-space and device-space - * axes will be aligned and one user-space unit will transform to one - * device-space unit. - **/ -void -cairo_identity_matrix (cairo_t *cr) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_identity_matrix (cr->gstate); -} - -/** - * cairo_user_to_device: - * @cr: a cairo context - * @x: X value of coordinate (in/out parameter) - * @y: Y value of coordinate (in/out parameter) - * - * Transform a coordinate from user space to device space by - * multiplying the given point by the current transformation matrix - * (CTM). - **/ -void -cairo_user_to_device (cairo_t *cr, double *x, double *y) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_device (cr->gstate, x, y); -} -slim_hidden_def (cairo_user_to_device); - -/** - * cairo_user_to_device_distance: - * @cr: a cairo context - * @dx: X component of a distance vector (in/out parameter) - * @dy: Y component of a distance vector (in/out parameter) - * - * Transform a distance vector from user space to device space. This - * function is similar to cairo_user_to_device() except that the - * translation components of the CTM will be ignored when transforming - * (@dx,@dy). - **/ -void -cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); -} -slim_hidden_def (cairo_user_to_device_distance); - -/** - * cairo_device_to_user: - * @cr: a cairo - * @x: X value of coordinate (in/out parameter) - * @y: Y value of coordinate (in/out parameter) - * - * Transform a coordinate from device space to user space by - * multiplying the given point by the inverse of the current - * transformation matrix (CTM). - **/ -void -cairo_device_to_user (cairo_t *cr, double *x, double *y) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_device_to_user (cr->gstate, x, y); -} - -/** - * cairo_device_to_user_distance: - * @cr: a cairo context - * @dx: X component of a distance vector (in/out parameter) - * @dy: Y component of a distance vector (in/out parameter) - * - * Transform a distance vector from device space to user space. This - * function is similar to cairo_device_to_user() except that the - * translation components of the inverse CTM will be ignored when - * transforming (@dx,@dy). - **/ -void -cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) -{ - if (unlikely (cr->status)) - return; - - _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); -} - -/** - * cairo_new_path: - * @cr: a cairo context - * - * Clears the current path. After this call there will be no path and - * no current point. - **/ -void -cairo_new_path (cairo_t *cr) -{ - if (unlikely (cr->status)) - return; - - _cairo_path_fixed_fini (cr->path); - _cairo_path_fixed_init (cr->path); -} -slim_hidden_def(cairo_new_path); - -/** - * cairo_move_to: - * @cr: a cairo context - * @x: the X coordinate of the new position - * @y: the Y coordinate of the new position - * - * Begin a new sub-path. After this call the current point will be (@x, - * @y). - **/ -void -cairo_move_to (cairo_t *cr, double x, double y) -{ - cairo_status_t status; - cairo_fixed_t x_fixed, y_fixed; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_backend (cr->gstate, &x, &y); - x_fixed = _cairo_fixed_from_double (x); - y_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_move_to); - -/** - * cairo_new_sub_path: - * @cr: a cairo context - * - * Begin a new sub-path. Note that the existing path is not - * affected. After this call there will be no current point. - * - * In many cases, this call is not needed since new sub-paths are - * frequently started with cairo_move_to(). - * - * A call to cairo_new_sub_path() is particularly useful when - * beginning a new sub-path with one of the cairo_arc() calls. This - * makes things easier as it is no longer necessary to manually - * compute the arc's initial coordinates for a call to - * cairo_move_to(). - * - * Since: 1.2 - **/ -void -cairo_new_sub_path (cairo_t *cr) -{ - if (unlikely (cr->status)) - return; - - _cairo_path_fixed_new_sub_path (cr->path); -} - -/** - * cairo_line_to: - * @cr: a cairo context - * @x: the X coordinate of the end of the new line - * @y: the Y coordinate of the end of the new line - * - * Adds a line to the path from the current point to position (@x, @y) - * in user-space coordinates. After this call the current point - * will be (@x, @y). - * - * If there is no current point before the call to cairo_line_to() - * this function will behave as cairo_move_to(@cr, @x, @y). - **/ -void -cairo_line_to (cairo_t *cr, double x, double y) -{ - cairo_status_t status; - cairo_fixed_t x_fixed, y_fixed; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_backend (cr->gstate, &x, &y); - x_fixed = _cairo_fixed_from_double (x); - y_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_line_to); - -/** - * cairo_curve_to: - * @cr: a cairo context - * @x1: the X coordinate of the first control point - * @y1: the Y coordinate of the first control point - * @x2: the X coordinate of the second control point - * @y2: the Y coordinate of the second control point - * @x3: the X coordinate of the end of the curve - * @y3: the Y coordinate of the end of the curve - * - * Adds a cubic Bézier spline to the path from the current point to - * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and - * (@x2, @y2) as the control points. After this call the current point - * will be (@x3, @y3). - * - * If there is no current point before the call to cairo_curve_to() - * this function will behave as if preceded by a call to - * cairo_move_to(@cr, @x1, @y1). - **/ -void -cairo_curve_to (cairo_t *cr, - double x1, double y1, - double x2, double y2, - double x3, double y3) -{ - cairo_status_t status; - cairo_fixed_t x1_fixed, y1_fixed; - cairo_fixed_t x2_fixed, y2_fixed; - cairo_fixed_t x3_fixed, y3_fixed; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); - _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); - _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); - - x1_fixed = _cairo_fixed_from_double (x1); - y1_fixed = _cairo_fixed_from_double (y1); - - x2_fixed = _cairo_fixed_from_double (x2); - y2_fixed = _cairo_fixed_from_double (y2); - - x3_fixed = _cairo_fixed_from_double (x3); - y3_fixed = _cairo_fixed_from_double (y3); - - status = _cairo_path_fixed_curve_to (cr->path, - x1_fixed, y1_fixed, - x2_fixed, y2_fixed, - x3_fixed, y3_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_curve_to); - -/** - * cairo_arc: - * @cr: a cairo context - * @xc: X position of the center of the arc - * @yc: Y position of the center of the arc - * @radius: the radius of the arc - * @angle1: the start angle, in radians - * @angle2: the end angle, in radians - * - * Adds a circular arc of the given @radius to the current path. The - * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in - * the direction of increasing angles to end at @angle2. If @angle2 is - * less than @angle1 it will be progressively increased by 2*M_PI - * until it is greater than @angle1. - * - * If there is a current point, an initial line segment will be added - * to the path to connect the current point to the beginning of the - * arc. If this initial line is undesired, it can be avoided by - * calling cairo_new_sub_path() before calling cairo_arc(). - * - * Angles are measured in radians. An angle of 0.0 is in the direction - * of the positive X axis (in user space). An angle of %M_PI/2.0 radians - * (90 degrees) is in the direction of the positive Y axis (in - * user space). Angles increase in the direction from the positive X - * axis toward the positive Y axis. So with the default transformation - * matrix, angles increase in a clockwise direction. - * - * (To convert from degrees to radians, use degrees * (M_PI / - * 180.).) - * - * This function gives the arc in the direction of increasing angles; - * see cairo_arc_negative() to get the arc in the direction of - * decreasing angles. - * - * The arc is circular in user space. To achieve an elliptical arc, - * you can scale the current transformation matrix by different - * amounts in the X and Y directions. For example, to draw an ellipse - * in the box given by @x, @y, @width, @height: - * - * - * cairo_save (cr); - * cairo_translate (cr, x + width / 2., y + height / 2.); - * cairo_scale (cr, width / 2., height / 2.); - * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); - * cairo_restore (cr); - * - **/ -void -cairo_arc (cairo_t *cr, - double xc, double yc, - double radius, - double angle1, double angle2) -{ - if (unlikely (cr->status)) - return; - - /* Do nothing, successfully, if radius is <= 0 */ - if (radius <= 0.0) { - cairo_line_to (cr, xc, yc); - return; - } - - while (angle2 < angle1) - angle2 += 2 * M_PI; - - cairo_line_to (cr, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - - _cairo_arc_path (cr, xc, yc, radius, - angle1, angle2); -} - -/** - * cairo_arc_negative: - * @cr: a cairo context - * @xc: X position of the center of the arc - * @yc: Y position of the center of the arc - * @radius: the radius of the arc - * @angle1: the start angle, in radians - * @angle2: the end angle, in radians - * - * Adds a circular arc of the given @radius to the current path. The - * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in - * the direction of decreasing angles to end at @angle2. If @angle2 is - * greater than @angle1 it will be progressively decreased by 2*M_PI - * until it is less than @angle1. - * - * See cairo_arc() for more details. This function differs only in the - * direction of the arc between the two angles. - **/ -void -cairo_arc_negative (cairo_t *cr, - double xc, double yc, - double radius, - double angle1, double angle2) -{ - if (unlikely (cr->status)) - return; - - /* Do nothing, successfully, if radius is <= 0 */ - if (radius <= 0.0) - return; - - while (angle2 > angle1) - angle2 -= 2 * M_PI; - - cairo_line_to (cr, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - - _cairo_arc_path_negative (cr, xc, yc, radius, - angle1, angle2); -} - -/* XXX: NYI -void -cairo_arc_to (cairo_t *cr, - double x1, double y1, - double x2, double y2, - double radius) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_arc_to (cr->gstate, - x1, y1, - x2, y2, - radius); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -*/ - -/** - * cairo_rel_move_to: - * @cr: a cairo context - * @dx: the X offset - * @dy: the Y offset - * - * Begin a new sub-path. After this call the current point will offset - * by (@x, @y). - * - * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy) - * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @dy). - * - * It is an error to call this function with no current point. Doing - * so will cause @cr to shutdown with a status of - * %CAIRO_STATUS_NO_CURRENT_POINT. - **/ -void -cairo_rel_move_to (cairo_t *cr, double dx, double dy) -{ - cairo_fixed_t dx_fixed, dy_fixed; - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); - - dx_fixed = _cairo_fixed_from_double (dx); - dy_fixed = _cairo_fixed_from_double (dy); - - status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_rel_line_to: - * @cr: a cairo context - * @dx: the X offset to the end of the new line - * @dy: the Y offset to the end of the new line - * - * Relative-coordinate version of cairo_line_to(). Adds a line to the - * path from the current point to a point that is offset from the - * current point by (@dx, @dy) in user space. After this call the - * current point will be offset by (@dx, @dy). - * - * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy) - * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @dy). - * - * It is an error to call this function with no current point. Doing - * so will cause @cr to shutdown with a status of - * %CAIRO_STATUS_NO_CURRENT_POINT. - **/ -void -cairo_rel_line_to (cairo_t *cr, double dx, double dy) -{ - cairo_fixed_t dx_fixed, dy_fixed; - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); - - dx_fixed = _cairo_fixed_from_double (dx); - dy_fixed = _cairo_fixed_from_double (dy); - - status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_rel_line_to); - -/** - * cairo_rel_curve_to: - * @cr: a cairo context - * @dx1: the X offset to the first control point - * @dy1: the Y offset to the first control point - * @dx2: the X offset to the second control point - * @dy2: the Y offset to the second control point - * @dx3: the X offset to the end of the curve - * @dy3: the Y offset to the end of the curve - * - * Relative-coordinate version of cairo_curve_to(). All offsets are - * relative to the current point. Adds a cubic Bézier spline to the - * path from the current point to a point offset from the current - * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and - * (@dx2, @dy2) as the control points. After this call the current - * point will be offset by (@dx3, @dy3). - * - * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1, - * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to - * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3). - * - * It is an error to call this function with no current point. Doing - * so will cause @cr to shutdown with a status of - * %CAIRO_STATUS_NO_CURRENT_POINT. - **/ -void -cairo_rel_curve_to (cairo_t *cr, - double dx1, double dy1, - double dx2, double dy2, - double dx3, double dy3) -{ - cairo_fixed_t dx1_fixed, dy1_fixed; - cairo_fixed_t dx2_fixed, dy2_fixed; - cairo_fixed_t dx3_fixed, dy3_fixed; - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1); - _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2); - _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3); - - dx1_fixed = _cairo_fixed_from_double (dx1); - dy1_fixed = _cairo_fixed_from_double (dy1); - - dx2_fixed = _cairo_fixed_from_double (dx2); - dy2_fixed = _cairo_fixed_from_double (dy2); - - dx3_fixed = _cairo_fixed_from_double (dx3); - dy3_fixed = _cairo_fixed_from_double (dy3); - - status = _cairo_path_fixed_rel_curve_to (cr->path, - dx1_fixed, dy1_fixed, - dx2_fixed, dy2_fixed, - dx3_fixed, dy3_fixed); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_rectangle: - * @cr: a cairo context - * @x: the X coordinate of the top left corner of the rectangle - * @y: the Y coordinate to the top left corner of the rectangle - * @width: the width of the rectangle - * @height: the height of the rectangle - * - * Adds a closed sub-path rectangle of the given size to the current - * path at position (@x, @y) in user-space coordinates. - * - * This function is logically equivalent to: - * - * 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); - * - **/ -void -cairo_rectangle (cairo_t *cr, - double x, double y, - double width, double height) -{ - if (unlikely (cr->status)) - return; - - cairo_move_to (cr, x, y); - cairo_rel_line_to (cr, width, 0); - cairo_rel_line_to (cr, 0, height); - cairo_rel_line_to (cr, -width, 0); - cairo_close_path (cr); -} - -#if 0 -/* XXX: NYI */ -void -cairo_stroke_to_path (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */ - - status = _cairo_gstate_stroke_path (cr->gstate); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -#endif - -/** - * cairo_close_path: - * @cr: a cairo context - * - * Adds a line segment to the path from the current point to the - * beginning of the current sub-path, (the most recent point passed to - * cairo_move_to()), and closes this sub-path. After this call the - * current point will be at the joined endpoint of the sub-path. - * - * The behavior of cairo_close_path() is distinct from simply calling - * cairo_line_to() with the equivalent coordinate in the case of - * stroking. When a closed sub-path is stroked, there are no caps on - * the ends of the sub-path. Instead, there is a line join connecting - * the final and initial segments of the sub-path. - * - * If there is no current point before the call to cairo_close_path(), - * this function will have no effect. - * - * Note: As of cairo version 1.2.4 any call to cairo_close_path() will - * place an explicit MOVE_TO element into the path immediately after - * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for - * example). This can simplify path processing in some cases as it may - * not be necessary to save the "last move_to point" during processing - * as the MOVE_TO immediately after the CLOSE_PATH will provide that - * point. - **/ -void -cairo_close_path (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_path_fixed_close_path (cr->path); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_close_path); - -/** - * cairo_path_extents: - * @cr: a cairo context - * @x1: left of the resulting extents - * @y1: top of the resulting extents - * @x2: right of the resulting extents - * @y2: bottom of the resulting extents - * - * Computes a bounding box in user-space coordinates covering the - * points on the current path. If the current path is empty, returns - * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule, - * surface dimensions and clipping are not taken into account. - * - * Contrast with cairo_fill_extents() and cairo_stroke_extents() which - * return the extents of only the area that would be "inked" by - * the corresponding drawing operations. - * - * The result of cairo_path_extents() is defined as equivalent to the - * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the - * line width approaches 0.0, (but never reaching the empty-rectangle - * returned by cairo_stroke_extents() for a line width of 0.0). - * - * Specifically, this means that zero-area sub-paths such as - * cairo_move_to();cairo_line_to() segments, (even degenerate cases - * where the coordinates to both calls are identical), will be - * considered as contributing to the extents. However, a lone - * cairo_move_to() will not contribute to the results of - * cairo_path_extents(). - * - * Since: 1.6 - **/ -void -cairo_path_extents (cairo_t *cr, - double *x1, double *y1, double *x2, double *y2) -{ - if (unlikely (cr->status)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - - return; - } - - _cairo_gstate_path_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); -} - -/** - * cairo_paint: - * @cr: a cairo context - * - * A drawing operator that paints the current source everywhere within - * the current clip region. - **/ -void -cairo_paint (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_paint (cr->gstate); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_paint); - -/** - * cairo_paint_with_alpha: - * @cr: a cairo context - * @alpha: alpha value, between 0 (transparent) and 1 (opaque) - * - * A drawing operator that paints the current source everywhere within - * the current clip region using a mask of constant alpha value - * @alpha. The effect is similar to cairo_paint(), but the drawing - * is faded out using the alpha value. - **/ -void -cairo_paint_with_alpha (cairo_t *cr, - double alpha) -{ - cairo_status_t status; - cairo_color_t color; - cairo_solid_pattern_t pattern; - - if (unlikely (cr->status)) - return; - - if (CAIRO_ALPHA_IS_OPAQUE (alpha)) { - cairo_paint (cr); - return; - } - - if (CAIRO_ALPHA_IS_ZERO (alpha) && - _cairo_operator_bounded_by_mask (cr->gstate->op)) { - return; - } - - _cairo_color_init_rgba (&color, 0., 0., 0., alpha); - _cairo_pattern_init_solid (&pattern, &color); - - status = _cairo_gstate_mask (cr->gstate, &pattern.base); - if (unlikely (status)) - _cairo_set_error (cr, status); - - _cairo_pattern_fini (&pattern.base); -} - -/** - * cairo_mask: - * @cr: a cairo context - * @pattern: a #cairo_pattern_t - * - * A drawing operator that paints the current source - * using the alpha channel of @pattern as a mask. (Opaque - * areas of @pattern are painted with the source, transparent - * areas are not painted.) - */ -void -cairo_mask (cairo_t *cr, - cairo_pattern_t *pattern) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (pattern == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - if (pattern->status) { - _cairo_set_error (cr, pattern->status); - return; - } - - status = _cairo_gstate_mask (cr->gstate, pattern); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_mask); - -/** - * cairo_mask_surface: - * @cr: a cairo context - * @surface: a #cairo_surface_t - * @surface_x: X coordinate at which to place the origin of @surface - * @surface_y: Y coordinate at which to place the origin of @surface - * - * A drawing operator that paints the current source - * using the alpha channel of @surface as a mask. (Opaque - * areas of @surface are painted with the source, transparent - * areas are not painted.) - */ -void -cairo_mask_surface (cairo_t *cr, - cairo_surface_t *surface, - double surface_x, - double surface_y) -{ - cairo_pattern_t *pattern; - cairo_matrix_t matrix; - - if (unlikely (cr->status)) - return; - - pattern = cairo_pattern_create_for_surface (surface); - - cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); - cairo_pattern_set_matrix (pattern, &matrix); - - cairo_mask (cr, pattern); - - cairo_pattern_destroy (pattern); -} - -/** - * cairo_stroke: - * @cr: a cairo context - * - * A drawing operator that strokes the current path according to the - * current line width, line join, line cap, and dash settings. After - * cairo_stroke(), the current path will be cleared from the cairo - * context. See cairo_set_line_width(), cairo_set_line_join(), - * cairo_set_line_cap(), cairo_set_dash(), and - * cairo_stroke_preserve(). - * - * Note: Degenerate segments and sub-paths are treated specially and - * provide a useful result. These can result in two different - * situations: - * - * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap - * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these - * segments will be drawn as circular dots or squares respectively. In - * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares - * is determined by the direction of the underlying path. - * - * 2. A sub-path created by cairo_move_to() followed by either a - * cairo_close_path() or one or more calls to cairo_line_to() to the - * same coordinate as the cairo_move_to(). If the cap style is - * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular - * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate - * sub-path will not be drawn at all, (since the correct orientation - * is indeterminate). - * - * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything - * to be drawn in the case of either degenerate segments or sub-paths. - **/ -void -cairo_stroke (cairo_t *cr) -{ - cairo_stroke_preserve (cr); - - cairo_new_path (cr); -} -slim_hidden_def(cairo_stroke); - -/** - * cairo_stroke_preserve: - * @cr: a cairo context - * - * A drawing operator that strokes the current path according to the - * current line width, line join, line cap, and dash settings. Unlike - * cairo_stroke(), cairo_stroke_preserve() preserves the path within the - * cairo context. - * - * See cairo_set_line_width(), cairo_set_line_join(), - * cairo_set_line_cap(), cairo_set_dash(), and - * cairo_stroke_preserve(). - **/ -void -cairo_stroke_preserve (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_stroke (cr->gstate, cr->path); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_stroke_preserve); - -/** - * cairo_fill: - * @cr: a cairo context - * - * A drawing operator that fills the current path according to the - * current fill rule, (each sub-path is implicitly closed before being - * filled). After cairo_fill(), the current path will be cleared from - * the cairo context. See cairo_set_fill_rule() and - * cairo_fill_preserve(). - **/ -void -cairo_fill (cairo_t *cr) -{ - cairo_fill_preserve (cr); - - cairo_new_path (cr); -} - -/** - * cairo_fill_preserve: - * @cr: a cairo context - * - * A drawing operator that fills the current path according to the - * current fill rule, (each sub-path is implicitly closed before being - * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the - * path within the cairo context. - * - * See cairo_set_fill_rule() and cairo_fill(). - **/ -void -cairo_fill_preserve (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_fill (cr->gstate, cr->path); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_fill_preserve); - -/** - * cairo_copy_page: - * @cr: a cairo context - * - * Emits the current page for backends that support multiple pages, but - * doesn't clear it, so, the contents of the current page will be retained - * for the next page too. Use cairo_show_page() if you want to get an - * empty page after the emission. - * - * This is a convenience function that simply calls - * cairo_surface_copy_page() on @cr's target. - **/ -void -cairo_copy_page (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_copy_page (cr->gstate); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_show_page: - * @cr: a cairo context - * - * Emits and clears the current page for backends that support multiple - * pages. Use cairo_copy_page() if you don't want to clear the page. - * - * This is a convenience function that simply calls - * cairo_surface_show_page() on @cr's target. - **/ -void -cairo_show_page (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_show_page (cr->gstate); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_in_stroke: - * @cr: a cairo context - * @x: X coordinate of the point to test - * @y: Y coordinate of the point to test - * - * Tests whether the given point is inside the area that would be - * affected by a cairo_stroke() operation given the current path and - * stroking parameters. Surface dimensions and clipping are not taken - * into account. - * - * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), - * cairo_set_line_cap(), cairo_set_dash(), and - * cairo_stroke_preserve(). - * - * Return value: A non-zero value if the point is inside, or zero if - * outside. - **/ -cairo_bool_t -cairo_in_stroke (cairo_t *cr, double x, double y) -{ - cairo_status_t status; - cairo_bool_t inside = FALSE; - - if (unlikely (cr->status)) - return FALSE; - - status = _cairo_gstate_in_stroke (cr->gstate, - cr->path, - x, y, &inside); - if (unlikely (status)) - _cairo_set_error (cr, status); - - return inside; -} - -/** - * cairo_in_fill: - * @cr: a cairo context - * @x: X coordinate of the point to test - * @y: Y coordinate of the point to test - * - * Tests whether the given point is inside the area that would be - * affected by a cairo_fill() operation given the current path and - * filling parameters. Surface dimensions and clipping are not taken - * into account. - * - * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). - * - * Return value: A non-zero value if the point is inside, or zero if - * outside. - **/ -cairo_bool_t -cairo_in_fill (cairo_t *cr, double x, double y) -{ - if (unlikely (cr->status)) - return FALSE; - - return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y); -} - -/** - * cairo_stroke_extents: - * @cr: a cairo context - * @x1: left of the resulting extents - * @y1: top of the resulting extents - * @x2: right of the resulting extents - * @y2: bottom of the resulting extents - * - * Computes a bounding box in user coordinates covering the area that - * would be affected, (the "inked" area), by a cairo_stroke() - * operation given the current path and stroke parameters. - * If the current path is empty, returns an empty rectangle ((0,0), (0,0)). - * Surface dimensions and clipping are not taken into account. - * - * Note that if the line width is set to exactly zero, then - * cairo_stroke_extents() will return an empty rectangle. Contrast with - * cairo_path_extents() which can be used to compute the non-empty - * bounds as the line width approaches zero. - * - * Note that cairo_stroke_extents() must necessarily do more work to - * compute the precise inked areas in light of the stroke parameters, - * so cairo_path_extents() may be more desirable for sake of - * performance if non-inked path extents are desired. - * - * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), - * cairo_set_line_cap(), cairo_set_dash(), and - * cairo_stroke_preserve(). - **/ -void -cairo_stroke_extents (cairo_t *cr, - double *x1, double *y1, double *x2, double *y2) -{ - cairo_status_t status; - - if (unlikely (cr->status)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - - return; - } - - status = _cairo_gstate_stroke_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_fill_extents: - * @cr: a cairo context - * @x1: left of the resulting extents - * @y1: top of the resulting extents - * @x2: right of the resulting extents - * @y2: bottom of the resulting extents - * - * Computes a bounding box in user coordinates covering the area that - * would be affected, (the "inked" area), by a cairo_fill() operation - * given the current path and fill parameters. If the current path is - * empty, returns an empty rectangle ((0,0), (0,0)). Surface - * dimensions and clipping are not taken into account. - * - * Contrast with cairo_path_extents(), which is similar, but returns - * non-zero extents for some paths with no inked area, (such as a - * simple line segment). - * - * Note that cairo_fill_extents() must necessarily do more work to - * compute the precise inked areas in light of the fill rule, so - * cairo_path_extents() may be more desirable for sake of performance - * if the non-inked path extents are desired. - * - * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). - **/ -void -cairo_fill_extents (cairo_t *cr, - double *x1, double *y1, double *x2, double *y2) -{ - cairo_status_t status; - - if (unlikely (cr->status)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - - return; - } - - status = _cairo_gstate_fill_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_clip: - * @cr: a cairo context - * - * Establishes a new clip region by intersecting the current clip - * region with the current path as it would be filled by cairo_fill() - * and according to the current fill rule (see cairo_set_fill_rule()). - * - * After cairo_clip(), the current path will be cleared from the cairo - * context. - * - * The current clip region affects all drawing operations by - * effectively masking out any changes to the surface that are outside - * the current clip region. - * - * Calling cairo_clip() can only make the clip region smaller, never - * larger. But the current clip is part of the graphics state, so a - * temporary restriction of the clip region can be achieved by - * calling cairo_clip() within a cairo_save()/cairo_restore() - * pair. The only other means of increasing the size of the clip - * region is cairo_reset_clip(). - **/ -void -cairo_clip (cairo_t *cr) -{ - cairo_clip_preserve (cr); - - cairo_new_path (cr); -} - -/** - * cairo_clip_preserve: - * @cr: a cairo context - * - * Establishes a new clip region by intersecting the current clip - * region with the current path as it would be filled by cairo_fill() - * and according to the current fill rule (see cairo_set_fill_rule()). - * - * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within - * the cairo context. - * - * The current clip region affects all drawing operations by - * effectively masking out any changes to the surface that are outside - * the current clip region. - * - * Calling cairo_clip_preserve() can only make the clip region smaller, never - * larger. But the current clip is part of the graphics state, so a - * temporary restriction of the clip region can be achieved by - * calling cairo_clip_preserve() within a cairo_save()/cairo_restore() - * pair. The only other means of increasing the size of the clip - * region is cairo_reset_clip(). - **/ -void -cairo_clip_preserve (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_clip (cr->gstate, cr->path); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def(cairo_clip_preserve); - -/** - * cairo_reset_clip: - * @cr: a cairo context - * - * Reset the current clip region to its original, unrestricted - * state. That is, set the clip region to an infinitely large shape - * containing the target surface. Equivalently, if infinity is too - * hard to grasp, one can imagine the clip region being reset to the - * exact bounds of the target surface. - * - * Note that code meant to be reusable should not call - * cairo_reset_clip() as it will cause results unexpected by - * higher-level code which calls cairo_clip(). Consider using - * cairo_save() and cairo_restore() around cairo_clip() as a more - * robust means of temporarily restricting the clip region. - **/ -void -cairo_reset_clip (cairo_t *cr) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_reset_clip (cr->gstate); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_clip_extents: - * @cr: a cairo context - * @x1: left of the resulting extents - * @y1: top of the resulting extents - * @x2: right of the resulting extents - * @y2: bottom of the resulting extents - * - * Computes a bounding box in user coordinates covering the area inside the - * current clip. - * - * Since: 1.4 - **/ -void -cairo_clip_extents (cairo_t *cr, - double *x1, double *y1, - double *x2, double *y2) -{ - if (unlikely (cr->status)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - - return; - } - - if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { - *x1 = -INFINITY; - *y1 = -INFINITY; - *x2 = +INFINITY; - *y2 = +INFINITY; - } -} - -/** - * cairo_in_clip: - * @cr: a cairo context - * @x: X coordinate of the point to test - * @y: Y coordinate of the point to test - * - * Tests whether the given point is inside the area that would be - * visible through the current clip, i.e. the area that would be filled by - * a cairo_paint() operation. - * - * See cairo_clip(), and cairo_clip_preserve(). - * - * Return value: A non-zero value if the point is inside, or zero if - * outside. - * - * Since: 1.10 - **/ -cairo_bool_t -cairo_in_clip (cairo_t *cr, double x, double y) -{ - if (unlikely (cr->status)) - return FALSE; - - return _cairo_gstate_in_clip (cr->gstate, x, y); -} - -static cairo_rectangle_list_t * -_cairo_rectangle_list_create_in_error (cairo_status_t status) -{ - cairo_rectangle_list_t *list; - - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - - list = malloc (sizeof (cairo_rectangle_list_t)); - if (unlikely (list == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - } - - list->status = status; - list->rectangles = NULL; - list->num_rectangles = 0; - return list; -} - -/** - * cairo_copy_clip_rectangle_list: - * @cr: a cairo context - * - * Gets the current clip region as a list of rectangles in user coordinates. - * Never returns %NULL. - * - * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to - * indicate that the clip region cannot be represented as a list of - * user-space rectangles. The status may have other values to indicate - * other errors. - * - * Returns: the current clip region as a list of rectangles in user coordinates, - * which should be destroyed using cairo_rectangle_list_destroy(). - * - * Since: 1.4 - **/ -cairo_rectangle_list_t * -cairo_copy_clip_rectangle_list (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_rectangle_list_create_in_error (cr->status); - - return _cairo_gstate_copy_clip_rectangle_list (cr->gstate); -} - -/** - * cairo_select_font_face: - * @cr: a #cairo_t - * @family: a font family name, encoded in UTF-8 - * @slant: the slant for the font - * @weight: the weight for the font - * - * Note: The cairo_select_font_face() function call is part of what - * the cairo designers call the "toy" text API. It is convenient for - * short demos and simple programs, but it is not expected to be - * adequate for serious text-using applications. - * - * Selects a family and style of font from a simplified description as - * a family name, slant and weight. Cairo provides no operation to - * list available family names on the system (this is a "toy", - * remember), but the standard CSS2 generic family names, ("serif", - * "sans-serif", "cursive", "fantasy", "monospace"), are likely to - * work as expected. - * - * If @family starts with the string "@cairo:", or if no native font - * backends are compiled in, cairo will use an internal font family. - * The internal font family recognizes many modifiers in the @family - * string, most notably, it recognizes the string "monospace". That is, - * the family name "@cairo:monospace" will use the monospace version of - * the internal font family. - * - * For "real" font selection, see the font-backend-specific - * font_face_create functions for the font backend you are using. (For - * example, if you are using the freetype-based cairo-ft font backend, - * see cairo_ft_font_face_create_for_ft_face() or - * cairo_ft_font_face_create_for_pattern().) The resulting font face - * could then be used with cairo_scaled_font_create() and - * cairo_set_scaled_font(). - * - * Similarly, when using the "real" font support, you can call - * directly into the underlying font system, (such as fontconfig or - * freetype), for operations such as listing available fonts, etc. - * - * It is expected that most applications will need to use a more - * comprehensive font handling and text layout library, (for example, - * pango), in conjunction with cairo. - * - * If text is drawn without a call to cairo_select_font_face(), (nor - * cairo_set_font_face() nor cairo_set_scaled_font()), the default - * family is platform-specific, but is essentially "sans-serif". - * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is - * %CAIRO_FONT_WEIGHT_NORMAL. - * - * This function is equivalent to a call to cairo_toy_font_face_create() - * followed by cairo_set_font_face(). - **/ -void -cairo_select_font_face (cairo_t *cr, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_font_extents: - * @cr: a #cairo_t - * @extents: a #cairo_font_extents_t object into which the results - * will be stored. - * - * Gets the font extents for the currently selected font. - **/ -void -cairo_font_extents (cairo_t *cr, - cairo_font_extents_t *extents) -{ - cairo_status_t status; - - extents->ascent = 0.0; - extents->descent = 0.0; - extents->height = 0.0; - extents->max_x_advance = 0.0; - extents->max_y_advance = 0.0; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_get_font_extents (cr->gstate, extents); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_set_font_face: - * @cr: a #cairo_t - * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font - * - * Replaces the current #cairo_font_face_t object in the #cairo_t with - * @font_face. The replaced font face in the #cairo_t will be - * destroyed if there are no other references to it. - **/ -void -cairo_set_font_face (cairo_t *cr, - cairo_font_face_t *font_face) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_font_face (cr->gstate, font_face); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_get_font_face: - * @cr: a #cairo_t - * - * Gets the current font face for a #cairo_t. - * - * Return value: the current font face. This object is owned by - * cairo. To keep a reference to it, you must call - * cairo_font_face_reference(). - * - * This function never returns %NULL. If memory cannot be allocated, a - * special "nil" #cairo_font_face_t object will be returned on which - * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using - * this nil object will cause its error state to propagate to other - * objects it is passed to, (for example, calling - * cairo_set_font_face() with a nil font will trigger an error that - * will shutdown the #cairo_t object). - **/ -cairo_font_face_t * -cairo_get_font_face (cairo_t *cr) -{ - cairo_status_t status; - cairo_font_face_t *font_face; - - if (unlikely (cr->status)) - return (cairo_font_face_t*) &_cairo_font_face_nil; - - status = _cairo_gstate_get_font_face (cr->gstate, &font_face); - if (unlikely (status)) { - _cairo_set_error (cr, status); - return (cairo_font_face_t*) &_cairo_font_face_nil; - } - - return font_face; -} - -/** - * cairo_set_font_size: - * @cr: a #cairo_t - * @size: the new font size, in user space units - * - * Sets the current font matrix to a scale by a factor of @size, replacing - * any font matrix previously set with cairo_set_font_size() or - * cairo_set_font_matrix(). This results in a font size of @size user space - * units. (More precisely, this matrix will result in the font's - * em-square being a @size by @size square in user space.) - * - * If text is drawn without a call to cairo_set_font_size(), (nor - * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default - * font size is 10.0. - **/ -void -cairo_set_font_size (cairo_t *cr, double size) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_font_size (cr->gstate, size); - if (unlikely (status)) - _cairo_set_error (cr, status); -} -slim_hidden_def (cairo_set_font_size); - -/** - * cairo_set_font_matrix - * @cr: a #cairo_t - * @matrix: a #cairo_matrix_t describing a transform to be applied to - * the current font. - * - * Sets the current font matrix to @matrix. The font matrix gives a - * transformation from the design space of the font (in this space, - * the em-square is 1 unit by 1 unit) to user space. Normally, a - * simple scale is used (see cairo_set_font_size()), but a more - * complex font matrix can be used to shear the font - * or stretch it unequally along the two axes - **/ -void -cairo_set_font_matrix (cairo_t *cr, - const cairo_matrix_t *matrix) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = _cairo_gstate_set_font_matrix (cr->gstate, matrix); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_get_font_matrix - * @cr: a #cairo_t - * @matrix: return value for the matrix - * - * Stores the current font matrix into @matrix. See - * cairo_set_font_matrix(). - **/ -void -cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) -{ - if (unlikely (cr->status)) { - cairo_matrix_init_identity (matrix); - return; - } - - _cairo_gstate_get_font_matrix (cr->gstate, matrix); -} - -/** - * cairo_set_font_options: - * @cr: a #cairo_t - * @options: font options to use - * - * Sets a set of custom font rendering options for the #cairo_t. - * Rendering options are derived by merging these options with the - * options derived from underlying surface; if the value in @options - * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value - * from the surface is used. - **/ -void -cairo_set_font_options (cairo_t *cr, - const cairo_font_options_t *options) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - status = cairo_font_options_status ((cairo_font_options_t *) options); - if (unlikely (status)) { - _cairo_set_error (cr, status); - return; - } - - _cairo_gstate_set_font_options (cr->gstate, options); -} -slim_hidden_def (cairo_set_font_options); - -/** - * cairo_get_font_options: - * @cr: a #cairo_t - * @options: a #cairo_font_options_t object into which to store - * the retrieved options. All existing values are overwritten - * - * Retrieves font rendering options set via #cairo_set_font_options. - * Note that the returned options do not include any options derived - * from the underlying surface; they are literally the options - * passed to cairo_set_font_options(). - **/ -void -cairo_get_font_options (cairo_t *cr, - cairo_font_options_t *options) -{ - /* check that we aren't trying to overwrite the nil object */ - if (cairo_font_options_status (options)) - return; - - if (unlikely (cr->status)) { - _cairo_font_options_init_default (options); - return; - } - - _cairo_gstate_get_font_options (cr->gstate, options); -} - -/** - * cairo_set_scaled_font: - * @cr: a #cairo_t - * @scaled_font: a #cairo_scaled_font_t - * - * Replaces the current font face, font matrix, and font options in - * the #cairo_t with those of the #cairo_scaled_font_t. Except for - * some translation, the current CTM of the #cairo_t should be the - * same as that of the #cairo_scaled_font_t, which can be accessed - * using cairo_scaled_font_get_ctm(). - * - * Since: 1.2 - **/ -void -cairo_set_scaled_font (cairo_t *cr, - const cairo_scaled_font_t *scaled_font) -{ - cairo_status_t status; - cairo_bool_t was_previous; - - if (unlikely (cr->status)) - return; - - if (scaled_font == NULL) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto BAIL; - } - - status = scaled_font->status; - if (unlikely (status)) - goto BAIL; - - if (scaled_font == cr->gstate->scaled_font) - return; - - was_previous = scaled_font == cr->gstate->previous_scaled_font; - - status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); - if (unlikely (status)) - goto BAIL; - - status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); - if (unlikely (status)) - goto BAIL; - - _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); - - /* XXX: Mozilla code assumes that the ctm of a scaled font doesn't need to - * match the context ctm. This assumption breaks the previous_scaled_font - * cache. So we avoid using the cache for now. - if (was_previous) - cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font); - */ - - return; - -BAIL: - _cairo_set_error (cr, status); -} - -/** - * cairo_get_scaled_font: - * @cr: a #cairo_t - * - * Gets the current scaled font for a #cairo_t. - * - * Return value: the current scaled font. This object is owned by - * cairo. To keep a reference to it, you must call - * cairo_scaled_font_reference(). - * - * This function never returns %NULL. If memory cannot be allocated, a - * special "nil" #cairo_scaled_font_t object will be returned on which - * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using - * this nil object will cause its error state to propagate to other - * objects it is passed to, (for example, calling - * cairo_set_scaled_font() with a nil font will trigger an error that - * will shutdown the #cairo_t object). - * - * Since: 1.4 - **/ -cairo_scaled_font_t * -cairo_get_scaled_font (cairo_t *cr) -{ - cairo_status_t status; - cairo_scaled_font_t *scaled_font; - - if (unlikely (cr->status)) - return _cairo_scaled_font_create_in_error (cr->status); - - status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); - if (unlikely (status)) { - _cairo_set_error (cr, status); - return _cairo_scaled_font_create_in_error (status); - } - - return scaled_font; -} - -/** - * cairo_text_extents: - * @cr: a #cairo_t - * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL - * @extents: a #cairo_text_extents_t object into which the results - * will be stored - * - * Gets the extents for a string of text. The extents describe a - * user-space rectangle that encloses the "inked" portion of the text, - * (as it would be drawn by cairo_show_text()). Additionally, the - * x_advance and y_advance values indicate the amount by which the - * current point would be advanced by cairo_show_text(). - * - * Note that whitespace characters do not directly contribute to the - * size of the rectangle (extents.width and extents.height). They do - * contribute indirectly by changing the position of non-whitespace - * characters. In particular, trailing whitespace characters are - * likely to not affect the size of the rectangle, though they will - * affect the x_advance and y_advance values. - **/ -void -cairo_text_extents (cairo_t *cr, - const char *utf8, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - cairo_glyph_t *glyphs = NULL; - int num_glyphs; - double x, y; - - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - if (unlikely (cr->status)) - return; - - if (utf8 == NULL) - return; - - cairo_get_current_point (cr, &x, &y); - - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, strlen (utf8), - &glyphs, &num_glyphs, - NULL, NULL, - NULL); - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_gstate_glyph_extents (cr->gstate, - glyphs, num_glyphs, - extents); - cairo_glyph_free (glyphs); - - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_glyph_extents: - * @cr: a #cairo_t - * @glyphs: an array of #cairo_glyph_t objects - * @num_glyphs: the number of elements in @glyphs - * @extents: a #cairo_text_extents_t object into which the results - * will be stored - * - * Gets the extents for an array of glyphs. The extents describe a - * user-space rectangle that encloses the "inked" portion of the - * glyphs, (as they would be drawn by cairo_show_glyphs()). - * Additionally, the x_advance and y_advance values indicate the - * amount by which the current point would be advanced by - * cairo_show_glyphs(). - * - * Note that whitespace glyphs do not contribute to the size of the - * rectangle (extents.width and extents.height). - **/ -void -cairo_glyph_extents (cairo_t *cr, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - if (unlikely (cr->status)) - return; - - if (num_glyphs == 0) - return; - - if (num_glyphs < 0) { - _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); - return; - } - - if (glyphs == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, - extents); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_show_text: - * @cr: a cairo context - * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL - * - * A drawing operator that generates the shape from a string of UTF-8 - * characters, rendered according to the current font_face, font_size - * (font_matrix), and font_options. - * - * This function first computes a set of glyphs for the string of - * text. The first glyph is placed so that its origin is at the - * current point. The origin of each subsequent glyph is offset from - * that of the previous glyph by the advance values of the previous - * glyph. - * - * After this call the current point is moved to the origin of where - * the next glyph would be placed in this same progression. That is, - * the current point will be at the origin of the final glyph offset - * by its advance values. This allows for easy display of a single - * logical string with multiple calls to cairo_show_text(). - * - * Note: The cairo_show_text() function call is part of what the cairo - * designers call the "toy" text API. It is convenient for short demos - * and simple programs, but it is not expected to be adequate for - * serious text-using applications. See cairo_show_glyphs() for the - * "real" text display API in cairo. - **/ -void -cairo_show_text (cairo_t *cr, const char *utf8) -{ - cairo_text_extents_t extents; - cairo_status_t status; - cairo_glyph_t *glyphs, *last_glyph; - cairo_text_cluster_t *clusters; - int utf8_len, num_glyphs, num_clusters; - cairo_text_cluster_flags_t cluster_flags; - double x, y; - cairo_bool_t has_show_text_glyphs; - cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; - cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; - - if (unlikely (cr->status)) - return; - - if (utf8 == NULL) - return; - - cairo_get_current_point (cr, &x, &y); - - utf8_len = strlen (utf8); - - has_show_text_glyphs = - cairo_surface_has_show_text_glyphs (cairo_get_target (cr)); - - glyphs = stack_glyphs; - num_glyphs = ARRAY_LENGTH (stack_glyphs); - - if (has_show_text_glyphs) { - clusters = stack_clusters; - num_clusters = ARRAY_LENGTH (stack_clusters); - } else { - clusters = NULL; - num_clusters = 0; - } - - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, utf8_len, - &glyphs, &num_glyphs, - has_show_text_glyphs ? &clusters : NULL, &num_clusters, - &cluster_flags); - if (unlikely (status)) - goto BAIL; - - if (num_glyphs == 0) - return; - - status = _cairo_gstate_show_text_glyphs (cr->gstate, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags); - if (unlikely (status)) - goto BAIL; - - last_glyph = &glyphs[num_glyphs - 1]; - status = _cairo_gstate_glyph_extents (cr->gstate, - last_glyph, 1, - &extents); - if (unlikely (status)) - goto BAIL; - - x = last_glyph->x + extents.x_advance; - y = last_glyph->y + extents.y_advance; - cairo_move_to (cr, x, y); - - BAIL: - if (glyphs != stack_glyphs) - cairo_glyph_free (glyphs); - if (clusters != stack_clusters) - cairo_text_cluster_free (clusters); - - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_show_glyphs: - * @cr: a cairo context - * @glyphs: array of glyphs to show - * @num_glyphs: number of glyphs to show - * - * A drawing operator that generates the shape from an array of glyphs, - * rendered according to the current font face, font size - * (font matrix), and font options. - **/ -void -cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (num_glyphs == 0) - return; - - if (num_glyphs < 0) { - _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); - return; - } - - if (glyphs == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - status = _cairo_gstate_show_text_glyphs (cr->gstate, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_show_text_glyphs: - * @cr: a cairo context - * @utf8: a string of text encoded in UTF-8 - * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated - * @glyphs: array of glyphs to show - * @num_glyphs: number of glyphs to show - * @clusters: array of cluster mapping information - * @num_clusters: number of clusters in the mapping - * @cluster_flags: cluster mapping flags - * - * This operation has rendering effects similar to cairo_show_glyphs() - * but, if the target surface supports it, uses the provided text and - * cluster mapping to embed the text for the glyphs shown in the output. - * If the target does not support the extended attributes, this function - * acts like the basic cairo_show_glyphs() as if it had been passed - * @glyphs and @num_glyphs. - * - * The mapping between @utf8 and @glyphs is provided by an array of - * clusters. Each cluster covers a number of - * text bytes and glyphs, and neighboring clusters cover neighboring - * areas of @utf8 and @glyphs. The clusters should collectively cover @utf8 - * and @glyphs in entirety. - * - * The first cluster always covers bytes from the beginning of @utf8. - * If @cluster_flags do not have the %CAIRO_TEXT_CLUSTER_FLAG_BACKWARD - * set, the first cluster also covers the beginning - * of @glyphs, otherwise it covers the end of the @glyphs array and - * following clusters move backward. - * - * See #cairo_text_cluster_t for constraints on valid clusters. - * - * Since: 1.8 - **/ -void -cairo_show_text_glyphs (cairo_t *cr, - const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - /* A slew of sanity checks */ - - /* Special case for NULL and -1 */ - if (utf8 == NULL && utf8_len == -1) - utf8_len = 0; - - /* No NULLs for non-zeros */ - if ((num_glyphs && glyphs == NULL) || - (utf8_len && utf8 == NULL) || - (num_clusters && clusters == NULL)) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - /* A -1 for utf8_len means NUL-terminated */ - if (utf8_len == -1) - utf8_len = strlen (utf8); - - /* Apart from that, no negatives */ - if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) { - _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); - return; - } - - /* Make sure clusters cover the entire glyphs and utf8 arrays, - * and that cluster boundaries are UTF-8 boundaries. */ - status = _cairo_validate_text_clusters (utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); - if (status == CAIRO_STATUS_INVALID_CLUSTERS) { - /* Either got invalid UTF-8 text, or cluster mapping is bad. - * Differentiate those. */ - - cairo_status_t status2; - - status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL); - if (status2) - status = status2; - - _cairo_set_error (cr, status); - return; - } - - if (num_glyphs == 0 && utf8_len == 0) - return; - - status = _cairo_gstate_show_text_glyphs (cr->gstate, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_text_path: - * @cr: a cairo context - * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL - * - * Adds closed paths for text to the current path. The generated - * path if filled, achieves an effect similar to that of - * cairo_show_text(). - * - * Text conversion and positioning is done similar to cairo_show_text(). - * - * Like cairo_show_text(), After this call the current point is - * moved to the origin of where the next glyph would be placed in - * this same progression. That is, the current point will be at - * the origin of the final glyph offset by its advance values. - * This allows for chaining multiple calls to to cairo_text_path() - * without having to set current point in between. - * - * Note: The cairo_text_path() function call is part of what the cairo - * designers call the "toy" text API. It is convenient for short demos - * and simple programs, but it is not expected to be adequate for - * serious text-using applications. See cairo_glyph_path() for the - * "real" text path API in cairo. - **/ -void -cairo_text_path (cairo_t *cr, const char *utf8) -{ - cairo_status_t status; - cairo_text_extents_t extents; - cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; - cairo_glyph_t *glyphs, *last_glyph; - int num_glyphs; - double x, y; - - if (unlikely (cr->status)) - return; - - if (utf8 == NULL) - return; - - cairo_get_current_point (cr, &x, &y); - - glyphs = stack_glyphs; - num_glyphs = ARRAY_LENGTH (stack_glyphs); - - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, strlen (utf8), - &glyphs, &num_glyphs, - NULL, NULL, - NULL); - - if (unlikely (status)) - goto BAIL; - - if (num_glyphs == 0) - return; - - status = _cairo_gstate_glyph_path (cr->gstate, - glyphs, num_glyphs, - cr->path); - - if (unlikely (status)) - goto BAIL; - - last_glyph = &glyphs[num_glyphs - 1]; - status = _cairo_gstate_glyph_extents (cr->gstate, - last_glyph, 1, - &extents); - - if (unlikely (status)) - goto BAIL; - - x = last_glyph->x + extents.x_advance; - y = last_glyph->y + extents.y_advance; - cairo_move_to (cr, x, y); - - BAIL: - if (glyphs != stack_glyphs) - cairo_glyph_free (glyphs); - - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_glyph_path: - * @cr: a cairo context - * @glyphs: array of glyphs to show - * @num_glyphs: number of glyphs to show - * - * Adds closed paths for the glyphs to the current path. The generated - * path if filled, achieves an effect similar to that of - * cairo_show_glyphs(). - **/ -void -cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (num_glyphs == 0) - return; - - if (num_glyphs < 0) { - _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); - return; - } - - if (glyphs == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - status = _cairo_gstate_glyph_path (cr->gstate, - glyphs, num_glyphs, - cr->path); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_get_operator: - * @cr: a cairo context - * - * Gets the current compositing operator for a cairo context. - * - * Return value: the current compositing operator. - **/ -cairo_operator_t -cairo_get_operator (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_OPERATOR_DEFAULT; - - return _cairo_gstate_get_operator (cr->gstate); -} - -/** - * cairo_get_tolerance: - * @cr: a cairo context - * - * Gets the current tolerance value, as set by cairo_set_tolerance(). - * - * Return value: the current tolerance value. - **/ -double -cairo_get_tolerance (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_TOLERANCE_DEFAULT; - - return _cairo_gstate_get_tolerance (cr->gstate); -} -slim_hidden_def (cairo_get_tolerance); - -/** - * cairo_get_antialias: - * @cr: a cairo context - * - * Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias(). - * - * Return value: the current shape antialiasing mode. - **/ -cairo_antialias_t -cairo_get_antialias (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_ANTIALIAS_DEFAULT; - - return _cairo_gstate_get_antialias (cr->gstate); -} - -/** - * cairo_has_current_point: - * @cr: a cairo context - * - * Returns whether a current point is defined on the current path. - * See cairo_get_current_point() for details on the current point. - * - * Return value: whether a current point is defined. - * - * Since: 1.6 - **/ -cairo_bool_t -cairo_has_current_point (cairo_t *cr) -{ - if (unlikely (cr->status)) - return FALSE; - - return cr->path->has_current_point; -} - -/** - * cairo_get_current_point: - * @cr: a cairo context - * @x: return value for X coordinate of the current point - * @y: return value for Y coordinate of the current point - * - * Gets the current point of the current path, which is - * conceptually the final point reached by the path so far. - * - * The current point is returned in the user-space coordinate - * system. If there is no defined current point or if @cr is in an - * error status, @x and @y will both be set to 0.0. It is possible to - * check this in advance with cairo_has_current_point(). - * - * Most path construction functions alter the current point. See the - * following for details on how they affect the current point: - * cairo_new_path(), cairo_new_sub_path(), - * cairo_append_path(), cairo_close_path(), - * cairo_move_to(), cairo_line_to(), cairo_curve_to(), - * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(), - * cairo_arc(), cairo_arc_negative(), cairo_rectangle(), - * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path(). - * - * Some functions use and alter the current point but do not - * otherwise change current path: - * cairo_show_text(). - * - * Some functions unset the current path and as a result, current point: - * cairo_fill(), cairo_stroke(). - **/ -void -cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret) -{ - cairo_fixed_t x_fixed, y_fixed; - double x, y; - - if (cr->status == CAIRO_STATUS_SUCCESS && - _cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) - { - x = _cairo_fixed_to_double (x_fixed); - y = _cairo_fixed_to_double (y_fixed); - _cairo_gstate_backend_to_user (cr->gstate, &x, &y); - } - else - { - x = 0.0; - y = 0.0; - } - - if (x_ret) - *x_ret = x; - if (y_ret) - *y_ret = y; -} -slim_hidden_def(cairo_get_current_point); - -/** - * cairo_get_fill_rule: - * @cr: a cairo context - * - * Gets the current fill rule, as set by cairo_set_fill_rule(). - * - * Return value: the current fill rule. - **/ -cairo_fill_rule_t -cairo_get_fill_rule (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_FILL_RULE_DEFAULT; - - return _cairo_gstate_get_fill_rule (cr->gstate); -} - -/** - * cairo_get_line_width: - * @cr: a cairo context - * - * This function returns the current line width value exactly as set by - * cairo_set_line_width(). Note that the value is unchanged even if - * the CTM has changed between the calls to cairo_set_line_width() and - * cairo_get_line_width(). - * - * Return value: the current line width. - **/ -double -cairo_get_line_width (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_LINE_WIDTH_DEFAULT; - - return _cairo_gstate_get_line_width (cr->gstate); -} -slim_hidden_def (cairo_get_line_width); - -/** - * cairo_get_line_cap: - * @cr: a cairo context - * - * Gets the current line cap style, as set by cairo_set_line_cap(). - * - * Return value: the current line cap style. - **/ -cairo_line_cap_t -cairo_get_line_cap (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_LINE_CAP_DEFAULT; - - return _cairo_gstate_get_line_cap (cr->gstate); -} - -/** - * cairo_get_line_join: - * @cr: a cairo context - * - * Gets the current line join style, as set by cairo_set_line_join(). - * - * Return value: the current line join style. - **/ -cairo_line_join_t -cairo_get_line_join (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_LINE_JOIN_DEFAULT; - - return _cairo_gstate_get_line_join (cr->gstate); -} - -/** - * cairo_get_miter_limit: - * @cr: a cairo context - * - * Gets the current miter limit, as set by cairo_set_miter_limit(). - * - * Return value: the current miter limit. - **/ -double -cairo_get_miter_limit (cairo_t *cr) -{ - if (unlikely (cr->status)) - return CAIRO_GSTATE_MITER_LIMIT_DEFAULT; - - return _cairo_gstate_get_miter_limit (cr->gstate); -} - -/** - * cairo_get_matrix: - * @cr: a cairo context - * @matrix: return value for the matrix - * - * Stores the current transformation matrix (CTM) into @matrix. - **/ -void -cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) -{ - if (unlikely (cr->status)) { - cairo_matrix_init_identity (matrix); - return; - } - - _cairo_gstate_get_matrix (cr->gstate, matrix); -} -slim_hidden_def (cairo_get_matrix); - -/** - * cairo_get_target: - * @cr: a cairo context - * - * Gets the target surface for the cairo context as passed to - * cairo_create(). - * - * This function will always return a valid pointer, but the result - * can be a "nil" surface if @cr is already in an error state, - * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). - * A nil surface is indicated by cairo_surface_status() - * != %CAIRO_STATUS_SUCCESS. - * - * Return value: the target surface. This object is owned by cairo. To - * keep a reference to it, you must call cairo_surface_reference(). - **/ -cairo_surface_t * -cairo_get_target (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_surface_create_in_error (cr->status); - - return _cairo_gstate_get_original_target (cr->gstate); -} -slim_hidden_def (cairo_get_target); - -/** - * cairo_get_group_target: - * @cr: a cairo context - * - * Gets the current destination surface for the context. This is either - * the original target surface as passed to cairo_create() or the target - * surface for the current group as started by the most recent call to - * cairo_push_group() or cairo_push_group_with_content(). - * - * This function will always return a valid pointer, but the result - * can be a "nil" surface if @cr is already in an error state, - * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). - * A nil surface is indicated by cairo_surface_status() - * != %CAIRO_STATUS_SUCCESS. - * - * Return value: the target surface. This object is owned by cairo. To - * keep a reference to it, you must call cairo_surface_reference(). - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_get_group_target (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_surface_create_in_error (cr->status); - - return _cairo_gstate_get_target (cr->gstate); -} - -/** - * cairo_copy_path: - * @cr: a cairo context - * - * Creates a copy of the current path and returns it to the user as a - * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate - * over the returned data structure. - * - * This function will always return a valid pointer, but the result - * will have no data (data==%NULL and - * num_data==0), if either of the following - * conditions hold: - * - * - * If there is insufficient memory to copy the path. In this - * case path->status will be set to - * %CAIRO_STATUS_NO_MEMORY. - * If @cr is already in an error state. In this case - * path->status will contain the same status that - * would be returned by cairo_status(). - * - * - * Return value: the copy of the current path. The caller owns the - * returned object and should call cairo_path_destroy() when finished - * with it. - **/ -cairo_path_t * -cairo_copy_path (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_path_create_in_error (cr->status); - - return _cairo_path_create (cr->path, cr->gstate); -} - -/** - * cairo_copy_path_flat: - * @cr: a cairo context - * - * Gets a flattened copy of the current path and returns it to the - * user as a #cairo_path_t. See #cairo_path_data_t for hints on - * how to iterate over the returned data structure. - * - * This function is like cairo_copy_path() except that any curves - * in the path will be approximated with piecewise-linear - * approximations, (accurate to within the current tolerance - * value). That is, the result is guaranteed to not have any elements - * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a - * series of %CAIRO_PATH_LINE_TO elements. - * - * This function will always return a valid pointer, but the result - * will have no data (data==%NULL and - * num_data==0), if either of the following - * conditions hold: - * - * - * If there is insufficient memory to copy the path. In this - * case path->status will be set to - * %CAIRO_STATUS_NO_MEMORY. - * If @cr is already in an error state. In this case - * path->status will contain the same status that - * would be returned by cairo_status(). - * - * - * Return value: the copy of the current path. The caller owns the - * returned object and should call cairo_path_destroy() when finished - * with it. - **/ -cairo_path_t * -cairo_copy_path_flat (cairo_t *cr) -{ - if (unlikely (cr->status)) - return _cairo_path_create_in_error (cr->status); - - return _cairo_path_create_flat (cr->path, cr->gstate); -} - -/** - * cairo_append_path: - * @cr: a cairo context - * @path: path to be appended - * - * Append the @path onto the current path. The @path may be either the - * return value from one of cairo_copy_path() or - * cairo_copy_path_flat() or it may be constructed manually. See - * #cairo_path_t for details on how the path data structure should be - * initialized, and note that path->status must be - * initialized to %CAIRO_STATUS_SUCCESS. - **/ -void -cairo_append_path (cairo_t *cr, - const cairo_path_t *path) -{ - cairo_status_t status; - - if (unlikely (cr->status)) - return; - - if (path == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - if (path->status) { - if (path->status > CAIRO_STATUS_SUCCESS && - path->status <= CAIRO_STATUS_LAST_STATUS) - _cairo_set_error (cr, path->status); - else - _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS); - return; - } - - if (path->num_data == 0) - return; - - if (path->data == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); - return; - } - - status = _cairo_path_append_to_context (path, cr); - if (unlikely (status)) - _cairo_set_error (cr, status); -} - -/** - * cairo_status: - * @cr: a cairo context - * - * Checks whether an error has previously occurred for this context. - * - * Returns: the current status of this context, see #cairo_status_t - **/ -cairo_status_t -cairo_status (cairo_t *cr) -{ - return cr->status; -} -slim_hidden_def (cairo_status); diff --git a/libs/cairo/cairo/src/cairo.h b/libs/cairo/cairo/src/cairo.h deleted file mode 100644 index 3a34e80bf..000000000 --- a/libs/cairo/cairo/src/cairo.h +++ /dev/null @@ -1,2696 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CAIRO_H -#define CAIRO_H - -#include "cairo-version.h" -#include "cairo-features.h" -#include "cairo-deprecated.h" - -#ifdef __cplusplus -# define CAIRO_BEGIN_DECLS extern "C" { -# define CAIRO_END_DECLS } -#else -# define CAIRO_BEGIN_DECLS -# define CAIRO_END_DECLS -#endif - -#ifndef cairo_public -# if defined (_MSC_VER) && ! defined (CAIRO_WIN32_STATIC_BUILD) -# define cairo_public __declspec(dllimport) -# else -# define cairo_public -# endif -#endif - -CAIRO_BEGIN_DECLS - -#define CAIRO_VERSION_ENCODE(major, minor, micro) ( \ - ((major) * 10000) \ - + ((minor) * 100) \ - + ((micro) * 1)) - -#define CAIRO_VERSION CAIRO_VERSION_ENCODE( \ - CAIRO_VERSION_MAJOR, \ - CAIRO_VERSION_MINOR, \ - CAIRO_VERSION_MICRO) - - -#define CAIRO_VERSION_STRINGIZE_(major, minor, micro) \ - #major"."#minor"."#micro -#define CAIRO_VERSION_STRINGIZE(major, minor, micro) \ - CAIRO_VERSION_STRINGIZE_(major, minor, micro) - -#define CAIRO_VERSION_STRING CAIRO_VERSION_STRINGIZE( \ - CAIRO_VERSION_MAJOR, \ - CAIRO_VERSION_MINOR, \ - CAIRO_VERSION_MICRO) - - -cairo_public int -cairo_version (void); - -cairo_public const char* -cairo_version_string (void); - -/** - * cairo_bool_t: - * - * #cairo_bool_t is used for boolean values. Returns of type - * #cairo_bool_t will always be either 0 or 1, but testing against - * these values explicitly is not encouraged; just use the - * value as a boolean condition. - * - * - * if (cairo_in_stroke (cr, x, y)) { - * /* do something */ - * } - * - **/ -typedef int cairo_bool_t; - -/** - * cairo_t: - * - * A #cairo_t contains the current state of the rendering device, - * including coordinates of yet to be drawn shapes. - * - * Cairo contexts, as #cairo_t objects are named, are central to - * cairo and all drawing with cairo is always done to a #cairo_t - * object. - * - * Memory management of #cairo_t is done with - * cairo_reference() and cairo_destroy(). - **/ -typedef struct _cairo cairo_t; - -/** - * cairo_surface_t: - * - * A #cairo_surface_t represents an image, either as the destination - * of a drawing operation or as source when drawing onto another - * surface. To draw to a #cairo_surface_t, create a cairo context - * with the surface as the target, using cairo_create(). - * - * There are different subtypes of #cairo_surface_t for - * different drawing backends; for example, cairo_image_surface_create() - * creates a bitmap image in memory. - * The type of a surface can be queried with cairo_surface_get_type(). - * - * The initial contents of a surface after creation depend upon the manner - * of its creation. If cairo creates the surface and backing storage for - * the user, it will be initially cleared; for example, - * cairo_image_surface_create() and cairo_surface_create_similar(). - * Alternatively, if the user passes in a reference to some backing storage - * and asks cairo to wrap that in a #cairo_surface_t, then the contents are - * not modified; for example, cairo_image_surface_create_for_data() and - * cairo_xlib_surface_create(). - * - * Memory management of #cairo_surface_t is done with - * cairo_surface_reference() and cairo_surface_destroy(). - **/ -typedef struct _cairo_surface cairo_surface_t; - -/** - * cairo_device_t: - * - * A #cairo_device_t represents the driver interface for drawing - * operations to a #cairo_surface_t. There are different subtypes of - * #cairo_device_t for different drawing backends; for example, - * cairo_xcb_device_create() creates a device that wraps the connection - * to an X Windows System using the XCB library. - * - * The type of a device can be queried with cairo_device_get_type(). - * - * Memory management of #cairo_device_t is done with - * cairo_device_reference() and cairo_device_destroy(). - * - * Since: 1.10 - **/ -typedef struct _cairo_device cairo_device_t; - -/** - * cairo_matrix_t: - * @xx: xx component of the affine transformation - * @yx: yx component of the affine transformation - * @xy: xy component of the affine transformation - * @yy: yy component of the affine transformation - * @x0: X translation component of the affine transformation - * @y0: Y translation component of the affine transformation - * - * A #cairo_matrix_t holds an affine transformation, such as a scale, - * rotation, shear, or a combination of those. The transformation of - * a point (x, y) is given by: - * - * x_new = xx * x + xy * y + x0; - * y_new = yx * x + yy * y + y0; - * - **/ -typedef struct _cairo_matrix { - double xx; double yx; - double xy; double yy; - double x0; double y0; -} cairo_matrix_t; - -/** - * cairo_pattern_t: - * - * A #cairo_pattern_t represents a source when drawing onto a - * surface. There are different subtypes of #cairo_pattern_t, - * for different types of sources; for example, - * cairo_pattern_create_rgb() creates a pattern for a solid - * opaque color. - * - * Other than various cairo_pattern_create_type() - * functions, some of the pattern types can be implicitly created - * using various cairo_set_source_type() functions; - * for example cairo_set_source_rgb(). - * - * The type of a pattern can be queried with cairo_pattern_get_type(). - * - * Memory management of #cairo_pattern_t is done with - * cairo_pattern_reference() and cairo_pattern_destroy(). - **/ -typedef struct _cairo_pattern cairo_pattern_t; - -/** - * cairo_destroy_func_t: - * @data: The data element being destroyed. - * - * #cairo_destroy_func_t the type of function which is called when a - * data element is destroyed. It is passed the pointer to the data - * element and should free any memory and resources allocated for it. - **/ -typedef void (*cairo_destroy_func_t) (void *data); - -/** - * cairo_surface_func_t: - * @surface: The surface being referred to. - * - * #cairo_surface_func_t the type of function which is used for callback - * when a surface needs to be apssed as a parameter. - */ -typedef void (*cairo_surface_func_t) (cairo_surface_t *surface); - -/** - * cairo_user_data_key_t: - * @unused: not used; ignore. - * - * #cairo_user_data_key_t is used for attaching user data to cairo - * data structures. The actual contents of the struct is never used, - * and there is no need to initialize the object; only the unique - * address of a #cairo_data_key_t object is used. Typically, you - * would just use the address of a static #cairo_data_key_t object. - **/ -typedef struct _cairo_user_data_key { - int unused; -} cairo_user_data_key_t; - -/** - * cairo_status_t: - * @CAIRO_STATUS_SUCCESS: no error has occurred - * @CAIRO_STATUS_NO_MEMORY: out of memory - * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() - * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() - * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined - * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) - * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t - * @CAIRO_STATUS_NULL_POINTER: %NULL pointer - * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 - * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid - * @CAIRO_STATUS_READ_ERROR: error while reading from input stream - * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream - * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished - * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation - * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation - * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t - * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t - * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* - * @CAIRO_STATUS_FILE_NOT_FOUND: file not found - * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting - * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2) - * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4) - * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4) - * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6) - * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6) - * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8) - * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) - * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8) - * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8) - * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8) - * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8) - * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8) - * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10) - * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) - * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10) - * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10) - * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of - * status values defined in this enumeration. When using this value, note - * that the version of cairo at run-time may have additional status values - * defined than the value of this symbol at compile-time. (Since 1.10) - * - * #cairo_status_t is used to indicate errors that can occur when - * using Cairo. In some cases it is returned directly by functions. - * but when using #cairo_t, the last error, if any, is stored in - * the context and can be retrieved with cairo_status(). - * - * New entries may be added in future versions. Use cairo_status_to_string() - * to get a human-readable representation of an error message. - **/ -typedef enum _cairo_status { - CAIRO_STATUS_SUCCESS = 0, - - CAIRO_STATUS_NO_MEMORY, - CAIRO_STATUS_INVALID_RESTORE, - CAIRO_STATUS_INVALID_POP_GROUP, - CAIRO_STATUS_NO_CURRENT_POINT, - CAIRO_STATUS_INVALID_MATRIX, - CAIRO_STATUS_INVALID_STATUS, - CAIRO_STATUS_NULL_POINTER, - CAIRO_STATUS_INVALID_STRING, - CAIRO_STATUS_INVALID_PATH_DATA, - CAIRO_STATUS_READ_ERROR, - CAIRO_STATUS_WRITE_ERROR, - CAIRO_STATUS_SURFACE_FINISHED, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH, - CAIRO_STATUS_PATTERN_TYPE_MISMATCH, - CAIRO_STATUS_INVALID_CONTENT, - CAIRO_STATUS_INVALID_FORMAT, - CAIRO_STATUS_INVALID_VISUAL, - CAIRO_STATUS_FILE_NOT_FOUND, - CAIRO_STATUS_INVALID_DASH, - CAIRO_STATUS_INVALID_DSC_COMMENT, - CAIRO_STATUS_INVALID_INDEX, - CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, - CAIRO_STATUS_TEMP_FILE_ERROR, - CAIRO_STATUS_INVALID_STRIDE, - CAIRO_STATUS_FONT_TYPE_MISMATCH, - CAIRO_STATUS_USER_FONT_IMMUTABLE, - CAIRO_STATUS_USER_FONT_ERROR, - CAIRO_STATUS_NEGATIVE_COUNT, - CAIRO_STATUS_INVALID_CLUSTERS, - CAIRO_STATUS_INVALID_SLANT, - CAIRO_STATUS_INVALID_WEIGHT, - CAIRO_STATUS_INVALID_SIZE, - CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, - CAIRO_STATUS_DEVICE_TYPE_MISMATCH, - CAIRO_STATUS_DEVICE_ERROR, - CAIRO_STATUS_NO_DEVICE, - - CAIRO_STATUS_LAST_STATUS -} cairo_status_t; - -/** - * cairo_content_t: - * @CAIRO_CONTENT_COLOR: The surface will hold color content only. - * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. - * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. - * - * #cairo_content_t is used to describe the content that a surface will - * contain, whether color information, alpha information (translucence - * vs. opacity), or both. - * - * Note: The large values here are designed to keep #cairo_content_t - * values distinct from #cairo_format_t values so that the - * implementation can detect the error if users confuse the two types. - **/ -typedef enum _cairo_content { - CAIRO_CONTENT_COLOR = 0x1000, - CAIRO_CONTENT_ALPHA = 0x2000, - CAIRO_CONTENT_COLOR_ALPHA = 0x3000 -} cairo_content_t; - -/** - * cairo_write_func_t: - * @closure: the output closure - * @data: the buffer containing the data to write - * @length: the amount of data to write - * - * #cairo_write_func_t is the type of function which is called when a - * backend needs to write data to an output stream. It is passed the - * closure which was specified by the user at the time the write - * function was registered, the data to write and the length of the - * data in bytes. The write function should return - * %CAIRO_STATUS_SUCCESS if all the data was successfully written, - * %CAIRO_STATUS_WRITE_ERROR otherwise. - * - * Returns: the status code of the write operation - **/ -typedef cairo_status_t (*cairo_write_func_t) (void *closure, - const unsigned char *data, - unsigned int length); - -/** - * cairo_read_func_t: - * @closure: the input closure - * @data: the buffer into which to read the data - * @length: the amount of data to read - * - * #cairo_read_func_t is the type of function which is called when a - * backend needs to read data from an input stream. It is passed the - * closure which was specified by the user at the time the read - * function was registered, the buffer to read the data into and the - * length of the data in bytes. The read function should return - * %CAIRO_STATUS_SUCCESS if all the data was successfully read, - * %CAIRO_STATUS_READ_ERROR otherwise. - * - * Returns: the status code of the read operation - **/ -typedef cairo_status_t (*cairo_read_func_t) (void *closure, - unsigned char *data, - unsigned int length); - -/* Functions for manipulating state objects */ -cairo_public cairo_t * -cairo_create (cairo_surface_t *target); - -cairo_public cairo_t * -cairo_reference (cairo_t *cr); - -cairo_public void -cairo_destroy (cairo_t *cr); - -cairo_public unsigned int -cairo_get_reference_count (cairo_t *cr); - -cairo_public void * -cairo_get_user_data (cairo_t *cr, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_set_user_data (cairo_t *cr, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -cairo_public void -cairo_save (cairo_t *cr); - -cairo_public void -cairo_restore (cairo_t *cr); - -cairo_public void -cairo_push_group (cairo_t *cr); - -cairo_public void -cairo_push_group_with_content (cairo_t *cr, cairo_content_t content); - -cairo_public cairo_pattern_t * -cairo_pop_group (cairo_t *cr); - -cairo_public void -cairo_pop_group_to_source (cairo_t *cr); - -/* Modify state */ - -/** - * cairo_operator_t: - * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) - * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) - * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer - * (bounded) - * @CAIRO_OPERATOR_IN: draw source where there was destination content - * (unbounded) - * @CAIRO_OPERATOR_OUT: draw source where there was no destination - * content (unbounded) - * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and - * only there - * @CAIRO_OPERATOR_DEST: ignore the source - * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source - * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was - * source content (unbounded) - * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no - * source content - * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content - * and only there (unbounded) - * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only - * one of them - * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated - * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are - * disjoint geometries - * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied. - * This causes the result to be at least as dark as the darker inputs. - * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and - * multiplied. This causes the result to be at least as light as the lighter - * inputs. - * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the - * lightness of the destination color. - * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it - * is darker, otherwise keeps the source. - * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it - * is lighter, otherwise keeps the source. - * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect - * the source color. - * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect - * the source color. - * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source - * color. - * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source - * color. - * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and - * destination color. - * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but - * with lower contrast. - * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source - * and the saturation and luminosity of the target. - * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation - * of the source and the hue and luminosity of the target. Painting with - * this mode onto a gray area prduces no change. - * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation - * of the source and the luminosity of the target. This preserves the gray - * levels of the target and is useful for coloring monochrome images or - * tinting color images. - * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of - * the source and the hue and saturation of the target. This produces an - * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. - * - * #cairo_operator_t is used to set the compositing operator for all cairo - * drawing operations. - * - * The default operator is %CAIRO_OPERATOR_OVER. - * - * The operators marked as unbounded modify their - * destination even outside of the mask layer (that is, their effect is not - * bound by the mask layer). However, their effect can still be limited by - * way of clipping. - * - * To keep things simple, the operator descriptions here - * document the behavior for when both source and destination are either fully - * transparent or fully opaque. The actual implementation works for - * translucent layers too. - * For a more detailed explanation of the effects of each operator, including - * the mathematical definitions, see - * http://cairographics.org/operators/. - **/ -typedef enum _cairo_operator { - CAIRO_OPERATOR_CLEAR, - - CAIRO_OPERATOR_SOURCE, - CAIRO_OPERATOR_OVER, - CAIRO_OPERATOR_IN, - CAIRO_OPERATOR_OUT, - CAIRO_OPERATOR_ATOP, - - CAIRO_OPERATOR_DEST, - CAIRO_OPERATOR_DEST_OVER, - CAIRO_OPERATOR_DEST_IN, - CAIRO_OPERATOR_DEST_OUT, - CAIRO_OPERATOR_DEST_ATOP, - - CAIRO_OPERATOR_XOR, - CAIRO_OPERATOR_ADD, - CAIRO_OPERATOR_SATURATE, - - CAIRO_OPERATOR_MULTIPLY, - CAIRO_OPERATOR_SCREEN, - CAIRO_OPERATOR_OVERLAY, - CAIRO_OPERATOR_DARKEN, - CAIRO_OPERATOR_LIGHTEN, - CAIRO_OPERATOR_COLOR_DODGE, - CAIRO_OPERATOR_COLOR_BURN, - CAIRO_OPERATOR_HARD_LIGHT, - CAIRO_OPERATOR_SOFT_LIGHT, - CAIRO_OPERATOR_DIFFERENCE, - CAIRO_OPERATOR_EXCLUSION, - CAIRO_OPERATOR_HSL_HUE, - CAIRO_OPERATOR_HSL_SATURATION, - CAIRO_OPERATOR_HSL_COLOR, - CAIRO_OPERATOR_HSL_LUMINOSITY -} cairo_operator_t; - -cairo_public void -cairo_set_operator (cairo_t *cr, cairo_operator_t op); - -cairo_public void -cairo_set_source (cairo_t *cr, cairo_pattern_t *source); - -cairo_public void -cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); - -cairo_public void -cairo_set_source_rgba (cairo_t *cr, - double red, double green, double blue, - double alpha); - -cairo_public void -cairo_set_source_surface (cairo_t *cr, - cairo_surface_t *surface, - double x, - double y); - -cairo_public void -cairo_set_tolerance (cairo_t *cr, double tolerance); - -/** - * cairo_antialias_t: - * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for - * the subsystem and target device - * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask - * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using - * shades of gray for black text on a white background, for example). - * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking - * advantage of the order of subpixel elements on devices - * such as LCD panels - * - * Specifies the type of antialiasing to do when rendering text or shapes. - **/ -typedef enum _cairo_antialias { - CAIRO_ANTIALIAS_DEFAULT, - CAIRO_ANTIALIAS_NONE, - CAIRO_ANTIALIAS_GRAY, - CAIRO_ANTIALIAS_SUBPIXEL -} cairo_antialias_t; - -cairo_public void -cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); - -/** - * cairo_fill_rule_t: - * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from - * left-to-right, counts +1. If the path crosses the ray - * from right to left, counts -1. (Left and right are determined - * from the perspective of looking along the ray from the starting - * point.) If the total count is non-zero, the point will be filled. - * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of - * intersections, without regard to the orientation of the contour. If - * the total number of intersections is odd, the point will be - * filled. - * - * #cairo_fill_rule_t is used to select how paths are filled. For both - * fill rules, whether or not a point is included in the fill is - * determined by taking a ray from that point to infinity and looking - * at intersections with the path. The ray can be in any direction, - * as long as it doesn't pass through the end point of a segment - * or have a tricky intersection such as intersecting tangent to the path. - * (Note that filling is not actually implemented in this way. This - * is just a description of the rule that is applied.) - * - * The default fill rule is %CAIRO_FILL_RULE_WINDING. - * - * New entries may be added in future versions. - **/ -typedef enum _cairo_fill_rule { - CAIRO_FILL_RULE_WINDING, - CAIRO_FILL_RULE_EVEN_ODD -} cairo_fill_rule_t; - -cairo_public void -cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); - -cairo_public void -cairo_set_line_width (cairo_t *cr, double width); - -/** - * cairo_line_cap_t: - * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point - * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point - * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point - * - * Specifies how to render the endpoints of the path when stroking. - * - * The default line cap style is %CAIRO_LINE_CAP_BUTT. - **/ -typedef enum _cairo_line_cap { - CAIRO_LINE_CAP_BUTT, - CAIRO_LINE_CAP_ROUND, - CAIRO_LINE_CAP_SQUARE -} cairo_line_cap_t; - -cairo_public void -cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); - -/** - * cairo_line_join_t: - * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see - * cairo_set_miter_limit() - * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the - * joint point - * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half - * the line width from the joint point - * - * Specifies how to render the junction of two lines when stroking. - * - * The default line join style is %CAIRO_LINE_JOIN_MITER. - **/ -typedef enum _cairo_line_join { - CAIRO_LINE_JOIN_MITER, - CAIRO_LINE_JOIN_ROUND, - CAIRO_LINE_JOIN_BEVEL -} cairo_line_join_t; - -cairo_public void -cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join); - -cairo_public void -cairo_set_dash (cairo_t *cr, - const double *dashes, - int num_dashes, - double offset); - -cairo_public void -cairo_set_miter_limit (cairo_t *cr, double limit); - -cairo_public void -cairo_translate (cairo_t *cr, double tx, double ty); - -cairo_public void -cairo_scale (cairo_t *cr, double sx, double sy); - -cairo_public void -cairo_rotate (cairo_t *cr, double angle); - -cairo_public void -cairo_transform (cairo_t *cr, - const cairo_matrix_t *matrix); - -cairo_public void -cairo_set_matrix (cairo_t *cr, - const cairo_matrix_t *matrix); - -cairo_public void -cairo_identity_matrix (cairo_t *cr); - -cairo_public void -cairo_user_to_device (cairo_t *cr, double *x, double *y); - -cairo_public void -cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy); - -cairo_public void -cairo_device_to_user (cairo_t *cr, double *x, double *y); - -cairo_public void -cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy); - -/* Path creation functions */ -cairo_public void -cairo_new_path (cairo_t *cr); - -cairo_public void -cairo_move_to (cairo_t *cr, double x, double y); - -cairo_public void -cairo_new_sub_path (cairo_t *cr); - -cairo_public void -cairo_line_to (cairo_t *cr, double x, double y); - -cairo_public void -cairo_curve_to (cairo_t *cr, - double x1, double y1, - double x2, double y2, - double x3, double y3); - -cairo_public void -cairo_arc (cairo_t *cr, - double xc, double yc, - double radius, - double angle1, double angle2); - -cairo_public void -cairo_arc_negative (cairo_t *cr, - double xc, double yc, - double radius, - double angle1, double angle2); - -/* XXX: NYI -cairo_public void -cairo_arc_to (cairo_t *cr, - double x1, double y1, - double x2, double y2, - double radius); -*/ - -cairo_public void -cairo_rel_move_to (cairo_t *cr, double dx, double dy); - -cairo_public void -cairo_rel_line_to (cairo_t *cr, double dx, double dy); - -cairo_public void -cairo_rel_curve_to (cairo_t *cr, - double dx1, double dy1, - double dx2, double dy2, - double dx3, double dy3); - -cairo_public void -cairo_rectangle (cairo_t *cr, - double x, double y, - double width, double height); - -/* XXX: NYI -cairo_public void -cairo_stroke_to_path (cairo_t *cr); -*/ - -cairo_public void -cairo_close_path (cairo_t *cr); - -cairo_public void -cairo_path_extents (cairo_t *cr, - double *x1, double *y1, - double *x2, double *y2); - -/* Painting functions */ -cairo_public void -cairo_paint (cairo_t *cr); - -cairo_public void -cairo_paint_with_alpha (cairo_t *cr, - double alpha); - -cairo_public void -cairo_mask (cairo_t *cr, - cairo_pattern_t *pattern); - -cairo_public void -cairo_mask_surface (cairo_t *cr, - cairo_surface_t *surface, - double surface_x, - double surface_y); - -cairo_public void -cairo_stroke (cairo_t *cr); - -cairo_public void -cairo_stroke_preserve (cairo_t *cr); - -cairo_public void -cairo_fill (cairo_t *cr); - -cairo_public void -cairo_fill_preserve (cairo_t *cr); - -cairo_public void -cairo_copy_page (cairo_t *cr); - -cairo_public void -cairo_show_page (cairo_t *cr); - -/* Insideness testing */ -cairo_public cairo_bool_t -cairo_in_stroke (cairo_t *cr, double x, double y); - -cairo_public cairo_bool_t -cairo_in_fill (cairo_t *cr, double x, double y); - -cairo_public cairo_bool_t -cairo_in_clip (cairo_t *cr, double x, double y); - -/* Rectangular extents */ -cairo_public void -cairo_stroke_extents (cairo_t *cr, - double *x1, double *y1, - double *x2, double *y2); - -cairo_public void -cairo_fill_extents (cairo_t *cr, - double *x1, double *y1, - double *x2, double *y2); - -/* Clipping */ -cairo_public void -cairo_reset_clip (cairo_t *cr); - -cairo_public void -cairo_clip (cairo_t *cr); - -cairo_public void -cairo_clip_preserve (cairo_t *cr); - -cairo_public void -cairo_clip_extents (cairo_t *cr, - double *x1, double *y1, - double *x2, double *y2); - -/** - * cairo_rectangle_t: - * @x: X coordinate of the left side of the rectangle - * @y: Y coordinate of the the top side of the rectangle - * @width: width of the rectangle - * @height: height of the rectangle - * - * A data structure for holding a rectangle. - * - * Since: 1.4 - **/ -typedef struct _cairo_rectangle { - double x, y, width, height; -} cairo_rectangle_t; - -/** - * cairo_rectangle_list_t: - * @status: Error status of the rectangle list - * @rectangles: Array containing the rectangles - * @num_rectangles: Number of rectangles in this list - * - * A data structure for holding a dynamically allocated - * array of rectangles. - * - * Since: 1.4 - **/ -typedef struct _cairo_rectangle_list { - cairo_status_t status; - cairo_rectangle_t *rectangles; - int num_rectangles; -} cairo_rectangle_list_t; - -cairo_public cairo_rectangle_list_t * -cairo_copy_clip_rectangle_list (cairo_t *cr); - -cairo_public void -cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); - -/* Font/Text functions */ - -/** - * cairo_scaled_font_t: - * - * A #cairo_scaled_font_t is a font scaled to a particular size and device - * resolution. A #cairo_scaled_font_t is most useful for low-level font - * usage where a library or application wants to cache a reference - * to a scaled font to speed up the computation of metrics. - * - * There are various types of scaled fonts, depending on the - * font backend they use. The type of a - * scaled font can be queried using cairo_scaled_font_get_type(). - * - * Memory management of #cairo_scaled_font_t is done with - * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). - **/ -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 - * font backend they use. The type of a - * font face can be queried using cairo_font_face_get_type(). - * - * Memory management of #cairo_font_face_t is done with - * cairo_font_face_reference() and cairo_font_face_destroy(). - **/ -typedef struct _cairo_font_face cairo_font_face_t; - -/** - * cairo_glyph_t: - * @index: glyph index in the font. The exact interpretation of the - * glyph index depends on the font technology being used. - * @x: the offset in the X direction between the origin used for - * drawing or measuring the string and the origin of this glyph. - * @y: the offset in the Y direction between the origin used for - * drawing or measuring the string and the origin of this glyph. - * - * The #cairo_glyph_t structure holds information about a single glyph - * when drawing or measuring text. A font is (in simple terms) a - * collection of shapes used to draw text. A glyph is one of these - * shapes. There can be multiple glyphs for a single character - * (alternates to be used in different contexts, for example), or a - * glyph can be a ligature of multiple - * characters. Cairo doesn't expose any way of converting input text - * into glyphs, so in order to use the Cairo interfaces that take - * arrays of glyphs, you must directly access the appropriate - * underlying font system. - * - * Note that the offsets given by @x and @y are not cumulative. When - * drawing or measuring text, each glyph is individually positioned - * with respect to the overall origin - **/ -typedef struct { - unsigned long index; - double x; - double y; -} cairo_glyph_t; - -cairo_public cairo_glyph_t * -cairo_glyph_allocate (int num_glyphs); - -cairo_public void -cairo_glyph_free (cairo_glyph_t *glyphs); - -/** - * cairo_text_cluster_t: - * @num_bytes: the number of bytes of UTF-8 text covered by cluster - * @num_glyphs: the number of glyphs covered by cluster - * - * The #cairo_text_cluster_t structure holds information about a single - * text cluster. A text cluster is a minimal - * mapping of some glyphs corresponding to some UTF-8 text. - * - * For a cluster to be valid, both @num_bytes and @num_glyphs should - * be non-negative, and at least one should be non-zero. - * Note that clusters with zero glyphs are not as well supported as - * normal clusters. For example, PDF rendering applications typically - * ignore those clusters when PDF text is being selected. - * - * See cairo_show_text_glyphs() for how clusters are used in advanced - * text operations. - * - * Since: 1.8 - **/ -typedef struct { - int num_bytes; - int num_glyphs; -} cairo_text_cluster_t; - -cairo_public cairo_text_cluster_t * -cairo_text_cluster_allocate (int num_clusters); - -cairo_public void -cairo_text_cluster_free (cairo_text_cluster_t *clusters); - -/** - * cairo_text_cluster_flags_t: - * @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array - * map to glyphs in the glyph array from end to start. - * - * Specifies properties of a text cluster mapping. - * - * Since: 1.8 - **/ -typedef enum _cairo_text_cluster_flags { - CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001 -} cairo_text_cluster_flags_t; - -/** - * cairo_text_extents_t: - * @x_bearing: the horizontal distance from the origin to the - * leftmost part of the glyphs as drawn. Positive if the - * glyphs lie entirely to the right of the origin. - * @y_bearing: the vertical distance from the origin to the - * topmost part of the glyphs as drawn. Positive only if the - * glyphs lie completely below the origin; will usually be - * negative. - * @width: width of the glyphs as drawn - * @height: height of the glyphs as drawn - * @x_advance:distance to advance in the X direction - * after drawing these glyphs - * @y_advance: distance to advance in the Y direction - * after drawing these glyphs. Will typically be zero except - * for vertical text layout as found in East-Asian languages. - * - * The #cairo_text_extents_t structure stores the extents of a single - * glyph or a string of glyphs in user-space coordinates. Because text - * extents are in user-space coordinates, they are mostly, but not - * entirely, independent of the current transformation matrix. If you call - * cairo_scale(cr, 2.0, 2.0), text will - * be drawn twice as big, but the reported text extents will not be - * doubled. They will change slightly due to hinting (so you can't - * assume that metrics are independent of the transformation matrix), - * but otherwise will remain unchanged. - **/ -typedef struct { - double x_bearing; - double y_bearing; - double width; - double height; - double x_advance; - double y_advance; -} cairo_text_extents_t; - -/** - * cairo_font_extents_t: - * @ascent: the distance that the font extends above the baseline. - * Note that this is not always exactly equal to the maximum - * of the extents of all the glyphs in the font, but rather - * is picked to express the font designer's intent as to - * how the font should align with elements above it. - * @descent: the distance that the font extends below the baseline. - * This value is positive for typical fonts that include - * portions below the baseline. Note that this is not always - * exactly equal to the maximum of the extents of all the - * glyphs in the font, but rather is picked to express the - * font designer's intent as to how the the font should - * align with elements below it. - * @height: the recommended vertical distance between baselines when - * setting consecutive lines of text with the font. This - * is greater than @ascent+@descent by a - * quantity known as the line spacing - * or external leading. When space - * is at a premium, most fonts can be set with only - * a distance of @ascent+@descent between lines. - * @max_x_advance: the maximum distance in the X direction that - * the the origin is advanced for any glyph in the font. - * @max_y_advance: the maximum distance in the Y direction that - * the the origin is advanced for any glyph in the font. - * this will be zero for normal fonts used for horizontal - * writing. (The scripts of East Asia are sometimes written - * vertically.) - * - * The #cairo_font_extents_t structure stores metric information for - * a font. Values are given in the current user-space coordinate - * system. - * - * Because font metrics are in user-space coordinates, they are - * mostly, but not entirely, independent of the current transformation - * matrix. If you call cairo_scale(cr, 2.0, 2.0), - * text will be drawn twice as big, but the reported text extents will - * not be doubled. They will change slightly due to hinting (so you - * can't assume that metrics are independent of the transformation - * matrix), but otherwise will remain unchanged. - **/ -typedef struct { - double ascent; - double descent; - double height; - double max_x_advance; - double max_y_advance; -} cairo_font_extents_t; - -/** - * cairo_font_slant_t: - * @CAIRO_FONT_SLANT_NORMAL: Upright font style - * @CAIRO_FONT_SLANT_ITALIC: Italic font style - * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style - * - * Specifies variants of a font face based on their slant. - **/ -typedef enum _cairo_font_slant { - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_SLANT_ITALIC, - CAIRO_FONT_SLANT_OBLIQUE -} cairo_font_slant_t; - -/** - * cairo_font_weight_t: - * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight - * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight - * - * Specifies variants of a font face based on their weight. - **/ -typedef enum _cairo_font_weight { - CAIRO_FONT_WEIGHT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD -} cairo_font_weight_t; - -/** - * cairo_subpixel_order_t: - * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for - * for the target device - * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally - * with red at the left - * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally - * with blue at the left - * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically - * with red at the top - * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically - * with blue at the top - * - * The subpixel order specifies the order of color elements within - * each pixel on the display device when rendering with an - * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. - **/ -typedef enum _cairo_subpixel_order { - CAIRO_SUBPIXEL_ORDER_DEFAULT, - CAIRO_SUBPIXEL_ORDER_RGB, - CAIRO_SUBPIXEL_ORDER_BGR, - CAIRO_SUBPIXEL_ORDER_VRGB, - CAIRO_SUBPIXEL_ORDER_VBGR -} cairo_subpixel_order_t; - -/** - * cairo_hint_style_t: - * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for - * font backend and target device - * @CAIRO_HINT_STYLE_NONE: Do not hint outlines - * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve - * contrast while retaining good fidelity to the original - * shapes. - * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength - * giving a compromise between fidelity to the original shapes - * and contrast - * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast - * - * Specifies the type of hinting to do on font outlines. Hinting - * is the process of fitting outlines to the pixel grid in order - * to improve the appearance of the result. Since hinting outlines - * involves distorting them, it also reduces the faithfulness - * to the original outline shapes. Not all of the outline hinting - * styles are supported by all font backends. - * - * New entries may be added in future versions. - **/ -typedef enum _cairo_hint_style { - CAIRO_HINT_STYLE_DEFAULT, - CAIRO_HINT_STYLE_NONE, - CAIRO_HINT_STYLE_SLIGHT, - CAIRO_HINT_STYLE_MEDIUM, - CAIRO_HINT_STYLE_FULL -} cairo_hint_style_t; - -/** - * cairo_hint_metrics_t: - * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default - * manner for the font backend and target device - * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics - * @CAIRO_HINT_METRICS_ON: Hint font metrics - * - * Specifies whether to hint font metrics; hinting font metrics - * means quantizing them so that they are integer values in - * device space. Doing this improves the consistency of - * letter and line spacing, however it also means that text - * will be laid out differently at different zoom factors. - **/ -typedef enum _cairo_hint_metrics { - CAIRO_HINT_METRICS_DEFAULT, - CAIRO_HINT_METRICS_OFF, - CAIRO_HINT_METRICS_ON -} cairo_hint_metrics_t; - -/** - * cairo_font_options_t: - * - * An opaque structure holding all options that are used when - * rendering fonts. - * - * Individual features of a #cairo_font_options_t can be set or - * accessed using functions named - * cairo_font_options_set_feature_name and - * cairo_font_options_get_feature_name, like - * cairo_font_options_set_antialias() and - * cairo_font_options_get_antialias(). - * - * New features may be added to a #cairo_font_options_t in the - * future. For this reason, cairo_font_options_copy(), - * cairo_font_options_equal(), cairo_font_options_merge(), and - * cairo_font_options_hash() should be used to copy, check - * for equality, merge, or compute a hash value of - * #cairo_font_options_t objects. - **/ -typedef struct _cairo_font_options cairo_font_options_t; - -cairo_public cairo_font_options_t * -cairo_font_options_create (void); - -cairo_public cairo_font_options_t * -cairo_font_options_copy (const cairo_font_options_t *original); - -cairo_public void -cairo_font_options_destroy (cairo_font_options_t *options); - -cairo_public cairo_status_t -cairo_font_options_status (cairo_font_options_t *options); - -cairo_public void -cairo_font_options_merge (cairo_font_options_t *options, - const cairo_font_options_t *other); -cairo_public cairo_bool_t -cairo_font_options_equal (const cairo_font_options_t *options, - const cairo_font_options_t *other); - -cairo_public unsigned long -cairo_font_options_hash (const cairo_font_options_t *options); - -cairo_public void -cairo_font_options_set_antialias (cairo_font_options_t *options, - cairo_antialias_t antialias); -cairo_public cairo_antialias_t -cairo_font_options_get_antialias (const cairo_font_options_t *options); - -cairo_public void -cairo_font_options_set_subpixel_order (cairo_font_options_t *options, - cairo_subpixel_order_t subpixel_order); -cairo_public cairo_subpixel_order_t -cairo_font_options_get_subpixel_order (const cairo_font_options_t *options); - -cairo_public void -cairo_font_options_set_hint_style (cairo_font_options_t *options, - cairo_hint_style_t hint_style); -cairo_public cairo_hint_style_t -cairo_font_options_get_hint_style (const cairo_font_options_t *options); - -cairo_public void -cairo_font_options_set_hint_metrics (cairo_font_options_t *options, - cairo_hint_metrics_t hint_metrics); -cairo_public cairo_hint_metrics_t -cairo_font_options_get_hint_metrics (const cairo_font_options_t *options); - -/* This interface is for dealing with text as text, not caring about the - font object inside the the cairo_t. */ - -cairo_public void -cairo_select_font_face (cairo_t *cr, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); - -cairo_public void -cairo_set_font_size (cairo_t *cr, double size); - -cairo_public void -cairo_set_font_matrix (cairo_t *cr, - const cairo_matrix_t *matrix); - -cairo_public void -cairo_get_font_matrix (cairo_t *cr, - cairo_matrix_t *matrix); - -cairo_public void -cairo_set_font_options (cairo_t *cr, - const cairo_font_options_t *options); - -cairo_public void -cairo_get_font_options (cairo_t *cr, - cairo_font_options_t *options); - -cairo_public void -cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); - -cairo_public cairo_font_face_t * -cairo_get_font_face (cairo_t *cr); - -cairo_public void -cairo_set_scaled_font (cairo_t *cr, - const cairo_scaled_font_t *scaled_font); - -cairo_public cairo_scaled_font_t * -cairo_get_scaled_font (cairo_t *cr); - -cairo_public void -cairo_show_text (cairo_t *cr, const char *utf8); - -cairo_public void -cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); - -cairo_public void -cairo_show_text_glyphs (cairo_t *cr, - const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags); - -cairo_public void -cairo_text_path (cairo_t *cr, const char *utf8); - -cairo_public void -cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); - -cairo_public void -cairo_text_extents (cairo_t *cr, - const char *utf8, - cairo_text_extents_t *extents); - -cairo_public void -cairo_glyph_extents (cairo_t *cr, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); - -cairo_public void -cairo_font_extents (cairo_t *cr, - cairo_font_extents_t *extents); - -/* Generic identifier for a font style */ - -cairo_public cairo_font_face_t * -cairo_font_face_reference (cairo_font_face_t *font_face); - -cairo_public void -cairo_font_face_destroy (cairo_font_face_t *font_face); - -cairo_public unsigned int -cairo_font_face_get_reference_count (cairo_font_face_t *font_face); - -cairo_public cairo_status_t -cairo_font_face_status (cairo_font_face_t *font_face); - - -/** - * cairo_font_type_t: - * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api - * @CAIRO_FONT_TYPE_FT: The font is of type FreeType - * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 - * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6) - * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) - * - * #cairo_font_type_t is used to describe the type of a given font - * face or scaled font. The font types are also known as "font - * backends" within cairo. - * - * The type of a font face is determined by the function used to - * create it, which will generally be of the form - * cairo_type_font_face_create(). The font face type can be queried - * with cairo_font_face_get_type() - * - * The various #cairo_font_face_t functions can be used with a font face - * of any type. - * - * The type of a scaled font is determined by the type of the font - * face passed to cairo_scaled_font_create(). The scaled font type can - * be queried with cairo_scaled_font_get_type() - * - * The various #cairo_scaled_font_t functions can be used with scaled - * fonts of any type, but some font backends also provide - * type-specific functions that must only be called with a scaled font - * of the appropriate type. These functions have names that begin with - * cairo_type_scaled_font() such as cairo_ft_scaled_font_lock_face(). - * - * The behavior of calling a type-specific function with a scaled font - * of the wrong type is undefined. - * - * New entries may be added in future versions. - * - * Since: 1.2 - **/ -typedef enum _cairo_font_type { - CAIRO_FONT_TYPE_TOY, - CAIRO_FONT_TYPE_FT, - CAIRO_FONT_TYPE_WIN32, - CAIRO_FONT_TYPE_QUARTZ, - CAIRO_FONT_TYPE_USER, - CAIRO_FONT_TYPE_DWRITE -} cairo_font_type_t; - -cairo_public cairo_font_type_t -cairo_font_face_get_type (cairo_font_face_t *font_face); - -cairo_public void * -cairo_font_face_get_user_data (cairo_font_face_t *font_face, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_font_face_set_user_data (cairo_font_face_t *font_face, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -/* Portable interface to general font features. */ - -cairo_public cairo_scaled_font_t * -cairo_scaled_font_create (cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options); - -cairo_public cairo_scaled_font_t * -cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font); - -cairo_public void -cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); - -cairo_public unsigned int -cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font); - -cairo_public cairo_status_t -cairo_scaled_font_status (cairo_scaled_font_t *scaled_font); - -cairo_public cairo_font_type_t -cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font); - -cairo_public void * -cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -cairo_public void -cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents); - -cairo_public void -cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, - const char *utf8, - cairo_text_extents_t *extents); - -cairo_public void -cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); - -cairo_public cairo_status_t -cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags); - -cairo_public cairo_font_face_t * -cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font); - -cairo_public void -cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *font_matrix); - -cairo_public void -cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *ctm); - -cairo_public void -cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, - cairo_matrix_t *scale_matrix); - -cairo_public void -cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, - cairo_font_options_t *options); - - -/* Toy fonts */ - -cairo_public cairo_font_face_t * -cairo_toy_font_face_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); - -cairo_public const char * -cairo_toy_font_face_get_family (cairo_font_face_t *font_face); - -cairo_public cairo_font_slant_t -cairo_toy_font_face_get_slant (cairo_font_face_t *font_face); - -cairo_public cairo_font_weight_t -cairo_toy_font_face_get_weight (cairo_font_face_t *font_face); - - -/* User fonts */ - -cairo_public cairo_font_face_t * -cairo_user_font_face_create (void); - -/* User-font method signatures */ - -/** - * cairo_user_scaled_font_init_func_t: - * @scaled_font: the scaled-font being created - * @cr: a cairo context, in font space - * @extents: font extents to fill in, in font space - * - * #cairo_user_scaled_font_init_func_t is the type of function which is - * called when a scaled-font needs to be created for a user font-face. - * - * The cairo context @cr is not used by the caller, but is prepared in font - * space, similar to what the cairo contexts passed to the render_glyph - * method will look like. The callback can use this context for extents - * computation for example. After the callback is called, @cr is checked - * for any error status. - * - * The @extents argument is where the user font sets the font extents for - * @scaled_font. It is in font space, which means that for most cases its - * ascent and descent members should add to 1.0. @extents is preset to - * hold a value of 1.0 for ascent, height, and max_x_advance, and 0.0 for - * descent and max_y_advance members. - * - * The callback is optional. If not set, default font extents as described - * in the previous paragraph will be used. - * - * Note that @scaled_font is not fully initialized at this - * point and trying to use it for text operations in the callback will result - * in deadlock. - * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error. - * - * Since: 1.8 - **/ -typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t *scaled_font, - cairo_t *cr, - cairo_font_extents_t *extents); - -/** - * cairo_user_scaled_font_render_glyph_func_t: - * @scaled_font: user scaled-font - * @glyph: glyph code to render - * @cr: cairo context to draw to, in font space - * @extents: glyph extents to fill in, in font space - * - * #cairo_user_scaled_font_render_glyph_func_t is the type of function which - * is called when a user scaled-font needs to render a glyph. - * - * The callback is mandatory, and expected to draw the glyph with code @glyph to - * the cairo context @cr. @cr is prepared such that the glyph drawing is done in - * font space. That is, the matrix set on @cr is the scale matrix of @scaled_font, - * The @extents argument is where the user font sets the font extents for - * @scaled_font. However, if user prefers to draw in user space, they can - * achieve that by changing the matrix on @cr. All cairo rendering operations - * to @cr are permitted, however, the result is undefined if any source other - * than the default source on @cr is used. That means, glyph bitmaps should - * be rendered using cairo_mask() instead of cairo_paint(). - * - * Other non-default settings on @cr include a font size of 1.0 (given that - * it is set up to be in font space), and font options corresponding to - * @scaled_font. - * - * The @extents argument is preset to have x_bearing, - * width, and y_advance of zero, - * y_bearing set to -font_extents.ascent, - * height to font_extents.ascent+font_extents.descent, - * and x_advance to font_extents.max_x_advance. - * The only field user needs to set in majority of cases is - * x_advance. - * If the width field is zero upon the callback returning - * (which is its preset value), the glyph extents are automatically computed - * based on the drawings done to @cr. This is in most cases exactly what the - * desired behavior is. However, if for any reason the callback sets the - * extents, it must be ink extents, and include the extents of all drawing - * done to @cr in the callback. - * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. - * - * Since: 1.8 - **/ -typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t *scaled_font, - unsigned long glyph, - cairo_t *cr, - cairo_text_extents_t *extents); - -/** - * cairo_user_scaled_font_text_to_glyphs_func_t: - * @scaled_font: the scaled-font being created - * @utf8: a string of text encoded in UTF-8 - * @utf8_len: length of @utf8 in bytes - * @glyphs: pointer to array of glyphs to fill, in font space - * @num_glyphs: pointer to number of glyphs - * @clusters: pointer to array of cluster mapping information to fill, or %NULL - * @num_clusters: pointer to number of clusters - * @cluster_flags: pointer to location to store cluster flags corresponding to the - * output @clusters - * - * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which - * is called to convert input text to an array of glyphs. This is used by the - * cairo_show_text() operation. - * - * Using this callback the user-font has full control on glyphs and their - * positions. That means, it allows for features like ligatures and kerning, - * as well as complex shaping required for scripts like - * Arabic and Indic. - * - * The @num_glyphs argument is preset to the number of glyph entries available - * in the @glyphs buffer. If the @glyphs buffer is %NULL, the value of - * @num_glyphs will be zero. If the provided glyph array is too short for - * the conversion (or for convenience), a new glyph array may be allocated - * using cairo_glyph_allocate() and placed in @glyphs. Upon return, - * @num_glyphs should contain the number of generated glyphs. If the value - * @glyphs points at has changed after the call, the caller will free the - * allocated glyph array using cairo_glyph_free(). - * The callback should populate the glyph indices and positions (in font space) - * assuming that the text is to be shown at the origin. - * - * If @clusters is not %NULL, @num_clusters and @cluster_flags are also - * non-%NULL, and cluster mapping should be computed. The semantics of how - * cluster array allocation works is similar to the glyph array. That is, - * if @clusters initially points to a non-%NULL value, that array may be used - * as a cluster buffer, and @num_clusters points to the number of cluster - * entries available there. If the provided cluster array is too short for - * the conversion (or for convenience), a new cluster array may be allocated - * using cairo_text_cluster_allocate() and placed in @clusters. Upon return, - * @num_clusters should contain the number of generated clusters. - * If the value @clusters points at has changed after the call, the caller - * will free the allocated cluster array using cairo_text_cluster_free(). - * - * The callback is optional. If @num_glyphs is negative upon - * the callback returning or if the return value - * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback - * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t. - * - * Note: While cairo does not impose any limitation on glyph indices, - * some applications may assume that a glyph index fits in a 16-bit - * unsigned integer. As such, it is advised that user-fonts keep their - * glyphs in the 0 to 65535 range. Furthermore, some applications may - * assume that glyph 0 is a special glyph-not-found glyph. User-fonts - * are advised to use glyph 0 for such purposes and do not use that - * glyph value for other purposes. - * - * Returns: %CAIRO_STATUS_SUCCESS upon success, - * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, - * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. - * - * Since: 1.8 - **/ -typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags); - -/** - * cairo_user_scaled_font_unicode_to_glyph_func_t: - * @scaled_font: the scaled-font being created - * @unicode: input unicode character code-point - * @glyph_index: output glyph index - * - * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which - * is called to convert an input Unicode character to a single glyph. - * This is used by the cairo_show_text() operation. - * - * This callback is used to provide the same functionality as the - * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t) - * but has much less control on the output, - * in exchange for increased ease of use. The inherent assumption to using - * this callback is that each character maps to one glyph, and that the - * mapping is context independent. It also assumes that glyphs are positioned - * according to their advance width. These mean no ligatures, kerning, or - * complex scripts can be implemented using this callback. - * - * The callback is optional, and only used if text_to_glyphs callback is not - * set or fails to return glyphs. If this callback is not set or if it returns - * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode - * code-points to glyph indices is assumed. - * - * Note: While cairo does not impose any limitation on glyph indices, - * some applications may assume that a glyph index fits in a 16-bit - * unsigned integer. As such, it is advised that user-fonts keep their - * glyphs in the 0 to 65535 range. Furthermore, some applications may - * assume that glyph 0 is a special glyph-not-found glyph. User-fonts - * are advised to use glyph 0 for such purposes and do not use that - * glyph value for other purposes. - * - * Returns: %CAIRO_STATUS_SUCCESS upon success, - * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, - * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. - * - * Since: 1.8 - **/ -typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font, - unsigned long unicode, - unsigned long *glyph_index); - -/* User-font method setters */ - -cairo_public void -cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_init_func_t init_func); - -cairo_public void -cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_render_glyph_func_t render_glyph_func); - -cairo_public void -cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); - -cairo_public void -cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, - cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func); - -/* User-font method getters */ - -cairo_public cairo_user_scaled_font_init_func_t -cairo_user_font_face_get_init_func (cairo_font_face_t *font_face); - -cairo_public cairo_user_scaled_font_render_glyph_func_t -cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face); - -cairo_public cairo_user_scaled_font_text_to_glyphs_func_t -cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); - -cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t -cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); - - -/* Query functions */ - -cairo_public cairo_operator_t -cairo_get_operator (cairo_t *cr); - -cairo_public cairo_pattern_t * -cairo_get_source (cairo_t *cr); - -cairo_public double -cairo_get_tolerance (cairo_t *cr); - -cairo_public cairo_antialias_t -cairo_get_antialias (cairo_t *cr); - -cairo_public cairo_bool_t -cairo_has_current_point (cairo_t *cr); - -cairo_public void -cairo_get_current_point (cairo_t *cr, double *x, double *y); - -cairo_public cairo_fill_rule_t -cairo_get_fill_rule (cairo_t *cr); - -cairo_public double -cairo_get_line_width (cairo_t *cr); - -cairo_public cairo_line_cap_t -cairo_get_line_cap (cairo_t *cr); - -cairo_public cairo_line_join_t -cairo_get_line_join (cairo_t *cr); - -cairo_public double -cairo_get_miter_limit (cairo_t *cr); - -cairo_public int -cairo_get_dash_count (cairo_t *cr); - -cairo_public void -cairo_get_dash (cairo_t *cr, double *dashes, double *offset); - -cairo_public void -cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix); - -cairo_public cairo_surface_t * -cairo_get_target (cairo_t *cr); - -cairo_public cairo_surface_t * -cairo_get_group_target (cairo_t *cr); - -/** - * cairo_path_data_type_t: - * @CAIRO_PATH_MOVE_TO: A move-to operation - * @CAIRO_PATH_LINE_TO: A line-to operation - * @CAIRO_PATH_CURVE_TO: A curve-to operation - * @CAIRO_PATH_CLOSE_PATH: A close-path operation - * - * #cairo_path_data_t is used to describe the type of one portion - * of a path when represented as a #cairo_path_t. - * See #cairo_path_data_t for details. - **/ -typedef enum _cairo_path_data_type { - CAIRO_PATH_MOVE_TO, - CAIRO_PATH_LINE_TO, - CAIRO_PATH_CURVE_TO, - CAIRO_PATH_CLOSE_PATH -} cairo_path_data_type_t; - -/** - * cairo_path_data_t: - * - * #cairo_path_data_t is used to represent the path data inside a - * #cairo_path_t. - * - * The data structure is designed to try to balance the demands of - * efficiency and ease-of-use. A path is represented as an array of - * #cairo_path_data_t, which is a union of headers and points. - * - * Each portion of the path is represented by one or more elements in - * the array, (one header followed by 0 or more points). The length - * value of the header is the number of array elements for the current - * portion including the header, (ie. length == 1 + # of points), and - * where the number of points for each element type is as follows: - * - * - * %CAIRO_PATH_MOVE_TO: 1 point - * %CAIRO_PATH_LINE_TO: 1 point - * %CAIRO_PATH_CURVE_TO: 3 points - * %CAIRO_PATH_CLOSE_PATH: 0 points - * - * - * The semantics and ordering of the coordinate values are consistent - * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and - * cairo_close_path(). - * - * Here is sample code for iterating through a #cairo_path_t: - * - * - * int i; - * cairo_path_t *path; - * cairo_path_data_t *data; - *   - * path = cairo_copy_path (cr); - *   - * for (i=0; i < path->num_data; i += path->data[i].header.length) { - * data = &path->data[i]; - * switch (data->header.type) { - * case CAIRO_PATH_MOVE_TO: - * do_move_to_things (data[1].point.x, data[1].point.y); - * break; - * case CAIRO_PATH_LINE_TO: - * do_line_to_things (data[1].point.x, data[1].point.y); - * break; - * case CAIRO_PATH_CURVE_TO: - * do_curve_to_things (data[1].point.x, data[1].point.y, - * data[2].point.x, data[2].point.y, - * data[3].point.x, data[3].point.y); - * break; - * case CAIRO_PATH_CLOSE_PATH: - * do_close_path_things (); - * break; - * } - * } - * cairo_path_destroy (path); - * - * - * As of cairo 1.4, cairo does not mind if there are more elements in - * a portion of the path than needed. Such elements can be used by - * users of the cairo API to hold extra values in the path data - * structure. For this reason, it is recommended that applications - * always use data->header.length to - * iterate over the path data, instead of hardcoding the number of - * elements for each element type. - **/ -typedef union _cairo_path_data_t cairo_path_data_t; -union _cairo_path_data_t { - struct { - cairo_path_data_type_t type; - int length; - } header; - struct { - double x, y; - } point; -}; - -/** - * cairo_path_t: - * @status: the current error status - * @data: the elements in the path - * @num_data: the number of elements in the data array - * - * A data structure for holding a path. This data structure serves as - * the return value for cairo_copy_path() and - * cairo_copy_path_flat() as well the input value for - * cairo_append_path(). - * - * See #cairo_path_data_t for hints on how to iterate over the - * actual data within the path. - * - * The num_data member gives the number of elements in the data - * array. This number is larger than the number of independent path - * portions (defined in #cairo_path_data_type_t), since the data - * includes both headers and coordinates for each portion. - **/ -typedef struct cairo_path { - cairo_status_t status; - cairo_path_data_t *data; - int num_data; -} cairo_path_t; - -cairo_public cairo_path_t * -cairo_copy_path (cairo_t *cr); - -cairo_public cairo_path_t * -cairo_copy_path_flat (cairo_t *cr); - -cairo_public void -cairo_append_path (cairo_t *cr, - const cairo_path_t *path); - -cairo_public void -cairo_path_destroy (cairo_path_t *path); - -/* Error status queries */ - -cairo_public cairo_status_t -cairo_status (cairo_t *cr); - -cairo_public const char * -cairo_status_to_string (cairo_status_t status); - -/* Backend device manipulation */ - -cairo_public cairo_device_t * -cairo_device_reference (cairo_device_t *device); - -/** - * cairo_device_type_t: - * @CAIRO_DEVICE_TYPE_DRM: The surface is of type Direct Render Manager - * @CAIRO_DEVICE_TYPE_GL: The surface is of type OpenGL - * @CAIRO_DEVICE_TYPE_SCRIPT: The surface is of type script - * @CAIRO_DEVICE_TYPE_XCB: The surface is of type xcb - * @CAIRO_DEVICE_TYPE_XLIB: The surface is of type xlib - * @CAIRO_DEVICE_TYPE_XML: The surface is of type XML - * cairo_surface_create_for_rectangle() - * - * #cairo_device_type_t is used to describe the type of a given - * device. The devices types are also known as "backends" within cairo. - * - * The device type can be queried with cairo_device_get_type() - * - * The various #cairo_device_t functions can be used with surfaces of - * any type, but some backends also provide type-specific functions - * that must only be called with a device of the appropriate - * type. These functions have names that begin with - * cairo_type_device such as cairo_xcb_device_debug_set_render_version(). - * - * The behavior of calling a type-specific function with a surface of - * the wrong type is undefined. - * - * New entries may be added in future versions. - * - * Since: 1.10 - **/ -typedef enum _cairo_device_type { - CAIRO_DEVICE_TYPE_DRM, - CAIRO_DEVICE_TYPE_GL, - CAIRO_DEVICE_TYPE_SCRIPT, - CAIRO_DEVICE_TYPE_XCB, - CAIRO_DEVICE_TYPE_XLIB, - CAIRO_DEVICE_TYPE_XML -} cairo_device_type_t; - -cairo_public cairo_device_type_t -cairo_device_get_type (cairo_device_t *device); - -cairo_public cairo_status_t -cairo_device_status (cairo_device_t *device); - -cairo_public cairo_status_t -cairo_device_acquire (cairo_device_t *device); - -cairo_public void -cairo_device_release (cairo_device_t *device); - -cairo_public void -cairo_device_flush (cairo_device_t *device); - -cairo_public void -cairo_device_finish (cairo_device_t *device); - -cairo_public void -cairo_device_destroy (cairo_device_t *device); - -cairo_public unsigned int -cairo_device_get_reference_count (cairo_device_t *device); - -cairo_public void * -cairo_device_get_user_data (cairo_device_t *device, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_device_set_user_data (cairo_device_t *device, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - - -/* Surface manipulation */ - -cairo_public cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_content_t content, - int width, - int height); - -cairo_public cairo_surface_t * -cairo_surface_create_for_rectangle (cairo_surface_t *target, - double x, - double y, - double width, - double height); - -cairo_public cairo_surface_t * -cairo_surface_reference (cairo_surface_t *surface); - -cairo_public void -cairo_surface_finish (cairo_surface_t *surface); - -cairo_public void -cairo_surface_destroy (cairo_surface_t *surface); - -cairo_public cairo_device_t * -cairo_surface_get_device (cairo_surface_t *surface); - -cairo_public unsigned int -cairo_surface_get_reference_count (cairo_surface_t *surface); - -cairo_public cairo_status_t -cairo_surface_status (cairo_surface_t *surface); - -/** - * cairo_surface_type_t: - * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image - * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf - * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps - * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib - * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb - * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz - * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz - * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32 - * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos - * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb - * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg - * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2 - * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface - * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image - * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 - * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10 - * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10 - * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 - * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 - * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 - * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10 - * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10 - * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10 - * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with - * cairo_surface_create_for_rectangle(), since 1.10 - * @CAIRO_SURFACE_TYPE_D2D: The surface is of type Direct2D - * - * #cairo_surface_type_t is used to describe the type of a given - * surface. The surface types are also known as "backends" or "surface - * backends" within cairo. - * - * The type of a surface is determined by the function used to create - * it, which will generally be of the form cairo_type_surface_create(), - * (though see cairo_surface_create_similar() as well). - * - * The surface type can be queried with cairo_surface_get_type() - * - * The various #cairo_surface_t functions can be used with surfaces of - * any type, but some backends also provide type-specific functions - * that must only be called with a surface of the appropriate - * type. These functions have names that begin with - * cairo_type_surface such as cairo_image_surface_get_width(). - * - * The behavior of calling a type-specific function with a surface of - * the wrong type is undefined. - * - * New entries may be added in future versions. - * - * Since: 1.2 - **/ -typedef enum _cairo_surface_type { - CAIRO_SURFACE_TYPE_IMAGE, - CAIRO_SURFACE_TYPE_PDF, - CAIRO_SURFACE_TYPE_PS, - CAIRO_SURFACE_TYPE_XLIB, - CAIRO_SURFACE_TYPE_XCB, - CAIRO_SURFACE_TYPE_GLITZ, - CAIRO_SURFACE_TYPE_QUARTZ, - CAIRO_SURFACE_TYPE_WIN32, - CAIRO_SURFACE_TYPE_BEOS, - CAIRO_SURFACE_TYPE_DIRECTFB, - CAIRO_SURFACE_TYPE_SVG, - CAIRO_SURFACE_TYPE_OS2, - CAIRO_SURFACE_TYPE_WIN32_PRINTING, - CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, - CAIRO_SURFACE_TYPE_SCRIPT, - CAIRO_SURFACE_TYPE_QT, - CAIRO_SURFACE_TYPE_RECORDING, - CAIRO_SURFACE_TYPE_VG, - CAIRO_SURFACE_TYPE_GL, - CAIRO_SURFACE_TYPE_DRM, - CAIRO_SURFACE_TYPE_TEE, - CAIRO_SURFACE_TYPE_XML, - CAIRO_SURFACE_TYPE_SKIA, - CAIRO_SURFACE_TYPE_SUBSURFACE, - CAIRO_SURFACE_TYPE_D2D -} cairo_surface_type_t; - -cairo_public cairo_surface_type_t -cairo_surface_get_type (cairo_surface_t *surface); - -cairo_public cairo_content_t -cairo_surface_get_content (cairo_surface_t *surface); - -#if CAIRO_HAS_PNG_FUNCTIONS - -cairo_public cairo_status_t -cairo_surface_write_to_png (cairo_surface_t *surface, - const char *filename); - -cairo_public cairo_status_t -cairo_surface_write_to_png_stream (cairo_surface_t *surface, - cairo_write_func_t write_func, - void *closure); - -#endif - -cairo_public void * -cairo_surface_get_user_data (cairo_surface_t *surface, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_surface_set_user_data (cairo_surface_t *surface, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -cairo_public void -cairo_surface_attach_snapshot (cairo_surface_t *surface, - cairo_surface_t *snapshot, - cairo_surface_func_t detach_func); - -cairo_public void -cairo_surface_detach_snapshot (cairo_surface_t *snapshot); - -#define CAIRO_MIME_TYPE_JPEG "image/jpeg" -#define CAIRO_MIME_TYPE_PNG "image/png" -#define CAIRO_MIME_TYPE_JP2 "image/jp2" -#define CAIRO_MIME_TYPE_URI "text/x-uri" - -cairo_public void -cairo_surface_get_mime_data (cairo_surface_t *surface, - const char *mime_type, - const unsigned char **data, - unsigned long *length); - -cairo_public cairo_status_t -cairo_surface_set_mime_data (cairo_surface_t *surface, - const char *mime_type, - const unsigned char *data, - unsigned long length, - cairo_destroy_func_t destroy, - void *closure); - -cairo_public void -cairo_surface_get_font_options (cairo_surface_t *surface, - cairo_font_options_t *options); - -cairo_public void -cairo_surface_flush (cairo_surface_t *surface); - -cairo_public void -cairo_surface_mark_dirty (cairo_surface_t *surface); - -cairo_public void -cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, - int x, - int y, - int width, - int height); - -cairo_public void -cairo_surface_set_device_offset (cairo_surface_t *surface, - double x_offset, - double y_offset); - -cairo_public void -cairo_surface_get_device_offset (cairo_surface_t *surface, - double *x_offset, - double *y_offset); - -cairo_public void -cairo_surface_set_fallback_resolution (cairo_surface_t *surface, - double x_pixels_per_inch, - double y_pixels_per_inch); - -cairo_public void -cairo_surface_get_fallback_resolution (cairo_surface_t *surface, - double *x_pixels_per_inch, - double *y_pixels_per_inch); - -cairo_public void -cairo_surface_copy_page (cairo_surface_t *surface); - -cairo_public void -cairo_surface_show_page (cairo_surface_t *surface); - -cairo_public cairo_bool_t -cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); - -/** - * _cairo_subpixel_antialiasing_t: - * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled - * for this surface. - * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled - * for this surface. - */ -typedef enum _cairo_subpixel_antialiasing_t { - CAIRO_SUBPIXEL_ANTIALIASING_ENABLED, - CAIRO_SUBPIXEL_ANTIALIASING_DISABLED -} cairo_subpixel_antialiasing_t; - -cairo_public void -cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, - cairo_subpixel_antialiasing_t enabled); - -cairo_public cairo_subpixel_antialiasing_t -cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface); - -/* Image-surface functions */ - -/** - * cairo_format_t: - * @CAIRO_FORMAT_INVALID: no such format exists or is supported. - * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with - * alpha in the upper 8 bits, then red, then green, then blue. - * The 32-bit quantities are stored native-endian. Pre-multiplied - * alpha is used. (That is, 50% transparent red is 0x80800000, - * not 0x80ff0000.) - * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with - * the upper 8 bits unused. Red, Green, and Blue are stored - * in the remaining 24 bits in that order. - * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding - * an alpha value. - * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding - * an alpha value. Pixels are packed together into 32-bit - * quantities. The ordering of the bits matches the - * endianess of the platform. On a big-endian machine, the - * first pixel is in the uppermost bit, on a little-endian - * machine the first pixel is in the least-significant bit. - * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity - * with red in the upper 5 bits, then green in the middle - * 6 bits, and blue in the lower 5 bits. - * - * #cairo_format_t is used to identify the memory format of - * image data. - * - * New entries may be added in future versions. - **/ -typedef enum _cairo_format { - CAIRO_FORMAT_INVALID = -1, - CAIRO_FORMAT_ARGB32 = 0, - CAIRO_FORMAT_RGB24 = 1, - CAIRO_FORMAT_A8 = 2, - CAIRO_FORMAT_A1 = 3, - CAIRO_FORMAT_RGB16_565 = 4 -} cairo_format_t; - -cairo_public cairo_surface_t * -cairo_image_surface_create (cairo_format_t format, - int width, - int height); - -cairo_public int -cairo_format_stride_for_width (cairo_format_t format, - int width); - -cairo_public cairo_surface_t * -cairo_image_surface_create_for_data (unsigned char *data, - cairo_format_t format, - int width, - int height, - int stride); - -cairo_public unsigned char * -cairo_image_surface_get_data (cairo_surface_t *surface); - -cairo_public cairo_format_t -cairo_image_surface_get_format (cairo_surface_t *surface); - -cairo_public int -cairo_image_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_image_surface_get_height (cairo_surface_t *surface); - -cairo_public int -cairo_image_surface_get_stride (cairo_surface_t *surface); - -#if CAIRO_HAS_PNG_FUNCTIONS - -cairo_public cairo_surface_t * -cairo_image_surface_create_from_png (const char *filename); - -cairo_public cairo_surface_t * -cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, - void *closure); - -#endif - -/* Recording-surface functions */ - -cairo_public cairo_surface_t * -cairo_recording_surface_create (cairo_content_t content, - const cairo_rectangle_t *extents); - -cairo_public void -cairo_recording_surface_ink_extents (cairo_surface_t *surface, - double *x0, - double *y0, - double *width, - double *height); - -/* Null-surface functions */ - -cairo_public cairo_surface_t * -cairo_null_surface_create (cairo_content_t content); - -/* Pattern creation functions */ - -cairo_public cairo_pattern_t * -cairo_pattern_create_rgb (double red, double green, double blue); - -cairo_public cairo_pattern_t * -cairo_pattern_create_rgba (double red, double green, double blue, - double alpha); - -cairo_public cairo_pattern_t * -cairo_pattern_create_for_surface (cairo_surface_t *surface); - -cairo_public cairo_pattern_t * -cairo_pattern_create_linear (double x0, double y0, - double x1, double y1); - -cairo_public cairo_pattern_t * -cairo_pattern_create_radial (double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1); - -cairo_public cairo_pattern_t * -cairo_pattern_reference (cairo_pattern_t *pattern); - -cairo_public void -cairo_pattern_destroy (cairo_pattern_t *pattern); - -cairo_public unsigned int -cairo_pattern_get_reference_count (cairo_pattern_t *pattern); - -cairo_public cairo_status_t -cairo_pattern_status (cairo_pattern_t *pattern); - -cairo_public void * -cairo_pattern_get_user_data (cairo_pattern_t *pattern, - const cairo_user_data_key_t *key); - -cairo_public cairo_status_t -cairo_pattern_set_user_data (cairo_pattern_t *pattern, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -/** - * cairo_pattern_type_t: - * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) - * color. It may be opaque or translucent. - * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image). - * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient. - * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient. - * - * #cairo_pattern_type_t is used to describe the type of a given pattern. - * - * The type of a pattern is determined by the function used to create - * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() - * functions create SOLID patterns. The remaining - * cairo_pattern_create functions map to pattern types in obvious - * ways. - * - * The pattern type can be queried with cairo_pattern_get_type() - * - * Most #cairo_pattern_t functions can be called with a pattern of any - * type, (though trying to change the extend or filter for a solid - * pattern will have no effect). A notable exception is - * cairo_pattern_add_color_stop_rgb() and - * cairo_pattern_add_color_stop_rgba() which must only be called with - * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern - * will be shutdown and put into an error state. - * - * New entries may be added in future versions. - * - * Since: 1.2 - **/ -typedef enum _cairo_pattern_type { - CAIRO_PATTERN_TYPE_SOLID, - CAIRO_PATTERN_TYPE_SURFACE, - CAIRO_PATTERN_TYPE_LINEAR, - CAIRO_PATTERN_TYPE_RADIAL -} cairo_pattern_type_t; - -cairo_public cairo_pattern_type_t -cairo_pattern_get_type (cairo_pattern_t *pattern); - -cairo_public void -cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, - double offset, - double red, double green, double blue); - -cairo_public void -cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, - double offset, - double red, double green, double blue, - double alpha); - -cairo_public void -cairo_pattern_set_matrix (cairo_pattern_t *pattern, - const cairo_matrix_t *matrix); - -cairo_public void -cairo_pattern_get_matrix (cairo_pattern_t *pattern, - cairo_matrix_t *matrix); - -/** - * cairo_extend_t: - * @CAIRO_EXTEND_NONE: pixels outside of the source pattern - * are fully transparent - * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating - * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting - * at the edges (Implemented for surface patterns since 1.6) - * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy - * the closest pixel from the source (Since 1.2; but only - * implemented for surface patterns since 1.6) - * - * #cairo_extend_t is used to describe how pattern color/alpha will be - * determined for areas "outside" the pattern's natural area, (for - * example, outside the surface bounds or outside the gradient - * geometry). - * - * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns - * and %CAIRO_EXTEND_PAD for gradient patterns. - * - * New entries may be added in future versions. - **/ -typedef enum _cairo_extend { - CAIRO_EXTEND_NONE, - CAIRO_EXTEND_REPEAT, - CAIRO_EXTEND_REFLECT, - CAIRO_EXTEND_PAD -} cairo_extend_t; - -cairo_public void -cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); - -cairo_public cairo_extend_t -cairo_pattern_get_extend (cairo_pattern_t *pattern); - -/** - * cairo_filter_t: - * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar - * to %CAIRO_FILTER_NEAREST - * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality - * similar to %CAIRO_FILTER_BILINEAR - * @CAIRO_FILTER_BEST: The highest-quality available, performance may - * not be suitable for interactive use. - * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering - * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions - * @CAIRO_FILTER_GAUSSIAN: This filter value is currently - * unimplemented, and should not be used in current code. - * - * #cairo_filter_t is used to indicate what filtering should be - * applied when reading pixel values from patterns. See - * cairo_pattern_set_source() for indicating the desired filter to be - * used with a particular pattern. - */ -typedef enum _cairo_filter { - CAIRO_FILTER_FAST, - CAIRO_FILTER_GOOD, - CAIRO_FILTER_BEST, - CAIRO_FILTER_NEAREST, - CAIRO_FILTER_BILINEAR, - CAIRO_FILTER_GAUSSIAN -} cairo_filter_t; - -cairo_public void -cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); - -cairo_public cairo_filter_t -cairo_pattern_get_filter (cairo_pattern_t *pattern); - -cairo_public cairo_status_t -cairo_pattern_get_rgba (cairo_pattern_t *pattern, - double *red, double *green, - double *blue, double *alpha); - -cairo_public cairo_status_t -cairo_pattern_get_surface (cairo_pattern_t *pattern, - cairo_surface_t **surface); - - -cairo_public cairo_status_t -cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, - int index, double *offset, - double *red, double *green, - double *blue, double *alpha); - -cairo_public cairo_status_t -cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, - int *count); - -cairo_public cairo_status_t -cairo_pattern_get_linear_points (cairo_pattern_t *pattern, - double *x0, double *y0, - double *x1, double *y1); - -cairo_public cairo_status_t -cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, - double *x0, double *y0, double *r0, - double *x1, double *y1, double *r1); - -/* Matrix functions */ - -cairo_public void -cairo_matrix_init (cairo_matrix_t *matrix, - double xx, double yx, - double xy, double yy, - double x0, double y0); - -cairo_public void -cairo_matrix_init_identity (cairo_matrix_t *matrix); - -cairo_public void -cairo_matrix_init_translate (cairo_matrix_t *matrix, - double tx, double ty); - -cairo_public void -cairo_matrix_init_scale (cairo_matrix_t *matrix, - double sx, double sy); - -cairo_public void -cairo_matrix_init_rotate (cairo_matrix_t *matrix, - double radians); - -cairo_public void -cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); - -cairo_public void -cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); - -cairo_public void -cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); - -cairo_public cairo_status_t -cairo_matrix_invert (cairo_matrix_t *matrix); - -cairo_public void -cairo_matrix_multiply (cairo_matrix_t *result, - const cairo_matrix_t *a, - const cairo_matrix_t *b); - -cairo_public void -cairo_matrix_transform_distance (const cairo_matrix_t *matrix, - double *dx, double *dy); - -cairo_public void -cairo_matrix_transform_point (const cairo_matrix_t *matrix, - double *x, double *y); - -/* Region functions */ - -/** - * cairo_region_t: - * - * A #cairo_region_t represents a set of integer-aligned rectangles. - * - * It allows set-theoretical operations like cairo_region_union() and - * cairo_region_intersect() to be performed on them. - * - * Memory management of #cairo_region_t is done with - * cairo_region_reference() and cairo_region_destroy(). - * - * Since: 1.10 - **/ -typedef struct _cairo_region cairo_region_t; - -/** - * cairo_rectangle_int_t: - * @x: X coordinate of the left side of the rectangle - * @y: Y coordinate of the the top side of the rectangle - * @width: width of the rectangle - * @height: height of the rectangle - * - * A data structure for holding a rectangle with integer coordinates. - * - * Since: 1.10 - **/ - -typedef struct _cairo_rectangle_int { - int x, y; - int width, height; -} cairo_rectangle_int_t; - -typedef enum _cairo_region_overlap { - CAIRO_REGION_OVERLAP_IN, /* completely inside region */ - CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ - CAIRO_REGION_OVERLAP_PART /* partly inside region */ -} cairo_region_overlap_t; - -cairo_public cairo_region_t * -cairo_region_create (void); - -cairo_public cairo_region_t * -cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); - -cairo_public cairo_region_t * -cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, - int count); - -cairo_public cairo_region_t * -cairo_region_copy (const cairo_region_t *original); - -cairo_public cairo_region_t * -cairo_region_reference (cairo_region_t *region); - -cairo_public void -cairo_region_destroy (cairo_region_t *region); - -cairo_public cairo_bool_t -cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b); - -cairo_public cairo_status_t -cairo_region_status (const cairo_region_t *region); - -cairo_public void -cairo_region_get_extents (const cairo_region_t *region, - cairo_rectangle_int_t *extents); - -cairo_public int -cairo_region_num_rectangles (const cairo_region_t *region); - -cairo_public void -cairo_region_get_rectangle (const cairo_region_t *region, - int nth, - cairo_rectangle_int_t *rectangle); - -cairo_public cairo_bool_t -cairo_region_is_empty (const cairo_region_t *region); - -cairo_public cairo_region_overlap_t -cairo_region_contains_rectangle (const cairo_region_t *region, - const cairo_rectangle_int_t *rectangle); - -cairo_public cairo_bool_t -cairo_region_contains_point (const cairo_region_t *region, int x, int y); - -cairo_public void -cairo_region_translate (cairo_region_t *region, int dx, int dy); - -cairo_public cairo_status_t -cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other); - -cairo_public cairo_status_t -cairo_region_subtract_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle); - -cairo_public cairo_status_t -cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other); - -cairo_public cairo_status_t -cairo_region_intersect_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle); - -cairo_public cairo_status_t -cairo_region_union (cairo_region_t *dst, const cairo_region_t *other); - -cairo_public cairo_status_t -cairo_region_union_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle); - -cairo_public cairo_status_t -cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other); - -cairo_public cairo_status_t -cairo_region_xor_rectangle (cairo_region_t *dst, - const cairo_rectangle_int_t *rectangle); - -/* Functions to be used while debugging (not intended for use in production code) */ -cairo_public void -cairo_debug_reset_static_data (void); - - -CAIRO_END_DECLS - -#endif /* CAIRO_H */ diff --git a/libs/cairo/cairo/src/cairoint.h b/libs/cairo/cairo/src/cairoint.h deleted file mode 100644 index 2f638f2d7..000000000 --- a/libs/cairo/cairo/src/cairoint.h +++ /dev/null @@ -1,2554 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * These definitions are solely for use by the implementation of cairo - * and constitute no kind of standard. If you need any of these - * functions, please drop me a note. Either the library needs new - * functionality, or there's a way to do what you need using the - * existing published interfaces. cworth@cworth.org - */ - -#ifndef _CAIROINT_H_ -#define _CAIROINT_H_ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef _MSC_VER -#define cairo_public __declspec(dllexport) -#endif - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#define _USE_MATH_DEFINES -#endif -#include -#include -#include - -#include "cairo.h" -#include - -#include "cairo-compiler-private.h" - -#if CAIRO_HAS_PS_SURFACE || \ - CAIRO_HAS_PDF_SURFACE || \ - CAIRO_HAS_SVG_SURFACE || \ - CAIRO_HAS_WIN32_SURFACE -#define CAIRO_HAS_FONT_SUBSET 1 -#endif - -#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET -#define CAIRO_HAS_PDF_OPERATORS 1 -#endif - -CAIRO_BEGIN_DECLS - -#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ -cairo_private FILE * -_cairo_win32_tmpfile (void); -#define tmpfile() _cairo_win32_tmpfile() -#endif - -#undef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#undef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -#ifndef NDEBUG -#undef assert -#define assert(expr) \ - do { if (!(expr)) fprintf(stderr, "Assertion failed at %s:%d: %s\n", \ - __FILE__, __LINE__, #expr); } while (0) -#endif - -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif - -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.707106781186547524400844362104849039 -#endif - -#undef ARRAY_LENGTH -#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0]))) - -#undef STRINGIFY -#undef STRINGIFY_ARG -#define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) -#define STRINGIFY_ARG(contents) #contents - -#if defined (__GNUC__) -#define cairo_container_of(ptr, type, member) ({ \ - const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ - (type *) ((char *) mptr__ - offsetof (type, member)); \ -}) -#else -#define cairo_container_of(ptr, type, member) \ - ((type *)((char *) (ptr) - (char *) &((type *)0)->member)) -#endif - - -#define ASSERT_NOT_REACHED \ -do { \ - assert (!"reached"); \ -} while (0) -#define COMPILE_TIME_ASSERT1(condition, line) \ - typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1] -#define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line) -#define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__) - -#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff)) -#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff) - -#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff)) -#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00) -#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0) - -#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short) -#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short) - -/* Reverse the bits in a byte with 7 operations (no 64-bit): - * Devised by Sean Anderson, July 13, 2001. - * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits - */ -#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) - -/* Return the number of 1 bits in mask. - * - * GCC 3.4 supports a "population count" builtin, which on many targets is - * implemented with a single instruction. There is a fallback definition - * in libgcc in case a target does not have one, which should be just as - * good as the open-coded solution below, (which is "HACKMEM 169"). - */ -static inline int cairo_const -_cairo_popcount (uint32_t mask) -{ -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - return __builtin_popcount (mask); -#else - register int y; - - y = (mask >> 1) &033333333333; - y = mask - y - ((y >>1) & 033333333333); - return (((y + (y >> 3)) & 030707070707) % 077); -#endif -} - -#ifdef WORDS_BIGENDIAN -#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c) -#else -#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c) -#endif - -#ifdef WORDS_BIGENDIAN - -#define cpu_to_be16(v) (v) -#define be16_to_cpu(v) (v) -#define cpu_to_be32(v) (v) -#define be32_to_cpu(v) (v) - -#else - -static inline uint16_t cairo_const -cpu_to_be16(uint16_t v) -{ - return (v << 8) | (v >> 8); -} - -static inline uint16_t cairo_const -be16_to_cpu(uint16_t v) -{ - return cpu_to_be16 (v); -} - -static inline uint32_t cairo_const -cpu_to_be32(uint32_t v) -{ - return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); -} - -static inline uint32_t cairo_const -be32_to_cpu(uint32_t v) -{ - return cpu_to_be32 (v); -} - -#endif - - -/* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales. - */ - -static inline int cairo_const -_cairo_isspace (int c) -{ - return (c == 0x20 || (c >= 0x09 && c <= 0x0d)); -} - -static inline int cairo_const -_cairo_isdigit (int c) -{ - return (c >= '0' && c <= '9'); -} - -#include "cairo-types-private.h" -#include "cairo-cache-private.h" -#include "cairo-reference-count-private.h" -#include "cairo-spans-private.h" - -cairo_private void -_cairo_box_from_doubles (cairo_box_t *box, - double *x1, double *y1, - double *x2, double *y2); - -cairo_private void -_cairo_box_to_doubles (const cairo_box_t *box, - double *x1, double *y1, - double *x2, double *y2); - -cairo_private void -_cairo_box_from_rectangle (cairo_box_t *box, - const cairo_rectangle_int_t *rectangle); - -cairo_private cairo_bool_t -_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, - const cairo_rectangle_int_t *contained_rectangle); - -cairo_private void -_cairo_box_round_to_rectangle (const cairo_box_t *box, - cairo_rectangle_int_t *rectangle); - -cairo_private void -_cairo_boxes_get_extents (const cairo_box_t *boxes, - int num_boxes, - cairo_box_t *extents); - -static inline void -_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect) -{ - rect->x = CAIRO_RECT_INT_MIN; - rect->y = CAIRO_RECT_INT_MIN; - rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; -} - -cairo_private cairo_bool_t -_cairo_rectangle_intersect (cairo_rectangle_int_t *dst, - const cairo_rectangle_int_t *src); - -cairo_private cairo_bool_t -_cairo_box_intersects_line_segment (cairo_box_t *box, - cairo_line_t *line) cairo_pure; - -cairo_private cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, - const cairo_point_t *point) cairo_pure; - -/* cairo-array.c structures and functions */ - -cairo_private void -_cairo_array_init (cairo_array_t *array, int element_size); - -cairo_private void -_cairo_array_init_snapshot (cairo_array_t *array, - const cairo_array_t *other); - -cairo_private void -_cairo_array_fini (cairo_array_t *array); - -cairo_private cairo_status_t -_cairo_array_grow_by (cairo_array_t *array, unsigned int additional); - -cairo_private void -_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements); - -cairo_private cairo_status_t -_cairo_array_append (cairo_array_t *array, const void *element); - -cairo_private cairo_status_t -_cairo_array_append_multiple (cairo_array_t *array, - const void *elements, - int num_elements); - -cairo_private cairo_status_t -_cairo_array_allocate (cairo_array_t *array, - unsigned int num_elements, - void **elements); - -cairo_private void * -_cairo_array_index (cairo_array_t *array, unsigned int index); - -cairo_private void -_cairo_array_copy_element (cairo_array_t *array, int index, void *dst); - -cairo_private int -_cairo_array_num_elements (cairo_array_t *array); - -cairo_private int -_cairo_array_size (cairo_array_t *array); - -typedef struct { - const cairo_user_data_key_t *key; - void *user_data; - cairo_destroy_func_t destroy; -} cairo_user_data_slot_t; - -cairo_private void -_cairo_user_data_array_init (cairo_user_data_array_t *array); - -cairo_private void -_cairo_user_data_array_fini (cairo_user_data_array_t *array); - -cairo_private void * -_cairo_user_data_array_get_data (cairo_user_data_array_t *array, - const cairo_user_data_key_t *key); - -cairo_private cairo_status_t -_cairo_user_data_array_set_data (cairo_user_data_array_t *array, - const cairo_user_data_key_t *key, - void *user_data, - cairo_destroy_func_t destroy); - -cairo_private cairo_status_t -_cairo_user_data_array_copy (cairo_user_data_array_t *dst, - cairo_user_data_array_t *src); - -cairo_private void -_cairo_user_data_array_foreach (cairo_user_data_array_t *array, - void (*func) (const void *key, - void *elt, - void *closure), - void *closure); - -#define _CAIRO_HASH_INIT_VALUE 5381 - -cairo_private unsigned long -_cairo_hash_string (const char *c); - -cairo_private unsigned long -_cairo_hash_bytes (unsigned long hash, - const void *bytes, - unsigned int length); - -#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash) -#define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) - -#include "cairo-scaled-font-private.h" - -struct _cairo_font_face { - /* hash_entry must be first */ - cairo_hash_entry_t hash_entry; - cairo_status_t status; - cairo_reference_count_t ref_count; - cairo_user_data_array_t user_data; - const cairo_font_face_backend_t *backend; -}; - -cairo_private void -_cairo_reset_static_data (void); - -cairo_private void -_cairo_toy_font_face_reset_static_data (void); - -cairo_private void -_cairo_ft_font_reset_static_data (void); - -cairo_private void -_cairo_win32_font_reset_static_data (void); - -/* the font backend interface */ - -struct _cairo_unscaled_font_backend { - void (*destroy) (void *unscaled_font); -}; - -/* #cairo_toy_font_face_t - simple family/slant/weight font faces used for - * the built-in font API - */ - -typedef struct _cairo_toy_font_face { - cairo_font_face_t base; - const char *family; - cairo_bool_t owns_family; - cairo_font_slant_t slant; - cairo_font_weight_t weight; - - cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */ -} cairo_toy_font_face_t; - -typedef enum _cairo_scaled_glyph_info { - CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), - CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), - CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2), - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3) -} cairo_scaled_glyph_info_t; - -typedef struct _cairo_scaled_font_subset { - cairo_scaled_font_t *scaled_font; - unsigned int font_id; - unsigned int subset_id; - - /* Index of glyphs array is subset_glyph_index. - * Value of glyphs array is scaled_font_glyph_index. - */ - unsigned long *glyphs; - unsigned long *to_unicode; - char **utf8; - char **glyph_names; - unsigned int num_glyphs; - cairo_bool_t is_composite; - cairo_bool_t is_scaled; -} cairo_scaled_font_subset_t; - -struct _cairo_scaled_font_backend { - cairo_font_type_t type; - - void - (*fini) (void *scaled_font); - - cairo_warn cairo_int_status_t - (*scaled_glyph_init) (void *scaled_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info); - - /* A backend only needs to implement this or ucs4_to_index(), not - * both. This allows the backend to do something more sophisticated - * then just converting characters one by one. - */ - cairo_warn cairo_int_status_t - (*text_to_glyphs) (void *scaled_font, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags); - - unsigned long - (*ucs4_to_index) (void *scaled_font, - uint32_t ucs4); - cairo_warn cairo_int_status_t - (*show_glyphs) (void *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs); - - cairo_warn cairo_int_status_t - (*load_truetype_table)(void *scaled_font, - unsigned long tag, - long offset, - unsigned char *buffer, - unsigned long *length); - - /* ucs4 is set to -1 if the unicode character could not be found - * for the glyph */ - cairo_warn cairo_int_status_t - (*index_to_ucs4)(void *scaled_font, - unsigned long index, - uint32_t *ucs4); -}; - -struct _cairo_font_face_backend { - cairo_font_type_t type; - - cairo_warn cairo_status_t - (*create_for_toy) (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face); - - /* The destroy() function is allowed to resurrect the font face - * by re-referencing. This is needed for the FreeType backend. - */ - void - (*destroy) (void *font_face); - - cairo_warn cairo_status_t - (*scaled_font_create) (void *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font); - - cairo_font_face_t * - (*get_implementation) (void *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options); -}; - -extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; - -/* concrete font backends */ -#if CAIRO_HAS_FT_FONT - -extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend; - -#endif - -#if CAIRO_HAS_WIN32_FONT - -extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend; - -#endif - -#if CAIRO_HAS_DWRITE_FONT - -extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend; - -#endif - -#if CAIRO_HAS_QUARTZ_FONT - -extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; - -#endif - -struct _cairo_surface_backend { - cairo_surface_type_t type; - - cairo_surface_t * - (*create_similar) (void *surface, - cairo_content_t content, - int width, - int height); - - cairo_warn cairo_status_t - (*finish) (void *surface); - - cairo_warn cairo_status_t - (*acquire_source_image) (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra); - - void - (*release_source_image) (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra); - - cairo_warn cairo_status_t - (*acquire_dest_image) (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra); - - void - (*release_dest_image) (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra); - - /* Create a new surface (@clone_out) with the following - * characteristics: - * - * 1. It is as compatible as possible with @surface (in terms of - * efficiency) - * - * 2. It has the same contents as @src within the given rectangle. - * - * 3. The offset of the similar surface with respect to the original - * surface is returned in the clone_offset vector. - * - if you clone the entire surface, this vector is zero. - * - if you clone (src_x, src_y)x(w, h) the vector is (src_x, src_y); - */ - cairo_warn cairo_status_t - (*clone_similar) (void *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out); - - /* XXX remove to a separate cairo_surface_compositor_t */ - /* XXX: dst should be the first argument for consistency */ - cairo_warn cairo_int_status_t - (*composite) (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - void *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - - cairo_warn cairo_int_status_t - (*fill_rectangles) (void *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - - /* XXX: dst should be the first argument for consistency */ - cairo_warn cairo_int_status_t - (*composite_trapezoids) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *region); - - cairo_warn cairo_span_renderer_t * - (*create_span_renderer) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region); - - cairo_warn cairo_bool_t - (*check_span_renderer) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *dst, - cairo_antialias_t antialias); - - cairo_warn cairo_int_status_t - (*copy_page) (void *surface); - - cairo_warn cairo_int_status_t - (*show_page) (void *surface); - - /* Get the extents of the current surface. For many surface types - * this will be as simple as { x=0, y=0, width=surface->width, - * height=surface->height}. - * - * If this function is not implemented, or if it returns - * FALSE the surface is considered to be - * boundless and infinite bounds are used for it. - */ - cairo_warn cairo_bool_t - (*get_extents) (void *surface, - cairo_rectangle_int_t *extents); - - /* - * This is an optional entry to let the surface manage its own glyph - * resources. If null, render against this surface, using image - * surfaces as glyphs. - */ - cairo_warn cairo_int_status_t - (*old_show_glyphs) (cairo_scaled_font_t *font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - void *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region); - - void - (*get_font_options) (void *surface, - cairo_font_options_t *options); - - cairo_warn cairo_status_t - (*flush) (void *surface); - - cairo_warn cairo_status_t - (*mark_dirty_rectangle) (void *surface, - int x, - int y, - int width, - int height); - - void - (*scaled_font_fini) (cairo_scaled_font_t *scaled_font); - - void - (*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - - /* OK, I'm starting over somewhat by defining the 5 top-level - * drawing operators for the surface backend here with consistent - * naming and argument-order conventions. */ - cairo_warn cairo_int_status_t - (*paint) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*mask) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*stroke) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*fill) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*show_glyphs) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - - cairo_surface_t * - (*snapshot) (void *surface); - - cairo_bool_t - (*is_similar) (void *surface_a, - void *surface_b); - - cairo_warn cairo_int_status_t - (*fill_stroke) (void *surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip); - - cairo_surface_t * - (*create_solid_pattern_surface) - (void *surface, - const cairo_solid_pattern_t *solid_pattern); - - cairo_bool_t - (*can_repaint_solid_pattern_surface) - (void *surface, - const cairo_solid_pattern_t *solid_pattern); - - cairo_bool_t - (*has_show_text_glyphs) (void *surface); - - cairo_warn cairo_int_status_t - (*show_text_glyphs) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); -}; - -#include "cairo-surface-private.h" - -struct _cairo_image_surface { - cairo_surface_t base; - - pixman_format_code_t pixman_format; - cairo_format_t format; - unsigned char *data; - - int width; - int height; - int stride; - int depth; - - pixman_image_t *pixman_image; - - unsigned owns_data : 1; - unsigned transparency : 2; -}; - -extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; - -#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE -#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD -#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD - -extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear; -extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; -extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white; - -typedef struct _cairo_surface_attributes { - cairo_matrix_t matrix; - cairo_extend_t extend; - cairo_filter_t filter; - cairo_bool_t has_component_alpha; - int x_offset; - int y_offset; - void *extra; -} cairo_surface_attributes_t; - -typedef struct _cairo_traps { - cairo_status_t status; - - const cairo_box_t *limits; - int num_limits; - - unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ - unsigned int has_intersections : 1; - unsigned int is_rectilinear : 1; - unsigned int is_rectangular : 1; - - int num_traps; - int traps_size; - cairo_trapezoid_t *traps; - cairo_trapezoid_t traps_embedded[16]; -} cairo_traps_t; - -#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL -#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL - -#define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" -#define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" -#define CAIRO_FT_FONT_FAMILY_DEFAULT "" -#define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" - -#if CAIRO_HAS_DWRITE_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend - -#elif CAIRO_HAS_WIN32_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend - -#elif CAIRO_HAS_QUARTZ_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend - -#elif CAIRO_HAS_FT_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend - -#else - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend - -#endif - -#define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER -#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 usr_vector; -} cairo_stroke_face_t; - -/* cairo.c */ - -static inline double cairo_const -_cairo_restrict_value (double value, double min, double max) -{ - if (value < min) - return min; - else if (value > max) - return max; - else - return value; -} - -/* C99 round() rounds to the nearest integral value with halfway cases rounded - * away from 0. _cairo_round rounds halfway cases toward negative infinity. - * This matches the rounding behaviour of _cairo_lround. */ -static inline double cairo_const -_cairo_round (double r) -{ - return floor (r + .5); -} - -#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L -cairo_private int -_cairo_lround (double d) cairo_const; -#else -#define _cairo_lround lround -#endif - -cairo_private uint16_t -_cairo_half_from_float (float f) cairo_const; - -cairo_private cairo_bool_t -_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const; - -cairo_private cairo_bool_t -_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const; - -enum { - CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1, - CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2, -}; - -cairo_private uint32_t -_cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const; -/* cairo-color.c */ -cairo_private const cairo_color_t * -_cairo_stock_color (cairo_stock_t stock) cairo_pure; - -#define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) -#define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) -#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) - -cairo_private uint16_t -_cairo_color_double_to_short (double d) cairo_const; - -cairo_private void -_cairo_color_init (cairo_color_t *color); - -cairo_private void -_cairo_color_init_rgb (cairo_color_t *color, - double red, double green, double blue); - -cairo_private void -_cairo_color_init_rgba (cairo_color_t *color, - double red, double green, double blue, - double alpha); - -cairo_private void -_cairo_color_multiply_alpha (cairo_color_t *color, - double alpha); - -cairo_private void -_cairo_color_get_rgba (cairo_color_t *color, - double *red, - double *green, - double *blue, - double *alpha); - -cairo_private void -_cairo_color_get_rgba_premultiplied (cairo_color_t *color, - double *red, - double *green, - double *blue, - double *alpha); - -cairo_private cairo_bool_t -_cairo_color_equal (const cairo_color_t *color_a, - const cairo_color_t *color_b) cairo_pure; - -cairo_private cairo_bool_t -_cairo_color_stop_equal (const cairo_color_stop_t *color_a, - const cairo_color_stop_t *color_b) cairo_pure; - -cairo_private cairo_content_t -_cairo_color_get_content (const cairo_color_t *color) cairo_pure; - -/* cairo-font-face.c */ - -extern const cairo_private cairo_font_face_t _cairo_font_face_nil; - -cairo_private void -_cairo_font_face_init (cairo_font_face_t *font_face, - const cairo_font_face_backend_t *backend); - -cairo_private cairo_status_t -_cairo_font_face_set_error (cairo_font_face_t *font_face, - cairo_status_t status); - -cairo_private void -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const cairo_unscaled_font_backend_t *backend); - -cairo_private_no_warn cairo_unscaled_font_t * -_cairo_unscaled_font_reference (cairo_unscaled_font_t *font); - -cairo_private void -_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); - -/* cairo-font-face-twin.c */ - -cairo_private cairo_font_face_t * -_cairo_font_face_twin_create_fallback (void); - -cairo_private cairo_status_t -_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face); - -/* cairo-font-face-twin-data.c */ - -extern const cairo_private int8_t _cairo_twin_outlines[]; -extern const cairo_private uint16_t _cairo_twin_charmap[128]; - -/* cairo-font-options.c */ - -cairo_private void -_cairo_font_options_init_default (cairo_font_options_t *options); - -cairo_private void -_cairo_font_options_init_copy (cairo_font_options_t *options, - const cairo_font_options_t *other); - -cairo_private void -_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, - cairo_lcd_filter_t lcd_filter); - -cairo_private cairo_lcd_filter_t -_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); - -cairo_private void -_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, - cairo_round_glyph_positions_t round); - -cairo_private cairo_round_glyph_positions_t -_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options); - -/* cairo-hull.c */ -cairo_private cairo_status_t -_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); - -/* cairo-lzw.c */ -cairo_private unsigned char * -_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out); - -/* cairo-misc.c */ -cairo_private cairo_status_t -_cairo_validate_text_clusters (const char *utf8, - int utf8_len, - const cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags); - -cairo_private cairo_status_t -_cairo_intern_string (const char **str_inout, int len); - -cairo_private void -_cairo_intern_string_reset_static_data (void); - -/* cairo-path-fixed.c */ -cairo_private cairo_path_fixed_t * -_cairo_path_fixed_create (void); - -cairo_private void -_cairo_path_fixed_init (cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, - const cairo_path_fixed_t *other); - -cairo_private cairo_bool_t -_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, - const cairo_path_fixed_t *other); - -cairo_private void -_cairo_path_fixed_fini (cairo_path_fixed_t *path); - -cairo_private void -_cairo_path_fixed_destroy (cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_path_fixed_move_to (cairo_path_fixed_t *path, - cairo_fixed_t x, - cairo_fixed_t y); - -cairo_private void -_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, - cairo_fixed_t dx, - cairo_fixed_t dy); - -cairo_private cairo_status_t -_cairo_path_fixed_line_to (cairo_path_fixed_t *path, - cairo_fixed_t x, - cairo_fixed_t y); - -cairo_private cairo_status_t -_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, - cairo_fixed_t dx, - cairo_fixed_t dy); - -cairo_private cairo_status_t -_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, - cairo_fixed_t x0, cairo_fixed_t y0, - cairo_fixed_t x1, cairo_fixed_t y1, - cairo_fixed_t x2, cairo_fixed_t y2); - -cairo_private cairo_status_t -_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, - cairo_fixed_t dx0, cairo_fixed_t dy0, - cairo_fixed_t dx1, cairo_fixed_t dy1, - cairo_fixed_t dx2, cairo_fixed_t dy2); - -cairo_private cairo_status_t -_cairo_path_fixed_close_path (cairo_path_fixed_t *path); - -cairo_private cairo_bool_t -_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, - cairo_fixed_t *x, - cairo_fixed_t *y); - -typedef cairo_status_t -(cairo_path_fixed_move_to_func_t) (void *closure, - const cairo_point_t *point); - -typedef cairo_status_t -(cairo_path_fixed_line_to_func_t) (void *closure, - const cairo_point_t *point); - -typedef cairo_status_t -(cairo_path_fixed_curve_to_func_t) (void *closure, - const cairo_point_t *p0, - const cairo_point_t *p1, - const cairo_point_t *p2); - -typedef cairo_status_t -(cairo_path_fixed_close_path_func_t) (void *closure); - -cairo_private cairo_status_t -_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, - cairo_direction_t dir, - cairo_path_fixed_move_to_func_t *move_to, - cairo_path_fixed_line_to_func_t *line_to, - cairo_path_fixed_curve_to_func_t *curve_to, - cairo_path_fixed_close_path_func_t *close_path, - void *closure); - -cairo_private cairo_status_t -_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, - cairo_direction_t dir, - cairo_path_fixed_move_to_func_t *move_to, - cairo_path_fixed_line_to_func_t *line_to, - cairo_path_fixed_close_path_func_t *close_path, - void *closure, - double tolerance); - -cairo_private cairo_bool_t -_cairo_path_fixed_extents (const cairo_path_fixed_t *path, - cairo_box_t *box); - -cairo_private void -_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_rectangle_int_t *extents); - -cairo_private void -_cairo_path_fixed_transform (cairo_path_fixed_t *path, - const cairo_matrix_t *matrix); - -cairo_private cairo_bool_t -_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, - cairo_box_t *box); - -cairo_private cairo_bool_t -_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, - cairo_box_t *box); - -/* cairo-path-in-fill.c */ -cairo_private cairo_bool_t -_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - double x, - double y); - -/* cairo-path-fill.c */ -cairo_private cairo_status_t -_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, - double tolerance, - cairo_polygon_t *polygon); - -cairo_private cairo_int_status_t -_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps); - -cairo_private cairo_status_t -_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *boxes); - -cairo_private cairo_region_t * -_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_traps_t *traps); - -/* cairo-path-stroke.c */ -cairo_private cairo_status_t -_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_polygon_t *polygon); - -cairo_private cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps); - -cairo_private cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_boxes_t *boxes); - -cairo_private cairo_status_t -_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_traps_t *traps); - -cairo_private cairo_status_t -_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_status_t (*add_triangle) (void *closure, - const cairo_point_t triangle[3]), - cairo_status_t (*add_triangle_fan) (void *closure, - const cairo_point_t *midpt, - const cairo_point_t *points, - int npoints), - cairo_status_t (*add_quad) (void *closure, - const cairo_point_t quad[4]), - void *closure); - -/* cairo-scaled-font.c */ - -cairo_private void -_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font); - -cairo_private cairo_status_t -_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, - cairo_status_t status); - -cairo_private cairo_scaled_font_t * -_cairo_scaled_font_create_in_error (cairo_status_t status); - -cairo_private void -_cairo_scaled_font_reset_static_data (void); - -cairo_private cairo_status_t -_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font); - -cairo_private cairo_status_t -_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - const cairo_scaled_font_backend_t *backend); - -cairo_private cairo_status_t -_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *fs_metrics); - -/* This should only be called on an error path by a scaled_font constructor */ -cairo_private void -_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -cairo_private cairo_status_t -_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents); - -cairo_private cairo_status_t -_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_rectangle_int_t *extents, - cairo_bool_t *overlap); - -cairo_private void -_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path); - -cairo_private void -_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_text_extents_t *fs_metrics); - -cairo_private void -_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_image_surface_t *surface); - -cairo_private void -_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_path_fixed_t *path); - -cairo_private void -_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface); - -cairo_private cairo_int_status_t -_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, - unsigned long index, - cairo_scaled_glyph_info_t info, - cairo_scaled_glyph_t **scaled_glyph_ret); - -cairo_private double -_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_scaled_font_map_destroy (void); - -/* cairo-stroke-style.c */ - -cairo_private void -_cairo_stroke_style_init (cairo_stroke_style_t *style); - -cairo_private cairo_status_t -_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, - const cairo_stroke_style_t *other); - -cairo_private void -_cairo_stroke_style_fini (cairo_stroke_style_t *style); - -cairo_private void -_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double *dx, double *dy); - -cairo_private double -_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style); - -cairo_private double -_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style); - -cairo_private cairo_bool_t -_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double tolerance); - -cairo_private void -_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - double tolerance, - double *dash_offset, - double *dashes, - unsigned int *num_dashes); - - -/* cairo-surface.c */ - -cairo_private cairo_surface_t * -_cairo_surface_create_in_error (cairo_status_t status); - -cairo_private cairo_status_t -_cairo_surface_copy_mime_data (cairo_surface_t *dst, - cairo_surface_t *src); - -cairo_private cairo_status_t -_cairo_surface_set_error (cairo_surface_t *surface, - cairo_status_t status); - -cairo_private void -_cairo_surface_set_resolution (cairo_surface_t *surface, - double x_res, - double y_res); - -cairo_private cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_content_t content, - int width, - int height); - -cairo_private cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_content_t content, - int width, - int height, - const cairo_color_t *color, - cairo_bool_t allow_fallback); - -cairo_private cairo_surface_t * -_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - const cairo_solid_pattern_t *solid_pattern); - -cairo_private cairo_int_status_t -_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, - cairo_surface_t *solid_surface, - const cairo_solid_pattern_t *solid_pattern); - -cairo_private void -_cairo_surface_init (cairo_surface_t *surface, - const cairo_surface_backend_t *backend, - cairo_device_t *device, - cairo_content_t content); - -cairo_private void -_cairo_surface_set_font_options (cairo_surface_t *surface, - cairo_font_options_t *options); - -cairo_private cairo_status_t -_cairo_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - int x, - int y, - int width, - int height); - -cairo_private cairo_status_t -_cairo_surface_fill_region (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_region_t *region); - -cairo_private cairo_status_t -_cairo_surface_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - -cairo_private cairo_status_t -_cairo_surface_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fill_stroke (cairo_surface_t *surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_show_text_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); - -cairo_private cairo_status_t -_cairo_surface_paint_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_mask_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_stroke_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_fill_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_glyphs_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int ntraps, - cairo_region_t *clip_region); - -cairo_private cairo_span_renderer_t * -_cairo_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region); - -cairo_private cairo_bool_t -_cairo_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias); - -cairo_private cairo_status_t -_cairo_surface_acquire_source_image (cairo_surface_t *surface, - cairo_image_surface_t **image_out, - void **image_extra); - -cairo_private void -_cairo_surface_release_source_image (cairo_surface_t *surface, - cairo_image_surface_t *image, - void *image_extra); - -cairo_private cairo_status_t -_cairo_surface_acquire_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra); - -cairo_private void -_cairo_surface_release_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra); - -cairo_private cairo_status_t -_cairo_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out); - -cairo_private cairo_surface_t * -_cairo_surface_snapshot (cairo_surface_t *surface); - -cairo_private cairo_surface_t * -_cairo_surface_has_snapshot (cairo_surface_t *surface, - const cairo_surface_backend_t *backend); - -cairo_private cairo_bool_t -_cairo_surface_is_similar (cairo_surface_t *surface_a, - cairo_surface_t *surface_b); - -cairo_private cairo_bool_t -_cairo_surface_get_extents (cairo_surface_t *surface, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - cairo_surface_attributes_t *mask_attr, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_bool_t -_cairo_surface_is_opaque (const cairo_surface_t *surface); - -cairo_private int -_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface); - -cairo_private void -_cairo_surface_set_device_scale (cairo_surface_t *surface, - double sx, - double sy); - -cairo_private cairo_bool_t -_cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; - -cairo_private void -_cairo_surface_release_device_reference (cairo_surface_t *surface); - -/* cairo-image-surface.c */ - -/* XXX: In cairo 1.2.0 we added a new %CAIRO_FORMAT_RGB16_565 but - * neglected to adjust this macro. The net effect is that it's - * impossible to externally create an image surface with this - * format. This is perhaps a good thing since we also neglected to fix - * up things like cairo_surface_write_to_png() for the new format - * (-Wswitch-enum will tell you where). Is it obvious that format was - * added in haste? - * - * The reason for the new format was to allow the xlib backend to be - * used on X servers with a 565 visual. So the new format did its job - * for that, even without being considered "valid" for the sake of - * things like cairo_image_surface_create(). - * - * Since 1.2.0 we ran into the same situtation with X servers with BGR - * visuals. This time we invented #cairo_internal_format_t instead, - * (see it for more discussion). - * - * The punchline is that %CAIRO_FORMAT_VALID must not conside any - * internal format to be valid. Also we need to decide if the - * RGB16_565 should be moved to instead be an internal format. If so, - * this macro need not change for it. (We probably will need to leave - * an RGB16_565 value in the header files for the sake of code that - * might have that value in it.) - * - * If we do decide to start fully supporting RGB16_565 as an external - * format, then %CAIRO_FORMAT_VALID needs to be adjusted to include - * it. But that should not happen before all necessary code is fixed - * to support it (at least cairo_surface_write_to_png() and a few spots - * in cairo-xlib-surface.c--again see -Wswitch-enum). - */ -#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ - (format) <= CAIRO_FORMAT_RGB16_565) - -/* pixman-required stride alignment in bytes. */ -#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) -#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \ - ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT) - -#define CAIRO_CONTENT_VALID(content) ((content) && \ - (((content) & ~(CAIRO_CONTENT_COLOR | \ - CAIRO_CONTENT_ALPHA | \ - CAIRO_CONTENT_COLOR_ALPHA))\ - == 0)) - -static inline cairo_bool_t -_cairo_valid_stride_alignment(int stride) -{ - return !(stride & (CAIRO_STRIDE_ALIGNMENT-1)); -} - -cairo_private int -_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; - -cairo_private cairo_format_t -_cairo_format_from_content (cairo_content_t content) cairo_const; - -cairo_private cairo_format_t -_cairo_format_from_pixman_format (pixman_format_code_t pixman_format); - -cairo_private cairo_content_t -_cairo_content_from_format (cairo_format_t format) cairo_const; - -cairo_private cairo_content_t -_cairo_content_from_pixman_format (pixman_format_code_t pixman_format); - -cairo_private cairo_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - pixman_format_code_t pixman_format); - -cairo_private pixman_format_code_t -_cairo_format_to_pixman_format_code (cairo_format_t format); - -cairo_private cairo_bool_t -_pixman_format_from_masks (cairo_format_masks_t *masks, - pixman_format_code_t *format_ret); - -cairo_private cairo_bool_t -_pixman_format_to_masks (pixman_format_code_t pixman_format, - cairo_format_masks_t *masks); - -cairo_private void -_cairo_image_reset_static_data (void); - -cairo_private cairo_surface_t * -_cairo_image_surface_create_with_pixman_format (unsigned char *data, - pixman_format_code_t pixman_format, - int width, - int height, - int stride); - -cairo_private cairo_surface_t * -_cairo_image_surface_create_with_content (cairo_content_t content, - int width, - int height); - -cairo_private void -_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); - -cairo_private cairo_image_surface_t * -_cairo_image_surface_coerce (cairo_image_surface_t *surface); - -cairo_private cairo_image_surface_t * -_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, - cairo_format_t format); - -cairo_private void -_cairo_image_surface_span_render_row (int y, - const cairo_half_open_span_t *spans, - unsigned num_spans, - uint8_t *data, - uint32_t stride); - -cairo_private cairo_image_transparency_t -_cairo_image_analyze_transparency (cairo_image_surface_t *image); - -cairo_private cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; - -cairo_private cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure; - -/* cairo-pen.c */ -cairo_private cairo_status_t -_cairo_pen_init (cairo_pen_t *pen, - double radius, - double tolerance, - const cairo_matrix_t *ctm); - -cairo_private void -_cairo_pen_init_empty (cairo_pen_t *pen); - -cairo_private cairo_status_t -_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other); - -cairo_private void -_cairo_pen_fini (cairo_pen_t *pen); - -cairo_private cairo_status_t -_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points); - -cairo_private cairo_status_t -_cairo_pen_add_points_for_slopes (cairo_pen_t *pen, - cairo_point_t *a, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -cairo_private int -_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, - const cairo_slope_t *slope); - -cairo_private int -_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, - const cairo_slope_t *slope); - -/* cairo-polygon.c */ -cairo_private void -_cairo_polygon_init (cairo_polygon_t *polygon); - -cairo_private void -_cairo_polygon_limit (cairo_polygon_t *polygon, - const cairo_box_t *boxes, - int num_boxes); - -cairo_private void -_cairo_polygon_fini (cairo_polygon_t *polygon); - -cairo_private cairo_status_t -_cairo_polygon_add_line (cairo_polygon_t *polygon, - const cairo_line_t *line, - int top, int bottom, - int dir); - -cairo_private cairo_status_t -_cairo_polygon_add_external_edge (void *polygon, - const cairo_point_t *p1, - const cairo_point_t *p2); - -cairo_private cairo_status_t -_cairo_polygon_move_to (cairo_polygon_t *polygon, - const cairo_point_t *point); - -cairo_private cairo_status_t -_cairo_polygon_line_to (cairo_polygon_t *polygon, - const cairo_point_t *point); - -cairo_private cairo_status_t -_cairo_polygon_close (cairo_polygon_t *polygon); - -#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status - -/* cairo-spline.c */ -cairo_private cairo_bool_t -_cairo_spline_init (cairo_spline_t *spline, - cairo_spline_add_point_func_t add_point_func, - void *closure, - const cairo_point_t *a, const cairo_point_t *b, - const cairo_point_t *c, const cairo_point_t *d); - -cairo_private cairo_status_t -_cairo_spline_decompose (cairo_spline_t *spline, double tolerance); - -cairo_private cairo_status_t -_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, - void *closure, - const cairo_point_t *p0, const cairo_point_t *p1, - const cairo_point_t *p2, const cairo_point_t *p3); - -/* cairo-matrix.c */ -cairo_private void -_cairo_matrix_get_affine (const cairo_matrix_t *matrix, - double *xx, double *yx, - double *xy, double *yy, - double *x0, double *y0); - -cairo_private void -_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, - double *x1, double *y1, - double *x2, double *y2, - cairo_bool_t *is_tight); - -cairo_private void -_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, - cairo_box_t *bbox, - cairo_bool_t *is_tight); - -cairo_private cairo_bool_t -_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private cairo_bool_t -_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private double -_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private cairo_status_t -_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, - double *sx, double *sy, int x_major); - -cairo_private cairo_bool_t -_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private cairo_bool_t -_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private cairo_bool_t -_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, - int *itx, int *ity); - -cairo_private cairo_bool_t -_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix); - -cairo_private cairo_bool_t -_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; - -cairo_private void -_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, - double radius, - double *major, - double *minor); - -cairo_private double -_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, - double radius) cairo_pure; - -cairo_private void -_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform, - double xc, - double yc); - -/* cairo-traps.c */ -cairo_private void -_cairo_traps_init (cairo_traps_t *traps); - -cairo_private void -_cairo_traps_limit (cairo_traps_t *traps, - const cairo_box_t *boxes, - int num_boxes); - -cairo_private cairo_status_t -_cairo_traps_init_boxes (cairo_traps_t *traps, - const cairo_boxes_t *boxes); - -cairo_private void -_cairo_traps_clear (cairo_traps_t *traps); - -cairo_private void -_cairo_traps_fini (cairo_traps_t *traps); - -#define _cairo_traps_status(T) (T)->status - -cairo_private void -_cairo_traps_translate (cairo_traps_t *traps, int x, int y); - -cairo_private cairo_status_t -_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, - const cairo_point_t *top_left, - const cairo_point_t *bottom_right); - -cairo_private void -_cairo_traps_add_trap (cairo_traps_t *traps, - cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, - const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, - const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *out); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, - cairo_fill_rule_t fill_rule); - -cairo_private cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_boxes_t *boxes); - -cairo_private int -_cairo_traps_contain (const cairo_traps_t *traps, - double x, double y); - -cairo_private void -_cairo_traps_extents (const cairo_traps_t *traps, - cairo_box_t *extents); - -cairo_private cairo_int_status_t -_cairo_traps_extract_region (cairo_traps_t *traps, - cairo_region_t **region); - -cairo_private cairo_status_t -_cairo_traps_path (const cairo_traps_t *traps, - cairo_path_fixed_t *path); - -cairo_private void -_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, - cairo_trapezoid_t *src_traps, - int num_traps, - double tx, double ty, - double sx, double sy); - -/* cairo-pattern.c */ - -cairo_private cairo_pattern_t * -_cairo_pattern_create_in_error (cairo_status_t status); - -cairo_private cairo_status_t -_cairo_pattern_create_copy (cairo_pattern_t **pattern, - const cairo_pattern_t *other); - -cairo_private cairo_status_t -_cairo_pattern_init_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private void -_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private cairo_status_t -_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private void -_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - const cairo_color_t *color); - -cairo_private void -_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, - cairo_surface_t *surface); - -cairo_private void -_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, - double x0, double y0, double x1, double y1); - -cairo_private void -_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, - double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1); - -cairo_private void -_cairo_pattern_fini (cairo_pattern_t *pattern); - -cairo_private cairo_pattern_t * -_cairo_pattern_create_solid (const cairo_color_t *color); - -cairo_private void -_cairo_pattern_transform (cairo_pattern_t *pattern, - const cairo_matrix_t *ctm_inverse); - -cairo_private cairo_bool_t -_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents, - cairo_color_t *color); - -cairo_private cairo_bool_t -_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); - -cairo_private cairo_bool_t -_cairo_pattern_is_opaque (const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents); - -cairo_private cairo_bool_t -_cairo_pattern_is_clear (const cairo_pattern_t *pattern); - -cairo_private_no_warn cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out); - -enum { - CAIRO_PATTERN_ACQUIRE_NONE = 0x0, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1 -}; -cairo_private cairo_int_status_t -_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **surface_out, - cairo_surface_attributes_t *attributes); - -cairo_private void -_cairo_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_surface_t *surface, - cairo_surface_attributes_t *attributes); - -cairo_private cairo_int_status_t -_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **src_out, - cairo_surface_t **mask_out, - cairo_surface_attributes_t *src_attributes, - cairo_surface_attributes_t *mask_attributes); - -cairo_private void -_cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents); - -cairo_private unsigned long -_cairo_pattern_hash (const cairo_pattern_t *pattern); - -cairo_private unsigned long -_cairo_linear_pattern_hash (unsigned long hash, - const cairo_linear_pattern_t *linear); - -cairo_private unsigned long -_cairo_radial_pattern_hash (unsigned long hash, - const cairo_radial_pattern_t *radial); - -cairo_private cairo_bool_t -_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, - const cairo_linear_pattern_t *b); - -cairo_private unsigned long -_cairo_pattern_size (const cairo_pattern_t *pattern); - -cairo_private cairo_bool_t -_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, - const cairo_radial_pattern_t *b); - -cairo_private cairo_bool_t -_cairo_pattern_equal (const cairo_pattern_t *a, - const cairo_pattern_t *b); - -cairo_private void -_cairo_pattern_reset_static_data (void); - -#if CAIRO_HAS_DRM_SURFACE - -cairo_private void -_cairo_drm_device_reset_static_data (void); - -#endif - -cairo_private void -_cairo_clip_reset_static_data (void); - -/* cairo-unicode.c */ - -cairo_private int -_cairo_utf8_get_char_validated (const char *p, - uint32_t *unicode); - -cairo_private cairo_status_t -_cairo_utf8_to_ucs4 (const char *str, - int len, - uint32_t **result, - int *items_written); - -cairo_private int -_cairo_ucs4_to_utf8 (uint32_t unicode, - char *utf8); - -#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT -# define CAIRO_HAS_UTF8_TO_UTF16 1 -#endif -#if CAIRO_HAS_UTF8_TO_UTF16 -cairo_private cairo_status_t -_cairo_utf8_to_utf16 (const char *str, - int len, - uint16_t **result, - int *items_written); -#endif - -/* cairo-observer.c */ - -cairo_private void -_cairo_observers_notify (cairo_list_t *observers, void *arg); - -/* Avoid unnecessary PLT entries. */ -slim_hidden_proto (cairo_clip_preserve); -slim_hidden_proto (cairo_close_path); -slim_hidden_proto (cairo_create); -slim_hidden_proto (cairo_curve_to); -slim_hidden_proto (cairo_destroy); -slim_hidden_proto (cairo_fill_preserve); -slim_hidden_proto (cairo_font_face_destroy); -slim_hidden_proto (cairo_font_face_get_user_data); -slim_hidden_proto_no_warn (cairo_font_face_reference); -slim_hidden_proto (cairo_font_face_set_user_data); -slim_hidden_proto (cairo_font_options_equal); -slim_hidden_proto (cairo_font_options_hash); -slim_hidden_proto (cairo_font_options_merge); -slim_hidden_proto (cairo_font_options_set_antialias); -slim_hidden_proto (cairo_font_options_set_hint_metrics); -slim_hidden_proto (cairo_font_options_set_hint_style); -slim_hidden_proto (cairo_font_options_set_subpixel_order); -slim_hidden_proto (cairo_font_options_status); -slim_hidden_proto (cairo_format_stride_for_width); -slim_hidden_proto (cairo_get_current_point); -slim_hidden_proto (cairo_get_line_width); -slim_hidden_proto (cairo_get_matrix); -slim_hidden_proto (cairo_get_target); -slim_hidden_proto (cairo_get_tolerance); -slim_hidden_proto (cairo_glyph_allocate); -slim_hidden_proto (cairo_glyph_free); -slim_hidden_proto (cairo_image_surface_create); -slim_hidden_proto (cairo_image_surface_create_for_data); -slim_hidden_proto (cairo_image_surface_get_data); -slim_hidden_proto (cairo_image_surface_get_format); -slim_hidden_proto (cairo_image_surface_get_height); -slim_hidden_proto (cairo_image_surface_get_stride); -slim_hidden_proto (cairo_image_surface_get_width); -slim_hidden_proto (cairo_line_to); -slim_hidden_proto (cairo_mask); -slim_hidden_proto (cairo_matrix_init); -slim_hidden_proto (cairo_matrix_init_identity); -slim_hidden_proto (cairo_matrix_init_rotate); -slim_hidden_proto (cairo_matrix_init_scale); -slim_hidden_proto (cairo_matrix_init_translate); -slim_hidden_proto (cairo_matrix_invert); -slim_hidden_proto (cairo_matrix_multiply); -slim_hidden_proto (cairo_matrix_scale); -slim_hidden_proto (cairo_matrix_transform_distance); -slim_hidden_proto (cairo_matrix_transform_point); -slim_hidden_proto (cairo_matrix_translate); -slim_hidden_proto (cairo_move_to); -slim_hidden_proto (cairo_new_path); -slim_hidden_proto (cairo_paint); -slim_hidden_proto (cairo_pattern_create_for_surface); -slim_hidden_proto (cairo_pattern_create_rgb); -slim_hidden_proto (cairo_pattern_create_rgba); -slim_hidden_proto (cairo_pattern_destroy); -slim_hidden_proto (cairo_pattern_get_extend); -slim_hidden_proto_no_warn (cairo_pattern_reference); -slim_hidden_proto (cairo_pattern_set_matrix); -slim_hidden_proto (cairo_pop_group); -slim_hidden_proto (cairo_push_group_with_content); -slim_hidden_proto (cairo_rel_line_to); -slim_hidden_proto (cairo_restore); -slim_hidden_proto (cairo_save); -slim_hidden_proto (cairo_scale); -slim_hidden_proto (cairo_scaled_font_create); -slim_hidden_proto (cairo_scaled_font_destroy); -slim_hidden_proto (cairo_scaled_font_extents); -slim_hidden_proto (cairo_scaled_font_get_ctm); -slim_hidden_proto (cairo_scaled_font_get_font_face); -slim_hidden_proto (cairo_scaled_font_get_font_matrix); -slim_hidden_proto (cairo_scaled_font_get_font_options); -slim_hidden_proto (cairo_scaled_font_glyph_extents); -slim_hidden_proto_no_warn (cairo_scaled_font_reference); -slim_hidden_proto (cairo_scaled_font_status); -slim_hidden_proto (cairo_scaled_font_get_user_data); -slim_hidden_proto (cairo_scaled_font_set_user_data); -slim_hidden_proto (cairo_scaled_font_text_to_glyphs); -slim_hidden_proto (cairo_set_font_options); -slim_hidden_proto (cairo_set_font_size); -slim_hidden_proto (cairo_set_line_cap); -slim_hidden_proto (cairo_set_line_join); -slim_hidden_proto (cairo_set_line_width); -slim_hidden_proto (cairo_set_matrix); -slim_hidden_proto (cairo_set_operator); -slim_hidden_proto (cairo_set_source); -slim_hidden_proto (cairo_set_source_rgb); -slim_hidden_proto (cairo_set_source_surface); -slim_hidden_proto (cairo_set_tolerance); -slim_hidden_proto (cairo_status); -slim_hidden_proto (cairo_stroke); -slim_hidden_proto (cairo_stroke_preserve); -slim_hidden_proto (cairo_surface_copy_page); -slim_hidden_proto (cairo_surface_destroy); -slim_hidden_proto (cairo_surface_finish); -slim_hidden_proto (cairo_surface_flush); -slim_hidden_proto (cairo_surface_get_content); -slim_hidden_proto (cairo_surface_get_device_offset); -slim_hidden_proto (cairo_surface_get_font_options); -slim_hidden_proto (cairo_surface_get_mime_data); -slim_hidden_proto (cairo_surface_get_type); -slim_hidden_proto (cairo_surface_has_show_text_glyphs); -slim_hidden_proto (cairo_surface_set_subpixel_antialiasing); -slim_hidden_proto (cairo_surface_get_subpixel_antialiasing); -slim_hidden_proto (cairo_surface_mark_dirty); -slim_hidden_proto (cairo_surface_mark_dirty_rectangle); -slim_hidden_proto_no_warn (cairo_surface_reference); -slim_hidden_proto (cairo_surface_set_device_offset); -slim_hidden_proto (cairo_surface_set_fallback_resolution); -slim_hidden_proto (cairo_surface_set_mime_data); -slim_hidden_proto (cairo_surface_show_page); -slim_hidden_proto (cairo_surface_status); -slim_hidden_proto (cairo_text_cluster_allocate); -slim_hidden_proto (cairo_text_cluster_free); -slim_hidden_proto (cairo_toy_font_face_create); -slim_hidden_proto (cairo_toy_font_face_get_slant); -slim_hidden_proto (cairo_toy_font_face_get_weight); -slim_hidden_proto (cairo_translate); -slim_hidden_proto (cairo_transform); -slim_hidden_proto (cairo_user_font_face_create); -slim_hidden_proto (cairo_user_font_face_set_init_func); -slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); -slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); -slim_hidden_proto (cairo_user_to_device); -slim_hidden_proto (cairo_user_to_device_distance); -slim_hidden_proto (cairo_version_string); -slim_hidden_proto (cairo_region_create); -slim_hidden_proto (cairo_region_create_rectangle); -slim_hidden_proto (cairo_region_create_rectangles); -slim_hidden_proto (cairo_region_copy); -slim_hidden_proto (cairo_region_reference); -slim_hidden_proto (cairo_region_destroy); -slim_hidden_proto (cairo_region_equal); -slim_hidden_proto (cairo_region_status); -slim_hidden_proto (cairo_region_get_extents); -slim_hidden_proto (cairo_region_num_rectangles); -slim_hidden_proto (cairo_region_get_rectangle); -slim_hidden_proto (cairo_region_is_empty); -slim_hidden_proto (cairo_region_contains_rectangle); -slim_hidden_proto (cairo_region_contains_point); -slim_hidden_proto (cairo_region_translate); -slim_hidden_proto (cairo_region_subtract); -slim_hidden_proto (cairo_region_subtract_rectangle); -slim_hidden_proto (cairo_region_intersect); -slim_hidden_proto (cairo_region_intersect_rectangle); -slim_hidden_proto (cairo_region_union); -slim_hidden_proto (cairo_region_union_rectangle); -slim_hidden_proto (cairo_region_xor); -slim_hidden_proto (cairo_region_xor_rectangle); - -#if CAIRO_HAS_PNG_FUNCTIONS - -slim_hidden_proto (cairo_surface_write_to_png_stream); - -#endif - -cairo_private_no_warn cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out); - -CAIRO_END_DECLS - -#include "cairo-mutex-private.h" -#include "cairo-fixed-private.h" -#include "cairo-wideint-private.h" -#include "cairo-malloc-private.h" -#include "cairo-hash-private.h" - -#if HAVE_VALGRIND -#include - -#define VG(x) x - -cairo_private void -_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); - -#else - -#define VG(x) -#define _cairo_debug_check_image_surface_is_defined(X) - -#endif - -cairo_private void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); - -cairo_private void -_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip); - -#endif diff --git a/libs/cairo/cairo/src/check-has-hidden-symbols.c b/libs/cairo/cairo/src/check-has-hidden-symbols.c deleted file mode 100644 index 120412776..000000000 --- a/libs/cairo/cairo/src/check-has-hidden-symbols.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "cairoint.h" - -CAIRO_HAS_HIDDEN_SYMBOLS diff --git a/libs/cairo/cairo/src/check-link.c b/libs/cairo/cairo/src/check-link.c deleted file mode 100644 index 66ca1b241..000000000 --- a/libs/cairo/cairo/src/check-link.c +++ /dev/null @@ -1,24 +0,0 @@ -#define CAIRO_VERSION_H 1 - -#include - -/* get the "real" version info instead of dummy cairo-version.h */ -#undef CAIRO_VERSION_H -#include "../cairo-version.h" - -#include - -int -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/libs/cairo/cairo/src/filterpublic.awk b/libs/cairo/cairo/src/filterpublic.awk deleted file mode 100644 index 98846102b..000000000 --- a/libs/cairo/cairo/src/filterpublic.awk +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/awk -f -# Reads cairo header files on stdin, and outputs a file with defines for -# renaming all public functions to Mozilla-specific names. -# Usage: -# cat *.h | awk -f ./filterpublic.awk | sort > cairo-rename.h -# -# pixman: -# grep '(' ../../libpixman/src/pixman.h | grep '^[a-z]' | sed 's, *(.*$,,' | sed 's,^.* ,,' - -BEGIN { state = "public"; } - -/^cairo_public/ { state = "function"; next; } -/[a-zA-Z_]+/ { - if (state == "function") { - print "#define " $1 " _moz_" $1; - state = "public"; - } - } - -# catch some one-off things -END { -} diff --git a/libs/cairo/cairo/src/moz.build b/libs/cairo/cairo/src/moz.build deleted file mode 100644 index 6d6f7c9d5..000000000 --- a/libs/cairo/cairo/src/moz.build +++ /dev/null @@ -1,266 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -CONFIGURE_SUBST_FILES += ['cairo-features.h'] - -EXPORTS.cairo += [ - '!cairo-features.h', - 'cairo-deprecated.h', - 'cairo-platform.h', - 'cairo-rename.h', - 'cairo-tee.h', - 'cairo-version.h', - 'cairo.h', - 'pixman-rename.h', -] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] not in ('uikit'): - EXPORTS.cairo += [ - 'cairo-pdf.h', - ] - SOURCES += [ - 'cairo-base85-stream.c', - 'cairo-cff-subset.c', - 'cairo-deflate-stream.c', - 'cairo-pdf-operators.c', - 'cairo-pdf-surface.c', - 'cairo-truetype-subset.c', - # cairo-type1-subset.c should be here, but it's only supported on freetype platforms - 'cairo-type1-fallback.c', - 'cairo-type3-glyph-surface.c', - ] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': - EXPORTS.cairo += [ - 'cairo-win32.h', - ] - if CONFIG['MOZ_ENABLE_DWRITE_FONT']: - SOURCES += [ - 'cairo-dwrite-font.cpp', - ] - if CONFIG['MOZ_ENABLE_D2D_SURFACE']: - SOURCES += [ - 'cairo-d2d-surface.cpp', - ] - SOURCES += [ - 'cairo-win32-font.c', - 'cairo-win32-surface.c', - ] - DEFINES['DISABLE_SOME_FLOATING_POINT'] = True - DEFINES['CAIRO_WIN32_STATIC_BUILD'] = True - if CONFIG['NS_PRINTING']: - SOURCES += [ - 'cairo-win32-printing-surface.c', - ] - else: - DEFINES['CAIRO_OMIT_WIN32_PRINTING'] = True -elif CONFIG['MOZ_WIDGET_TOOLKIT'] in {'uikit'}: - EXPORTS.cairo += [ - 'cairo-quartz-image.h', - 'cairo-quartz.h', - ] - SOURCES += [ - 'cairo-quartz-font.c', - 'cairo-quartz-image-surface.c', - 'cairo-quartz-surface.c', - ] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'beos': - EXPORTS.cairo += [ - 'cairo-beos.h', - ] - SOURCES += [ - 'cairo-beos-surface.cpp', - ] -elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: - EXPORTS.cairo += [ - 'cairo-ps.h', - ] - SOURCES += [ - 'cairo-ps-surface.c', - ] - -if CONFIG['MOZ_X11']: - EXPORTS.cairo += [ - 'cairo-xlib-xrender.h', - 'cairo-xlib.h', - ] - SOURCES += [ - 'cairo-xlib-display.c', - 'cairo-xlib-screen.c', - 'cairo-xlib-surface.c', - 'cairo-xlib-visual.c', - ] - -if CONFIG['MOZ_ENABLE_CAIRO_FT']: - EXPORTS.cairo += [ - 'cairo-ft.h', - ] - SOURCES += [ - 'cairo-ft-font.c', - 'cairo-type1-subset.c', - ] - -SOURCES += [ - 'cairo-bentley-ottmann-rectangular.c', # redefinition of '_cairo_bo_trap' - 'cairo-bentley-ottmann-rectilinear.c', # redefinition of '_cairo_bo_trap' - 'cairo-bentley-ottmann.c', # redefinition of '_cairo_bo_trap' - 'cairo-surface-wrapper.c', # redefinition of '_copy_transformed_pattern' -] - -SOURCES += [ - 'cairo-analysis-surface.c', - 'cairo-arc.c', - 'cairo-array.c', - 'cairo-atomic.c', - 'cairo-base64-stream.c', - 'cairo-botor-scan-converter.c', - 'cairo-boxes.c', - 'cairo-cache.c', - 'cairo-clip.c', - 'cairo-color.c', - 'cairo-composite-rectangles.c', - 'cairo-debug.c', - 'cairo-device.c', - 'cairo-fixed.c', - 'cairo-font-face-twin-data.c', - 'cairo-font-face-twin.c', - 'cairo-font-face.c', - 'cairo-font-options.c', - 'cairo-freed-pool.c', - 'cairo-freelist.c', - 'cairo-gstate.c', - 'cairo-hash.c', - 'cairo-hull.c', - 'cairo-image-info.c', - 'cairo-image-surface.c', - 'cairo-lzw.c', - 'cairo-matrix.c', - 'cairo-misc.c', - 'cairo-mutex.c', - 'cairo-observer.c', - 'cairo-output-stream.c', - 'cairo-paginated-surface.c', - 'cairo-path-bounds.c', - 'cairo-path-fill.c', - 'cairo-path-fixed.c', - 'cairo-path-in-fill.c', - 'cairo-path-stroke.c', - 'cairo-path.c', - 'cairo-pattern.c', - 'cairo-pen.c', - 'cairo-polygon.c', - 'cairo-recording-surface.c', - 'cairo-rectangle.c', - 'cairo-rectangular-scan-converter.c', - 'cairo-region.c', - 'cairo-scaled-font-subsets.c', - 'cairo-scaled-font.c', - 'cairo-slope.c', - 'cairo-spans.c', - 'cairo-spline.c', - 'cairo-stroke-style.c', - 'cairo-surface-clipper.c', - 'cairo-surface-fallback.c', - 'cairo-surface-offset.c', - 'cairo-surface-snapshot.c', - 'cairo-surface-subsurface.c', - 'cairo-surface.c', - 'cairo-tee-surface.c', - 'cairo-tor-scan-converter.c', - 'cairo-toy-font-face.c', - 'cairo-traps.c', - 'cairo-unicode.c', - 'cairo-user-font.c', - 'cairo-version.c', - 'cairo-wideint.c', - 'cairo.c', -] - -# We allow warnings for third-party code that can be updated from upstream. -ALLOW_COMPILER_WARNINGS = True - -FINAL_LIBRARY = 'gkmedias' - -DEFINES['PACKAGE_VERSION'] = '"moz"' -DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' - -for var in ('CAIRO_HAS_PTHREAD', '_GNU_SOURCE'): - DEFINES[var] = True - -for var in ('MOZ_TREE_CAIRO', 'MOZ_TREE_PIXMAN'): - if CONFIG[var]: - DEFINES[var] = True - -if CONFIG['GNU_CC']: - DEFINES['HAVE_CXX11_ATOMIC_PRIMITIVES'] = True - # We would normally use autoconf to set these up, using AC_CHECK_SIZEOF. - # But AC_CHECK_SIZEOF requires running programs to determine the sizes, - # and that doesn't work so well with cross-compiling. So instead we - # use these magic macros, available since at least GCC 4.3, to define - # the preprocessor macros cairo wanted from autoconf. - DEFINES['SIZEOF_VOID_P'] = '__SIZEOF_POINTER__' - DEFINES['SIZEOF_INT'] = '__SIZEOF_INT__' - DEFINES['SIZEOF_LONG'] = '__SIZEOF_LONG__' - DEFINES['SIZEOF_LONG_LONG'] = '__SIZEOF_LONG_LONG__' - -# Normally determined by cairo's configure script. -DEFINES['HAVE_UINT64_T'] = True - -if CONFIG['MOZ_TREE_FREETYPE']: - DEFINES['HAVE_FT_LIBRARY_SETLCDFILTER'] = True - DEFINES['FT_LCD_FILTER_H'] = '%s/modules/freetype2/include/freetype/ftlcdfil.h' % TOPSRCDIR - -# Suppress warnings in third-party code. -if CONFIG['_MSC_VER']: - CFLAGS += [ - '-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition - '-wd4018', # '>' : signed/unsigned mismatch - '-wd4047', # different levels of indirection - '-wd4101', # unreferenced local variable - '-wd4133', # 'function' : incompatible types - '-wd4146', # unary minus operator applied to unsigned type - '-wd4311', # 'variable' : pointer truncation from 'type' to 'type' - '-wd4477', # format string '%s' requires an argument of type 'type' - '-wd4996', # The compiler encountered a deprecated declaration. - ] - CXXFLAGS += [ - '-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition - '-wd4018', # '>' : signed/unsigned mismatch - '-wd4146', # unary minus operator applied to unsigned type - '-wd4828', # illegal in the current source character set - '-wd4838', # requires a narrowing conversion -] -if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']: - CFLAGS += [ - '-Wno-enum-compare', - '-Wno-int-to-pointer-cast', - '-Wno-sign-compare', - '-Wno-type-limits', - '-Wno-missing-field-initializers', - '-Wno-conversion', - '-Wno-unused-but-set-variable', - ] - -if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']: - CFLAGS += [ - '-Wno-incompatible-pointer-types', - '-Wno-tautological-compare', - '-Wno-tautological-constant-out-of-range-compare', - '-Wno-error=uninitialized', - ] - -if CONFIG['CLANG_CL']: - CFLAGS += [ - '-Wno-deprecated-register', - '-Wno-macro-redefined', - '-Wno-unused-variable', - ] - -if CONFIG['MOZ_X11']: - CFLAGS += CONFIG['XCFLAGS'] - -if CONFIG['MOZ_ENABLE_CAIRO_FT']: - CFLAGS += CONFIG['CAIRO_FT_CFLAGS'] - CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] diff --git a/libs/cairo/cairo/src/pixman-rename.h b/libs/cairo/cairo/src/pixman-rename.h deleted file mode 100644 index 431cd92e9..000000000 --- a/libs/cairo/cairo/src/pixman-rename.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifdef MOZ_TREE_PIXMAN -#define pixman_composite_glyphs _moz_pixman_composite_glyphs -#define pixman_composite_glyphs_no_mask _moz_pixman_composite_glyphs_no_mask -#define pixman_glyph_cache_create _moz_pixman_glyph_cache_create -#define pixman_glyph_cache_destroy _moz_pixman_glyph_cache_destroy -#define pixman_glyph_cache_freeze _moz_pixman_glyph_cache_freeze -#define pixman_glyph_cache_insert _moz_pixman_glyph_cache_insert -#define pixman_glyph_cache_lookup _moz_pixman_glyph_cache_lookup -#define pixman_glyph_cache_remove _moz_pixman_glyph_cache_remove -#define pixman_glyph_cache_thaw _moz_pixman_glyph_cache_thaw -#define pixman_glyph_get_extents _moz_pixman_glyph_get_extents -#define pixman_glyph_get_mask_format _moz_pixman_glyph_get_mask_format -#define pixman_region_set_static_pointers _moz_pixman_region_set_static_pointers -#define pixman_region_init _moz_pixman_region_init -#define pixman_region_init_rect _moz_pixman_region_init_rect -#define pixman_region_init_rects _moz_pixman_region_init_rects -#define pixman_region_init_with_extents _moz_pixman_region_init_with_extents -#define pixman_region_fini _moz_pixman_region_fini -#define pixman_region_translate _moz_pixman_region_translate -#define pixman_region_copy _moz_pixman_region_copy -#define pixman_region_intersect _moz_pixman_region_intersect -#define pixman_region_union _moz_pixman_region_union -#define pixman_region_union_rect _moz_pixman_region_union_rect -#define pixman_region_subtract _moz_pixman_region_subtract -#define pixman_region_inverse _moz_pixman_region_inverse -#define pixman_region_contains_point _moz_pixman_region_contains_point -#define pixman_region_contains_rectangle _moz_pixman_region_contains_rectangle -#define pixman_region_not_empty _moz_pixman_region_not_empty -#define pixman_region_extents _moz_pixman_region_extents -#define pixman_region_n_rects _moz_pixman_region_n_rects -#define pixman_region_rectangles _moz_pixman_region_rectangles -#define pixman_region_equal _moz_pixman_region_equal -#define pixman_region_selfcheck _moz_pixman_region_selfcheck -#define pixman_region_reset _moz_pixman_region_reset -#define pixman_region_clear _moz_pixman_region_clear -#define pixman_region_print _moz_pixman_region_print -#define pixman_region32_init _moz_pixman_region32_init -#define pixman_region32_init_rect _moz_pixman_region32_init_rect -#define pixman_region32_init_rects _moz_pixman_region32_init_rects -#define pixman_region32_init_with_extents _moz_pixman_region32_init_with_extents -#define pixman_region32_init_from_image _moz_pixman_region32_init_from_image -#define pixman_region32_fini _moz_pixman_region32_fini -#define pixman_region32_translate _moz_pixman_region32_translate -#define pixman_region32_copy _moz_pixman_region32_copy -#define pixman_region32_intersect _moz_pixman_region32_intersect -#define pixman_region32_intersect_rect _moz_pixman_region32_intersect_rect -#define pixman_region32_union _moz_pixman_region32_union -#define pixman_region32_union_rect _moz_pixman_region32_union_rect -#define pixman_region32_subtract _moz_pixman_region32_subtract -#define pixman_region32_inverse _moz_pixman_region32_inverse -#define pixman_region32_contains_point _moz_pixman_region32_contains_point -#define pixman_region32_contains_rectangle _moz_pixman_region32_contains_rectangle -#define pixman_region32_not_empty _moz_pixman_region32_not_empty -#define pixman_region32_extents _moz_pixman_region32_extents -#define pixman_region32_n_rects _moz_pixman_region32_n_rects -#define pixman_region32_rectangles _moz_pixman_region32_rectangles -#define pixman_region32_equal _moz_pixman_region32_equal -#define pixman_region32_selfcheck _moz_pixman_region32_selfcheck -#define pixman_region32_reset _moz_pixman_region32_reset -#define pixman_region32_clear _moz_pixman_region32_clear -#define pixman_region32_print _moz_pixman_region32_print -#define pixman_blt _moz_pixman_blt -#define pixman_fill _moz_pixman_fill -#define pixman_transform_point_3d _moz_pixman_transform_point_3d -#define pixman_version _moz_pixman_version -#define pixman_version_string _moz_pixman_version_string -#define pixman_format_supported_destination _moz_pixman_format_supported_destination -#define pixman_format_supported_source _moz_pixman_format_supported_source -#define pixman_image_create_solid_fill _moz_pixman_image_create_solid_fill -#define pixman_image_create_linear_gradient _moz_pixman_image_create_linear_gradient -#define pixman_image_create_radial_gradient _moz_pixman_image_create_radial_gradient -#define pixman_image_create_conical_gradient _moz_pixman_image_create_conical_gradient -#define pixman_image_create_bits _moz_pixman_image_create_bits -#define pixman_image_ref _moz_pixman_image_ref -#define pixman_image_unref _moz_pixman_image_unref -#define pixman_image_set_clip_region _moz_pixman_image_set_clip_region -#define pixman_image_set_clip_region32 _moz_pixman_image_set_clip_region32 -#define pixman_image_set_has_client_clip _moz_pixman_image_set_has_client_clip -#define pixman_image_set_transform _moz_pixman_image_set_transform -#define pixman_image_set_repeat _moz_pixman_image_set_repeat -#define pixman_image_set_filter _moz_pixman_image_set_filter -#define pixman_image_set_source_clipping _moz_pixman_image_set_source_clipping -#define pixman_image_set_alpha_map _moz_pixman_image_set_alpha_map -#define pixman_image_set_component_alpha _moz_pixman_image_set_component_alpha -#define pixman_image_set_accessors _moz_pixman_image_set_accessors -#define pixman_image_set_indexed _moz_pixman_image_set_indexed -#define pixman_image_get_data _moz_pixman_image_get_data -#define pixman_image_get_width _moz_pixman_image_get_width -#define pixman_image_get_height _moz_pixman_image_get_height -#define pixman_image_get_stride _moz_pixman_image_get_stride -#define pixman_image_get_depth _moz_pixman_image_get_depth -#define pixman_image_fill_rectangles _moz_pixman_image_fill_rectangles -#define pixman_compute_composite_region _moz_pixman_compute_composite_region -#define pixman_image_composite _moz_pixman_image_composite -#define pixman_sample_ceil_y _moz_pixman_sample_ceil_y -#define pixman_sample_floor_y _moz_pixman_sample_floor_y -#define pixman_edge_step _moz_pixman_edge_step -#define pixman_edge_init _moz_pixman_edge_init -#define pixman_line_fixed_edge_init _moz_pixman_line_fixed_edge_init -#define pixman_rasterize_edges _moz_pixman_rasterize_edges -#define pixman_add_traps _moz_pixman_add_traps -#define pixman_add_trapezoids _moz_pixman_add_trapezoids -#define pixman_add_triangles _moz_pixman_add_triangles -#define pixman_composite_trapezoids _moz_pixman_composite_trapezoids -#define pixman_composite_triangles _moz_pixman_composite_triangles -#define pixman_rasterize_trapezoid _moz_pixman_rasterize_trapezoid -#define pixman_disable_out_of_bounds_workaround _moz_pixman_disable_out_of_bounds_workaround -#define pixman_f_transform_bounds _moz_pixman_f_transform_bounds -#define pixman_f_transform_from_pixman_transform _moz_pixman_f_transform_from_pixman_transform -#define pixman_f_transform_init_identity _moz_pixman_f_transform_init_identity -#define pixman_f_transform_init_rotate _moz_pixman_f_transform_init_rotate -#define pixman_f_transform_init_scale _moz_pixman_f_transform_init_scale -#define pixman_f_transform_init_translate _moz_pixman_f_transform_init_translate -#define pixman_f_transform_invert _moz_pixman_f_transform_invert -#define pixman_f_transform_multiply _moz_pixman_f_transform_multiply -#define pixman_f_transform_point _moz_pixman_f_transform_point -#define pixman_f_transform_point_3d _moz_pixman_f_transform_point_3d -#define pixman_f_transform_rotate _moz_pixman_f_transform_rotate -#define pixman_f_transform_scale _moz_pixman_f_transform_scale -#define pixman_f_transform_translate _moz_pixman_f_transform_translate -#define pixman_image_composite32 _moz_pixman_image_composite32 -#define pixman_image_fill_boxes _moz_pixman_image_fill_boxes -#define pixman_image_get_component_alpha _moz_pixman_image_get_component_alpha -#define pixman_image_get_destroy_data _moz_pixman_image_get_destroy_data -#define pixman_image_get_format _moz_pixman_image_get_format -#define pixman_image_set_destroy_function _moz_pixman_image_set_destroy_function -#define pixman_region_init_from_image _moz_pixman_region_init_from_image -#define pixman_region_intersect_rect _moz_pixman_region_intersect_rect -#define pixman_transform_bounds _moz_pixman_transform_bounds -#define pixman_transform_from_pixman_f_transform _moz_pixman_transform_from_pixman_f_transform -#define pixman_transform_init_identity _moz_pixman_transform_init_identity -#define pixman_transform_init_rotate _moz_pixman_transform_init_rotate -#define pixman_transform_init_scale _moz_pixman_transform_init_scale -#define pixman_transform_init_translate _moz_pixman_transform_init_translate -#define pixman_transform_invert _moz_pixman_transform_invert -#define pixman_transform_is_identity _moz_pixman_transform_is_identity -#define pixman_transform_is_int_translate _moz_pixman_transform_is_int_translate -#define pixman_transform_is_inverse _moz_pixman_transform_is_inverse -#define pixman_transform_is_scale _moz_pixman_transform_is_scale -#define pixman_transform_multiply _moz_pixman_transform_multiply -#define pixman_transform_point _moz_pixman_transform_point -#define pixman_transform_rotate _moz_pixman_transform_rotate -#define pixman_transform_scale _moz_pixman_transform_scale -#define pixman_transform_translate _moz_pixman_transform_translate -#endif diff --git a/libs/cairo/cairo/src/test-fallback-surface.c b/libs/cairo/cairo/src/test-fallback-surface.c deleted file mode 100644 index ff07bc81e..000000000 --- a/libs/cairo/cairo/src/test-fallback-surface.c +++ /dev/null @@ -1,206 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to test a mythical backend that uses nothing but - * fallbacks. - * - * The defining feature of this backend is that it has as many %NULL - * backend function entries as possible. The ones that aren't %NULL are - * simply those that must be implemented to have working fallbacks. - * (Except for create_similar---fallbacks would work fine without - * that---I implemented it here in order to create as many surfaces as - * possible of type test_fallback_surface_t during the test suite - * run). - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new backend, starting with the - * minimal all-fallbacks approach and working up gradually from - * there. - */ - -#include "cairoint.h" - -#include "test-fallback-surface.h" -#include "cairo-error-private.h" - -typedef struct _test_fallback_surface { - cairo_surface_t base; - - /* This is a cairo_image_surface to hold the actual contents. */ - cairo_surface_t *backing; -} test_fallback_surface_t; - -static const cairo_surface_backend_t test_fallback_surface_backend; - -slim_hidden_proto (_cairo_test_fallback_surface_create); - -cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height) -{ - test_fallback_surface_t *surface; - cairo_surface_t *backing; - - backing = _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status (backing)) - return backing; - - surface = malloc (sizeof (test_fallback_surface_t)); - if (unlikely (surface == NULL)) { - cairo_surface_destroy (backing); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (&surface->base, - &test_fallback_surface_backend, - NULL, /* device */ - content); - - surface->backing = backing; - - return &surface->base; -} -slim_hidden_def (_cairo_test_fallback_surface_create); - -static cairo_surface_t * -_test_fallback_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - assert (CAIRO_CONTENT_VALID (content)); - - return _cairo_test_fallback_surface_create (content, - width, height); -} - -static cairo_status_t -_test_fallback_surface_finish (void *abstract_surface) -{ - test_fallback_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->backing); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_fallback_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_source_image (surface->backing, - image_out, image_extra); -} - -static void -_test_fallback_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->backing, - image, image_extra); -} - -static cairo_status_t -_test_fallback_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_dest_image (surface->backing, - interest_rect, - image_out, - image_rect_out, - image_extra); -} - -static void -_test_fallback_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_dest_image (surface->backing, - interest_rect, - image, - image_rect, - image_extra); -} - -static cairo_status_t -_test_fallback_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - test_fallback_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_test_fallback_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->backing, rectangle); -} - -static const cairo_surface_backend_t test_fallback_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, - _test_fallback_surface_create_similar, - _test_fallback_surface_finish, - _test_fallback_surface_acquire_source_image, - _test_fallback_surface_release_source_image, - _test_fallback_surface_acquire_dest_image, - _test_fallback_surface_release_dest_image, - _test_fallback_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_fallback_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - NULL /* snapshot */ -}; diff --git a/libs/cairo/cairo/src/test-fallback-surface.h b/libs/cairo/cairo/src/test-fallback-surface.h deleted file mode 100644 index c2534c184..000000000 --- a/libs/cairo/cairo/src/test-fallback-surface.h +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef TEST_FALLBACK_SURFACE_H -#define TEST_FALLBACK_SURFACE_H - -#include "cairo.h" - -CAIRO_BEGIN_DECLS - -cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height); - -CAIRO_END_DECLS - -#endif /* TEST_FALLBACK_SURFACE_H */ diff --git a/libs/cairo/cairo/src/test-meta-surface.c b/libs/cairo/cairo/src/test-meta-surface.c deleted file mode 100644 index 305174eb5..000000000 --- a/libs/cairo/cairo/src/test-meta-surface.c +++ /dev/null @@ -1,311 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to help exercise the meta-surface paths in cairo. - * - * The defining feature of this backend is that it uses a meta surface - * to record all operations, and then replays everything to an image - * surface. - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new meta-surface-based - * backend. - */ - -#include "cairoint.h" - -#include "test-meta-surface.h" - -#include "cairo-meta-surface-private.h" - -typedef struct _test_meta_surface { - cairo_surface_t base; - - /* This is a cairo_meta_surface to record all operations. */ - cairo_surface_t *meta; - - /* And this is a cairo_image_surface to hold the final result. */ - cairo_surface_t *image; - - cairo_bool_t image_reflects_meta; -} test_meta_surface_t; - -static const cairo_surface_backend_t test_meta_surface_backend; - -static cairo_int_status_t -_test_meta_surface_show_page (void *abstract_surface); - -cairo_surface_t * -_cairo_test_meta_surface_create (cairo_content_t content, - int width, - int height) -{ - test_meta_surface_t *surface; - cairo_status_t status; - - surface = malloc (sizeof (test_meta_surface_t)); - if (unlikely (surface == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_surface_init (&surface->base, &test_meta_surface_backend, - content); - - surface->meta = _cairo_meta_surface_create (content, width, height); - status = cairo_surface_status (surface->meta); - if (status) - goto FAIL_CLEANUP_SURFACE; - - surface->image = _cairo_image_surface_create_with_content (content, - width, height); - status = cairo_surface_status (surface->image); - if (status) - goto FAIL_CLEANUP_META; - - surface->image_reflects_meta = FALSE; - - return &surface->base; - - FAIL_CLEANUP_META: - cairo_surface_destroy (surface->meta); - FAIL_CLEANUP_SURFACE: - free (surface); - FAIL: - return _cairo_surface_create_in_error (status); -} - -static cairo_status_t -_test_meta_surface_finish (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->meta); - cairo_surface_destroy (surface->image); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_meta_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_meta_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (! surface->image_reflects_meta) { - status = _test_meta_surface_show_page (abstract_surface); - if (status) - return status; - } - - return _cairo_surface_acquire_source_image (surface->image, - image_out, image_extra); -} - -static void -_test_meta_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_meta_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->image, - image, image_extra); -} - -static cairo_int_status_t -_test_meta_surface_show_page (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->image_reflects_meta) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_meta_surface_replay (surface->meta, surface->image); - if (status) - return status; - - surface->image_reflects_meta = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_test_meta_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - test_meta_surface_t *surface = abstract_surface; - - return _cairo_surface_intersect_clip_path (surface->meta, - path, fill_rule, - tolerance, antialias); -} - -static cairo_int_status_t -_test_meta_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_get_extents (surface->image, rectangle); -} - -static cairo_int_status_t -_test_meta_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_paint (surface->meta, op, source, extents); -} - -static cairo_int_status_t -_test_meta_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_mask (surface->meta, op, source, mask, extents); -} - -static cairo_int_status_t -_test_meta_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_stroke (surface->meta, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, extents); -} - -static cairo_int_status_t -_test_meta_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_fill (surface->meta, op, source, - path, fill_rule, - tolerance, antialias, extents); -} - -static cairo_bool_t -_test_meta_surface_has_show_text_glyphs (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - - return cairo_surface_has_show_text_glyphs (surface->meta); -} - -static cairo_int_status_t -_test_meta_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_show_text_glyphs (surface->meta, op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - scaled_font, extents); -} - - -static cairo_surface_t * -_test_meta_surface_snapshot (void *abstract_other) -{ - test_meta_surface_t *other = abstract_other; - - return _cairo_surface_snapshot (other->meta); -} - -static const cairo_surface_backend_t test_meta_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, - NULL, /* create_similar */ - _test_meta_surface_finish, - _test_meta_surface_acquire_source_image, - _test_meta_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - _test_meta_surface_show_page, - NULL, /* set_clip_region */ - _test_meta_surface_intersect_clip_path, - _test_meta_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _test_meta_surface_paint, - _test_meta_surface_mask, - _test_meta_surface_stroke, - _test_meta_surface_fill, - NULL, /* show_glyphs */ - _test_meta_surface_snapshot, - NULL, /* is_similar */ - NULL, /* reset */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _test_meta_surface_has_show_text_glyphs, - _test_meta_surface_show_text_glyphs -}; diff --git a/libs/cairo/cairo/src/test-meta-surface.h b/libs/cairo/cairo/src/test-meta-surface.h deleted file mode 100644 index 78b7a5752..000000000 --- a/libs/cairo/cairo/src/test-meta-surface.h +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef TEST_META_SURFACE_H -#define TEST_META_SURFACE_H - -#include "cairo.h" - -CAIRO_BEGIN_DECLS - -cairo_surface_t * -_cairo_test_meta_surface_create (cairo_content_t content, - int width, - int height); - -CAIRO_END_DECLS - -#endif /* TEST_META_SURFACE_H */ diff --git a/libs/cairo/cairo/src/test-paginated-surface.c b/libs/cairo/cairo/src/test-paginated-surface.c deleted file mode 100644 index 5d09308f4..000000000 --- a/libs/cairo/cairo/src/test-paginated-surface.c +++ /dev/null @@ -1,260 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to help exercise the paginated-surface paths in cairo. - * - * The defining feature of this backend is that it uses a paginated - * surface to record all operations, and then replays everything to an - * image surface. - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new paginated-surface-based - * backend. - */ - -#include "cairoint.h" - -#include "test-paginated-surface.h" - -#include "cairo-error-private.h" -#include "cairo-paginated-private.h" - -typedef struct _test_paginated_surface { - cairo_surface_t base; - cairo_surface_t *target; - cairo_paginated_mode_t paginated_mode; -} test_paginated_surface_t; - -static const cairo_surface_backend_t test_paginated_surface_backend; -static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend; - -cairo_surface_t * -_cairo_test_paginated_surface_create (cairo_surface_t *target) -{ - cairo_status_t status; - cairo_surface_t *paginated; - test_paginated_surface_t *surface; - - status = cairo_surface_status (target); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - surface = malloc (sizeof (test_paginated_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &test_paginated_surface_backend, - NULL, /* device */ - target->content); - - surface->target = cairo_surface_reference (target); - - paginated = _cairo_paginated_surface_create (&surface->base, - target->content, - &test_paginated_surface_paginated_backend); - status = paginated->status; - if (status == CAIRO_STATUS_SUCCESS) { - /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); - return paginated; - } - - cairo_surface_destroy (target); - free (surface); - return _cairo_surface_create_in_error (status); -} - -static cairo_status_t -_test_paginated_surface_finish (void *abstract_surface) -{ - test_paginated_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->target); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_test_paginated_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_paginated_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->target, rectangle); -} - -static cairo_int_status_t -_test_paginated_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_paint (surface->target, op, source, clip); -} - -static cairo_int_status_t -_test_paginated_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_mask (surface->target, - op, source, mask, clip); -} - -static cairo_int_status_t -_test_paginated_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_stroke (surface->target, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); -} - -static cairo_int_status_t -_test_paginated_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_fill (surface->target, op, source, - path, fill_rule, - tolerance, antialias, - clip); -} - -static cairo_bool_t -_test_paginated_surface_has_show_text_glyphs (void *abstract_surface) -{ - test_paginated_surface_t *surface = abstract_surface; - - return cairo_surface_has_show_text_glyphs (surface->target); -} - -static cairo_int_status_t -_test_paginated_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - test_paginated_surface_t *surface = abstract_surface; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_STATUS_SUCCESS; - - return _cairo_surface_show_text_glyphs (surface->target, op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); -} - - -static void -_test_paginated_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t mode) -{ - test_paginated_surface_t *surface = abstract_surface; - - surface->paginated_mode = mode; -} - -static const cairo_surface_backend_t test_paginated_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, - - /* Since we are a paginated user, we get to regard most of the - * surface backend interface as historical cruft and ignore it. */ - - NULL, /* create_similar */ - _test_paginated_surface_finish, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_paginated_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here is the more "modern" section of the surface backend - * interface which is mostly just drawing functions */ - - _test_paginated_surface_paint, - _test_paginated_surface_mask, - _test_paginated_surface_stroke, - _test_paginated_surface_fill, - NULL, /* replaced by show_text_glyphs */ - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - _test_paginated_surface_has_show_text_glyphs, - _test_paginated_surface_show_text_glyphs -}; - -static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = { - NULL, /* start_page */ - _test_paginated_surface_set_paginated_mode -}; diff --git a/libs/cairo/cairo/src/test-paginated-surface.h b/libs/cairo/cairo/src/test-paginated-surface.h deleted file mode 100644 index 5f2d1d593..000000000 --- a/libs/cairo/cairo/src/test-paginated-surface.h +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#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/libs/cairo/moz.build b/libs/cairo/moz.build index 2aed0f42f..347846347 100644 --- a/libs/cairo/moz.build +++ b/libs/cairo/moz.build @@ -3,4 +3,201 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -DIRS += ['cairo/src'] +CONFIGURE_SUBST_FILES += ['src/cairo-features.h'] + +DEFINES['PACKAGE_VERSION'] = '"moz"' +DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' + +for var in ('CAIRO_HAS_PTHREAD', '_GNU_SOURCE'): + DEFINES[var] = True + +for var in ('MOZ_TREE_CAIRO', 'MOZ_TREE_PIXMAN'): + if CONFIG[var]: + DEFINES[var] = True + +# Normally determined by cairo's configure script. +DEFINES['HAVE_UINT64_T'] = True + +if CONFIG['MOZ_TREE_FREETYPE']: + DEFINES['HAVE_FT_LIBRARY_SETLCDFILTER'] = True + DEFINES['FT_LCD_FILTER_H'] = '%s/modules/freetype2/include/freetype/ftlcdfil.h' % TOPSRCDIR + +EXPORTS.cairo += [ + '!src/cairo-features.h', + 'src/cairo-deprecated.h', + 'src/cairo-pdf.h', + 'src/cairo-platform.h', + 'src/cairo-rename.h', + 'src/cairo-tee.h', + 'src/cairo-version.h', + 'src/cairo.h', + 'src/pixman-rename.h', +] + +SOURCES += [ + 'src/cairo-analysis-surface.c', + 'src/cairo-arc.c', + 'src/cairo-array.c', + 'src/cairo-atomic.c', + 'src/cairo-base64-stream.c', + 'src/cairo-base85-stream.c', + 'src/cairo-bentley-ottmann-rectangular.c', # redefinition of '_cairo_bo_trap' + 'src/cairo-bentley-ottmann-rectilinear.c', # redefinition of '_cairo_bo_trap' + 'src/cairo-bentley-ottmann.c', # redefinition of '_cairo_bo_trap' + 'src/cairo-botor-scan-converter.c', + 'src/cairo-boxes.c', + 'src/cairo-cache.c', + 'src/cairo-cff-subset.c', + 'src/cairo-clip.c', + 'src/cairo-color.c', + 'src/cairo-composite-rectangles.c', + 'src/cairo-debug.c', + 'src/cairo-deflate-stream.c', + 'src/cairo-device.c', + 'src/cairo-fixed.c', + 'src/cairo-font-face-twin-data.c', + 'src/cairo-font-face-twin.c', + 'src/cairo-font-face.c', + 'src/cairo-font-options.c', + 'src/cairo-freed-pool.c', + 'src/cairo-freelist.c', + 'src/cairo-gstate.c', + 'src/cairo-hash.c', + 'src/cairo-hull.c', + 'src/cairo-image-info.c', + 'src/cairo-image-surface.c', + 'src/cairo-lzw.c', + 'src/cairo-matrix.c', + 'src/cairo-misc.c', + 'src/cairo-mutex.c', + 'src/cairo-observer.c', + 'src/cairo-output-stream.c', + 'src/cairo-paginated-surface.c', + 'src/cairo-path-bounds.c', + 'src/cairo-path-fill.c', + 'src/cairo-path-fixed.c', + 'src/cairo-path-in-fill.c', + 'src/cairo-path-stroke.c', + 'src/cairo-path.c', + 'src/cairo-pattern.c', + 'src/cairo-pdf-operators.c', + 'src/cairo-pdf-surface.c', + 'src/cairo-pen.c', + 'src/cairo-polygon.c', + 'src/cairo-recording-surface.c', + 'src/cairo-rectangle.c', + 'src/cairo-rectangular-scan-converter.c', + 'src/cairo-region.c', + 'src/cairo-scaled-font-subsets.c', + 'src/cairo-scaled-font.c', + 'src/cairo-slope.c', + 'src/cairo-spans.c', + 'src/cairo-spline.c', + 'src/cairo-stroke-style.c', + 'src/cairo-surface-clipper.c', + 'src/cairo-surface-fallback.c', + 'src/cairo-surface-offset.c', + 'src/cairo-surface-snapshot.c', + 'src/cairo-surface-subsurface.c', + 'src/cairo-surface-wrapper.c', # redefinition of '_copy_transformed_pattern' + 'src/cairo-surface.c', + 'src/cairo-tee-surface.c', + 'src/cairo-tor-scan-converter.c', + 'src/cairo-toy-font-face.c', + 'src/cairo-traps.c', + 'src/cairo-truetype-subset.c', + 'src/cairo-type1-fallback.c', +# 'src/cairo-type1-subset.c', should be here, but it's only supported on freetype platforms + 'src/cairo-type3-glyph-surface.c', + 'src/cairo-unicode.c', + 'src/cairo-user-font.c', + 'src/cairo-version.c', + 'src/cairo-wideint.c', + 'src/cairo.c', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + EXPORTS.cairo += ['src/cairo-win32.h'] + DEFINES['DISABLE_SOME_FLOATING_POINT'] = True + DEFINES['CAIRO_WIN32_STATIC_BUILD'] = True + SOURCES += [ + 'src/cairo-win32-font.c', + 'src/cairo-win32-surface.c', + ] + if CONFIG['_MSC_VER']: + CFLAGS += [ + '-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition + '-wd4018', # '>' : signed/unsigned mismatch + '-wd4047', # different levels of indirection + '-wd4101', # unreferenced local variable + '-wd4133', # 'function' : incompatible types + '-wd4146', # unary minus operator applied to unsigned type + '-wd4311', # 'variable' : pointer truncation from 'type' to 'type' + '-wd4477', # format string '%s' requires an argument of type 'type' + '-wd4996', # The compiler encountered a deprecated declaration. + ] + CXXFLAGS += [ + '-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition + '-wd4018', # '>' : signed/unsigned mismatch + '-wd4146', # unary minus operator applied to unsigned type + '-wd4828', # illegal in the current source character set + '-wd4838', # requires a narrowing conversion + ] + if CONFIG['MOZ_ENABLE_DWRITE_FONT']: + SOURCES += ['src/cairo-dwrite-font.cpp'] + if CONFIG['MOZ_ENABLE_D2D_SURFACE']: + SOURCES += ['src/cairo-d2d-surface.cpp'] + if CONFIG['NS_PRINTING']: + SOURCES += ['src/cairo-win32-printing-surface.c'] + else: + DEFINES['CAIRO_OMIT_WIN32_PRINTING'] = True +elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + EXPORTS.cairo += ['src/cairo-ps.h'] + SOURCES += ['src/cairo-ps-surface.c'] + if CONFIG['GNU_CC']: + DEFINES['HAVE_CXX11_ATOMIC_PRIMITIVES'] = True + # We would normally use autoconf to set these up, using AC_CHECK_SIZEOF. + # But AC_CHECK_SIZEOF requires running programs to determine the sizes, + # and that doesn't work so well with cross-compiling. So instead we + # use these magic macros, available since at least GCC 4.3, to define + # the preprocessor macros cairo wanted from autoconf. + DEFINES['SIZEOF_VOID_P'] = '__SIZEOF_POINTER__' + DEFINES['SIZEOF_INT'] = '__SIZEOF_INT__' + DEFINES['SIZEOF_LONG'] = '__SIZEOF_LONG__' + DEFINES['SIZEOF_LONG_LONG'] = '__SIZEOF_LONG_LONG__' + CFLAGS += [ + '-Wno-enum-compare', + '-Wno-int-to-pointer-cast', + '-Wno-sign-compare', + '-Wno-type-limits', + '-Wno-missing-field-initializers', + '-Wno-conversion', + '-Wno-unused-but-set-variable', + ] + if CONFIG['MOZ_X11']: + EXPORTS.cairo += [ + 'src/cairo-xlib-xrender.h', + 'src/cairo-xlib.h', + ] + SOURCES += [ + 'src/cairo-xlib-display.c', + 'src/cairo-xlib-screen.c', + 'src/cairo-xlib-surface.c', + 'src/cairo-xlib-visual.c', + ] + CFLAGS += CONFIG['XCFLAGS'] + if CONFIG['MOZ_ENABLE_CAIRO_FT']: + EXPORTS.cairo += ['src/cairo-ft.h'] + SOURCES += [ + 'src/cairo-ft-font.c', + 'src/cairo-type1-subset.c', + ] + CFLAGS += CONFIG['CAIRO_FT_CFLAGS'] + CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] + +LOCAL_INCLUDES += ['!src'] + +# We allow warnings for third-party code that can be updated from upstream. +ALLOW_COMPILER_WARNINGS = True + +FINAL_LIBRARY = 'gkmedias' diff --git a/libs/cairo/src/cairo-analysis-surface-private.h b/libs/cairo/src/cairo-analysis-surface-private.h new file mode 100644 index 000000000..81832126c --- /dev/null +++ b/libs/cairo/src/cairo-analysis-surface-private.h @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_ANALYSIS_SURFACE_H +#define CAIRO_ANALYSIS_SURFACE_H + +#include "cairoint.h" + +CAIRO_BEGIN_DECLS + +cairo_private cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target); + +cairo_private void +_cairo_analysis_surface_set_ctm (cairo_surface_t *surface, + const cairo_matrix_t *ctm); + +cairo_private void +_cairo_analysis_surface_get_ctm (cairo_surface_t *surface, + cairo_matrix_t *ctm); + +cairo_private cairo_region_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *surface); + +cairo_private cairo_region_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface); + +cairo_private void +_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface, + cairo_box_t *bbox); + +cairo_private cairo_int_status_t +_cairo_analysis_surface_merge_status (cairo_int_status_t status_a, + cairo_int_status_t status_b); + +CAIRO_END_DECLS + +#endif /* CAIRO_ANALYSIS_SURFACE_H */ diff --git a/libs/cairo/src/cairo-analysis-surface.c b/libs/cairo/src/cairo-analysis-surface.c new file mode 100644 index 000000000..2be419994 --- /dev/null +++ b/libs/cairo/src/cairo-analysis-surface.c @@ -0,0 +1,893 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-region-private.h" + +typedef struct { + cairo_surface_t base; + + cairo_surface_t *target; + + cairo_bool_t first_op; + cairo_bool_t has_supported; + cairo_bool_t has_unsupported; + + cairo_region_t supported_region; + cairo_region_t fallback_region; + cairo_box_t page_bbox; + + cairo_bool_t has_ctm; + cairo_matrix_t ctm; + +} cairo_analysis_surface_t; + +cairo_int_status_t +_cairo_analysis_surface_merge_status (cairo_int_status_t status_a, + cairo_int_status_t status_b) +{ + /* fatal errors should be checked and propagated at source */ + assert (! _cairo_status_is_error (status_a)); + assert (! _cairo_status_is_error (status_b)); + + /* return the most important status */ + if (status_a == CAIRO_INT_STATUS_UNSUPPORTED || + status_b == CAIRO_INT_STATUS_UNSUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK || + status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN || + status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + + if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + /* at this point we have checked all the valid internal codes, so... */ + assert (status_a == CAIRO_STATUS_SUCCESS && + status_b == CAIRO_STATUS_SUCCESS); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, + const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *surface_pattern; + cairo_bool_t old_has_ctm; + cairo_matrix_t old_ctm, p2d; + cairo_status_t status; + cairo_surface_t *source; + + assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); + surface_pattern = (const cairo_surface_pattern_t *) pattern; + assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); + + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + + p2d = pattern->matrix; + status = cairo_matrix_invert (&p2d); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm); + surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); + + source = surface_pattern->surface; + if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + source = sub->target; + } + + status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base); + + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + + return status; +} + +static cairo_int_status_t +_add_operation (cairo_analysis_surface_t *surface, + cairo_rectangle_int_t *rect, + cairo_int_status_t backend_status) +{ + cairo_int_status_t status; + cairo_box_t bbox; + + if (rect->width == 0 || rect->height == 0) { + /* Even though the operation is not visible we must be careful + * to not allow unsupported operations to be replayed to the + * backend during CAIRO_PAGINATED_MODE_RENDER */ + if (backend_status == CAIRO_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + { + return CAIRO_STATUS_SUCCESS; + } + else + { + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + } + + _cairo_box_from_rectangle (&bbox, rect); + + if (surface->has_ctm) { + int tx, ty; + + if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) { + rect->x += tx; + rect->y += ty; + } else { + _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, + &bbox, NULL); + + if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { + /* Even though the operation is not visible we must be + * careful to not allow unsupported operations to be + * replayed to the backend during + * CAIRO_PAGINATED_MODE_RENDER */ + if (backend_status == CAIRO_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + { + return CAIRO_STATUS_SUCCESS; + } + else + { + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + } + + _cairo_box_round_to_rectangle (&bbox, rect); + } + } + + if (surface->first_op) { + surface->first_op = FALSE; + surface->page_bbox = bbox; + } else { + if (bbox.p1.x < surface->page_bbox.p1.x) + surface->page_bbox.p1.x = bbox.p1.x; + if (bbox.p1.y < surface->page_bbox.p1.y) + surface->page_bbox.p1.y = bbox.p1.y; + if (bbox.p2.x > surface->page_bbox.p2.x) + surface->page_bbox.p2.x = bbox.p2.x; + if (bbox.p2.y > surface->page_bbox.p2.y) + surface->page_bbox.p2.y = bbox.p2.y; + } + + /* If the operation is completely enclosed within the fallback + * region there is no benefit in emitting a native operation as + * the fallback image will be painted on top. + */ + if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { + /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates + * that the backend only supports this operation if the + * transparency removed. If the extents of this operation does + * not intersect any other native operation, the operation is + * natively supported and the backend will blend the + * transparency into the white background. + */ + if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) + backend_status = CAIRO_STATUS_SUCCESS; + } + + if (backend_status == CAIRO_STATUS_SUCCESS) { + /* Add the operation to the supported region. Operations in + * this region will be emitted as native operations. + */ + surface->has_supported = TRUE; + return cairo_region_union_rectangle (&surface->supported_region, rect); + } + + /* Add the operation to the unsupported region. This region will + * be painted as an image after all native operations have been + * emitted. + */ + surface->has_unsupported = TRUE; + status = cairo_region_union_rectangle (&surface->fallback_region, rect); + + /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate + * unsupported operations to the recording surface as using + * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to + * invoke the cairo-surface-fallback path then return + * CAIRO_STATUS_SUCCESS. + */ + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + else + return status; +} + +static cairo_status_t +_cairo_analysis_surface_finish (void *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + _cairo_region_fini (&surface->supported_region); + _cairo_region_fini (&surface->fallback_region); + + cairo_surface_destroy (surface->target); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_analysis_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_analysis_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static void +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +{ + const cairo_rectangle_int_t *clip_extents; + cairo_bool_t is_empty; + + clip_extents = NULL; + if (clip != NULL) + clip_extents = _cairo_clip_get_extents (clip); + + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (extents, clip_extents); +} + +static void +_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + is_empty = _cairo_surface_get_extents (&surface->base, extents); + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + + _cairo_pattern_get_extents (source, &source_extents); + is_empty = _cairo_rectangle_intersect (extents, &source_extents); + } + + _rectangle_intersect_clip (extents, clip); +} + +static cairo_int_status_t +_cairo_analysis_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->paint == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->paint (surface->target, + op, source, clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + cairo_rectangle_int_t extents; + cairo_bool_t is_empty; + + if (surface->target->backend->mask == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->mask (surface->target, + op, source, mask, clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { + cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; + if (_cairo_surface_is_recording (surface_pattern->surface)) { + backend_source_status = + _analyze_recording_surface_pattern (surface, source); + if (_cairo_status_is_error (backend_source_status)) + return backend_source_status; + } + } + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; + if (_cairo_surface_is_recording (surface_pattern->surface)) { + backend_mask_status = + _analyze_recording_surface_pattern (surface, mask); + if (_cairo_status_is_error (backend_mask_status)) + return backend_mask_status; + } + } + + backend_status = + _cairo_analysis_surface_merge_status (backend_source_status, + backend_mask_status); + } + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + _cairo_pattern_get_extents (mask, &mask_extents); + is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t backend_status; + cairo_rectangle_int_t extents; + cairo_bool_t is_empty; + + if (surface->target->backend->stroke == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->stroke (surface->target, op, + source, path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + /* If the backend can handle the stroke, then mark the approximate + * extents of the operation. However, if we need to fallback in order + * to draw the stroke, then ensure that the fallback is as tight as + * possible -- both to minimise output file size and to ensure good + * quality printed output for neighbouring regions. + */ + if (backend_status == CAIRO_STATUS_SUCCESS) { + _cairo_path_fixed_approximate_stroke_extents (path, + style, ctm, + &mask_extents); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; + } + + is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t backend_status; + cairo_rectangle_int_t extents; + cairo_bool_t is_empty; + + if (surface->target->backend->fill == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->fill (surface->target, op, + source, path, fill_rule, + tolerance, antialias, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + /* We want speed for the likely case where the operation can be + * performed natively, but accuracy if we have to resort to + * using images. + */ + if (backend_status == CAIRO_STATUS_SUCCESS) { + _cairo_path_fixed_approximate_fill_extents (path, &mask_extents); + } else { + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + } + is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status, backend_status; + cairo_rectangle_int_t extents, glyph_extents; + cairo_bool_t is_empty; + + /* Adapted from _cairo_surface_show_glyphs */ + if (surface->target->backend->show_glyphs != NULL) { + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip, + remaining_glyphs); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + else if (surface->target->backend->show_text_glyphs != NULL) + { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + else + { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + if (_cairo_operator_bounded_by_mask (op)) { + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_bool_t +_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface) +{ + cairo_analysis_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_cairo_analysis_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status, backend_status; + cairo_rectangle_int_t extents, glyph_extents; + cairo_bool_t is_empty; + + /* Adapted from _cairo_surface_show_glyphs */ + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + if (surface->target->backend->show_text_glyphs != NULL) { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (_cairo_status_is_error (backend_status)) + return backend_status; + } + if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->target->backend->show_glyphs != NULL) + { + int remaining_glyphs = num_glyphs; + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip, + &remaining_glyphs); + if (_cairo_status_is_error (backend_status)) + return backend_status; + + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (remaining_glyphs == 0) + backend_status = CAIRO_STATUS_SUCCESS; + } + + if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + backend_status = _analyze_recording_surface_pattern (surface, source); + + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); + + if (_cairo_operator_bounded_by_mask (op)) { + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static const cairo_surface_backend_t cairo_analysis_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + NULL, /* create_similar */ + _cairo_analysis_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_analysis_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _cairo_analysis_surface_paint, + _cairo_analysis_surface_mask, + _cairo_analysis_surface_stroke, + _cairo_analysis_surface_fill, + _cairo_analysis_surface_show_glyphs, + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + _cairo_analysis_surface_has_show_text_glyphs, + _cairo_analysis_surface_show_text_glyphs +}; + +cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target) +{ + cairo_analysis_surface_t *surface; + cairo_status_t status; + + status = target->status; + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = malloc (sizeof (cairo_analysis_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + /* I believe the content type here is truly arbitrary. I'm quite + * sure nothing will ever use this value. */ + _cairo_surface_init (&surface->base, + &cairo_analysis_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + cairo_matrix_init_identity (&surface->ctm); + surface->has_ctm = FALSE; + + surface->target = cairo_surface_reference (target); + surface->first_op = TRUE; + surface->has_supported = FALSE; + surface->has_unsupported = FALSE; + + _cairo_region_init (&surface->supported_region); + _cairo_region_init (&surface->fallback_region); + + surface->page_bbox.p1.x = 0; + surface->page_bbox.p1.y = 0; + surface->page_bbox.p2.x = 0; + surface->page_bbox.p2.y = 0; + + return &surface->base; +} + +void +_cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, + const cairo_matrix_t *ctm) +{ + cairo_analysis_surface_t *surface; + + if (abstract_surface->status) + return; + + surface = (cairo_analysis_surface_t *) abstract_surface; + + surface->ctm = *ctm; + surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); +} + +void +_cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface, + cairo_matrix_t *ctm) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + *ctm = surface->ctm; +} + + +cairo_region_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->supported_region; +} + +cairo_region_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->fallback_region; +} + +cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->has_supported; +} + +cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->has_unsupported; +} + +void +_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, + cairo_box_t *bbox) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + *bbox = surface->page_bbox; +} + +/* null surface type: a surface that does nothing (has no side effects, yay!) */ + +static cairo_int_status_t +_return_success (void) +{ + return CAIRO_STATUS_SUCCESS; +} + +/* These typedefs are just to silence the compiler... */ +typedef cairo_int_status_t +(*_paint_func) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +typedef cairo_int_status_t +(*_mask_func) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +typedef cairo_int_status_t +(*_stroke_func) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +typedef cairo_int_status_t +(*_fill_func) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +typedef cairo_int_status_t +(*_show_glyphs_func) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +static const cairo_surface_backend_t cairo_null_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + + NULL, /* create_similar */ + NULL, /* finish */ + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + NULL, /* get_extents */ + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + (_paint_func) _return_success, /* paint */ + (_mask_func) _return_success, /* mask */ + (_stroke_func) _return_success, /* stroke */ + (_fill_func) _return_success, /* fill */ + (_show_glyphs_func) _return_success, /* show_glyphs */ + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* has_show_text_glyphs */ + NULL /* show_text_glyphs */ +}; + +cairo_surface_t * +cairo_null_surface_create (cairo_content_t content) +{ + cairo_surface_t *surface; + + surface = malloc (sizeof (cairo_surface_t)); + if (unlikely (surface == NULL)) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (surface, + &cairo_null_surface_backend, + NULL, /* device */ + content); + + return surface; +} diff --git a/libs/cairo/src/cairo-arc-private.h b/libs/cairo/src/cairo-arc-private.h new file mode 100644 index 000000000..e8dcc1394 --- /dev/null +++ b/libs/cairo/src/cairo-arc-private.h @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_ARC_PRIVATE_H +#define CAIRO_ARC_PRIVATE_H + +#include "cairoint.h" + +cairo_private void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2); + +cairo_private void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2); + +#endif /* CAIRO_ARC_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-arc.c b/libs/cairo/src/cairo-arc.c new file mode 100644 index 000000000..54a0723d0 --- /dev/null +++ b/libs/cairo/src/cairo-arc.c @@ -0,0 +1,260 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-arc-private.h" + +/* Spline deviation from the circle in radius would be given by: + + error = sqrt (x**2 + y**2) - 1 + + A simpler error function to work with is: + + e = x**2 + y**2 - 1 + + From "Good approximation of circles by curvature-continuous Bezier + curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric + Design 8 (1990) 22-41, we learn: + + abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) + + and + abs (error) =~ 1/2 * e + + Of course, this error value applies only for the particular spline + approximation that is used in _cairo_gstate_arc_segment. +*/ +static double +_arc_error_normalized (double angle) +{ + return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); +} + +static double +_arc_max_angle_for_tolerance_normalized (double tolerance) +{ + double angle, error; + int i; + + /* Use table lookup to reduce search time in most cases. */ + struct { + double angle; + double error; + } table[] = { + { M_PI / 1.0, 0.0185185185185185036127 }, + { M_PI / 2.0, 0.000272567143730179811158 }, + { M_PI / 3.0, 2.38647043651461047433e-05 }, + { M_PI / 4.0, 4.2455377443222443279e-06 }, + { M_PI / 5.0, 1.11281001494389081528e-06 }, + { M_PI / 6.0, 3.72662000942734705475e-07 }, + { M_PI / 7.0, 1.47783685574284411325e-07 }, + { M_PI / 8.0, 6.63240432022601149057e-08 }, + { M_PI / 9.0, 3.2715520137536980553e-08 }, + { M_PI / 10.0, 1.73863223499021216974e-08 }, + { M_PI / 11.0, 9.81410988043554039085e-09 }, + }; + int table_size = ARRAY_LENGTH (table); + + for (i = 0; i < table_size; i++) + if (table[i].error < tolerance) + return table[i].angle; + + ++i; + do { + angle = M_PI / i++; + error = _arc_error_normalized (angle); + } while (error > tolerance); + + return angle; +} + +static int +_arc_segments_needed (double angle, + double radius, + cairo_matrix_t *ctm, + double tolerance) +{ + double major_axis, max_angle; + + /* the error is amplified by at most the length of the + * major axis of the circle; see cairo-pen.c for a more detailed analysis + * of this. */ + major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius); + max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis); + + return ceil (fabs (angle) / max_angle); +} + +/* We want to draw a single spline approximating a circular arc radius + R from angle A to angle B. Since we want a symmetric spline that + matches the endpoints of the arc in position and slope, we know + that the spline control points must be: + + (R * cos(A), R * sin(A)) + (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) + (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) + (R * cos(B), R * sin(B)) + + for some value of h. + + "Approximation of circular arcs by cubic poynomials", Michael + Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides + various values of h along with error analysis for each. + + From that paper, a very practical value of h is: + + h = 4/3 * tan(angle/4) + + This value does not give the spline with minimal error, but it does + provide a very good approximation, (6th-order convergence), and the + error expression is quite simple, (see the comment for + _arc_error_normalized). +*/ +static void +_cairo_arc_segment (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_A, + double angle_B) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin (angle_A); + r_cos_A = radius * cos (angle_A); + r_sin_B = radius * sin (angle_B); + r_cos_B = radius * cos (angle_B); + + h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); + + cairo_curve_to (cr, + xc + r_cos_A - h * r_sin_A, + yc + r_sin_A + h * r_cos_A, + xc + r_cos_B + h * r_sin_B, + yc + r_sin_B - h * r_cos_B, + xc + r_cos_B, + yc + r_sin_B); +} + +static void +_cairo_arc_in_direction (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_min, + double angle_max, + cairo_direction_t dir) +{ + if (cairo_status (cr)) + return; + + while (angle_max - angle_min > 4 * M_PI) + angle_max -= 2 * M_PI; + + /* Recurse if drawing arc larger than pi */ + if (angle_max - angle_min > M_PI) { + double angle_mid = angle_min + (angle_max - angle_min) / 2.0; + if (dir == CAIRO_DIRECTION_FORWARD) { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + } else { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + } + } else if (angle_max != angle_min) { + cairo_matrix_t ctm; + int i, segments; + double angle, angle_step; + + cairo_get_matrix (cr, &ctm); + segments = _arc_segments_needed (angle_max - angle_min, + radius, &ctm, + cairo_get_tolerance (cr)); + angle_step = (angle_max - angle_min) / (double) segments; + + if (dir == CAIRO_DIRECTION_FORWARD) { + angle = angle_min; + } else { + angle = angle_max; + angle_step = - angle_step; + } + + for (i = 0; i < segments; i++, angle += angle_step) { + _cairo_arc_segment (cr, xc, yc, + radius, + angle, + angle + angle_step); + } + } +} + +/** + * _cairo_arc_path + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Compute a path for the given arc and append it onto the current + * path within @cr. The arc will be accurate within the current + * tolerance and given the current transformation. + **/ +void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle1, angle2, + CAIRO_DIRECTION_FORWARD); +} + +/** + * _cairo_arc_path_negative: + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * @ctm: the current transformation matrix + * @tolerance: the current tolerance value + * @path: the path onto which the arc will be appended + * + * Compute a path for the given arc (defined in the negative + * direction) and append it onto the current path within @cr. The arc + * will be accurate within the current tolerance and given the current + * transformation. + **/ +void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle2, angle1, + CAIRO_DIRECTION_REVERSE); +} diff --git a/libs/cairo/src/cairo-array.c b/libs/cairo/src/cairo-array.c new file mode 100644 index 000000000..442540c58 --- /dev/null +++ b/libs/cairo/src/cairo-array.c @@ -0,0 +1,529 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/** + * _cairo_array_init: + * + * Initialize a new #cairo_array_t object to store objects each of size + * @element_size. + * + * The #cairo_array_t object provides grow-by-doubling storage. It + * never interprets the data passed to it, nor does it provide any + * sort of callback mechanism for freeing resources held onto by + * stored objects. + * + * When finished using the array, _cairo_array_fini() should be + * called to free resources allocated during use of the array. + **/ +void +_cairo_array_init (cairo_array_t *array, int element_size) +{ + array->size = 0; + array->num_elements = 0; + array->element_size = element_size; + array->elements = NULL; + + array->is_snapshot = FALSE; + +} + +/** + * _cairo_array_init_snapshot: + * @array: A #cairo_array_t to be initialized as a snapshot + * @other: The #cairo_array_t from which to create the snapshot + * + * Initialize @array as an immutable copy of @other. It is an error to + * call an array-modifying function (other than _cairo_array_fini) on + * @array after calling this function. + **/ +void +_cairo_array_init_snapshot (cairo_array_t *array, + const cairo_array_t *other) +{ + array->size = other->size; + array->num_elements = other->num_elements; + array->element_size = other->element_size; + array->elements = other->elements; + + array->is_snapshot = TRUE; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); +} + +/** + * _cairo_array_fini: + * @array: A #cairo_array_t + * + * Free all resources associated with @array. After this call, @array + * should not be used again without a subsequent call to + * _cairo_array_init() again first. + **/ +void +_cairo_array_fini (cairo_array_t *array) +{ + if (array->is_snapshot) + return; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + if (array->elements) { + free (* array->elements); + free (array->elements); + } +} + +/** + * _cairo_array_grow_by: + * @array: a #cairo_array_t + * + * Increase the size of @array (if needed) so that there are at least + * @additional free spaces in the array. The actual size of the array + * is always increased by doubling as many times as necessary. + **/ +cairo_status_t +_cairo_array_grow_by (cairo_array_t *array, unsigned int additional) +{ + char *new_elements; + unsigned int old_size = array->size; + unsigned int required_size = array->num_elements + additional; + unsigned int new_size; + + assert (! array->is_snapshot); + + /* check for integer overflow */ + if (required_size > INT_MAX || required_size < array->num_elements) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (required_size <= old_size) + return CAIRO_STATUS_SUCCESS; + + if (old_size == 0) + new_size = 1; + else + new_size = old_size * 2; + + while (new_size < required_size) + new_size = new_size * 2; + + if (array->elements == NULL) { + array->elements = malloc (sizeof (char *)); + if (unlikely (array->elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *array->elements = NULL; + } + + array->size = new_size; + new_elements = _cairo_realloc_ab (*array->elements, + array->size, array->element_size); + + if (unlikely (new_elements == NULL)) { + array->size = old_size; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *array->elements = new_elements; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_truncate: + * @array: a #cairo_array_t + * + * Truncate size of the array to @num_elements if less than the + * current size. No memory is actually freed. The stored objects + * beyond @num_elements are simply "forgotten". + **/ +void +_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) +{ + assert (! array->is_snapshot); + + if (num_elements < array->num_elements) + array->num_elements = num_elements; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); +} + +/** + * _cairo_array_index: + * @array: a #cairo_array_t + * Returns: A pointer to the object stored at @index. + * + * If the resulting value is assigned to a pointer to an object of the same + * element_size as initially passed to _cairo_array_init() then that + * pointer may be used for further direct indexing with []. For + * example: + * + * + * cairo_array_t array; + * double *values; + * + * _cairo_array_init (&array, sizeof(double)); + * ... calls to _cairo_array_append() here ... + * + * values = _cairo_array_index (&array, 0); + * for (i = 0; i < _cairo_array_num_elements (&array); i++) + * ... use values[i] here ... + * + **/ +void * +_cairo_array_index (cairo_array_t *array, unsigned int index) +{ + /* We allow an index of 0 for the no-elements case. + * This makes for cleaner calling code which will often look like: + * + * elements = _cairo_array_index (array, num_elements); + * for (i=0; i < num_elements; i++) { + * ... use elements[i] here ... + * } + * + * which in the num_elements==0 case gets the NULL pointer here, + * but never dereferences it. + */ + if (index == 0 && array->num_elements == 0) + return NULL; + + assert (index < array->num_elements); + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + return (void *) &(*array->elements)[index * array->element_size]; +} + +/** + * _cairo_array_copy_element: + * @array: a #cairo_array_t + * + * Copy a single element out of the array from index @index into the + * location pointed to by @dst. + **/ +void +_cairo_array_copy_element (cairo_array_t *array, int index, void *dst) +{ + memcpy (dst, _cairo_array_index (array, index), array->element_size); +} + +/** + * _cairo_array_append: + * @array: a #cairo_array_t + * + * Append a single item onto the array by growing the array by at + * least one element, then copying element_size bytes from @element + * into the array. The address of the resulting object within the + * array can be determined with: + * + * _cairo_array_index (array, _cairo_array_num_elements (array) - 1); + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_append (cairo_array_t *array, + const void *element) +{ + assert (! array->is_snapshot); + + return _cairo_array_append_multiple (array, element, 1); +} + +/** + * _cairo_array_append_multiple: + * @array: a #cairo_array_t + * + * Append one or more items onto the array by growing the array by + * @num_elements, then copying @num_elements * element_size bytes from + * @elements into the array. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_append_multiple (cairo_array_t *array, + const void *elements, + int num_elements) +{ + cairo_status_t status; + void *dest; + + assert (! array->is_snapshot); + + status = _cairo_array_allocate (array, num_elements, &dest); + if (unlikely (status)) + return status; + + memcpy (dest, elements, num_elements * array->element_size); + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_allocate: + * @array: a #cairo_array_t + * + * Allocate space at the end of the array for @num_elements additional + * elements, providing the address of the new memory chunk in + * @elements. This memory will be unitialized, but will be accounted + * for in the return value of _cairo_array_num_elements(). + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_allocate (cairo_array_t *array, + unsigned int num_elements, + void **elements) +{ + cairo_status_t status; + + assert (! array->is_snapshot); + + status = _cairo_array_grow_by (array, num_elements); + if (unlikely (status)) + return status; + + assert (array->num_elements + num_elements <= array->size); + + *elements = &(*array->elements)[array->num_elements * array->element_size]; + + array->num_elements += num_elements; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_num_elements: + * @array: a #cairo_array_t + * Returns: The number of elements stored in @array. + * + * This space was left intentionally blank, but gtk-doc filled it. + **/ +int +_cairo_array_num_elements (cairo_array_t *array) +{ + return array->num_elements; +} + +/** + * _cairo_array_size: + * @array: a #cairo_array_t + * Returns: The number of elements for which there is currently space + * allocated in @array. + * + * This space was left intentionally blank, but gtk-doc filled it. + **/ +int +_cairo_array_size (cairo_array_t *array) +{ + return array->size; +} + +/** + * _cairo_user_data_array_init: + * @array: a #cairo_user_data_array_t + * + * Initializes a #cairo_user_data_array_t structure for future + * use. After initialization, the array has no keys. Call + * _cairo_user_data_array_fini() to free any allocated memory + * when done using the array. + **/ +void +_cairo_user_data_array_init (cairo_user_data_array_t *array) +{ + _cairo_array_init (array, sizeof (cairo_user_data_slot_t)); +} + +/** + * _cairo_user_data_array_fini: + * @array: a #cairo_user_data_array_t + * + * Destroys all current keys in the user data array and deallocates + * any memory allocated for the array itself. + **/ +void +_cairo_user_data_array_fini (cairo_user_data_array_t *array) +{ + unsigned int num_slots; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + num_slots = array->num_elements; + if (num_slots) { + cairo_user_data_slot_t *slots; + + slots = _cairo_array_index (array, 0); + do { + if (slots->user_data != NULL && slots->destroy != NULL) + slots->destroy (slots->user_data); + slots++; + } while (--num_slots); + } + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + _cairo_array_fini (array); +} + +/** + * _cairo_user_data_array_get_data: + * @array: a #cairo_user_data_array_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Returns user data previously attached using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key) +{ + int i, num_slots; + cairo_user_data_slot_t *slots; + + /* We allow this to support degenerate objects such as cairo_surface_nil. */ + if (array == NULL) + return NULL; + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) + return slots[i].user_data; + } + + return NULL; +} + +/** + * _cairo_user_data_array_set_data: + * @array: a #cairo_user_data_array_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach + * @destroy: a #cairo_destroy_func_t which will be called when the + * user data array is destroyed or when new user data is attached using the + * same key. + * + * Attaches user data to a user data array. To remove user data, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + cairo_status_t status; + int i, num_slots; + cairo_user_data_slot_t *slots, *slot, new_slot; + + if (user_data) { + new_slot.key = key; + new_slot.user_data = user_data; + new_slot.destroy = destroy; + } else { + new_slot.key = NULL; + new_slot.user_data = NULL; + new_slot.destroy = NULL; + } + + slot = NULL; + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) { + slot = &slots[i]; + if (slot->destroy && slot->user_data) + slot->destroy (slot->user_data); + break; + } + if (user_data && slots[i].user_data == NULL) { + slot = &slots[i]; /* Have to keep searching for an exact match */ + } + } + + if (array->num_elements != 0 && *array->elements == NULL) + abort(); + + if (slot) { + *slot = new_slot; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_array_append (array, &new_slot); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + cairo_user_data_array_t *src) +{ + /* discard any existing user-data */ + if (dst->num_elements != 0) { + _cairo_user_data_array_fini (dst); + _cairo_user_data_array_init (dst); + } + + if (src->num_elements == 0) + return CAIRO_STATUS_SUCCESS; + + return _cairo_array_append_multiple (dst, + _cairo_array_index (src, 0), + src->num_elements); +} + +void +_cairo_user_data_array_foreach (cairo_user_data_array_t *array, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].user_data != NULL) + func (slots[i].key, slots[i].user_data, closure); + } +} diff --git a/libs/cairo/src/cairo-atomic-private.h b/libs/cairo/src/cairo-atomic-private.h new file mode 100644 index 000000000..e14c5fdaf --- /dev/null +++ b/libs/cairo/src/cairo-atomic-private.h @@ -0,0 +1,385 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_ATOMIC_PRIVATE_H +#define CAIRO_ATOMIC_PRIVATE_H + +# include "cairo-compiler-private.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_WIN32_ATOMIC_PRIMITIVES +#include +#endif + +/* The autoconf on OpenBSD 4.5 produces the malformed constant name + * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ +#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__) +# define SIZEOF_VOID_P SIZEOF_VOID__ +#endif + +CAIRO_BEGIN_DECLS + +/* C++11 atomic primitives were designed to be more flexible than the + * __sync_* family of primitives. Despite the name, they are available + * in C as well as C++. The motivating reason for using them is that + * for _cairo_atomic_{int,ptr}_get, the compiler is able to see that + * the load is intended to be atomic, as opposed to the __sync_* + * version, below, where the load looks like a plain load. Having + * the load appear atomic to the compiler is particular important for + * tools like ThreadSanitizer so they don't report false positives on + * memory operations that we intend to be atomic. + */ +#if HAVE_CXX11_ATOMIC_PRIMITIVES + +#define HAS_ATOMIC_OPS 1 + +typedef int cairo_atomic_int_t; + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x) +{ + return __atomic_load_n(x, __ATOMIC_SEQ_CST); +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return __atomic_load_n(x, __ATOMIC_RELAXED); +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + __atomic_store_n(x, val, __ATOMIC_RELAXED); +} + +static cairo_always_inline void * +_cairo_atomic_ptr_get (void **x) +{ + return __atomic_load_n(x, __ATOMIC_SEQ_CST); +} + +# define _cairo_atomic_int_inc(x) ((void) __atomic_fetch_add(x, 1, __ATOMIC_SEQ_CST)) +# define _cairo_atomic_int_dec(x) ((void) __atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST)) +# define _cairo_atomic_int_dec_and_test(x) (__atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST) == 1) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +static cairo_always_inline cairo_bool_t +_cairo_atomic_int_cmpxchg_impl(cairo_atomic_int_t *x, + cairo_atomic_int_t oldv, + cairo_atomic_int_t newv) +{ + cairo_atomic_int_t expected = oldv; + return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +#define _cairo_atomic_int_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_impl(x, oldv, newv) + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_impl(cairo_atomic_int_t *x, + cairo_atomic_int_t oldv, + cairo_atomic_int_t newv) +{ + cairo_atomic_int_t expected = oldv; + (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; +} + +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_return_old_impl(x, oldv, newv) + +static cairo_always_inline cairo_bool_t +_cairo_atomic_ptr_cmpxchg_impl(void **x, void *oldv, void *newv) +{ + void *expected = oldv; + return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_impl(x, oldv, newv) + +static cairo_always_inline void * +_cairo_atomic_ptr_cmpxchg_return_old_impl(void **x, void *oldv, void *newv) +{ + void *expected = oldv; + (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; +} + +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_return_old_impl(x, oldv, newv) + +#endif + +#if HAVE_WIN32_ATOMIC_PRIMITIVES + +#define HAS_ATOMIC_OPS 1 + +typedef volatile long cairo_atomic_int_t; + +# define _cairo_atomic_int_get(x) ((int)*x) +# define _cairo_atomic_int_get_relaxed(x) ((int)*(x)) +# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) +# define _cairo_atomic_ptr_get(x) ((void*)*x) + +# define _cairo_atomic_int_inc(x) ((void) InterlockedIncrement(x)) +# define _cairo_atomic_int_dec(x) ((void) InterlockedDecrement(x)) +# define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement(x) == 0) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) (InterlockedCompareExchange(x, newv, oldv) == oldv) +# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) InterlockedCompareExchange(x, newv, oldv) + +typedef volatile void* cairo_atomic_intptr_t; + +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv) == oldv) +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv)) + +#endif + +#if HAVE_INTEL_ATOMIC_PRIMITIVES + +#define HAS_ATOMIC_OPS 1 + +typedef int cairo_atomic_int_t; + +#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x) +{ + __sync_synchronize (); + return *x; +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return *x; +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + *x = val; +} + +static cairo_always_inline void * +_cairo_atomic_ptr_get (void **x) +{ + __sync_synchronize (); + return *x; +} +#else +# define _cairo_atomic_int_get(x) (*x) +# define _cairo_atomic_int_get_relaxed(x) (*(x)) +# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) +# define _cairo_atomic_ptr_get(x) (*x) +#endif + +# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) +# define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv) +# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + __sync_bool_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) + +# define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_intptr_to_voidptr (__sync_val_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)) + +#endif + +#if HAVE_LIB_ATOMIC_OPS +#include + +#define HAS_ATOMIC_OPS 1 + +typedef AO_t cairo_atomic_int_t; + +# define _cairo_atomic_int_get(x) (AO_load_full (x)) +# define _cairo_atomic_int_get_relaxed(x) (AO_load_full (x)) +# define _cairo_atomic_int_set_relaxed(x, val) (AO_store_full ((x), (val))) + +# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) +# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef unsigned int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef unsigned long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef unsigned long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x)) +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) + +#endif + +#if HAVE_OS_ATOMIC_OPS +#include + +#define HAS_ATOMIC_OPS 1 + +typedef int32_t cairo_atomic_int_t; + +# define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x)) +# define _cairo_atomic_int_get_relaxed(x) (*(x)) +# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) + +# define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x)) +# define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x) + +#if SIZEOF_VOID_P==4 +typedef int32_t cairo_atomic_intptr_t; +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) + +#elif SIZEOF_VOID_P==8 +typedef int64_t cairo_atomic_intptr_t; +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) + +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x)) + +#endif + +#ifndef HAS_ATOMIC_OPS + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef unsigned int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef unsigned long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef unsigned long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +typedef cairo_atomic_intptr_t cairo_atomic_int_t; + +cairo_private void +_cairo_atomic_int_inc (cairo_atomic_int_t *x); + +cairo_private cairo_bool_t +_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x); + +cairo_private cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv); + +cairo_private void * +_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv); + +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv) +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv) + +#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER +cairo_private cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x); +cairo_private cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x); +void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val); +# define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) +#else +# define _cairo_atomic_int_get(x) (*x) +# define _cairo_atomic_int_get_relaxed(x) (*(x)) +# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) +# define _cairo_atomic_ptr_get(x) (*x) +#endif + +#else + +/* Workaround GCC complaining about casts */ +static cairo_always_inline void * +_cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x) +{ + return (void *) x; +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) +{ + cairo_atomic_int_t curr; + + do { + curr = _cairo_atomic_int_get (x); + } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv)); + + return curr; +} + +static cairo_always_inline void * +_cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) +{ + void *curr; + + do { + curr = _cairo_atomic_ptr_get (x); + } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv)); + + return curr; +} +#endif + +#ifndef _cairo_atomic_int_cmpxchg_return_old +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv) +#endif + +#ifndef _cairo_atomic_ptr_cmpxchg_return_old +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv) +#endif + +#ifndef _cairo_atomic_int_cmpxchg +#define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv) +#endif + +#ifndef _cairo_atomic_ptr_cmpxchg +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv) +#endif + +#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) +#define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv) + +#define _cairo_status_set_error(status, err) do { \ + /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ + * an unsigned integer instead, and about ignoring the return value. */ \ + int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ + (void) ret__; \ +} while (0) + +CAIRO_END_DECLS + +#endif diff --git a/libs/cairo/src/cairo-atomic.c b/libs/cairo/src/cairo-atomic.c new file mode 100644 index 000000000..fd5ee930b --- /dev/null +++ b/libs/cairo/src/cairo-atomic.c @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-atomic-private.h" +#include "cairo-mutex-private.h" + +#ifdef HAS_ATOMIC_OPS +COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) || + sizeof(void*) == sizeof(long) || + sizeof(void*) == sizeof(long long)); +#else +void +_cairo_atomic_int_inc (cairo_atomic_intptr_t *x) +{ + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + *x += 1; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); +} + +cairo_bool_t +_cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) +{ + cairo_bool_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = --*x == 0; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +cairo_atomic_intptr_t +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv) +{ + cairo_atomic_intptr_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + if (ret == oldv) + *x = newv; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +void * +_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv) +{ + void *ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + if (ret == oldv) + *x = newv; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER +cairo_atomic_intptr_t +_cairo_atomic_int_get (cairo_atomic_intptr_t *x) +{ + cairo_atomic_intptr_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +cairo_atomic_intptr_t +_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x) +{ + return _cairo_atomic_int_get (x); +} + +void +_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val) +{ + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + *x = val; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); +} +#endif + +#endif diff --git a/libs/cairo/src/cairo-base64-stream.c b/libs/cairo/src/cairo-base64-stream.c new file mode 100644 index 000000000..02ca8bd45 --- /dev/null +++ b/libs/cairo/src/cairo-base64-stream.c @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-base85-stream.c b/libs/cairo/src/cairo-base85-stream.c new file mode 100644 index 000000000..ba19ff4a2 --- /dev/null +++ b/libs/cairo/src/cairo-base85-stream.c @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-bentley-ottmann-rectangular.c b/libs/cairo/src/cairo-bentley-ottmann-rectangular.c new file mode 100644 index 000000000..4736f4f41 --- /dev/null +++ b/libs/cairo/src/cairo-bentley-ottmann-rectangular.c @@ -0,0 +1,805 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-combsort-private.h" +#include "cairo-list-private.h" + +#include + +typedef struct _rectangle rectangle_t; +typedef struct _edge edge_t; + +struct _edge { + edge_t *next, *prev; + edge_t *right; + cairo_fixed_t x, top; + int dir; +}; + +struct _rectangle { + edge_t left, right; + int32_t top, bottom; +}; + +#define UNROLL3(x) x x x + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef struct _pqueue { + int size, max_size; + + rectangle_t **elements; + rectangle_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _sweep_line { + rectangle_t **rectangles; + pqueue_t pq; + edge_t head, tail; + edge_t *insert_left, *insert_right; + int32_t current_y; + int32_t last_y; + + cairo_fill_rule_t fill_rule; + + jmp_buf unwind; +} sweep_line_t; + +#define DEBUG_TRAPS 0 + +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } + fprintf (file, "\n"); + fclose (file); + } +} +#else +#define dump_traps(traps, filename) +#endif + +static inline int +rectangle_compare_start (const rectangle_t *a, + const rectangle_t *b) +{ + return a->top - b->top; +} + +static inline int +rectangle_compare_stop (const rectangle_t *a, + const rectangle_t *b) +{ + return a->bottom - b->bottom; +} + +static inline void +pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; + pq->elements[PQ_FIRST_ENTRY] = NULL; +} + +static inline void +pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_bool_t +pqueue_grow (pqueue_t *pq) +{ + rectangle_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + } + + pq->elements = new_elements; + return TRUE; +} + +static inline void +pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) +{ + rectangle_t **elements; + int i, parent; + + if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) { + if (unlikely (! pqueue_grow (&sweep->pq))) { + longjmp (sweep->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + elements = sweep->pq.elements; + for (i = ++sweep->pq.size; + i != PQ_FIRST_ENTRY && + rectangle_compare_stop (rectangle, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = rectangle; +} + +static inline void +pqueue_pop (pqueue_t *pq) +{ + rectangle_t **elements = pq->elements; + rectangle_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + rectangle_compare_stop (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (rectangle_compare_stop (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline rectangle_t * +rectangle_pop_start (sweep_line_t *sweep_line) +{ + return *sweep_line->rectangles++; +} + +static inline rectangle_t * +rectangle_peek_stop (sweep_line_t *sweep_line) +{ + return sweep_line->pq.elements[PQ_FIRST_ENTRY]; +} + +CAIRO_COMBSORT_DECLARE (_rectangle_sort, + rectangle_t *, + rectangle_compare_start) + +static void +sweep_line_init (sweep_line_t *sweep_line, + rectangle_t **rectangles, + int num_rectangles, + cairo_fill_rule_t fill_rule) +{ + _rectangle_sort (rectangles, num_rectangles); + rectangles[num_rectangles] = NULL; + sweep_line->rectangles = rectangles; + + sweep_line->head.x = INT32_MIN; + sweep_line->head.right = NULL; + sweep_line->head.dir = 0; + sweep_line->head.next = &sweep_line->tail; + /* we need to initialize prev so that we can check + * if this edge is the left most and make sure + * we always insert to the right of it, even if + * our x coordinate matches */ + sweep_line->head.prev = NULL; + + sweep_line->tail.x = INT32_MAX; + sweep_line->tail.right = NULL; + sweep_line->tail.dir = 0; + sweep_line->tail.prev = &sweep_line->head; + sweep_line->tail.next = NULL; + + sweep_line->insert_left = &sweep_line->tail; + sweep_line->insert_right = &sweep_line->tail; + + sweep_line->current_y = INT32_MIN; + sweep_line->last_y = INT32_MIN; + + sweep_line->fill_rule = fill_rule; + + pqueue_init (&sweep_line->pq); +} + +static void +sweep_line_fini (sweep_line_t *sweep_line) +{ + pqueue_fini (&sweep_line->pq); +} + +static void +edge_end_box (sweep_line_t *sweep_line, + edge_t *left, + int32_t bot, + cairo_bool_t do_traps, + void *container) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (left->top < bot)) { + if (do_traps) { + cairo_line_t _left = { + { left->x, left->top }, + { left->x, bot }, + }, _right = { + { left->right->x, left->top }, + { left->right->x, bot }, + }; + _cairo_traps_add_trap (container, left->top, bot, &_left, &_right); + status = _cairo_traps_status ((cairo_traps_t *) container); + } else { + cairo_box_t box; + + box.p1.x = left->x; + box.p1.y = left->top; + box.p2.x = left->right->x; + box.p2.y = bot; + + status = _cairo_boxes_add (container, &box); + } + } + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + + left->right = NULL; +} + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline void +edge_start_or_continue_box (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right, + int top, + cairo_bool_t do_traps, + void *container) +{ + if (left->right == right) + return; + + if (left->right != NULL) { + if (right != NULL && left->right->x == right->x) { + /* continuation on right, so just swap edges */ + left->right = right; + return; + } + + edge_end_box (sweep_line, + left, top, do_traps, container); + } + + if (right != NULL && left->x != right->x) { + left->top = top; + left->right = right; + } +} + +static inline void +active_edges_to_traps (sweep_line_t *sweep, + cairo_bool_t do_traps, + void *container) +{ + int top = sweep->current_y; + edge_t *pos; + + if (sweep->last_y == sweep->current_y) + return; + + pos = sweep->head.next; + if (pos == &sweep->tail) + return; + + if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) { + do { + edge_t *left, *right; + int winding; + + left = pos; + winding = left->dir; + + right = left->next; + + /* Check if there is a co-linear edge with an existing trap */ + if (left->right == NULL) { + while (unlikely (right->x == left->x)) { + winding += right->dir; + if (right->right != NULL) { + /* continuation on left */ + left->top = right->top; + left->right = right->right; + right->right = NULL; + winding -= right->dir; + break; + } + + right = right->next; + } + + if (winding == 0) { + pos = right; + continue; + } + } + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + + do { + /* End all subsumed traps */ + if (unlikely (right->right != NULL)) { + edge_end_box (sweep, + right, top, do_traps, container); + } + + winding += right->dir; + if (winding == 0) { + /* skip co-linear edges */ + if (likely (right->x != right->next->x)) + break; + } + + right = right->next; + } while (TRUE); + + edge_start_or_continue_box (sweep, + left, right, top, + do_traps, container); + + pos = right->next; + } while (pos != &sweep->tail); + } else { + do { + edge_t *right = pos->next; + int count = 0; + + do { + /* End all subsumed traps */ + if (unlikely (right->right != NULL)) { + edge_end_box (sweep, + right, top, do_traps, container); + } + + if (++count & 1) { + /* skip co-linear edges */ + if (likely (right->x != right->next->x)) + break; + } + + right = right->next; + } while (TRUE); + + edge_start_or_continue_box (sweep, + pos, right, top, + do_traps, container); + + pos = right->next; + } while (pos != &sweep->tail); + } + + sweep->last_y = sweep->current_y; +} + +static inline void +sweep_line_delete_edge (sweep_line_t *sweep_line, + edge_t *edge, + cairo_bool_t do_traps, + void *container) +{ + if (edge->right != NULL) { + edge_t *next = edge->next; + if (next->x == edge->x) { + next->top = edge->top; + next->right = edge->right; + } else { + edge_end_box (sweep_line, + edge, + sweep_line->current_y, + do_traps, container); + } + } + + if (sweep_line->insert_left == edge) + sweep_line->insert_left = edge->next; + if (sweep_line->insert_right == edge) + sweep_line->insert_right = edge->next; + + edge->prev->next = edge->next; + edge->next->prev = edge->prev; +} + +static inline cairo_bool_t +sweep_line_delete (sweep_line_t *sweep, + rectangle_t *rectangle, + cairo_bool_t do_traps, + void *container) +{ + cairo_bool_t update; + + update = TRUE; + if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && + rectangle->left.prev->dir == rectangle->left.dir) + { + update = rectangle->left.next != &rectangle->right; + } + + sweep_line_delete_edge (sweep, + &rectangle->left, + do_traps, container); + + sweep_line_delete_edge (sweep, + &rectangle->right, + do_traps, container); + + pqueue_pop (&sweep->pq); + return update; +} + +static inline void +insert_edge (edge_t *edge, edge_t *pos) +{ + if (pos->x != edge->x) { + if (pos->x > edge->x) { + do { + UNROLL3({ + if (pos->prev->x <= edge->x) + break; + pos = pos->prev; + }) + } while (TRUE); + } else { + do { + UNROLL3({ + pos = pos->next; + if (pos->x >= edge->x) + break; + }) + } while (TRUE); + } + } + if (pos->prev) { + pos->prev->next = edge; + edge->prev = pos->prev; + edge->next = pos; + pos->prev = edge; + } else { + /* we have edge that shares an x coordinate with the left most sentinal. + * instead of inserting before pos and ruining our sentinal we insert after pos. */ + pos->next->prev = edge; + edge->next = pos->next; + edge->prev = pos; + pos->next = edge; + } +} + +static inline cairo_bool_t +sweep_line_insert (sweep_line_t *sweep, + rectangle_t *rectangle) +{ + edge_t *pos; + + /* right edge */ + pos = sweep->insert_right; + insert_edge (&rectangle->right, pos); + sweep->insert_right = &rectangle->right; + + /* left edge */ + pos = sweep->insert_left; + if (pos->x > sweep->insert_right->x) + pos = sweep->insert_right->prev; + insert_edge (&rectangle->left, pos); + sweep->insert_left = &rectangle->left; + + pqueue_push (sweep, rectangle); + + if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && + rectangle->left.prev->dir == rectangle->left.dir) + { + return rectangle->left.next != &rectangle->right; + } + + return TRUE; +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, + int num_rectangles, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + sweep_line_t sweep_line; + rectangle_t *rectangle; + cairo_status_t status; + cairo_bool_t update = FALSE; + + sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule); + if ((status = setjmp (sweep_line.unwind))) + goto unwind; + + rectangle = rectangle_pop_start (&sweep_line); + do { + if (rectangle->top != sweep_line.current_y) { + rectangle_t *stop; + + stop = rectangle_peek_stop (&sweep_line); + while (stop != NULL && stop->bottom < rectangle->top) { + if (stop->bottom != sweep_line.current_y) { + if (update) { + active_edges_to_traps (&sweep_line, + do_traps, container); + update = FALSE; + } + + sweep_line.current_y = stop->bottom; + } + + update |= sweep_line_delete (&sweep_line, stop, do_traps, container); + + stop = rectangle_peek_stop (&sweep_line); + } + + if (update) { + active_edges_to_traps (&sweep_line, do_traps, container); + update = FALSE; + } + + sweep_line.current_y = rectangle->top; + } + + update |= sweep_line_insert (&sweep_line, rectangle); + } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL); + + while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { + if (rectangle->bottom != sweep_line.current_y) { + if (update) { + active_edges_to_traps (&sweep_line, do_traps, container); + update = FALSE; + } + + sweep_line.current_y = rectangle->bottom; + } + + update |= sweep_line_delete (&sweep_line, rectangle, do_traps, container); + } + +unwind: + sweep_line_fini (&sweep_line); + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; + rectangle_t *rectangles; + rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; + rectangle_t **rectangles_ptrs; + cairo_status_t status; + int i; + + assert (traps->is_rectangular); + + if (unlikely (traps->num_traps <= 1)) { + if (traps->num_traps == 1) { + cairo_trapezoid_t *trap = traps->traps; + if (trap->left.p1.x > trap->right.p1.x) { + cairo_line_t tmp = trap->left; + trap->left = trap->right; + trap->right = tmp; + } + } + return CAIRO_STATUS_SUCCESS; + } + + dump_traps (traps, "bo-rects-traps-in.txt"); + + rectangles = stack_rectangles; + rectangles_ptrs = stack_rectangles_ptrs; + if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { + rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, + sizeof (rectangle_t) + + sizeof (rectangle_t *), + sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); + } + + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { + rectangles[i].left.x = traps->traps[i].left.p1.x; + rectangles[i].left.dir = 1; + + rectangles[i].right.x = traps->traps[i].right.p1.x; + rectangles[i].right.dir = -1; + } else { + rectangles[i].right.x = traps->traps[i].left.p1.x; + rectangles[i].right.dir = 1; + + rectangles[i].left.x = traps->traps[i].right.p1.x; + rectangles[i].left.dir = -1; + } + + rectangles[i].left.right = NULL; + rectangles[i].right.right = NULL; + + rectangles[i].top = traps->traps[i].top; + rectangles[i].bottom = traps->traps[i].bottom; + + rectangles_ptrs[i] = &rectangles[i]; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i, + fill_rule, + TRUE, traps); + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + if (rectangles != stack_rectangles) + free (rectangles); + + dump_traps (traps, "bo-rects-traps-out.txt"); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *out) +{ + rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; + rectangle_t *rectangles; + rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; + rectangle_t **rectangles_ptrs; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int i, j; + + if (unlikely (in->num_boxes == 0)) { + _cairo_boxes_clear (out); + return CAIRO_STATUS_SUCCESS; + } + + if (in->num_boxes == 1) { + if (in == out) { + cairo_box_t *box = &in->chunks.base[0]; + + if (box->p1.x > box->p2.x) { + cairo_fixed_t tmp = box->p1.x; + box->p1.x = box->p2.x; + box->p2.x = tmp; + } + } else { + cairo_box_t box = in->chunks.base[0]; + + if (box.p1.x > box.p2.x) { + cairo_fixed_t tmp = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = tmp; + } + + _cairo_boxes_clear (out); + status = _cairo_boxes_add (out, &box); + assert (status == CAIRO_STATUS_SUCCESS); + } + return CAIRO_STATUS_SUCCESS; + } + + rectangles = stack_rectangles; + rectangles_ptrs = stack_rectangles_ptrs; + if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { + rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, + sizeof (rectangle_t) + + sizeof (rectangle_t *), + sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); + } + + j = 0; + for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + if (box[i].p1.x < box[i].p2.x) { + rectangles[j].left.x = box[i].p1.x; + rectangles[j].left.dir = 1; + + rectangles[j].right.x = box[i].p2.x; + rectangles[j].right.dir = -1; + } else { + rectangles[j].right.x = box[i].p1.x; + rectangles[j].right.dir = 1; + + rectangles[j].left.x = box[i].p2.x; + rectangles[j].left.dir = -1; + } + + rectangles[j].left.right = NULL; + rectangles[j].right.right = NULL; + + rectangles[j].top = box[i].p1.y; + rectangles[j].bottom = box[i].p2.y; + + rectangles_ptrs[j] = &rectangles[j]; + j++; + } + } + + _cairo_boxes_clear (out); + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, j, + fill_rule, + FALSE, out); + if (rectangles != stack_rectangles) + free (rectangles); + + return status; +} diff --git a/libs/cairo/src/cairo-bentley-ottmann-rectilinear.c b/libs/cairo/src/cairo-bentley-ottmann-rectilinear.c new file mode 100644 index 000000000..b00d2823b --- /dev/null +++ b/libs/cairo/src/cairo-bentley-ottmann-rectilinear.c @@ -0,0 +1,634 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-combsort-private.h" +#include "cairo-error-private.h" + +typedef struct _cairo_bo_edge cairo_bo_edge_t; +typedef struct _cairo_bo_trap cairo_bo_trap_t; + +/* A deferred trapezoid of an edge */ +struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +}; + +struct _cairo_bo_edge { + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_trap_t deferred_trap; +}; + +typedef enum { + CAIRO_BO_EVENT_TYPE_START, + CAIRO_BO_EVENT_TYPE_STOP +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *edge; +} cairo_bo_event_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_event_t **events; + cairo_bo_edge_t *head; + cairo_bo_edge_t *stopped; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +static inline int +_cairo_point_compare (const cairo_point_t *a, + const cairo_point_t *b) +{ + int cmp; + + cmp = a->y - b->y; + if (likely (cmp)) + return cmp; + + return a->x - b->x; +} + +static inline int +_cairo_bo_edge_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + cmp = a->edge.line.p1.x - b->edge.line.p1.x; + if (likely (cmp)) + return cmp; + + return b->edge.bottom - a->edge.bottom; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_point_compare (&a->point, &b->point); + if (likely (cmp)) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line) +{ + return *sweep_line->events++; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_event_t **events, + int num_events) +{ + _cairo_bo_event_queue_sort (events, num_events); + events[num_events] = NULL; + sweep_line->events = events; + + sweep_line->head = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static void +_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; + + cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0) + prev = next, next = prev->next; + + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0) + next = prev, prev = next->prev; + + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } + + sweep_line->current_edge = edge; +} + +static void +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; + + if (edge->next != NULL) + edge->next->prev = edge->prev; + + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; +} + +static inline cairo_bool_t +edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + return a->edge.line.p1.x == b->edge.line.p1.x; +} + +static cairo_status_t +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_bool_t do_traps, + void *container) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + if (do_traps) { + _cairo_traps_add_trap (container, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); + status = _cairo_traps_status ((cairo_traps_t *) container); + } else { + cairo_box_t box; + + box.p1.x = left->edge.line.p1.x; + box.p1.y = trap->top; + box.p2.x = trap->right->edge.line.p1.x; + box.p2.y = bot; + status = _cairo_boxes_add (container, &box); + } + } + + trap->right = NULL; + + return status; +} + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_bool_t do_traps, + void *container) +{ + cairo_status_t status; + + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && edges_collinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, do_traps, container); + if (unlikely (status)) + return status; + } + + if (right != NULL && ! edges_collinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + } + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_active_edges_to_traps (cairo_bo_edge_t *left, + int32_t top, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + cairo_bo_edge_t *right; + cairo_status_t status; + + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + while (left != NULL) { + int in_out; + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + in_out = left->edge.dir; + + /* Check if there is a co-linear edge with an existing trap */ + right = left->next; + if (left->deferred_trap.right == NULL) { + while (right != NULL && right->deferred_trap.right == NULL) + right = right->next; + + if (right != NULL && edges_collinear (left, right)) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->deferred_trap.right = NULL; + } + } + + /* End all subsumed traps */ + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, do_traps, container); + if (unlikely (status)) + return status; + } + + in_out += right->edge.dir; + if (in_out == 0) { + /* skip co-linear edges */ + if (right->next == NULL || + ! edges_collinear (right, right->next)) + { + break; + } + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, top, + do_traps, container); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } else { + while (left != NULL) { + int in_out = 0; + + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, do_traps, container); + if (unlikely (status)) + return status; + } + + if ((in_out++ & 1) == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_collinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, top, + do_traps, container); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events, + int num_events, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_status_t status; + + _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events); + + while ((event = _cairo_bo_event_dequeue (&sweep_line))) { + if (event->point.y != sweep_line.current_y) { + status = _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, do_traps, container); + if (unlikely (status)) + return status; + + sweep_line.current_y = event->point.y; + } + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + _cairo_bo_sweep_line_insert (&sweep_line, event->edge); + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + _cairo_bo_sweep_line_delete (&sweep_line, event->edge); + + if (event->edge->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (event->edge, + sweep_line.current_y, + do_traps, container); + if (unlikely (status)) + return status; + } + + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; + cairo_bo_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; + cairo_bo_edge_t *edges; + int num_events; + int i, j; + + if (unlikely (polygon->num_edges == 0)) + return CAIRO_STATUS_SUCCESS; + + num_events = 2 * polygon->num_edges; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_edge_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); + } + + for (i = j = 0; i < polygon->num_edges; i++) { + edges[i].edge = polygon->edges[i]; + edges[i].deferred_trap.right = NULL; + edges[i].prev = NULL; + edges[i].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = polygon->edges[i].top; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = polygon->edges[i].bottom; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + } + + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, + TRUE, traps); + if (events != stack_events) + free (events); + + traps->is_rectilinear = TRUE; + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes) +{ + cairo_status_t status; + cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; + cairo_bo_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; + cairo_bo_edge_t *edges; + int num_events; + int i, j; + + if (unlikely (polygon->num_edges == 0)) + return CAIRO_STATUS_SUCCESS; + + num_events = 2 * polygon->num_edges; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_edge_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); + } + + for (i = j = 0; i < polygon->num_edges; i++) { + edges[i].edge = polygon->edges[i]; + edges[i].deferred_trap.right = NULL; + edges[i].prev = NULL; + edges[i].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = polygon->edges[i].top; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = polygon->edges[i].bottom; + events[j].point.x = polygon->edges[i].line.p1.x; + events[j].edge = &edges[i]; + j++; + } + + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, + FALSE, boxes); + if (events != stack_events) + free (events); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; + cairo_bo_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; + cairo_bo_edge_t *edges; + cairo_status_t status; + int i, j, k; + + if (unlikely (traps->num_traps == 0)) + return CAIRO_STATUS_SUCCESS; + + assert (traps->is_rectilinear); + + i = 4 * traps->num_traps; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (i > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (i, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_edge_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + i); + edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); + } + + for (i = j = k = 0; i < traps->num_traps; i++) { + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].left; + edges[k].edge.dir = 1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].right; + edges[k].edge.dir = -1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, + TRUE, traps); + traps->is_rectilinear = TRUE; + + if (events != stack_events) + free (events); + + return status; +} diff --git a/libs/cairo/src/cairo-bentley-ottmann.c b/libs/cairo/src/cairo-bentley-ottmann.c new file mode 100644 index 000000000..c333becaa --- /dev/null +++ b/libs/cairo/src/cairo-bentley-ottmann.c @@ -0,0 +1,2100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-private.h" + +#define DEBUG_PRINT_STATE 0 +#define DEBUG_EVENTS 0 +#define DEBUG_TRAPS 0 + +typedef cairo_point_t cairo_bo_point32_t; + +typedef struct _cairo_bo_intersect_ordinate { + int32_t ordinate; + enum { EXACT, INEXACT } exactness; +} cairo_bo_intersect_ordinate_t; + +typedef struct _cairo_bo_intersect_point { + cairo_bo_intersect_ordinate_t x; + cairo_bo_intersect_ordinate_t y; +} cairo_bo_intersect_point_t; + +typedef struct _cairo_bo_edge cairo_bo_edge_t; +typedef struct _cairo_bo_trap cairo_bo_trap_t; + +/* A deferred trapezoid of an edge */ +struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +}; + +struct _cairo_bo_edge { + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_trap_t deferred_trap; +}; + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef enum { + CAIRO_BO_EVENT_TYPE_STOP, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + CAIRO_BO_EVENT_TYPE_START +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_point_t point; +} cairo_bo_event_t; + +typedef struct _cairo_bo_start_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t edge; +} cairo_bo_start_event_t; + +typedef struct _cairo_bo_queue_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *e1; + cairo_bo_edge_t *e2; +} cairo_bo_queue_event_t; + +typedef struct _pqueue { + int size, max_size; + + cairo_bo_event_t **elements; + cairo_bo_event_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _cairo_bo_event_queue { + cairo_freepool_t pool; + pqueue_t pqueue; + cairo_bo_event_t **start_events; +} cairo_bo_event_queue_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_edge_t *head; + cairo_bo_edge_t *stopped; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + cairo_box_t extents; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + +#if 0 + if (traps->has_limits) { + printf ("%s: limits=(%d, %d, %d, %d)\n", + filename, + traps->limits.p1.x, traps->limits.p1.y, + traps->limits.p2.x, traps->limits.p2.y); + } +#endif + _cairo_traps_extents (traps, &extents); + printf ("%s: extents=(%d, %d, %d, %d)\n", + filename, + extents.p1.x, extents.p1.y, + extents.p2.x, extents.p2.y); + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } + fprintf (file, "\n"); + fclose (file); + } +} + +static void +dump_edges (cairo_bo_start_event_t *events, + int num_edges, + const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < num_edges; n++) { + fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n", + events[n].edge.edge.line.p1.x, + events[n].edge.edge.line.p1.y, + events[n].edge.edge.line.p2.x, + events[n].edge.edge.line.p2.y, + events[n].edge.edge.top, + events[n].edge.edge.bottom, + events[n].edge.edge.dir); + } + fprintf (file, "\n"); + fclose (file); + } +} +#endif + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == line->p1.y) + return line->p1.x; + if (y == line->p2.y) + return line->p2.x; + + x = line->p1.x; + dy = line->p2.y - line->p1.y; + if (dy != 0) { + x += _cairo_fixed_mul_div_floor (y - line->p1.y, + line->p2.x - line->p1.x, + dy); + } + + return x; +} + +static inline int +_cairo_bo_point32_compare (cairo_bo_point32_t const *a, + cairo_bo_point32_t const *b) +{ + int cmp; + + cmp = a->y - b->y; + if (cmp) + return cmp; + + return a->x - b->x; +} + +/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the + * slope a is respectively greater than, equal to, or less than the + * slope of b. + * + * For each edge, consider the direction vector formed from: + * + * top -> bottom + * + * which is: + * + * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) + * + * We then define the slope of each edge as dx/dy, (which is the + * inverse of the slope typically used in math instruction). We never + * compute a slope directly as the value approaches infinity, but we + * can derive a slope comparison without division as follows, (where + * the ? represents our compare operator). + * + * 1. slope(a) ? slope(b) + * 2. adx/ady ? bdx/bdy + * 3. (adx * bdy) ? (bdx * ady) + * + * Note that from step 2 to step 3 there is no change needed in the + * sign of the result since both ady and bdy are guaranteed to be + * greater than or equal to 0. + * + * When using this slope comparison to sort edges, some care is needed + * when interpreting the results. Since the slope compare operates on + * distance vectors from top to bottom it gives a correct left to + * right sort for edges that have a common top point, (such as two + * edges with start events at the same location). On the other hand, + * the sense of the result will be exactly reversed for two edges that + * have a common stop point. + */ +static inline int +_slope_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; + int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; + + /* Since the dy's are all positive by construction we can fast + * path several common cases. + */ + + /* First check for vertical lines. */ + if (adx == 0) + return -bdx; + if (bdx == 0) + return adx; + + /* Then where the two edges point in different directions wrt x. */ + if ((adx ^ bdx) < 0) + return adx; + + /* Finally we actually need to do the general comparison. */ + { + int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; + int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; + cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } +} + +/* + * We need to compare the x-coordinates of a pair of lines for a particular y, + * without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy + * - (Y - A_y) * A_dx * B_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 128 bit arithmetic. For certain, but common, + * input we can reduce this down to a single 32 bit compare by inspecting the + * deltas. + * + * (And put the burden of the work on developing fast 128 bit ops, which are + * required throughout the tessellator.) + * + * See the similar discussion for _slope_compare(). + */ +static int +edges_compare_x_for_y_general (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t dx; + int32_t adx, ady; + int32_t bdx, bdy; + enum { + HAVE_NONE = 0x0, + HAVE_DX = 0x1, + HAVE_ADX = 0x2, + HAVE_DX_ADX = HAVE_DX | HAVE_ADX, + HAVE_BDX = 0x4, + HAVE_DX_BDX = HAVE_DX | HAVE_BDX, + HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, + HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX + } have_dx_adx_bdx = HAVE_ALL; + + /* don't bother solving for abscissa if the edges' bounding boxes + * can be used to order them. */ + { + int32_t amin, amax; + int32_t bmin, bmax; + if (a->edge.line.p1.x < a->edge.line.p2.x) { + amin = a->edge.line.p1.x; + amax = a->edge.line.p2.x; + } else { + amin = a->edge.line.p2.x; + amax = a->edge.line.p1.x; + } + if (b->edge.line.p1.x < b->edge.line.p2.x) { + bmin = b->edge.line.p1.x; + bmax = b->edge.line.p2.x; + } else { + bmin = b->edge.line.p2.x; + bmax = b->edge.line.p1.x; + } + if (amax < bmin) return -1; + if (amin > bmax) return +1; + } + + ady = a->edge.line.p2.y - a->edge.line.p1.y; + adx = a->edge.line.p2.x - a->edge.line.p1.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; + + bdy = b->edge.line.p2.y - b->edge.line.p1.y; + bdx = b->edge.line.p2.x - b->edge.line.p1.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; + + dx = a->edge.line.p1.x - b->edge.line.p1.x; + if (dx == 0) + have_dx_adx_bdx &= ~HAVE_DX; + +#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) + switch (have_dx_adx_bdx) { + default: + case HAVE_NONE: + return 0; + case HAVE_DX: + /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ + return dx; /* ady * bdy is positive definite */ + case HAVE_ADX: + /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ + return adx; /* bdy * (y - a->top.y) is positive definite */ + case HAVE_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy */ + return -bdx; /* ady * (y - b->top.y) is positive definite */ + case HAVE_ADX_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ + if ((adx ^ bdx) < 0) { + return adx; + } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ + cairo_int64_t adx_bdy, bdx_ady; + + /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ + + adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } else + return _cairo_int128_cmp (A, B); + case HAVE_DX_ADX: + /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ + if ((-adx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t ady_dx, dy_adx; + + ady_dx = _cairo_int32x32_64_mul (ady, dx); + dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); + + return _cairo_int64_cmp (ady_dx, dy_adx); + } + case HAVE_DX_BDX: + /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ + if ((bdx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t bdy_dx, dy_bdx; + + bdy_dx = _cairo_int32x32_64_mul (bdy, dx); + dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ + return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); + } +#undef B +#undef A +#undef L +} + +/* + * We need to compare the x-coordinate of a line for a particular y wrt to a + * given x, without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ X + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy (and (Y - A_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 64 bit arithmetic. + * + * See the similar discussion for _slope_compare() and + * edges_compare_x_for_y_general(). + */ +static int +edge_compare_for_y_against_x (const cairo_bo_edge_t *a, + int32_t y, + int32_t x) +{ + int32_t adx, ady; + int32_t dx, dy; + cairo_int64_t L, R; + + if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) + return 1; + if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) + return -1; + + adx = a->edge.line.p2.x - a->edge.line.p1.x; + dx = x - a->edge.line.p1.x; + + if (adx == 0) + return -dx; + if (dx == 0 || (adx ^ dx) < 0) + return adx; + + dy = y - a->edge.line.p1.y; + ady = a->edge.line.p2.y - a->edge.line.p1.y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static int +edges_compare_x_for_y (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* If the sweep-line is currently on an end-point of a line, + * then we know its precise x value (and considering that we often need to + * compare events at end-points, this happens frequently enough to warrant + * special casing). + */ + enum { + HAVE_NEITHER = 0x0, + HAVE_AX = 0x1, + HAVE_BX = 0x2, + HAVE_BOTH = HAVE_AX | HAVE_BX + } have_ax_bx = HAVE_BOTH; + int32_t ax, bx; + + if (y == a->edge.line.p1.y) + ax = a->edge.line.p1.x; + else if (y == a->edge.line.p2.y) + ax = a->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_AX; + + if (y == b->edge.line.p1.y) + bx = b->edge.line.p1.x; + else if (y == b->edge.line.p2.y) + bx = b->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_BX; + + switch (have_ax_bx) { + default: + case HAVE_NEITHER: + return edges_compare_x_for_y_general (a, b, y); + case HAVE_AX: + return -edge_compare_for_y_against_x (b, y, ax); + case HAVE_BX: + return edge_compare_for_y_against_x (a, y, bx); + case HAVE_BOTH: + return ax - bx; + } +} + +static inline int +_line_equal (const cairo_line_t *a, const cairo_line_t *b) +{ + return a->p1.x == b->p1.x && a->p1.y == b->p1.y && + a->p2.x == b->p2.x && a->p2.y == b->p2.y; +} + +static int +_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, + const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + /* compare the edges if not identical */ + if (! _line_equal (&a->edge.line, &b->edge.line)) { + cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); + if (cmp) + return cmp; + + /* The two edges intersect exactly at y, so fall back on slope + * comparison. We know that this compare_edges function will be + * called only when starting a new edge, (not when stopping an + * edge), so we don't have to worry about conditionally inverting + * the sense of _slope_compare. */ + cmp = _slope_compare (a, b); + if (cmp) + return cmp; + } + + /* We've got two collinear edges now. */ + return b->edge.bottom - a->edge.bottom; +} + +static inline cairo_int64_t +det32_64 (int32_t a, int32_t b, + int32_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); +} + +static inline cairo_int128_t +det64x32_128 (cairo_int64_t a, int32_t b, + cairo_int64_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), + _cairo_int64x32_128_mul (c, b)); +} + +/* Compute the intersection of two lines as defined by two edges. The + * result is provided as a coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or + * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. + */ +static cairo_bool_t +intersect_lines (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_intersect_point_t *intersection) +{ + cairo_int64_t a_det, b_det; + + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm begins. + * What we're doing to mitigate this is to perform clamping in + * cairo_bo_tessellate_polygon(). + */ + int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; + int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; + + int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; + int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; + + cairo_int64_t den_det; + cairo_int64_t R; + cairo_quorem64_t qr; + + den_det = det32_64 (dx1, dy1, dx2, dy2); + + /* Q: Can we determine that the lines do not intersect (within range) + * much more cheaply than computing the intersection point i.e. by + * avoiding the division? + * + * X = ax + t * adx = bx + s * bdx; + * Y = ay + t * ady = by + s * bdy; + * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) + * => t * L = R + * + * Therefore we can reject any intersection (under the criteria for + * valid intersection events) if: + * L^R < 0 => t < 0, or + * L t > 1 + * + * (where top/bottom must at least extend to the line endpoints). + * + * A similar substitution can be performed for s, yielding: + * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) + */ + R = det32_64 (dx2, dy2, + b->edge.line.p1.x - a->edge.line.p1.x, + b->edge.line.p1.y - a->edge.line.p1.y); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + R = det32_64 (dy1, dx1, + a->edge.line.p1.y - b->edge.line.p1.y, + a->edge.line.p1.x - b->edge.line.p1.x); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + /* We now know that the two lines should intersect within range. */ + + a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, + a->edge.line.p2.x, a->edge.line.p2.y); + b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, + b->edge.line.p2.x, b->edge.line.p2.y); + + /* x = det (a_det, dx1, b_det, dx2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, + b_det, dx2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->x.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->x.exactness = INEXACT; + } +#endif + intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); + + /* y = det (a_det, dy1, b_det, dy2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, + b_det, dy2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->y.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->y.exactness = INEXACT; + } +#endif + intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); + + return TRUE; +} + +static int +_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, + int32_t b) +{ + /* First compare the quotient */ + if (a.ordinate > b) + return +1; + if (a.ordinate < b) + return -1; + /* With quotient identical, if remainder is 0 then compare equal */ + /* Otherwise, the non-zero remainder makes a > b */ + return INEXACT == a.exactness; +} + +/* Does the given edge contain the given point. The point must already + * be known to be contained within the line determined by the edge, + * (most likely the point results from an intersection of this edge + * with another). + * + * If we had exact arithmetic, then this function would simply be a + * matter of examining whether the y value of the point lies within + * the range of y values of the edge. But since intersection points + * are not exact due to being rounded to the nearest integer within + * the available precision, we must also examine the x value of the + * point. + * + * The definition of "contains" here is that the given intersection + * point will be seen by the sweep line after the start event for the + * given edge and before the stop event for the edge. See the comments + * in the implementation for more details. + */ +static cairo_bool_t +_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, + cairo_bo_intersect_point_t *point) +{ + int cmp_top, cmp_bottom; + + /* XXX: When running the actual algorithm, we don't actually need to + * compare against edge->top at all here, since any intersection above + * top is eliminated early via a slope comparison. We're leaving these + * here for now only for the sake of the quadratic-time intersection + * finder which needs them. + */ + + cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.top); + cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.bottom); + + if (cmp_top < 0 || cmp_bottom > 0) + { + return FALSE; + } + + if (cmp_top > 0 && cmp_bottom < 0) + { + return TRUE; + } + + /* At this stage, the point lies on the same y value as either + * edge->top or edge->bottom, so we have to examine the x value in + * order to properly determine containment. */ + + /* If the y value of the point is the same as the y value of the + * top of the edge, then the x value of the point must be greater + * to be considered as inside the edge. Similarly, if the y value + * of the point is the same as the y value of the bottom of the + * edge, then the x value of the point must be less to be + * considered as inside. */ + + if (cmp_top == 0) { + cairo_fixed_t top_x; + + top_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.top); + return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; + } else { /* cmp_bottom == 0 */ + cairo_fixed_t bot_x; + + bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.bottom); + return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; + } +} + +/* Compute the intersection of two edges. The result is provided as a + * coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection + * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the + * intersection of the lines defined by the edges occurs outside of + * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges + * are exactly parallel. + * + * Note that when determining if a candidate intersection is "inside" + * an edge, we consider both the infinitesimal shortening and the + * infinitesimal tilt rules described by John Hobby. Specifically, if + * the intersection is exactly the same as an edge point, it is + * effectively outside (no intersection is returned). Also, if the + * intersection point has the same + */ +static cairo_bool_t +_cairo_bo_edge_intersect (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_point32_t *intersection) +{ + cairo_bo_intersect_point_t quorem; + + if (! intersect_lines (a, b, &quorem)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) + return FALSE; + + /* Now that we've correctly compared the intersection point and + * determined that it lies within the edge, then we know that we + * no longer need any more bits of storage for the intersection + * than we do for our edge coordinates. We also no longer need the + * remainder from the division. */ + intersection->x = quorem.x.ordinate; + intersection->y = quorem.y.ordinate; + + return TRUE; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_bo_point32_compare (&a->point, &b->point); + if (cmp) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline void +_pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static inline void +_pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_status_t +_pqueue_grow (pqueue_t *pq) +{ + cairo_bo_event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pq->elements = new_elements; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) +{ + cairo_bo_event_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + cairo_bo_event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (pqueue_t *pq) +{ + cairo_bo_event_t **elements = pq->elements; + cairo_bo_event_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + cairo_bo_event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (cairo_bo_event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, + cairo_bo_event_type_t type, + cairo_bo_edge_t *e1, + cairo_bo_edge_t *e2, + const cairo_point_t *point) +{ + cairo_bo_queue_event_t *event; + + event = _cairo_freepool_alloc (&queue->pool); + if (unlikely (event == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event->type = type; + event->e1 = e1; + event->e2 = e2; + event->point = *point; + + return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); +} + +static void +_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, + cairo_bo_event_t *event) +{ + _cairo_freepool_free (&queue->pool, event); +} + +static cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) +{ + cairo_bo_event_t *event, *cmp; + + event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; + cmp = *event_queue->start_events; + if (event == NULL || + (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) + { + event = cmp; + event_queue->start_events++; + } + else + { + _pqueue_pop (&event_queue->pqueue); + } + + return event; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, + cairo_bo_event_t **start_events, + int num_events) +{ + _cairo_bo_event_queue_sort (start_events, num_events); + start_events[num_events] = NULL; + + event_queue->start_events = start_events; + + _cairo_freepool_init (&event_queue->pool, + sizeof (cairo_bo_queue_event_t)); + _pqueue_init (&event_queue->pqueue); + event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; +} + +static cairo_status_t +_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *edge) +{ + cairo_bo_point32_t point; + + point.y = edge->edge.bottom; + point.x = _line_compute_intersection_x_for_y (&edge->edge.line, + point.y); + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_STOP, + edge, NULL, + &point); +} + +static void +_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) +{ + _pqueue_fini (&event_queue->pqueue); + _cairo_freepool_fini (&event_queue->pool); +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + cairo_bo_point32_t intersection; + + if (_line_equal (&left->edge.line, &right->edge.line)) + return CAIRO_STATUS_SUCCESS; + + /* The names "left" and "right" here are correct descriptions of + * the order of the two edges within the active edge list. So if a + * slope comparison also puts left less than right, then we know + * that the intersection of these two segments has already + * occurred before the current sweep line position. */ + if (_slope_compare (left, right) <= 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_bo_edge_intersect (left, right, &intersection)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + left, right, + &intersection); +} + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) +{ + sweep_line->head = NULL; + sweep_line->stopped = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static cairo_status_t +_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; + + cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, + sweep_line->current_edge, + edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + next, edge) < 0) + { + prev = next, next = prev->next; + } + + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + prev, edge) > 0) + { + next = prev, prev = next->prev; + } + + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } + + sweep_line->current_edge = edge; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; + + if (edge->next != NULL) + edge->next->prev = edge->prev; + + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; +} + +static void +_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + if (left->prev != NULL) + left->prev->next = right; + else + sweep_line->head = right; + + if (right->next != NULL) + right->next->prev = left; + + left->next = right->next; + right->next = left; + + right->prev = left->prev; + left->prev = right; +} + +#if DEBUG_PRINT_STATE +static void +_cairo_bo_edge_print (cairo_bo_edge_t *edge) +{ + printf ("(0x%x, 0x%x)-(0x%x, 0x%x)", + edge->edge.line.p1.x, edge->edge.line.p1.y, + edge->edge.line.p2.x, edge->edge.line.p2.y); +} + +static void +_cairo_bo_event_print (cairo_bo_event_t *event) +{ + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + printf ("Start: "); + break; + case CAIRO_BO_EVENT_TYPE_STOP: + printf ("Stop: "); + break; + case CAIRO_BO_EVENT_TYPE_INTERSECTION: + printf ("Intersection: "); + break; + } + printf ("(%d, %d)\t", event->point.x, event->point.y); + _cairo_bo_edge_print (event->e1); + if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) { + printf (" X "); + _cairo_bo_edge_print (event->e2); + } + printf ("\n"); +} + +static void +_cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue) +{ + /* XXX: fixme to print the start/stop array too. */ + printf ("Event queue:\n"); +} + +static void +_cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) +{ + cairo_bool_t first = TRUE; + cairo_bo_edge_t *edge; + + printf ("Sweep line from edge list: "); + first = TRUE; + for (edge = sweep_line->head; + edge; + edge = edge->next) + { + if (!first) + printf (", "); + _cairo_bo_edge_print (edge); + first = FALSE; + } + printf ("\n"); +} + +static void +print_state (const char *msg, + cairo_bo_event_t *event, + cairo_bo_event_queue_t *event_queue, + cairo_bo_sweep_line_t *sweep_line) +{ + printf ("%s ", msg); + _cairo_bo_event_print (event); + _cairo_bo_event_queue_print (event_queue); + _cairo_bo_sweep_line_print (sweep_line); + printf ("\n"); +} +#endif + +#if DEBUG_EVENTS +static void CAIRO_PRINTF_FORMAT (1, 2) +event_log (const char *fmt, ...) +{ + FILE *file; + + if (getenv ("CAIRO_DEBUG_EVENTS") == NULL) + return; + + file = fopen ("bo-events.txt", "a"); + if (file != NULL) { + va_list ap; + + va_start (ap, fmt); + vfprintf (file, fmt, ap); + va_end (ap); + + fclose (file); + } +} +#endif + +static inline cairo_bool_t +edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + if (_line_equal (&a->edge.line, &b->edge.line)) + return TRUE; + + if (_slope_compare (a, b)) + return FALSE; + + /* The choice of y is not truly arbitrary since we must guarantee that it + * is greater than the start of either line. + */ + if (a->edge.line.p1.y == b->edge.line.p1.y) { + return a->edge.line.p1.x == b->edge.line.p1.x; + } else if (a->edge.line.p1.y < b->edge.line.p1.y) { + return edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; + } else { + return edge_compare_for_y_against_x (a, + b->edge.line.p1.y, + b->edge.line.p1.x) == 0; + } +} + +/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ +static cairo_status_t +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_traps_t *traps) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + _cairo_traps_add_trap (traps, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); + +#if DEBUG_PRINT_STATE + printf ("Deferred trap: left=(%x, %x)-(%x,%x) " + "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n", + left->edge.line.p1.x, left->edge.line.p1.y, + left->edge.line.p2.x, left->edge.line.p2.y, + trap->right->edge.line.p1.x, trap->right->edge.line.p1.y, + trap->right->edge.line.p2.x, trap->right->edge.line.p2.y, + trap->top, bot); +#endif +#if DEBUG_EVENTS + event_log ("end trap: %lu %lu %d %d\n", + (long) left, + (long) trap->right, + trap->top, + bot); +#endif + } + + trap->right = NULL; + + return _cairo_traps_status (traps); +} + + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_traps_t *traps) +{ + cairo_status_t status; + + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && edges_colinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, traps); + if (unlikely (status)) + return status; + } + + if (right != NULL && ! edges_colinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + +#if DEBUG_EVENTS + event_log ("begin trap: %lu %lu %d\n", + (long) left, + (long) right, + top); +#endif + } + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_active_edges_to_traps (cairo_bo_edge_t *left, + int32_t top, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_bo_edge_t *right; + cairo_status_t status; + +#if DEBUG_PRINT_STATE + printf ("Processing active edges for %x\n", top); +#endif + + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + while (left != NULL) { + int in_out; + + /* Greedily search for the closing edge, so that we generate the + * maximal span width with the minimal number of trapezoids. + */ + in_out = left->edge.dir; + + /* Check if there is a co-linear edge with an existing trap */ + right = left->next; + if (left->deferred_trap.right == NULL) { + while (right != NULL && right->deferred_trap.right == NULL) + right = right->next; + + if (right != NULL && edges_colinear (left, right)) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->deferred_trap.right = NULL; + } + } + + /* End all subsumed traps */ + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + in_out += right->edge.dir; + if (in_out == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_colinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } else { + while (left != NULL) { + int in_out = 0; + + right = left->next; + while (right != NULL) { + if (right->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (right, top, traps); + if (unlikely (status)) + return status; + } + + if ((in_out++ & 1) == 0) { + cairo_bo_edge_t *next; + cairo_bool_t skip = FALSE; + + /* skip co-linear edges */ + next = right->next; + if (next != NULL) + skip = edges_colinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, + top, traps); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } + + return CAIRO_STATUS_SUCCESS; +} + + +/* Execute a single pass of the Bentley-Ottmann algorithm on edges, + * generating trapezoids according to the fill_rule and appending them + * to traps. */ +static cairo_status_t +_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, + int num_events, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps, + int *num_intersections) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ + int intersection_count = 0; + cairo_bo_event_queue_t event_queue; + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_bo_edge_t *left, *right; + cairo_bo_edge_t *e1, *e2; + +#if DEBUG_EVENTS + { + int i; + + for (i = 0; i < num_events; i++) { + cairo_bo_start_event_t *event = + ((cairo_bo_start_event_t **) start_events)[i]; + event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n", + (long) &events[i].edge, + event->edge.edge.line.p1.x, + event->edge.edge.line.p1.y, + event->edge.edge.line.p2.x, + event->edge.edge.line.p2.y, + event->edge.top, + event->edge.bottom, + event->edge.edge.dir); + } + } +#endif + + _cairo_bo_event_queue_init (&event_queue, start_events, num_events); + _cairo_bo_sweep_line_init (&sweep_line); + + while ((event = _cairo_bo_event_dequeue (&event_queue))) { + if (event->point.y != sweep_line.current_y) { + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (e1, + e1->edge.bottom, + traps); + if (unlikely (status)) + goto unwind; + } + } + sweep_line.stopped = NULL; + + status = _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, traps); + if (unlikely (status)) + goto unwind; + + sweep_line.current_y = event->point.y; + } + +#if DEBUG_EVENTS + event_log ("event: %d (%ld, %ld) %lu, %lu\n", + event->type, + (long) event->point.x, + (long) event->point.y, + (long) event->e1, + (long) event->e2); +#endif + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + e1 = &((cairo_bo_start_event_t *) event)->edge; + + status = _cairo_bo_sweep_line_insert (&sweep_line, e1); + if (unlikely (status)) + goto unwind; + + status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); + if (unlikely (status)) + goto unwind; + + /* check to see if this is a continuation of a stopped edge */ + /* XXX change to an infinitesimal lengthening rule */ + for (left = sweep_line.stopped; left; left = left->next) { + if (e1->edge.top <= left->edge.bottom && + edges_colinear (e1, left)) + { + e1->deferred_trap = left->deferred_trap; + if (left->prev != NULL) + left->prev = left->next; + else + sweep_line.stopped = left->next; + if (left->next != NULL) + left->next->prev = left->prev; + break; + } + } + + left = e1->prev; + right = e1->next; + + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + _cairo_bo_event_queue_delete (&event_queue, event); + + left = e1->prev; + right = e1->next; + + _cairo_bo_sweep_line_delete (&sweep_line, e1); + + /* first, check to see if we have a continuation via a fresh edge */ + if (e1->deferred_trap.right != NULL) { + e1->next = sweep_line.stopped; + if (sweep_line.stopped != NULL) + sweep_line.stopped->prev = e1; + sweep_line.stopped = e1; + e1->prev = NULL; + } + + if (left != NULL && right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_INTERSECTION: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + e2 = ((cairo_bo_queue_event_t *) event)->e2; + _cairo_bo_event_queue_delete (&event_queue, event); + + /* skip this intersection if its edges are not adjacent */ + if (e2 != e1->next) + break; + + intersection_count++; + + left = e1->prev; + right = e2->next; + + _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); + + /* after the swap e2 is left of e1 */ + + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + } + } + + *num_intersections = intersection_count; + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); + if (unlikely (status)) + break; + } + } + unwind: + _cairo_bo_event_queue_fini (&event_queue); + +#if DEBUG_EVENTS + event_log ("\n"); +#endif + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule) +{ + int intersections; + cairo_status_t status; + cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; + cairo_bo_start_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + int num_events; + int i; + + num_events = polygon->num_edges; + if (unlikely (0 == num_events)) + return CAIRO_STATUS_SUCCESS; + + events = stack_events; + event_ptrs = stack_event_ptrs; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_start_event_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + } + + for (i = 0; i < num_events; i++) { + event_ptrs[i] = (cairo_bo_event_t *) &events[i]; + + events[i].type = CAIRO_BO_EVENT_TYPE_START; + events[i].point.y = polygon->edges[i].top; + events[i].point.x = + _line_compute_intersection_x_for_y (&polygon->edges[i].line, + events[i].point.y); + + events[i].edge.edge = polygon->edges[i]; + events[i].edge.deferred_trap.right = NULL; + events[i].edge.prev = NULL; + events[i].edge.next = NULL; + } + +#if DEBUG_TRAPS + dump_edges (events, num_events, "bo-polygon-edges.txt"); +#endif + + /* XXX: This would be the convenient place to throw in multiple + * passes of the Bentley-Ottmann algorithm. It would merely + * require storing the results of each pass into a temporary + * cairo_traps_t. */ + status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, + num_events, + fill_rule, traps, + &intersections); +#if DEBUG_TRAPS + dump_traps (traps, "bo-polygon-out.txt"); +#endif + + if (events != stack_events) + free (events); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_polygon_t polygon; + int i; + + if (unlikely (0 == traps->num_traps)) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-in.txt"); +#endif + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + for (i = 0; i < traps->num_traps; i++) { + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].left, + traps->traps[i].top, + traps->traps[i].bottom, + 1); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].right, + traps->traps[i].top, + traps->traps[i].bottom, + -1); + if (unlikely (status)) + goto CLEANUP; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, + fill_rule); + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-out.txt"); +#endif + + CLEANUP: + _cairo_polygon_fini (&polygon); + + return status; +} + +#if 0 +static cairo_bool_t +edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, + int num_edges) + +{ + int i, j; + cairo_bo_edge_t *a, *b; + cairo_bo_point32_t intersection; + + /* We must not be given any upside-down edges. */ + for (i = 0; i < num_edges; i++) { + assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0); + edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS; + } + + for (i = 0; i < num_edges; i++) { + for (j = 0; j < num_edges; j++) { + if (i == j) + continue; + + a = &edges[i]; + b = &edges[j]; + + if (! _cairo_bo_edge_intersect (a, b, &intersection)) + continue; + + printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n", + intersection.x, + intersection.y, + a->line.p1.x, a->line.p1.y, + a->line.p2.x, a->line.p2.y, + b->line.p1.x, b->line.p1.y, + b->line.p2.x, b->line.p2.y); + + return TRUE; + } + } + return FALSE; +} + +#define TEST_MAX_EDGES 10 + +typedef struct test { + const char *name; + const char *description; + int num_edges; + cairo_bo_edge_t edges[TEST_MAX_EDGES]; +} test_t; + +static test_t +tests[] = { + { + "3 near misses", + "3 edges all intersecting very close to each other", + 3, + { + { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL }, + { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL }, + { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL } + } + }, + { + "inconsistent data", + "Derived from random testing---was leading to skip list and edge list disagreeing.", + 2, + { + { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL }, + { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL } + } + }, + { + "failed sort", + "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?", + 3, + { + { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL }, + { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL }, + { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL }, + } + }, + { + "minimal-intersection", + "Intersection of a two from among the smallest possible edges.", + 2, + { + { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL }, + { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL } + } + }, + { + "simple", + "A simple intersection of two edges at an integer (2,2).", + 2, + { + { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL }, + { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL } + } + }, + { + "bend-to-horizontal", + "With intersection truncation one edge bends to horizontal", + 2, + { + { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL }, + { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL } + } + } +}; + +/* + { + "endpoint", + "An intersection that occurs at the endpoint of a segment.", + { + { { 4, 6}, { 5, 6}, NULL, { { NULL }} }, + { { 4, 5}, { 5, 7}, NULL, { { NULL }} }, + { { 0, 0}, { 0, 0}, NULL, { { NULL }} }, + } + } + { + name = "overlapping", + desc = "Parallel segments that share an endpoint, with different slopes.", + edges = { + { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}}, + { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}}, + { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}}, + { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}}, + { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}}, + { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}} + } + }, + { + name = "hobby_stage_3", + desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.", + edges = { + { top = { x = -1, y = -2}, bottom = { x = 4, y = 2}}, + { top = { x = 5, y = 3}, bottom = { x = 9, y = 5}}, + { top = { x = 5, y = 3}, bottom = { x = 6, y = 3}}, + } + }, + { + name = "hobby", + desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.", + edges = { + { top = { x = 0, y = 0}, bottom = { x = 9, y = 5}}, + { top = { x = 0, y = 0}, bottom = { x = 13, y = 6}}, + { top = { x = -1, y = -2}, bottom = { x = 9, y = 5}} + } + }, + { + name = "slope", + desc = "Edges with same start/stop points but different slopes", + edges = { + { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}}, + { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}}, + { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}}, + { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}} + } + }, + { + name = "horizontal", + desc = "Test of a horizontal edge", + edges = { + { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}}, + { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}} + } + }, + { + name = "vertical", + desc = "Test of a vertical edge", + edges = { + { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, + { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} + } + }, + { + name = "congruent", + desc = "Two overlapping edges with the same slope", + edges = { + { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, + { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}}, + { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} + } + }, + { + name = "multi", + desc = "Several segments with a common intersection point", + edges = { + { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} }, + { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} }, + { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} }, + { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} }, + { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} }, + { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} } + } + } +}; +*/ + +static int +run_test (const char *test_name, + cairo_bo_edge_t *test_edges, + int num_edges) +{ + int i, intersections, passes; + cairo_bo_edge_t *edges; + cairo_array_t intersected_edges; + + printf ("Testing: %s\n", test_name); + + _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); + + intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges); + if (intersections) + printf ("Pass 1 found %d intersections:\n", intersections); + + + /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a + * pass of Hobby's tolerance-square algorithm instead. */ + passes = 1; + while (intersections) { + int num_edges = _cairo_array_num_elements (&intersected_edges); + passes++; + edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t)); + assert (edges != NULL); + memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t)); + _cairo_array_fini (&intersected_edges); + _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); + intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges); + free (edges); + + if (intersections){ + printf ("Pass %d found %d remaining intersections:\n", passes, intersections); + } else { + if (passes > 3) + for (i = 0; i < passes; i++) + printf ("*"); + printf ("No remainining intersections found after pass %d\n", passes); + } + } + + if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0), + _cairo_array_num_elements (&intersected_edges))) + printf ("*** FAIL ***\n"); + else + printf ("PASS\n"); + + _cairo_array_fini (&intersected_edges); + + return 0; +} + +#define MAX_RANDOM 300 + +int +main (void) +{ + char random_name[] = "random-XX"; + cairo_bo_edge_t random_edges[MAX_RANDOM], *edge; + unsigned int i, num_random; + test_t *test; + + for (i = 0; i < ARRAY_LENGTH (tests); i++) { + test = &tests[i]; + run_test (test->name, test->edges, test->num_edges); + } + + for (num_random = 0; num_random < MAX_RANDOM; num_random++) { + srand (0); + for (i = 0; i < num_random; i++) { + do { + edge = &random_edges[i]; + edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + if (edge->line.p1.y > edge->line.p2.y) { + int32_t tmp = edge->line.p1.y; + edge->line.p1.y = edge->line.p2.y; + edge->line.p2.y = tmp; + } + } while (edge->line.p1.y == edge->line.p2.y); + } + + sprintf (random_name, "random-%02d", num_random); + + run_test (random_name, random_edges, num_random); + } + + return 0; +} +#endif diff --git a/libs/cairo/src/cairo-botor-scan-converter.c b/libs/cairo/src/cairo-botor-scan-converter.c new file mode 100644 index 000000000..ae060dc31 --- /dev/null +++ b/libs/cairo/src/cairo-botor-scan-converter.c @@ -0,0 +1,2162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-list-private.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-private.h" + +#include + +#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 t > 1 + * + * (where top/bottom must at least extend to the line endpoints). + * + * A similar substitution can be performed for s, yielding: + * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) + */ + R = det32_64 (dx2, dy2, + b->edge.line.p1.x - a->edge.line.p1.x, + b->edge.line.p1.y - a->edge.line.p1.y); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + R = det32_64 (dy1, dx1, + a->edge.line.p1.y - b->edge.line.p1.y, + a->edge.line.p1.x - b->edge.line.p1.x); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + /* We now know that the two lines should intersect within range. */ + + a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, + a->edge.line.p2.x, a->edge.line.p2.y); + b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, + b->edge.line.p2.x, b->edge.line.p2.y); + + /* x = det (a_det, dx1, b_det, dx2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, + b_det, dx2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->x.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->x.exactness = INEXACT; + } +#endif + intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); + + /* y = det (a_det, dy1, b_det, dy2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, + b_det, dy2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->y.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + /* compute ceiling away from zero */ + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + intersection->y.exactness = INEXACT; + } +#endif + intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); + + return TRUE; +} + +static int +bo_intersect_ordinate_32_compare (int32_t a, int32_t b, int exactness) +{ + int cmp; + + /* First compare the quotient */ + cmp = a - b; + if (cmp) + return cmp; + + /* With quotient identical, if remainder is 0 then compare equal */ + /* Otherwise, the non-zero remainder makes a > b */ + return -(INEXACT == exactness); +} + +/* Does the given edge contain the given point. The point must already + * be known to be contained within the line determined by the edge, + * (most likely the point results from an intersection of this edge + * with another). + * + * If we had exact arithmetic, then this function would simply be a + * matter of examining whether the y value of the point lies within + * the range of y values of the edge. But since intersection points + * are not exact due to being rounded to the nearest integer within + * the available precision, we must also examine the x value of the + * point. + * + * The definition of "contains" here is that the given intersection + * point will be seen by the sweep line after the start event for the + * given edge and before the stop event for the edge. See the comments + * in the implementation for more details. + */ +static cairo_bool_t +bo_edge_contains_intersect_point (const edge_t *edge, + cairo_bo_intersect_point_t *point) +{ + int cmp_top, cmp_bottom; + + /* XXX: When running the actual algorithm, we don't actually need to + * compare against edge->top at all here, since any intersection above + * top is eliminated early via a slope comparison. We're leaving these + * here for now only for the sake of the quadratic-time intersection + * finder which needs them. + */ + + cmp_top = bo_intersect_ordinate_32_compare (point->y.ordinate, + edge->edge.top, + point->y.exactness); + if (cmp_top < 0) + return FALSE; + + cmp_bottom = bo_intersect_ordinate_32_compare (point->y.ordinate, + edge->edge.bottom, + point->y.exactness); + if (cmp_bottom > 0) + return FALSE; + + if (cmp_top > 0 && cmp_bottom < 0) + return TRUE; + + /* At this stage, the point lies on the same y value as either + * edge->top or edge->bottom, so we have to examine the x value in + * order to properly determine containment. */ + + /* If the y value of the point is the same as the y value of the + * top of the edge, then the x value of the point must be greater + * to be considered as inside the edge. Similarly, if the y value + * of the point is the same as the y value of the bottom of the + * edge, then the x value of the point must be less to be + * considered as inside. */ + + if (cmp_top == 0) { + cairo_fixed_t top_x; + + top_x = line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.top); + return bo_intersect_ordinate_32_compare (top_x, point->x.ordinate, point->x.exactness) < 0; + } else { /* cmp_bottom == 0 */ + cairo_fixed_t bot_x; + + bot_x = line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.bottom); + return bo_intersect_ordinate_32_compare (point->x.ordinate, bot_x, point->x.exactness) < 0; + } +} + +static cairo_bool_t +edge_intersect (const edge_t *a, + const edge_t *b, + cairo_point_t *intersection) +{ + cairo_bo_intersect_point_t quorem; + + if (! intersect_lines (a, b, &quorem)) + return FALSE; + + if (a->edge.top != a->edge.line.p1.y || a->edge.bottom != a->edge.line.p2.y) { + if (! bo_edge_contains_intersect_point (a, &quorem)) + return FALSE; + } + + if (b->edge.top != b->edge.line.p1.y || b->edge.bottom != b->edge.line.p2.y) { + if (! bo_edge_contains_intersect_point (b, &quorem)) + return FALSE; + } + + /* Now that we've correctly compared the intersection point and + * determined that it lies within the edge, then we know that we + * no longer need any more bits of storage for the intersection + * than we do for our edge coordinates. We also no longer need the + * remainder from the division. */ + intersection->x = quorem.x.ordinate; + intersection->y = quorem.y.ordinate; + + return TRUE; +} + +static inline int +event_compare (const event_t *a, const event_t *b) +{ + return a->y - b->y; +} + +static void +pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static void +pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_bool_t +pqueue_grow (pqueue_t *pq) +{ + event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (event_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (event_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + } + + pq->elements = new_elements; + return TRUE; +} + +static inline void +pqueue_push (sweep_line_t *sweep_line, event_t *event) +{ + event_t **elements; + int i, parent; + + if (unlikely (sweep_line->queue.pq.size + 1 == sweep_line->queue.pq.max_size)) { + if (unlikely (! pqueue_grow (&sweep_line->queue.pq))) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + elements = sweep_line->queue.pq.elements; + for (i = ++sweep_line->queue.pq.size; + i != PQ_FIRST_ENTRY && + event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; +} + +static inline void +pqueue_pop (pqueue_t *pq) +{ + event_t **elements = pq->elements; + event_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline void +event_insert (sweep_line_t *sweep_line, + event_type_t type, + edge_t *e1, + edge_t *e2, + cairo_fixed_t y) +{ + queue_event_t *event; + + event = _cairo_freepool_alloc (&sweep_line->queue.pool); + if (unlikely (event == NULL)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + event->y = y; + event->type = type; + event->e1 = e1; + event->e2 = e2; + + pqueue_push (sweep_line, (event_t *) event); +} + +static void +event_delete (sweep_line_t *sweep_line, + event_t *event) +{ + _cairo_freepool_free (&sweep_line->queue.pool, event); +} + +static inline event_t * +event_next (sweep_line_t *sweep_line) +{ + event_t *event, *cmp; + + event = sweep_line->queue.pq.elements[PQ_FIRST_ENTRY]; + cmp = *sweep_line->queue.start_events; + if (event == NULL || + (cmp != NULL && event_compare (cmp, event) < 0)) + { + event = cmp; + sweep_line->queue.start_events++; + } + else + { + pqueue_pop (&sweep_line->queue.pq); + } + + return event; +} + +CAIRO_COMBSORT_DECLARE (start_event_sort, event_t *, event_compare) + +static inline void +event_insert_stop (sweep_line_t *sweep_line, + edge_t *edge) +{ + event_insert (sweep_line, + EVENT_TYPE_STOP, + edge, NULL, + edge->edge.bottom); +} + +static inline void +event_insert_if_intersect_below_current_y (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right) +{ + cairo_point_t intersection; + + /* start points intersect */ + if (left->edge.line.p1.x == right->edge.line.p1.x && + left->edge.line.p1.y == right->edge.line.p1.y) + { + return; + } + + /* end points intersect, process DELETE events first */ + if (left->edge.line.p2.x == right->edge.line.p2.x && + left->edge.line.p2.y == right->edge.line.p2.y) + { + return; + } + + if (slope_compare (left, right) <= 0) + return; + + if (! edge_intersect (left, right, &intersection)) + return; + + event_insert (sweep_line, + EVENT_TYPE_INTERSECTION, + left, right, + intersection.y); +} + +static inline edge_t * +link_to_edge (cairo_list_t *link) +{ + return (edge_t *) link; +} + +static void +sweep_line_insert (sweep_line_t *sweep_line, + edge_t *edge) +{ + cairo_list_t *pos; + cairo_fixed_t y = sweep_line->current_subrow; + + pos = sweep_line->insert_cursor; + if (pos == &sweep_line->active) + pos = sweep_line->active.next; + if (pos != &sweep_line->active) { + int cmp; + + cmp = sweep_line_compare_edges (link_to_edge (pos), + edge, + y); + if (cmp < 0) { + while (pos->next != &sweep_line->active && + sweep_line_compare_edges (link_to_edge (pos->next), + edge, + y) < 0) + { + pos = pos->next; + } + } else if (cmp > 0) { + do { + pos = pos->prev; + } while (pos != &sweep_line->active && + sweep_line_compare_edges (link_to_edge (pos), + edge, + y) > 0); + } + } + cairo_list_add (&edge->link, pos); + sweep_line->insert_cursor = &edge->link; +} + +inline static void +coverage_rewind (struct coverage *cells) +{ + cells->cursor = &cells->head; +} + +static void +coverage_init (struct coverage *cells) +{ + _cairo_freepool_init (&cells->pool, + sizeof (struct cell)); + cells->head.prev = NULL; + cells->head.next = &cells->tail; + cells->head.x = INT_MIN; + cells->tail.prev = &cells->head; + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->count = 0; + coverage_rewind (cells); +} + +static void +coverage_fini (struct coverage *cells) +{ + _cairo_freepool_fini (&cells->pool); +} + +inline static void +coverage_reset (struct coverage *cells) +{ + cells->head.next = &cells->tail; + cells->tail.prev = &cells->head; + cells->count = 0; + _cairo_freepool_reset (&cells->pool); + coverage_rewind (cells); +} + +inline static struct cell * +coverage_alloc (sweep_line_t *sweep_line, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = _cairo_freepool_alloc (&sweep_line->coverage.pool); + if (unlikely (NULL == cell)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + tail->prev->next = cell; + cell->prev = tail->prev; + cell->next = tail; + tail->prev = cell; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + sweep_line->coverage.count++; + return cell; +} + +inline static struct cell * +coverage_find (sweep_line_t *sweep_line, int x) +{ + struct cell *cell; + + cell = sweep_line->coverage.cursor; + if (unlikely (cell->x > x)) { + do { + if (cell->prev->x < x) + break; + cell = cell->prev; + } while (TRUE); + } else { + if (cell->x == x) + return cell; + + do { + UNROLL3({ + cell = cell->next; + if (cell->x >= x) + break; + }); + } while (TRUE); + } + + if (cell->x != x) + cell = coverage_alloc (sweep_line, cell, x); + + return sweep_line->coverage.cursor = cell; +} + +static void +coverage_render_cells (sweep_line_t *sweep_line, + cairo_fixed_t left, cairo_fixed_t right, + cairo_fixed_t y1, cairo_fixed_t y2, + int sign) +{ + int fx1, fx2; + int ix1, ix2; + int dx, dy; + + /* Orient the edge left-to-right. */ + dx = right - left; + if (dx >= 0) { + ix1 = _cairo_fixed_integer_part (left); + fx1 = _cairo_fixed_fractional_part (left); + + ix2 = _cairo_fixed_integer_part (right); + fx2 = _cairo_fixed_fractional_part (right); + + dy = y2 - y1; + } else { + ix1 = _cairo_fixed_integer_part (right); + fx1 = _cairo_fixed_fractional_part (right); + + ix2 = _cairo_fixed_integer_part (left); + fx2 = _cairo_fixed_fractional_part (left); + + dx = -dx; + sign = -sign; + dy = y1 - y2; + y1 = y2 - dy; + y2 = y1 + dy; + } + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx); + struct cell *cell; + + cell = sweep_line->coverage.cursor; + if (cell->x != ix1) { + if (unlikely (cell->x > ix1)) { + do { + if (cell->prev->x < ix1) + break; + cell = cell->prev; + } while (TRUE); + } else do { + UNROLL3({ + if (cell->x >= ix1) + break; + cell = cell->next; + }); + } while (TRUE); + + if (cell->x != ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + } + + cell->uncovered_area += sign * y.quo * (STEP_X + fx1); + cell->covered_height += sign * y.quo; + y.quo += y1; + + cell = cell->next; + if (cell->x != ++ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + if (ix1 < ix2) { + struct quorem dydx_full = floored_divrem (STEP_X*dy, dx); + + do { + cairo_fixed_t y_skip = dydx_full.quo; + y.rem += dydx_full.rem; + if (y.rem >= dx) { + ++y_skip; + y.rem -= dx; + } + + y.quo += y_skip; + + y_skip *= sign; + cell->covered_height += y_skip; + cell->uncovered_area += y_skip*STEP_X; + + cell = cell->next; + if (cell->x != ++ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + } while (ix1 != ix2); + } + cell->uncovered_area += sign*(y2 - y.quo)*fx2; + cell->covered_height += sign*(y2 - y.quo); + sweep_line->coverage.cursor = cell; + } +} + +inline static void +full_inc_edge (edge_t *edge) +{ + edge->x.quo += edge->dxdy_full.quo; + edge->x.rem += edge->dxdy_full.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } +} + +static void +full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign) +{ + struct cell *cell; + cairo_fixed_t x1, x2; + int ix1, ix2; + int frac; + + edge->current_sign = sign; + + ix1 = _cairo_fixed_integer_part (edge->x.quo); + + if (edge->vertical) { + frac = _cairo_fixed_fractional_part (edge->x.quo); + cell = coverage_find (sweep_line, ix1); + cell->covered_height += sign * STEP_Y; + cell->uncovered_area += sign * 2 * frac * STEP_Y; + return; + } + + x1 = edge->x.quo; + full_inc_edge (edge); + x2 = edge->x.quo; + + ix2 = _cairo_fixed_integer_part (edge->x.quo); + + /* Edge is entirely within a column? */ + if (likely (ix1 == ix2)) { + frac = _cairo_fixed_fractional_part (x1) + + _cairo_fixed_fractional_part (x2); + cell = coverage_find (sweep_line, ix1); + cell->covered_height += sign * STEP_Y; + cell->uncovered_area += sign * frac * STEP_Y; + return; + } + + coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign); +} + +static void +full_nonzero (sweep_line_t *sweep_line) +{ + cairo_list_t *pos; + + sweep_line->is_vertical = TRUE; + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = left->edge.dir; + + sweep_line->is_vertical &= left->vertical; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + full_add_edge (sweep_line, left, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + sweep_line->is_vertical &= right->vertical; + + winding += right->edge.dir; + if (0 == winding) { + if (pos == &sweep_line->active || + link_to_edge (pos)->x.quo != right->x.quo) + { + break; + } + } + + if (! right->vertical) + full_inc_edge (right); + } while (TRUE); + + full_add_edge (sweep_line, left, +1); + full_add_edge (sweep_line, right, -1); + } while (pos != &sweep_line->active); +} + +static void +full_evenodd (sweep_line_t *sweep_line) +{ + cairo_list_t *pos; + + sweep_line->is_vertical = TRUE; + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = 0; + + sweep_line->is_vertical &= left->vertical; + + pos = left->link.next; + do { + if (pos == &sweep_line->active) { + full_add_edge (sweep_line, left, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + sweep_line->is_vertical &= right->vertical; + + if (++winding & 1) { + if (pos == &sweep_line->active || + link_to_edge (pos)->x.quo != right->x.quo) + { + break; + } + } + + if (! right->vertical) + full_inc_edge (right); + } while (TRUE); + + full_add_edge (sweep_line, left, +1); + full_add_edge (sweep_line, right, -1); + } while (pos != &sweep_line->active); +} + +static void +render_rows (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line, + int y, int height, + cairo_span_renderer_t *renderer) +{ + cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; + cairo_half_open_span_t *spans = spans_stack; + struct cell *cell; + int prev_x, cover; + int num_spans; + cairo_status_t status; + + if (unlikely (sweep_line->coverage.count == 0)) { + status = renderer->render_rows (renderer, y, height, NULL, 0); + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + return; + } + + /* Allocate enough spans for the row. */ + + num_spans = 2*sweep_line->coverage.count+2; + if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) { + spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t)); + if (unlikely (spans == NULL)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + /* Form the spans from the coverage and areas. */ + num_spans = 0; + prev_x = self->xmin; + cover = 0; + cell = sweep_line->coverage.head.next; + do { + int x = cell->x; + int area; + + if (x > prev_x) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = AREA_TO_ALPHA (cover); + ++num_spans; + } + + cover += cell->covered_height*STEP_X*2; + area = cover - cell->uncovered_area; + + spans[num_spans].x = x; + spans[num_spans].coverage = AREA_TO_ALPHA (area); + ++num_spans; + + prev_x = x + 1; + } while ((cell = cell->next) != &sweep_line->coverage.tail); + + if (prev_x <= self->xmax) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = AREA_TO_ALPHA (cover); + ++num_spans; + } + + if (cover && prev_x < self->xmax) { + spans[num_spans].x = self->xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + + status = renderer->render_rows (renderer, y, height, spans, num_spans); + + if (unlikely (spans != spans_stack)) + free (spans); + + coverage_reset (&sweep_line->coverage); + + if (unlikely (status)) + longjmp (sweep_line->unwind, status); +} + +static void +full_repeat (sweep_line_t *sweep) +{ + edge_t *edge; + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { + if (edge->current_sign) + full_add_edge (sweep, edge, edge->current_sign); + else if (! edge->vertical) + full_inc_edge (edge); + } +} + +static void +full_reset (sweep_line_t *sweep) +{ + edge_t *edge; + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) + edge->current_sign = 0; +} + +static void +full_step (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line, + cairo_fixed_t row, + cairo_span_renderer_t *renderer) +{ + int top, bottom; + + top = _cairo_fixed_integer_part (sweep_line->current_row); + bottom = _cairo_fixed_integer_part (row); + if (cairo_list_is_empty (&sweep_line->active)) { + cairo_status_t status; + + status = renderer->render_rows (renderer, top, bottom - top, NULL, 0); + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + + return; + } + + if (self->fill_rule == CAIRO_FILL_RULE_WINDING) + full_nonzero (sweep_line); + else + full_evenodd (sweep_line); + + if (sweep_line->is_vertical || bottom == top + 1) { + render_rows (self, sweep_line, top, bottom - top, renderer); + full_reset (sweep_line); + return; + } + + render_rows (self, sweep_line, top++, 1, renderer); + do { + full_repeat (sweep_line); + render_rows (self, sweep_line, top, 1, renderer); + } while (++top != bottom); + + full_reset (sweep_line); +} + +cairo_always_inline static void +sub_inc_edge (edge_t *edge, + cairo_fixed_t height) +{ + if (height == 1) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + } else { + edge->x.quo += height * edge->dxdy.quo; + edge->x.rem += height * edge->dxdy.rem; + if (edge->x.rem >= 0) { + int carry = edge->x.rem / edge->dy + 1; + edge->x.quo += carry; + edge->x.rem -= carry * edge->dy; + } + } +} + +static void +sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign) +{ + struct run *run; + + run = _cairo_freepool_alloc (&sweep_line->runs); + if (unlikely (run == NULL)) + longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + run->y = y; + run->sign = sign; + run->next = edge->runs; + edge->runs = run; + + edge->current_sign = sign; +} + +inline static cairo_bool_t +edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y) +{ + /* XXX is compare_x_for_y() worth executing during sub steps? */ + return line_equal (&left->edge.line, &right->edge.line); + //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0; +} + +static void +sub_nonzero (sweep_line_t *sweep_line) +{ + cairo_fixed_t y = sweep_line->current_subrow; + cairo_fixed_t fy = _cairo_fixed_fractional_part (y); + cairo_list_t *pos; + + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = left->edge.dir; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + + winding += right->edge.dir; + if (0 == winding) { + if (pos == &sweep_line->active || + ! edges_coincident (right, link_to_edge (pos), y)) + { + break; + } + } + + if (right->current_sign) + sub_add_run (sweep_line, right, fy, 0); + } while (TRUE); + + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + if (right->current_sign != -1) + sub_add_run (sweep_line, right, fy, -1); + } while (pos != &sweep_line->active); +} + +static void +sub_evenodd (sweep_line_t *sweep_line) +{ + cairo_fixed_t y = sweep_line->current_subrow; + cairo_fixed_t fy = _cairo_fixed_fractional_part (y); + cairo_list_t *pos; + + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = 0; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + + if (++winding & 1) { + if (pos == &sweep_line->active || + ! edges_coincident (right, link_to_edge (pos), y)) + { + break; + } + } + + if (right->current_sign) + sub_add_run (sweep_line, right, fy, 0); + } while (TRUE); + + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + if (right->current_sign != -1) + sub_add_run (sweep_line, right, fy, -1); + } while (pos != &sweep_line->active); +} + +cairo_always_inline static void +sub_step (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line) +{ + if (cairo_list_is_empty (&sweep_line->active)) + return; + + if (self->fill_rule == CAIRO_FILL_RULE_WINDING) + sub_nonzero (sweep_line); + else + sub_evenodd (sweep_line); +} + +static void +coverage_render_runs (sweep_line_t *sweep, edge_t *edge, + cairo_fixed_t y1, cairo_fixed_t y2) +{ + struct run tail; + struct run *run = &tail; + + tail.next = NULL; + tail.y = y2; + + /* Order the runs top->bottom */ + while (edge->runs) { + struct run *r; + + r = edge->runs; + edge->runs = r->next; + r->next = run; + run = r; + } + + if (run->y > y1) + sub_inc_edge (edge, run->y - y1); + + do { + cairo_fixed_t x1, x2; + + y1 = run->y; + y2 = run->next->y; + + x1 = edge->x.quo; + if (y2 - y1 == STEP_Y) + full_inc_edge (edge); + else + sub_inc_edge (edge, y2 - y1); + x2 = edge->x.quo; + + if (run->sign) { + int ix1, ix2; + + ix1 = _cairo_fixed_integer_part (x1); + ix2 = _cairo_fixed_integer_part (x2); + + /* Edge is entirely within a column? */ + if (likely (ix1 == ix2)) { + struct cell *cell; + int frac; + + frac = _cairo_fixed_fractional_part (x1) + + _cairo_fixed_fractional_part (x2); + cell = coverage_find (sweep, ix1); + cell->covered_height += run->sign * (y2 - y1); + cell->uncovered_area += run->sign * (y2 - y1) * frac; + } else { + coverage_render_cells (sweep, x1, x2, y1, y2, run->sign); + } + } + + run = run->next; + } while (run->next != NULL); +} + +static void +coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2) +{ + struct cell *cell; + struct run *run; + int height = 0; + + for (run = edge->runs; run != NULL; run = run->next) { + if (run->sign) + height += run->sign * (y2 - run->y); + y2 = run->y; + } + + cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo)); + cell->covered_height += height; + cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height; +} + +cairo_always_inline static void +sub_emit (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep, + cairo_span_renderer_t *renderer) +{ + edge_t *edge; + + sub_step (self, sweep); + + /* convert the runs into coverages */ + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { + if (edge->runs == NULL) { + if (! edge->vertical) { + if (edge->flags & START) { + sub_inc_edge (edge, + STEP_Y - _cairo_fixed_fractional_part (edge->edge.top)); + edge->flags &= ~START; + } else + full_inc_edge (edge); + } + } else { + if (edge->vertical) { + coverage_render_vertical_runs (sweep, edge, STEP_Y); + } else { + int y1 = 0; + if (edge->flags & START) { + y1 = _cairo_fixed_fractional_part (edge->edge.top); + edge->flags &= ~START; + } + coverage_render_runs (sweep, edge, y1, STEP_Y); + } + } + edge->current_sign = 0; + edge->runs = NULL; + } + + cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) { + int y2 = _cairo_fixed_fractional_part (edge->edge.bottom); + if (edge->vertical) { + coverage_render_vertical_runs (sweep, edge, y2); + } else { + int y1 = 0; + if (edge->flags & START) + y1 = _cairo_fixed_fractional_part (edge->edge.top); + coverage_render_runs (sweep, edge, y1, y2); + } + } + cairo_list_init (&sweep->stopped); + + _cairo_freepool_reset (&sweep->runs); + + render_rows (self, sweep, + _cairo_fixed_integer_part (sweep->current_row), 1, + renderer); +} + +static void +sweep_line_init (sweep_line_t *sweep_line, + event_t **start_events, + int num_events) +{ + cairo_list_init (&sweep_line->active); + cairo_list_init (&sweep_line->stopped); + sweep_line->insert_cursor = &sweep_line->active; + + sweep_line->current_row = INT32_MIN; + sweep_line->current_subrow = INT32_MIN; + + coverage_init (&sweep_line->coverage); + _cairo_freepool_init (&sweep_line->runs, sizeof (struct run)); + + start_event_sort (start_events, num_events); + start_events[num_events] = NULL; + + sweep_line->queue.start_events = start_events; + + _cairo_freepool_init (&sweep_line->queue.pool, + sizeof (queue_event_t)); + pqueue_init (&sweep_line->queue.pq); + sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL; +} + +static void +sweep_line_delete (sweep_line_t *sweep_line, + edge_t *edge) +{ + if (sweep_line->insert_cursor == &edge->link) + sweep_line->insert_cursor = edge->link.prev; + + cairo_list_del (&edge->link); + if (edge->runs) + cairo_list_add_tail (&edge->link, &sweep_line->stopped); + edge->flags |= STOP; +} + +static void +sweep_line_swap (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right) +{ + right->link.prev = left->link.prev; + left->link.next = right->link.next; + right->link.next = &left->link; + left->link.prev = &right->link; + left->link.next->prev = &left->link; + right->link.prev->next = &right->link; +} + +static void +sweep_line_fini (sweep_line_t *sweep_line) +{ + pqueue_fini (&sweep_line->queue.pq); + _cairo_freepool_fini (&sweep_line->queue.pool); + coverage_fini (&sweep_line->coverage); + _cairo_freepool_fini (&sweep_line->runs); +} + +static cairo_status_t +botor_generate (cairo_botor_scan_converter_t *self, + event_t **start_events, + cairo_span_renderer_t *renderer) +{ + cairo_status_t status; + sweep_line_t sweep_line; + cairo_fixed_t ybot; + event_t *event; + cairo_list_t *left, *right; + edge_t *e1, *e2; + int bottom; + + sweep_line_init (&sweep_line, start_events, self->num_edges); + if ((status = setjmp (sweep_line.unwind))) + goto unwind; + + ybot = self->extents.p2.y; + sweep_line.current_subrow = self->extents.p1.y; + sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y); + event = *sweep_line.queue.start_events++; + do { + /* Can we process a full step in one go? */ + if (event->y >= sweep_line.current_row + STEP_Y) { + bottom = _cairo_fixed_floor (event->y); + full_step (self, &sweep_line, bottom, renderer); + sweep_line.current_row = bottom; + sweep_line.current_subrow = bottom; + } + + do { + if (event->y > sweep_line.current_subrow) { + sub_step (self, &sweep_line); + sweep_line.current_subrow = event->y; + } + + do { + /* Update the active list using Bentley-Ottmann */ + switch (event->type) { + case EVENT_TYPE_START: + e1 = ((start_event_t *) event)->edge; + + sweep_line_insert (&sweep_line, e1); + event_insert_stop (&sweep_line, e1); + + left = e1->link.prev; + right = e1->link.next; + + if (left != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), e1); + } + + if (right != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + e1, link_to_edge (right)); + } + + break; + + case EVENT_TYPE_STOP: + e1 = ((queue_event_t *) event)->e1; + event_delete (&sweep_line, event); + + left = e1->link.prev; + right = e1->link.next; + + sweep_line_delete (&sweep_line, e1); + + if (left != &sweep_line.active && + right != &sweep_line.active) + { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), + link_to_edge (right)); + } + + break; + + case EVENT_TYPE_INTERSECTION: + e1 = ((queue_event_t *) event)->e1; + e2 = ((queue_event_t *) event)->e2; + + event_delete (&sweep_line, event); + if (e1->flags & STOP) + break; + if (e2->flags & STOP) + break; + + /* skip this intersection if its edges are not adjacent */ + if (&e2->link != e1->link.next) + break; + + left = e1->link.prev; + right = e2->link.next; + + sweep_line_swap (&sweep_line, e1, e2); + + /* after the swap e2 is left of e1 */ + if (left != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), e2); + } + + if (right != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + e1, link_to_edge (right)); + } + + break; + } + + event = event_next (&sweep_line); + if (event == NULL) + goto end; + } while (event->y == sweep_line.current_subrow); + } while (event->y < sweep_line.current_row + STEP_Y); + + bottom = sweep_line.current_row + STEP_Y; + sub_emit (self, &sweep_line, renderer); + sweep_line.current_subrow = bottom; + sweep_line.current_row = sweep_line.current_subrow; + } while (TRUE); + + end: + /* flush any partial spans */ + if (sweep_line.current_subrow != sweep_line.current_row) { + sub_emit (self, &sweep_line, renderer); + sweep_line.current_row += STEP_Y; + sweep_line.current_subrow = sweep_line.current_row; + } + /* clear the rest */ + if (sweep_line.current_subrow < ybot) { + bottom = _cairo_fixed_integer_part (sweep_line.current_row); + status = renderer->render_rows (renderer, + bottom, _cairo_fixed_integer_ceil (ybot) - bottom, + NULL, 0); + } + + unwind: + sweep_line_fini (&sweep_line); + + return status; +} + +static cairo_status_t +_cairo_botor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_botor_scan_converter_t *self = converter; + start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)]; + start_event_t *events; + event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + event_t **event_ptrs; + struct _cairo_botor_scan_converter_chunk *chunk; + cairo_status_t status; + int num_events; + int i, j; + + num_events = self->num_edges; + if (unlikely (0 == num_events)) { + return renderer->render_rows (renderer, + _cairo_fixed_integer_floor (self->extents.p1.y), + _cairo_fixed_integer_ceil (self->extents.p2.y) - + _cairo_fixed_integer_floor (self->extents.p1.y), + NULL, 0); + } + + events = stack_events; + event_ptrs = stack_event_ptrs; + if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (start_event_t) + sizeof (event_t *), + sizeof (event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (event_t **) (events + num_events); + } + + j = 0; + for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { + edge_t *edge; + + edge = chunk->base; + for (i = 0; i < chunk->count; i++) { + event_ptrs[j] = (event_t *) &events[j]; + + events[j].y = edge->edge.top; + events[j].type = EVENT_TYPE_START; + events[j].edge = edge; + + edge++, j++; + } + } + + status = botor_generate (self, event_ptrs, renderer); + + if (events != stack_events) + free (events); + + return status; +} + +static edge_t * +botor_allocate_edge (cairo_botor_scan_converter_t *self) +{ + struct _cairo_botor_scan_converter_chunk *chunk; + + chunk = self->tail; + if (chunk->count == chunk->size) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (edge_t), + sizeof (struct _cairo_botor_scan_converter_chunk)); + if (unlikely (chunk->next == NULL)) + return NULL; + + chunk = chunk->next; + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = chunk + 1; + self->tail = chunk; + } + + return (edge_t *) chunk->base + chunk->count++; +} + +static cairo_status_t +botor_add_edge (cairo_botor_scan_converter_t *self, + const cairo_edge_t *edge) +{ + edge_t *e; + cairo_fixed_t dx, dy; + + e = botor_allocate_edge (self); + if (unlikely (e == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&e->link); + e->edge = *edge; + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; + + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } else { + e->vertical = FALSE; + e->dxdy = floored_divrem (dx, dy); + if (edge->top == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (edge->top - edge->line.p1.y, + dx, dy); + e->x.quo += edge->line.p1.x; + } + + if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) { + e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy); + } else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } + } + + e->x.rem = -e->dy; + e->current_sign = 0; + e->runs = NULL; + e->flags = START; + + self->num_edges++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_botor_scan_converter_add_edge (void *converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_botor_scan_converter_t *self = converter; + cairo_edge_t edge; + + edge.line.p1 = *p1; + edge.line.p2 = *p2; + edge.top = top; + edge.bottom = bottom; + edge.dir = dir; + + return botor_add_edge (self, &edge); +} + +static cairo_status_t +_cairo_botor_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) +{ + cairo_botor_scan_converter_t *self = converter; + cairo_status_t status; + int i; + + for (i = 0; i < polygon->num_edges; i++) { + status = botor_add_edge (self, &polygon->edges[i]); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_botor_scan_converter_destroy (void *converter) +{ + cairo_botor_scan_converter_t *self = converter; + struct _cairo_botor_scan_converter_chunk *chunk, *next; + + for (chunk = self->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } +} + +void +_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, + const cairo_box_t *extents, + cairo_fill_rule_t fill_rule) +{ + self->base.destroy = _cairo_botor_scan_converter_destroy; + self->base.add_edge = _cairo_botor_scan_converter_add_edge; + self->base.add_polygon = _cairo_botor_scan_converter_add_polygon; + self->base.generate = _cairo_botor_scan_converter_generate; + + self->extents = *extents; + self->fill_rule = fill_rule; + + self->xmin = _cairo_fixed_integer_floor (extents->p1.x); + self->xmax = _cairo_fixed_integer_ceil (extents->p2.x); + + self->chunks.base = self->buf; + self->chunks.next = NULL; + self->chunks.count = 0; + self->chunks.size = sizeof (self->buf) / sizeof (edge_t); + self->tail = &self->chunks; + + self->num_edges = 0; +} diff --git a/libs/cairo/src/cairo-boxes-private.h b/libs/cairo/src/cairo-boxes-private.h new file mode 100644 index 000000000..e61a64d7c --- /dev/null +++ b/libs/cairo/src/cairo-boxes-private.h @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_BOXES_H +#define CAIRO_BOXES_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +struct _cairo_boxes_t { + cairo_status_t status; + cairo_box_t limit; + const cairo_box_t *limits; + int num_limits; + int num_boxes; + unsigned int is_pixel_aligned : 1; + + struct _cairo_boxes_chunk { + struct _cairo_boxes_chunk *next; + cairo_box_t *base; + int count; + int size; + } chunks, *tail; + cairo_box_t boxes_embedded[32]; +}; + +cairo_private void +_cairo_boxes_init (cairo_boxes_t *boxes); + +cairo_private void +_cairo_boxes_init_for_array (cairo_boxes_t *boxes, + cairo_box_t *array, + int num_boxes); + +cairo_private void +_cairo_boxes_limit (cairo_boxes_t *boxes, + const cairo_box_t *limits, + int num_limits); + +cairo_private cairo_status_t +_cairo_boxes_add (cairo_boxes_t *boxes, + const cairo_box_t *box); + +cairo_private void +_cairo_boxes_extents (const cairo_boxes_t *boxes, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_boxes_clear (cairo_boxes_t *boxes); + +cairo_private void +_cairo_boxes_fini (cairo_boxes_t *boxes); + +#endif /* CAIRO_BOXES_H */ diff --git a/libs/cairo/src/cairo-boxes.c b/libs/cairo/src/cairo-boxes.c new file mode 100644 index 000000000..e29dd7e13 --- /dev/null +++ b/libs/cairo/src/cairo-boxes.c @@ -0,0 +1,271 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" + +void +_cairo_boxes_init (cairo_boxes_t *boxes) +{ + boxes->status = CAIRO_STATUS_SUCCESS; + boxes->num_limits = 0; + boxes->num_boxes = 0; + + boxes->tail = &boxes->chunks; + boxes->chunks.next = NULL; + boxes->chunks.base = boxes->boxes_embedded; + boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); + boxes->chunks.count = 0; + + boxes->is_pixel_aligned = TRUE; +} + +void +_cairo_boxes_init_for_array (cairo_boxes_t *boxes, + cairo_box_t *array, + int num_boxes) +{ + int n; + + boxes->status = CAIRO_STATUS_SUCCESS; + boxes->num_limits = 0; + boxes->num_boxes = num_boxes; + + boxes->tail = &boxes->chunks; + boxes->chunks.next = NULL; + boxes->chunks.base = array; + boxes->chunks.size = num_boxes; + boxes->chunks.count = num_boxes; + + for (n = 0; n < num_boxes; n++) { + if (! _cairo_fixed_is_integer (array[n].p1.x) || + ! _cairo_fixed_is_integer (array[n].p1.y) || + ! _cairo_fixed_is_integer (array[n].p2.x) || + ! _cairo_fixed_is_integer (array[n].p2.y)) + { + break; + } + } + + boxes->is_pixel_aligned = n == num_boxes; +} + +void +_cairo_boxes_limit (cairo_boxes_t *boxes, + const cairo_box_t *limits, + int num_limits) +{ + int n; + + boxes->limits = limits; + boxes->num_limits = num_limits; + + if (boxes->num_limits) { + boxes->limit = limits[0]; + for (n = 1; n < num_limits; n++) { + if (limits[n].p1.x < boxes->limit.p1.x) + boxes->limit.p1.x = limits[n].p1.x; + + if (limits[n].p1.y < boxes->limit.p1.y) + boxes->limit.p1.y = limits[n].p1.y; + + if (limits[n].p2.x > boxes->limit.p2.x) + boxes->limit.p2.x = limits[n].p2.x; + + if (limits[n].p2.y > boxes->limit.p2.y) + boxes->limit.p2.y = limits[n].p2.y; + } + } +} + +static void +_cairo_boxes_add_internal (cairo_boxes_t *boxes, + const cairo_box_t *box) +{ + struct _cairo_boxes_chunk *chunk; + + if (unlikely (boxes->status)) + return; + + chunk = boxes->tail; + if (unlikely (chunk->count == chunk->size)) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (cairo_box_t), + sizeof (struct _cairo_boxes_chunk)); + + if (unlikely (chunk->next == NULL)) { + boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return; + } + + chunk = chunk->next; + boxes->tail = chunk; + + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = (cairo_box_t *) (chunk + 1); + } + + chunk->base[chunk->count++] = *box; + boxes->num_boxes++; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (box->p1.x) && + _cairo_fixed_is_integer (box->p1.y) && + _cairo_fixed_is_integer (box->p2.x) && + _cairo_fixed_is_integer (box->p2.y); + } +} + +cairo_status_t +_cairo_boxes_add (cairo_boxes_t *boxes, + const cairo_box_t *box) +{ + if (box->p1.y == box->p2.y) + return CAIRO_STATUS_SUCCESS; + + if (box->p1.x == box->p2.x) + return CAIRO_STATUS_SUCCESS; + + if (boxes->num_limits) { + cairo_point_t p1, p2; + cairo_bool_t reversed = FALSE; + int n; + + /* support counter-clockwise winding for rectangular tessellation */ + if (box->p1.x < box->p2.x) { + p1.x = box->p1.x; + p2.x = box->p2.x; + } else { + p2.x = box->p1.x; + p1.x = box->p2.x; + reversed = ! reversed; + } + + if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x) + return CAIRO_STATUS_SUCCESS; + + if (box->p1.y < box->p2.y) { + p1.y = box->p1.y; + p2.y = box->p2.y; + } else { + p2.y = box->p1.y; + p1.y = box->p2.y; + reversed = ! reversed; + } + + if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y) + return CAIRO_STATUS_SUCCESS; + + for (n = 0; n < boxes->num_limits; n++) { + const cairo_box_t *limits = &boxes->limits[n]; + cairo_box_t _box; + cairo_point_t _p1, _p2; + + if (p1.x >= limits->p2.x || p2.x <= limits->p1.x) + continue; + if (p1.y >= limits->p2.y || p2.y <= limits->p1.y) + continue; + + /* Otherwise, clip the box to the limits. */ + _p1 = p1; + if (_p1.x < limits->p1.x) + _p1.x = limits->p1.x; + if (_p1.y < limits->p1.y) + _p1.y = limits->p1.y; + + _p2 = p2; + if (_p2.x > limits->p2.x) + _p2.x = limits->p2.x; + if (_p2.y > limits->p2.y) + _p2.y = limits->p2.y; + + if (_p2.y <= _p1.y || _p2.x <= _p1.x) + continue; + + _box.p1.y = _p1.y; + _box.p2.y = _p2.y; + if (reversed) { + _box.p1.x = _p2.x; + _box.p2.x = _p1.x; + } else { + _box.p1.x = _p1.x; + _box.p2.x = _p2.x; + } + + _cairo_boxes_add_internal (boxes, &_box); + } + } else { + _cairo_boxes_add_internal (boxes, box); + } + + return boxes->status; +} + +void +_cairo_boxes_extents (const cairo_boxes_t *boxes, + cairo_rectangle_int_t *extents) +{ + const struct _cairo_boxes_chunk *chunk; + cairo_box_t box; + int i; + + box.p1.y = box.p1.x = INT_MAX; + box.p2.y = box.p2.x = INT_MIN; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *b = chunk->base; + for (i = 0; i < chunk->count; i++) { + if (b[i].p1.x < box.p1.x) + box.p1.x = b[i].p1.x; + + if (b[i].p1.y < box.p1.y) + box.p1.y = b[i].p1.y; + + if (b[i].p2.x > box.p2.x) + box.p2.x = b[i].p2.x; + + if (b[i].p2.y > box.p2.y) + box.p2.y = b[i].p2.y; + } + } + + _cairo_box_round_to_rectangle (&box, extents); +} + +void +_cairo_boxes_clear (cairo_boxes_t *boxes) +{ + struct _cairo_boxes_chunk *chunk, *next; + + for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } + + boxes->tail = &boxes->chunks; + boxes->chunks.next = 0; + boxes->chunks.count = 0; + boxes->num_boxes = 0; + + boxes->is_pixel_aligned = TRUE; +} + +void +_cairo_boxes_fini (cairo_boxes_t *boxes) +{ + struct _cairo_boxes_chunk *chunk, *next; + + for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } +} diff --git a/libs/cairo/src/cairo-cache-private.h b/libs/cairo/src/cairo-cache-private.h new file mode 100644 index 000000000..06940a63a --- /dev/null +++ b/libs/cairo/src/cairo-cache-private.h @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_CACHE_PRIVATE_H +#define CAIRO_CACHE_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +/** + * cairo_cache_entry_t: + * + * A #cairo_cache_entry_t contains both a key and a value for + * #cairo_cache_t. User-derived types for #cairo_cache_entry_t must + * have a #cairo_cache_entry_t as their first field. For example: + * + * typedef _my_entry { + * cairo_cache_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the #cairo_cache_t functions as follows without requiring a cast: + * + * _cairo_cache_insert (cache, &my_entry->base, size); + * + * IMPORTANT: The caller is responsible for initializing + * my_entry->base.hash with a hash code derived from the key. The + * essential property of the hash code is that keys_equal must never + * return %TRUE for two keys that have different hashes. The best hash + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns %FALSE. + * + * The user must also initialize my_entry->base.size to indicate + * the size of the current entry. What units to use for size is + * entirely up to the caller, (though the same units must be used for + * the max_size parameter passed to _cairo_cache_create()). If all + * entries are close to the same size, the simplest thing to do is to + * just use units of "entries", (eg. set size==1 in all entries and + * set max_size to the number of entries which you want to be saved + * in the cache). + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the #cairo_cache_t functions accept an entry which + * will be used exclusively as a "key", (indicated by a parameter name + * of key). In these cases, the value-related fields of the entry need + * not be initialized if so desired. + **/ +typedef struct _cairo_cache_entry { + uintptr_t hash; + unsigned long size; +} cairo_cache_entry_t; + +typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry); + +struct _cairo_cache { + cairo_hash_table_t *hash_table; + + cairo_cache_predicate_func_t predicate; + cairo_destroy_func_t entry_destroy; + + unsigned long max_size; + unsigned long size; + + int freeze_count; +}; + +typedef cairo_bool_t +(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); + +typedef void +(*cairo_cache_callback_func_t) (void *entry, + void *closure); + +cairo_private cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_cache_predicate_func_t predicate, + cairo_destroy_func_t entry_destroy, + unsigned long max_size); + +cairo_private void +_cairo_cache_fini (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_freeze (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_thaw (cairo_cache_t *cache); + +cairo_private void * +_cairo_cache_lookup (cairo_cache_t *cache, + cairo_cache_entry_t *key); + +cairo_private cairo_status_t +_cairo_cache_insert (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_foreach (cairo_cache_t *cache, + cairo_cache_callback_func_t cache_callback, + void *closure); + +#endif diff --git a/libs/cairo/src/cairo-cache.c b/libs/cairo/src/cairo-cache.c new file mode 100644 index 000000000..021b012fd --- /dev/null +++ b/libs/cairo/src/cairo-cache.c @@ -0,0 +1,304 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-cff-subset.c b/libs/cairo/src/cairo-cff-subset.c new file mode 100644 index 000000000..6a5060047 --- /dev/null +++ b/libs/cairo/src/cairo-cff-subset.c @@ -0,0 +1,2246 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Useful links: + * http://www.adobe.com/devnet/font/pdfs/5176.CFF.pdf + */ + +#define _BSD_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-truetype-subset-private.h" +#include + +/* CFF Dict Operators. If the high byte is 0 the command is encoded + * with a single byte. */ +#define BASEFONTNAME_OP 0x0c16 +#define CIDCOUNT_OP 0x0c22 +#define CHARSET_OP 0x000f +#define CHARSTRINGS_OP 0x0011 +#define COPYRIGHT_OP 0x0c00 +#define ENCODING_OP 0x0010 +#define FAMILYNAME_OP 0x0003 +#define FDARRAY_OP 0x0c24 +#define FDSELECT_OP 0x0c25 +#define FONTBBOX_OP 0x0005 +#define FONTNAME_OP 0x0c26 +#define FULLNAME_OP 0x0002 +#define LOCAL_SUB_OP 0x0013 +#define NOTICE_OP 0x0001 +#define POSTSCRIPT_OP 0x0c15 +#define PRIVATE_OP 0x0012 +#define ROS_OP 0x0c1e +#define UNIQUEID_OP 0x000d +#define VERSION_OP 0x0000 +#define WEIGHT_OP 0x0004 +#define XUID_OP 0x000e + +#define NUM_STD_STRINGS 391 + +typedef struct _cff_header { + uint8_t major; + uint8_t minor; + uint8_t header_size; + uint8_t offset_size; +} cff_header_t; + +typedef struct _cff_index_element { + cairo_bool_t is_copy; + unsigned char *data; + int length; +} cff_index_element_t; + +typedef struct _cff_dict_operator { + cairo_hash_entry_t base; + + unsigned short operator; + unsigned char *operand; + int operand_length; + int operand_offset; +} cff_dict_operator_t; + +typedef struct _cairo_cff_font { + + cairo_scaled_font_subset_t *scaled_font_subset; + const cairo_scaled_font_backend_t *backend; + + /* Font Data */ + unsigned char *data; + unsigned long data_length; + unsigned char *current_ptr; + unsigned char *data_end; + cff_header_t *header; + char *font_name; + char *ps_name; + cairo_hash_table_t *top_dict; + cairo_hash_table_t *private_dict; + cairo_array_t strings_index; + cairo_array_t charstrings_index; + cairo_array_t global_sub_index; + cairo_array_t local_sub_index; + int num_glyphs; + cairo_bool_t is_cid; + int units_per_em; + + /* CID Font Data */ + int *fdselect; + unsigned int num_fontdicts; + cairo_hash_table_t **fd_dict; + cairo_hash_table_t **fd_private_dict; + cairo_array_t *fd_local_sub_index; + + /* Subsetted Font Data */ + char *subset_font_name; + cairo_array_t charstrings_subset_index; + cairo_array_t strings_subset_index; + int *fdselect_subset; + unsigned int num_subset_fontdicts; + int *fd_subset_map; + int *private_dict_offset; + cairo_array_t output; + + /* Subset Metrics */ + int *widths; + int x_min, y_min, x_max, y_max; + int ascent, descent; + +} cairo_cff_font_t; + +/* Encoded integer using maximum sized encoding. This is required for + * operands that are later modified after encoding. */ +static unsigned char * +encode_integer_max (unsigned char *p, int i) +{ + *p++ = 29; + *p++ = i >> 24; + *p++ = (i >> 16) & 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + return p; +} + +static unsigned char * +encode_integer (unsigned char *p, int i) +{ + if (i >= -107 && i <= 107) { + *p++ = i + 139; + } else if (i >= 108 && i <= 1131) { + i -= 108; + *p++ = (i >> 8)+ 247; + *p++ = i & 0xff; + } else if (i >= -1131 && i <= -108) { + i = -i - 108; + *p++ = (i >> 8)+ 251; + *p++ = i & 0xff; + } else if (i >= -32768 && i <= 32767) { + *p++ = 28; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + } else { + p = encode_integer_max (p, i); + } + return p; +} + +static unsigned char * +decode_integer (unsigned char *p, int *integer) +{ + if (*p == 28) { + *integer = (int)(p[1]<<8 | p[2]); + p += 3; + } else if (*p == 29) { + *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); + p += 5; + } else if (*p >= 32 && *p <= 246) { + *integer = *p++ - 139; + } else if (*p <= 250) { + *integer = (p[0] - 247) * 256 + p[1] + 108; + p += 2; + } else if (*p <= 254) { + *integer = -(p[0] - 251) * 256 - p[1] - 108; + p += 2; + } else { + *integer = 0; + p += 1; + } + return p; +} + +static unsigned char * +decode_operator (unsigned char *p, unsigned short *operator) +{ + unsigned short op = 0; + + op = *p++; + if (op == 12) { + op <<= 8; + op |= *p++; + } + *operator = op; + return p; +} + +/* return 0 if not an operand */ +static int +operand_length (unsigned char *p) +{ + unsigned char *begin = p; + + if (*p == 28) + return 3; + + if (*p == 29) + return 5; + + if (*p >= 32 && *p <= 246) + return 1; + + if (*p >= 247 && *p <= 254) + return 2; + + if (*p == 30) { + while ((*p & 0x0f) != 0x0f) + p++; + return p - begin + 1; + } + + return 0; +} + +static unsigned char * +encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) +{ + while (--offset_size >= 0) { + p[offset_size] = (unsigned char) (offset & 0xff); + offset >>= 8; + } + return p + offset_size; +} + +static unsigned long +decode_index_offset(unsigned char *p, int off_size) +{ + unsigned long offset = 0; + + while (off_size-- > 0) + offset = offset*256 + *p++; + return offset; +} + +static void +cff_index_init (cairo_array_t *index) +{ + _cairo_array_init (index, sizeof (cff_index_element_t)); +} + +static cairo_int_status_t +cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr) +{ + cff_index_element_t element; + unsigned char *data, *p; + cairo_status_t status; + int offset_size, count, start, i; + int end = 0; + + p = *ptr; + if (p + 2 > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + count = be16_to_cpu( *((uint16_t *)p) ); + p += 2; + if (count > 0) { + offset_size = *p++; + if (p + (count + 1)*offset_size > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + data = p + offset_size*(count + 1) - 1; + start = decode_index_offset (p, offset_size); + p += offset_size; + for (i = 0; i < count; i++) { + end = decode_index_offset (p, offset_size); + p += offset_size; + if (p > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + element.length = end - start; + element.is_copy = FALSE; + element.data = data + start; + status = _cairo_array_append (index, &element); + if (unlikely (status)) + return status; + start = end; + } + p = data + end; + } + *ptr = p; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cff_index_write (cairo_array_t *index, cairo_array_t *output) +{ + int offset_size; + int offset; + int num_elem; + int i; + cff_index_element_t *element; + uint16_t count; + unsigned char buf[5]; + cairo_status_t status; + + num_elem = _cairo_array_num_elements (index); + count = cpu_to_be16 ((uint16_t) num_elem); + status = _cairo_array_append_multiple (output, &count, 2); + if (unlikely (status)) + return status; + + if (num_elem == 0) + return CAIRO_STATUS_SUCCESS; + + /* Find maximum offset to determine offset size */ + offset = 1; + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + offset += element->length; + } + if (offset < 0x100) + offset_size = 1; + else if (offset < 0x10000) + offset_size = 2; + else if (offset < 0x1000000) + offset_size = 3; + else + offset_size = 4; + + buf[0] = (unsigned char) offset_size; + status = _cairo_array_append (output, buf); + if (unlikely (status)) + return status; + + offset = 1; + encode_index_offset (buf, offset_size, offset); + status = _cairo_array_append_multiple (output, buf, offset_size); + if (unlikely (status)) + return status; + + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + offset += element->length; + encode_index_offset (buf, offset_size, offset); + status = _cairo_array_append_multiple (output, buf, offset_size); + if (unlikely (status)) + return status; + } + + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + status = _cairo_array_append_multiple (output, + element->data, + element->length); + if (unlikely (status)) + return status; + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cff_index_append (cairo_array_t *index, unsigned char *object , int length) +{ + cff_index_element_t element; + + element.length = length; + element.is_copy = FALSE; + element.data = object; + + return _cairo_array_append (index, &element); +} + +static cairo_status_t +cff_index_append_copy (cairo_array_t *index, + const unsigned char *object, + unsigned int length) +{ + cff_index_element_t element; + cairo_status_t status; + + element.length = length; + element.is_copy = TRUE; + element.data = malloc (element.length); + if (unlikely (element.data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (element.data, object, element.length); + + status = _cairo_array_append (index, &element); + if (unlikely (status)) { + free (element.data); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +cff_index_fini (cairo_array_t *index) +{ + cff_index_element_t *element; + int i; + + for (i = 0; i < _cairo_array_num_elements (index); i++) { + element = _cairo_array_index (index, i); + if (element->is_copy) + free (element->data); + } + _cairo_array_fini (index); +} + +static cairo_bool_t +_cairo_cff_dict_equal (const void *key_a, const void *key_b) +{ + const cff_dict_operator_t *op_a = key_a; + const cff_dict_operator_t *op_b = key_b; + + return op_a->operator == op_b->operator; +} + +static cairo_status_t +cff_dict_init (cairo_hash_table_t **dict) +{ + *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); + if (unlikely (*dict == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_dict_init_key (cff_dict_operator_t *key, int operator) +{ + key->base.hash = (unsigned long) operator; + key->operator = operator; +} + +static cairo_status_t +cff_dict_create_operator (int operator, + unsigned char *operand, + int size, + cff_dict_operator_t **out) +{ + cff_dict_operator_t *op; + + op = malloc (sizeof (cff_dict_operator_t)); + if (unlikely (op == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_dict_init_key (op, operator); + op->operand = malloc (size); + if (unlikely (op->operand == NULL)) { + free (op); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (op->operand, operand, size); + op->operand_length = size; + op->operand_offset = -1; + + *out = op; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) +{ + unsigned char *end; + cairo_array_t operands; + cff_dict_operator_t *op; + unsigned short operator; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int size; + + end = p + dict_size; + _cairo_array_init (&operands, 1); + while (p < end) { + size = operand_length (p); + if (size != 0) { + status = _cairo_array_append_multiple (&operands, p, size); + if (unlikely (status)) + goto fail; + + p += size; + } else { + p = decode_operator (p, &operator); + status = cff_dict_create_operator (operator, + _cairo_array_index (&operands, 0), + _cairo_array_num_elements (&operands), + &op); + if (unlikely (status)) + goto fail; + + status = _cairo_hash_table_insert (dict, &op->base); + if (unlikely (status)) + goto fail; + + _cairo_array_truncate (&operands, 0); + } + } + +fail: + _cairo_array_fini (&operands); + + return status; +} + +static void +cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + free (op->operand); + _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); + free (op); + } +} + +static unsigned char * +cff_dict_get_operands (cairo_hash_table_t *dict, + unsigned short operator, + int *size) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + *size = op->operand_length; + return op->operand; + } + + return NULL; +} + +static cairo_status_t +cff_dict_set_operands (cairo_hash_table_t *dict, + unsigned short operator, + unsigned char *operand, + int size) +{ + cff_dict_operator_t key, *op; + cairo_status_t status; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + free (op->operand); + op->operand = malloc (size); + if (unlikely (op->operand == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (op->operand, operand, size); + op->operand_length = size; + } + else + { + status = cff_dict_create_operator (operator, operand, size, &op); + if (unlikely (status)) + return status; + + status = _cairo_hash_table_insert (dict, &op->base); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static int +cff_dict_get_location (cairo_hash_table_t *dict, + unsigned short operator, + int *size) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + *size = op->operand_length; + return op->operand_offset; + } + + return -1; +} + +typedef struct _dict_write_info { + cairo_array_t *output; + cairo_status_t status; +} dict_write_info_t; + +static void +cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info) +{ + unsigned char data; + + op->operand_offset = _cairo_array_num_elements (write_info->output); + write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length); + if (write_info->status) + return; + + if (op->operator & 0xff00) { + data = op->operator >> 8; + write_info->status = _cairo_array_append (write_info->output, &data); + if (write_info->status) + return; + } + data = op->operator & 0xff; + write_info->status = _cairo_array_append (write_info->output, &data); +} + +static void +_cairo_dict_collect (void *entry, void *closure) +{ + dict_write_info_t *write_info = closure; + cff_dict_operator_t *op = entry; + + if (write_info->status) + return; + + /* The ROS operator is handled separately in cff_dict_write() */ + if (op->operator != ROS_OP) + cairo_dict_write_operator (op, write_info); +} + +static cairo_status_t +cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) +{ + dict_write_info_t write_info; + cff_dict_operator_t key, *op; + + write_info.output = output; + write_info.status = CAIRO_STATUS_SUCCESS; + + /* The CFF specification requires that the Top Dict of CID fonts + * begin with the ROS operator. */ + _cairo_dict_init_key (&key, ROS_OP); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) + cairo_dict_write_operator (op, &write_info); + + _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); + + return write_info.status; +} + +static void +_cff_dict_entry_pluck (void *_entry, void *dict) +{ + cff_dict_operator_t *entry = _entry; + + _cairo_hash_table_remove (dict, &entry->base); + free (entry->operand); + free (entry); +} + +static void +cff_dict_fini (cairo_hash_table_t *dict) +{ + _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); + _cairo_hash_table_destroy (dict); +} + +static cairo_int_status_t +cairo_cff_font_read_header (cairo_cff_font_t *font) +{ + if (font->data_length < sizeof (cff_header_t)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->header = (cff_header_t *) font->data; + font->current_ptr = font->data + font->header->header_size; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_read_name (cairo_cff_font_t *font) +{ + cairo_array_t index; + cairo_int_status_t status; + + /* The original font name is not used in the subset. Read the name + * index to skip over it. */ + cff_index_init (&index); + status = cff_index_read (&index, &font->current_ptr, font->data_end); + cff_index_fini (&index); + + return status; +} + +static cairo_int_status_t +cairo_cff_font_read_private_dict (cairo_cff_font_t *font, + cairo_hash_table_t *private_dict, + cairo_array_t *local_sub_index, + unsigned char *ptr, + int size) +{ + cairo_int_status_t status; + unsigned char buf[10]; + unsigned char *end_buf; + int offset; + int i; + unsigned char *operand; + unsigned char *p; + + status = cff_dict_read (private_dict, ptr, size); + if (unlikely (status)) + return status; + + operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); + if (operand) { + decode_integer (operand, &offset); + p = ptr + offset; + status = cff_index_read (local_sub_index, &p, font->data_end); + if (unlikely (status)) + return status; + + /* Use maximum sized encoding to reserve space for later modification. */ + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) +{ + int type, num_ranges, first, last, fd, i, j; + + font->fdselect = calloc (font->num_glyphs, sizeof (int)); + if (unlikely (font->fdselect == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + type = *p++; + if (type == 0) + { + for (i = 0; i < font->num_glyphs; i++) + font->fdselect[i] = *p++; + } else if (type == 3) { + num_ranges = be16_to_cpu( *((uint16_t *)p) ); + p += 2; + for (i = 0; i < num_ranges; i++) + { + first = be16_to_cpu( *((uint16_t *)p) ); + p += 2; + fd = *p++; + last = be16_to_cpu( *((uint16_t *)p) ); + for (j = first; j < last; j++) + font->fdselect[j] = fd; + } + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) +{ + cairo_array_t index; + cff_index_element_t *element; + unsigned int i; + int size; + unsigned char *operand; + int offset; + cairo_int_status_t status; + unsigned char buf[100]; + unsigned char *end_buf; + + cff_index_init (&index); + status = cff_index_read (&index, &ptr, font->data_end); + if (unlikely (status)) + goto fail; + + font->num_fontdicts = _cairo_array_num_elements (&index); + + font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); + if (unlikely (font->fd_dict == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); + if (unlikely (font->fd_private_dict == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); + if (unlikely (font->fd_local_sub_index == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + for (i = 0; i < font->num_fontdicts; i++) { + status = cff_dict_init (&font->fd_dict[i]); + if (unlikely (status)) + goto fail; + + element = _cairo_array_index (&index, i); + status = cff_dict_read (font->fd_dict[i], element->data, element->length); + if (unlikely (status)) + goto fail; + + operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); + if (operand == NULL) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto fail; + } + operand = decode_integer (operand, &size); + decode_integer (operand, &offset); + status = cff_dict_init (&font->fd_private_dict[i]); + if (unlikely (status)) + goto fail; + + cff_index_init (&font->fd_local_sub_index[i]); + status = cairo_cff_font_read_private_dict (font, + font->fd_private_dict[i], + &font->fd_local_sub_index[i], + font->data + offset, + size); + if (unlikely (status)) + goto fail; + + /* Set integer operand to max value to use max size encoding to reserve + * space for any value later */ + end_buf = encode_integer_max (buf, 0); + end_buf = encode_integer_max (end_buf, 0); + status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + } + + return CAIRO_STATUS_SUCCESS; + +fail: + cff_index_fini (&index); + + return status; +} + +static cairo_int_status_t +cairo_cff_font_read_top_dict (cairo_cff_font_t *font) +{ + cairo_array_t index; + cff_index_element_t *element; + unsigned char buf[20]; + unsigned char *end_buf; + unsigned char *operand; + cairo_int_status_t status; + unsigned char *p; + int size; + int offset; + + cff_index_init (&index); + status = cff_index_read (&index, &font->current_ptr, font->data_end); + if (unlikely (status)) + goto fail; + + element = _cairo_array_index (&index, 0); + status = cff_dict_read (font->top_dict, element->data, element->length); + if (unlikely (status)) + goto fail; + + if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) + font->is_cid = TRUE; + else + font->is_cid = FALSE; + + operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size); + decode_integer (operand, &offset); + p = font->data + offset; + status = cff_index_read (&font->charstrings_index, &p, font->data_end); + if (unlikely (status)) + goto fail; + font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); + + if (font->is_cid) { + operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); + decode_integer (operand, &offset); + status = cairo_cff_font_read_fdselect (font, font->data + offset); + if (unlikely (status)) + goto fail; + + operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); + decode_integer (operand, &offset); + status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); + if (unlikely (status)) + goto fail; + } else { + operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); + operand = decode_integer (operand, &size); + decode_integer (operand, &offset); + status = cairo_cff_font_read_private_dict (font, + font->private_dict, + &font->local_sub_index, + font->data + offset, + size); + if (unlikely (status)) + goto fail; + } + + /* Use maximum sized encoding to reserve space for later modification. */ + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (font->top_dict, + CHARSTRINGS_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + status = cff_dict_set_operands (font->top_dict, + FDSELECT_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + status = cff_dict_set_operands (font->top_dict, + FDARRAY_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + status = cff_dict_set_operands (font->top_dict, + CHARSET_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + cff_dict_remove (font->top_dict, ENCODING_OP); + cff_dict_remove (font->top_dict, PRIVATE_OP); + + /* Remove the unique identifier operators as the subsetted font is + * not the same is the original font. */ + cff_dict_remove (font->top_dict, UNIQUEID_OP); + cff_dict_remove (font->top_dict, XUID_OP); + +fail: + cff_index_fini (&index); + + return status; +} + +static cairo_int_status_t +cairo_cff_font_read_strings (cairo_cff_font_t *font) +{ + return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end); +} + +static cairo_int_status_t +cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) +{ + return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); +} + +typedef cairo_int_status_t +(*font_read_t) (cairo_cff_font_t *font); + +static const font_read_t font_read_funcs[] = { + cairo_cff_font_read_header, + cairo_cff_font_read_name, + cairo_cff_font_read_top_dict, + cairo_cff_font_read_strings, + cairo_cff_font_read_global_subroutines, +}; + +static cairo_int_status_t +cairo_cff_font_read_font (cairo_cff_font_t *font) +{ + cairo_int_status_t status; + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { + status = font_read_funcs[i] (font); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) +{ + cairo_status_t status; + unsigned char buf[30]; + unsigned char *p; + int sid1, sid2; + const char *registry = "Adobe"; + const char *ordering = "Identity"; + + sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)registry, + strlen(registry)); + if (unlikely (status)) + return status; + + sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)ordering, + strlen(ordering)); + if (unlikely (status)) + return status; + + p = encode_integer (buf, sid1); + p = encode_integer (p, sid2); + p = encode_integer (p, 0); + status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); + if (unlikely (status)) + return status; + + p = encode_integer (buf, font->scaled_font_subset->num_glyphs); + status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, + cairo_hash_table_t *dict, + int operator) +{ + int size; + unsigned char *p; + int sid; + unsigned char buf[100]; + cff_index_element_t *element; + cairo_status_t status; + + p = cff_dict_get_operands (dict, operator, &size); + if (!p) + return CAIRO_STATUS_SUCCESS; + + decode_integer (p, &sid); + if (sid < NUM_STD_STRINGS) + return CAIRO_STATUS_SUCCESS; + + element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); + sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append (&font->strings_subset_index, element->data, element->length); + if (unlikely (status)) + return status; + + p = encode_integer (buf, sid); + status = cff_dict_set_operands (dict, operator, buf, p - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const int dict_strings[] = { + VERSION_OP, + NOTICE_OP, + COPYRIGHT_OP, + FULLNAME_OP, + FAMILYNAME_OP, + WEIGHT_OP, + POSTSCRIPT_OP, + BASEFONTNAME_OP, + FONTNAME_OP, +}; + +static cairo_status_t +cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, + cairo_hash_table_t *dict) +{ + cairo_status_t status; + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { + status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_charstrings (cairo_cff_font_t *font) +{ + cff_index_element_t *element; + unsigned int i; + cairo_status_t status; + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + element = _cairo_array_index (&font->charstrings_index, + font->scaled_font_subset->glyphs[i]); + status = cff_index_append (&font->charstrings_subset_index, + element->data, + element->length); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) +{ + unsigned int i; + int fd; + int *reverse_map; + + font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, + sizeof (int)); + if (unlikely (font->fdselect_subset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (font->fd_subset_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + reverse_map = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (reverse_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < font->num_fontdicts; i++) + reverse_map[i] = -1; + + font->num_subset_fontdicts = 0; + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + fd = font->fdselect[font->scaled_font_subset->glyphs[i]]; + if (reverse_map[fd] < 0) { + font->fd_subset_map[font->num_subset_fontdicts] = fd; + reverse_map[fd] = font->num_subset_fontdicts++; + } + font->fdselect_subset[i] = reverse_map[fd]; + } + + free (reverse_map); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) +{ + unsigned char buf[100]; + unsigned char *end_buf; + cairo_status_t status; + + font->num_fontdicts = 1; + font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); + if (unlikely (font->fd_dict == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (cff_dict_init (&font->fd_dict[0])) { + free (font->fd_dict); + font->fd_dict = NULL; + font->num_fontdicts = 0; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + font->fd_subset_map = malloc (sizeof (int)); + if (unlikely (font->fd_subset_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->private_dict_offset = malloc (sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->fd_subset_map[0] = 0; + font->num_subset_fontdicts = 1; + + /* Set integer operand to max value to use max size encoding to reserve + * space for any value later */ + end_buf = encode_integer_max (buf, 0); + end_buf = encode_integer_max (end_buf, 0); + status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_strings (cairo_cff_font_t *font) +{ + cairo_status_t status; + unsigned int i; + + status = cairo_cff_font_subset_dict_strings (font, font->top_dict); + if (unlikely (status)) + return status; + + if (font->is_cid) { + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + + status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + } + } else { + status = cairo_cff_font_subset_dict_strings (font, font->private_dict); + } + + return status; +} + +static cairo_status_t +cairo_cff_font_subset_font (cairo_cff_font_t *font) +{ + cairo_status_t status; + + status = cairo_cff_font_set_ros_strings (font); + if (unlikely (status)) + return status; + + status = cairo_cff_font_subset_charstrings (font); + if (unlikely (status)) + return status; + + if (font->is_cid) + status = cairo_cff_font_subset_fontdict (font); + else + status = cairo_cff_font_create_cid_fontdict (font); + if (unlikely (status)) + return status; + + status = cairo_cff_font_subset_strings (font); + if (unlikely (status)) + return status; + + return status; +} + +/* Set the operand of the specified operator in the (already written) + * top dict to point to the current position in the output + * array. Operands updated with this function must have previously + * been encoded with the 5-byte (max) integer encoding. */ +static void +cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font, + int operator) +{ + int cur_pos; + int offset; + int size; + unsigned char buf[10]; + unsigned char *buf_end; + unsigned char *op_ptr; + + cur_pos = _cairo_array_num_elements (&font->output); + buf_end = encode_integer_max (buf, cur_pos); + offset = cff_dict_get_location (font->top_dict, operator, &size); + assert (offset > 0); + op_ptr = _cairo_array_index (&font->output, offset); + memcpy (op_ptr, buf, buf_end - buf); +} + +static cairo_status_t +cairo_cff_font_write_header (cairo_cff_font_t *font) +{ + return _cairo_array_append_multiple (&font->output, + font->header, + font->header->header_size); +} + +static cairo_status_t +cairo_cff_font_write_name (cairo_cff_font_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_array_t index; + + cff_index_init (&index); + + status = cff_index_append_copy (&index, + (unsigned char *) font->subset_font_name, + strlen(font->subset_font_name)); + if (unlikely (status)) + goto FAIL; + + status = cff_index_write (&index, &font->output); + if (unlikely (status)) + goto FAIL; + +FAIL: + cff_index_fini (&index); + + return status; +} + +static cairo_status_t +cairo_cff_font_write_top_dict (cairo_cff_font_t *font) +{ + uint16_t count; + unsigned char buf[10]; + unsigned char *p; + int offset_index; + int dict_start, dict_size; + int offset_size = 4; + cairo_status_t status; + + /* Write an index containing the top dict */ + + count = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &count, 2); + if (unlikely (status)) + return status; + buf[0] = offset_size; + status = _cairo_array_append (&font->output, buf); + if (unlikely (status)) + return status; + encode_index_offset (buf, offset_size, 1); + status = _cairo_array_append_multiple (&font->output, buf, offset_size); + if (unlikely (status)) + return status; + + /* Reserve space for last element of offset array and update after + * dict is written */ + offset_index = _cairo_array_num_elements (&font->output); + status = _cairo_array_append_multiple (&font->output, buf, offset_size); + if (unlikely (status)) + return status; + + dict_start = _cairo_array_num_elements (&font->output); + status = cff_dict_write (font->top_dict, &font->output); + if (unlikely (status)) + return status; + dict_size = _cairo_array_num_elements (&font->output) - dict_start; + + encode_index_offset (buf, offset_size, dict_size + 1); + p = _cairo_array_index (&font->output, offset_index); + memcpy (p, buf, offset_size); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_strings (cairo_cff_font_t *font) +{ + return cff_index_write (&font->strings_subset_index, &font->output); +} + +static cairo_status_t +cairo_cff_font_write_global_subrs (cairo_cff_font_t *font) +{ + return cff_index_write (&font->global_sub_index, &font->output); +} + +static cairo_status_t +cairo_cff_font_write_fdselect (cairo_cff_font_t *font) +{ + unsigned char data; + unsigned int i; + cairo_int_status_t status; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP); + + if (font->is_cid) { + data = 0; + status = _cairo_array_append (&font->output, &data); + if (unlikely (status)) + return status; + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + data = font->fdselect_subset[i]; + status = _cairo_array_append (&font->output, &data); + if (unlikely (status)) + return status; + } + } else { + unsigned char byte; + uint16_t word; + + status = _cairo_array_grow_by (&font->output, 9); + if (unlikely (status)) + return status; + + byte = 3; + status = _cairo_array_append (&font->output, &byte); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (0); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + byte = 0; + status = _cairo_array_append (&font->output, &byte); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_charset (cairo_cff_font_t *font) +{ + unsigned char byte; + uint16_t word; + cairo_status_t status; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); + status = _cairo_array_grow_by (&font->output, 5); + if (unlikely (status)) + return status; + + byte = 2; + status = _cairo_array_append (&font->output, &byte); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_charstrings (cairo_cff_font_t *font) +{ + cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP); + + return cff_index_write (&font->charstrings_subset_index, &font->output); +} + +static cairo_status_t +cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) +{ + unsigned int i; + cairo_int_status_t status; + unsigned int offset_array; + uint32_t *offset_array_ptr; + int offset_base; + uint16_t count; + uint8_t offset_size = 4; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); + count = cpu_to_be16 (font->num_subset_fontdicts); + status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); + if (unlikely (status)) + return status; + status = _cairo_array_append (&font->output, &offset_size); + if (unlikely (status)) + return status; + + offset_array = _cairo_array_num_elements (&font->output); + status = _cairo_array_allocate (&font->output, + (font->num_subset_fontdicts + 1)*offset_size, + (void **) &offset_array_ptr); + if (unlikely (status)) + return status; + offset_base = _cairo_array_num_elements (&font->output) - 1; + *offset_array_ptr = cpu_to_be32(1); + offset_array += sizeof(uint32_t); + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], + &font->output); + if (unlikely (status)) + return status; + + offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array); + *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); + offset_array += sizeof(uint32_t); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_private_dict (cairo_cff_font_t *font, + int dict_num, + cairo_hash_table_t *parent_dict, + cairo_hash_table_t *private_dict) +{ + int offset; + int size; + unsigned char buf[10]; + unsigned char *buf_end; + unsigned char *p; + cairo_status_t status; + + /* Write private dict and update offset and size in top dict */ + font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); + status = cff_dict_write (private_dict, &font->output); + if (unlikely (status)) + return status; + + size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; + /* private entry has two operands - size and offset */ + buf_end = encode_integer_max (buf, size); + buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]); + offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size); + assert (offset > 0); + p = _cairo_array_index (&font->output, offset); + memcpy (p, buf, buf_end - buf); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_local_sub (cairo_cff_font_t *font, + int dict_num, + cairo_hash_table_t *private_dict, + cairo_array_t *local_sub_index) +{ + int offset; + int size; + unsigned char buf[10]; + unsigned char *buf_end; + unsigned char *p; + cairo_status_t status; + + if (_cairo_array_num_elements (local_sub_index) > 0) { + /* Write local subroutines and update offset in private + * dict. Local subroutines offset is relative to start of + * private dict */ + offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; + buf_end = encode_integer_max (buf, offset); + offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size); + assert (offset > 0); + p = _cairo_array_index (&font->output, offset); + memcpy (p, buf, buf_end - buf); + status = cff_index_write (local_sub_index, &font->output); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) +{ + unsigned int i; + cairo_int_status_t status; + + if (font->is_cid) { + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_write_private_dict ( + font, + i, + font->fd_dict[font->fd_subset_map[i]], + font->fd_private_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + } + + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_write_local_sub ( + font, + i, + font->fd_private_dict[font->fd_subset_map[i]], + &font->fd_local_sub_index[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + } + } else { + status = cairo_cff_font_write_private_dict (font, + 0, + font->fd_dict[0], + font->private_dict); + if (unlikely (status)) + return status; + + status = cairo_cff_font_write_local_sub (font, + 0, + font->private_dict, + &font->local_sub_index); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +typedef cairo_status_t +(*font_write_t) (cairo_cff_font_t *font); + +static const font_write_t font_write_funcs[] = { + cairo_cff_font_write_header, + cairo_cff_font_write_name, + cairo_cff_font_write_top_dict, + cairo_cff_font_write_strings, + cairo_cff_font_write_global_subrs, + cairo_cff_font_write_charset, + cairo_cff_font_write_fdselect, + cairo_cff_font_write_charstrings, + cairo_cff_font_write_cid_fontdict, + cairo_cff_font_write_cid_private_dict_and_local_sub, +}; + +static cairo_status_t +cairo_cff_font_write_subset (cairo_cff_font_t *font) +{ + cairo_int_status_t status; + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) { + status = font_write_funcs[i] (font); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_generate (cairo_cff_font_t *font, + const char **data, + unsigned long *length) +{ + cairo_int_status_t status; + + status = cairo_cff_font_read_font (font); + if (unlikely (status)) + return status; + + status = cairo_cff_font_subset_font (font); + if (unlikely (status)) + return status; + + status = cairo_cff_font_write_subset (font); + if (unlikely (status)) + return status; + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_create_set_widths (cairo_cff_font_t *font) +{ + unsigned long size; + unsigned long long_entry_size; + unsigned long short_entry_size; + unsigned int i; + tt_hhea_t hhea; + int num_hmetrics; + unsigned char buf[10]; + int glyph_index; + cairo_int_status_t status; + + size = sizeof (tt_hhea_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char*) &hhea, &size); + if (unlikely (status)) + return status; + num_hmetrics = be16_to_cpu (hhea.num_hmetrics); + + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + glyph_index = font->scaled_font_subset->glyphs[i]; + long_entry_size = 2 * sizeof (int16_t); + short_entry_size = sizeof (int16_t); + if (glyph_index < num_hmetrics) { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + glyph_index * long_entry_size, + buf, &short_entry_size); + if (unlikely (status)) + return status; + } + else + { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + (num_hmetrics - 1) * long_entry_size, + buf, &short_entry_size); + if (unlikely (status)) + return status; + } + font->widths[i] = be16_to_cpu (*((int16_t*)buf)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_cff_font_t **font_return, + const char *subset_name) +{ + const cairo_scaled_font_backend_t *backend; + cairo_status_t status; + cairo_cff_font_t *font; + tt_head_t head; + tt_hhea_t hhea; + unsigned long size, data_length; + + backend = scaled_font_subset->scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + data_length = 0; + status = backend->load_truetype_table( scaled_font_subset->scaled_font, + TT_TAG_CFF, 0, NULL, &data_length); + if (unlikely (status)) + return status; + + size = sizeof (tt_head_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char *) &head, &size); + if (unlikely (status)) + return status; + + size = sizeof (tt_hhea_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char *) &hhea, &size); + if (unlikely (status)) + return status; + + size = 0; + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_hmtx, 0, NULL, &size); + if (unlikely (status)) + return status; + + font = malloc (sizeof (cairo_cff_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = backend; + font->scaled_font_subset = scaled_font_subset; + + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail2; + + font->subset_font_name = strdup (subset_name); + if (unlikely (font->subset_font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + font->x_min = (int16_t) be16_to_cpu (head.x_min); + font->y_min = (int16_t) be16_to_cpu (head.y_min); + font->x_max = (int16_t) be16_to_cpu (head.x_max); + font->y_max = (int16_t) be16_to_cpu (head.y_max); + font->ascent = (int16_t) be16_to_cpu (hhea.ascender); + font->descent = (int16_t) be16_to_cpu (hhea.descender); + font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em); + if (font->units_per_em == 0) + font->units_per_em = 1000; + + font->font_name = NULL; + status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + &font->ps_name, + &font->font_name); + if (_cairo_status_is_error (status)) + goto fail3; + + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->ps_name == NULL) { + font->ps_name = malloc (30); + if (unlikely (font->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + snprintf(font->ps_name, 30, "CairoFont-%u-%u", + scaled_font_subset->font_id, + scaled_font_subset->subset_id); + } + + font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + status = cairo_cff_font_create_set_widths (font); + if (unlikely (status)) + goto fail5; + + font->data_length = data_length; + font->data = malloc (data_length); + if (unlikely (font->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail5; + } + status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font, + TT_TAG_CFF, 0, font->data, + &font->data_length); + if (unlikely (status)) + goto fail6; + + font->data_end = font->data + font->data_length; + + status = cff_dict_init (&font->top_dict); + if (unlikely (status)) + goto fail6; + + status = cff_dict_init (&font->private_dict); + if (unlikely (status)) + goto fail7; + + cff_index_init (&font->strings_index); + cff_index_init (&font->charstrings_index); + cff_index_init (&font->global_sub_index); + cff_index_init (&font->local_sub_index); + cff_index_init (&font->charstrings_subset_index); + cff_index_init (&font->strings_subset_index); + font->fdselect = NULL; + font->fd_dict = NULL; + font->fd_private_dict = NULL; + font->fd_local_sub_index = NULL; + font->fdselect_subset = NULL; + font->fd_subset_map = NULL; + font->private_dict_offset = NULL; + + *font_return = font; + + return CAIRO_STATUS_SUCCESS; + +fail7: + _cairo_hash_table_destroy (font->top_dict); +fail6: + free (font->data); +fail5: + free (font->widths); +fail4: + if (font->font_name) + free (font->font_name); +fail3: + free (font->subset_font_name); +fail2: + _cairo_array_fini (&font->output); + free (font); + + return status; +} + +static void +cairo_cff_font_destroy (cairo_cff_font_t *font) +{ + unsigned int i; + + free (font->widths); + if (font->font_name) + free (font->font_name); + free (font->ps_name); + free (font->subset_font_name); + _cairo_array_fini (&font->output); + cff_dict_fini (font->top_dict); + cff_dict_fini (font->private_dict); + cff_index_fini (&font->strings_index); + cff_index_fini (&font->charstrings_index); + cff_index_fini (&font->global_sub_index); + cff_index_fini (&font->local_sub_index); + cff_index_fini (&font->charstrings_subset_index); + cff_index_fini (&font->strings_subset_index); + + /* If we bailed out early as a result of an error some of the + * following cairo_cff_font_t members may still be NULL */ + if (font->fd_dict) { + for (i = 0; i < font->num_fontdicts; i++) { + if (font->fd_dict[i]) + cff_dict_fini (font->fd_dict[i]); + } + free (font->fd_dict); + } + if (font->fd_subset_map) + free (font->fd_subset_map); + if (font->private_dict_offset) + free (font->private_dict_offset); + + if (font->is_cid) { + if (font->fdselect) + free (font->fdselect); + if (font->fdselect_subset) + free (font->fdselect_subset); + if (font->fd_private_dict) { + for (i = 0; i < font->num_fontdicts; i++) { + if (font->fd_private_dict[i]) + cff_dict_fini (font->fd_private_dict[i]); + } + free (font->fd_private_dict); + } + if (font->fd_local_sub_index) { + for (i = 0; i < font->num_fontdicts; i++) + cff_index_fini (&font->fd_local_sub_index[i]); + free (font->fd_local_sub_index); + } + } + + if (font->data) + free (font->data); + + free (font); +} + +cairo_status_t +_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, + const char *subset_name, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ + cairo_status_t status; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned int i; + + status = _cairo_cff_font_create (font_subset, &font, subset_name); + if (unlikely (status)) + return status; + + status = cairo_cff_font_generate (font, &data, &length); + if (unlikely (status)) + goto fail1; + + cff_subset->ps_name = strdup (font->ps_name); + if (unlikely (cff_subset->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + if (font->font_name) { + cff_subset->font_name = strdup (font->font_name); + if (cff_subset->font_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + } else { + cff_subset->font_name = NULL; + } + + cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (cff_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em; + + cff_subset->x_min = (double)font->x_min/font->units_per_em; + cff_subset->y_min = (double)font->y_min/font->units_per_em; + cff_subset->x_max = (double)font->x_max/font->units_per_em; + cff_subset->y_max = (double)font->y_max/font->units_per_em; + cff_subset->ascent = (double)font->ascent/font->units_per_em; + cff_subset->descent = (double)font->descent/font->units_per_em; + + cff_subset->data = malloc (length); + if (unlikely (cff_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (cff_subset->data, data, length); + cff_subset->data_length = length; + + cairo_cff_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail4: + free (cff_subset->widths); + fail3: + if (cff_subset->font_name) + free (cff_subset->font_name); + fail2: + free (cff_subset->ps_name); + fail1: + cairo_cff_font_destroy (font); + + return status; +} + +void +_cairo_cff_subset_fini (cairo_cff_subset_t *subset) +{ + free (subset->ps_name); + if (subset->font_name) + free (subset->font_name); + free (subset->widths); + free (subset->data); +} + +static cairo_int_status_t +_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_cff_font_t **font_return, + const char *subset_name) +{ + cairo_status_t status; + cairo_cff_font_t *font; + + font = malloc (sizeof (cairo_cff_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = NULL; + font->scaled_font_subset = scaled_font_subset; + + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail1; + + font->subset_font_name = strdup (subset_name); + if (unlikely (font->subset_font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + font->ps_name = strdup (subset_name); + if (unlikely (font->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + font->font_name = NULL; + + font->x_min = 0; + font->y_min = 0; + font->x_max = 0; + font->y_max = 0; + font->ascent = 0; + font->descent = 0; + + font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + font->data_length = 0; + font->data = NULL; + font->data_end = NULL; + + status = cff_dict_init (&font->top_dict); + if (unlikely (status)) + goto fail4; + + status = cff_dict_init (&font->private_dict); + if (unlikely (status)) + goto fail5; + + cff_index_init (&font->strings_index); + cff_index_init (&font->charstrings_index); + cff_index_init (&font->global_sub_index); + cff_index_init (&font->local_sub_index); + cff_index_init (&font->charstrings_subset_index); + cff_index_init (&font->strings_subset_index); + font->fdselect = NULL; + font->fd_dict = NULL; + font->fd_private_dict = NULL; + font->fd_local_sub_index = NULL; + font->fdselect_subset = NULL; + font->fd_subset_map = NULL; + font->private_dict_offset = NULL; + + *font_return = font; + + return CAIRO_STATUS_SUCCESS; + +fail5: + _cairo_hash_table_destroy (font->top_dict); +fail4: + free (font->widths); +fail3: + if (font->font_name) + free (font->font_name); + free (font->ps_name); +fail2: + free (font->subset_font_name); +fail1: + _cairo_array_fini (&font->output); + free (font); + return status; +} + +static cairo_int_status_t +cairo_cff_font_fallback_generate (cairo_cff_font_t *font, + cairo_type2_charstrings_t *type2_subset, + const char **data, + unsigned long *length) +{ + cairo_int_status_t status; + cff_header_t header; + cairo_array_t *charstring; + unsigned char buf[40]; + unsigned char *end_buf; + unsigned int i; + + /* Create header */ + header.major = 1; + header.minor = 0; + header.header_size = 4; + header.offset_size = 4; + font->header = &header; + + /* Create Top Dict */ + font->is_cid = FALSE; + end_buf = encode_integer (buf, type2_subset->x_min); + end_buf = encode_integer (end_buf, type2_subset->y_min); + end_buf = encode_integer (end_buf, type2_subset->x_max); + end_buf = encode_integer (end_buf, type2_subset->y_max); + status = cff_dict_set_operands (font->top_dict, + FONTBBOX_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (font->top_dict, + CHARSTRINGS_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cff_dict_set_operands (font->top_dict, + FDSELECT_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cff_dict_set_operands (font->top_dict, + FDARRAY_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cff_dict_set_operands (font->top_dict, + CHARSET_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cairo_cff_font_set_ros_strings (font); + if (unlikely (status)) + return status; + + /* Create CID FD dictionary */ + status = cairo_cff_font_create_cid_fontdict (font); + if (unlikely (status)) + return status; + + /* Create charstrings */ + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + charstring = _cairo_array_index(&type2_subset->charstrings, i); + + status = cff_index_append (&font->charstrings_subset_index, + _cairo_array_index (charstring, 0), + _cairo_array_num_elements (charstring)); + + if (unlikely (status)) + return status; + } + + status = cairo_cff_font_write_subset (font); + if (unlikely (status)) + return status; + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, + const char *subset_name, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ + cairo_status_t status; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned int i; + cairo_type2_charstrings_t type2_subset; + + status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); + if (unlikely (status)) + return status; + + status = _cairo_type2_charstrings_init (&type2_subset, font_subset); + if (unlikely (status)) + goto fail1; + + status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); + if (unlikely (status)) + goto fail2; + + cff_subset->font_name = NULL; + cff_subset->ps_name = strdup (font->ps_name); + if (unlikely (cff_subset->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + + cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (cff_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + cff_subset->widths[i] = (double)type2_subset.widths[i]/1000; + + cff_subset->x_min = (double)type2_subset.x_min/1000; + cff_subset->y_min = (double)type2_subset.y_min/1000; + cff_subset->x_max = (double)type2_subset.x_max/1000; + cff_subset->y_max = (double)type2_subset.y_max/1000; + cff_subset->ascent = (double)type2_subset.y_max/1000; + cff_subset->descent = (double)type2_subset.y_min/1000; + + cff_subset->data = malloc (length); + if (unlikely (cff_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (cff_subset->data, data, length); + cff_subset->data_length = length; + cff_subset->data_length = length; + + _cairo_type2_charstrings_fini (&type2_subset); + cairo_cff_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail4: + free (cff_subset->widths); + fail3: + free (cff_subset->ps_name); + fail2: + _cairo_type2_charstrings_fini (&type2_subset); + fail1: + cairo_cff_font_destroy (font); + + return status; +} + +void +_cairo_cff_fallback_fini (cairo_cff_subset_t *subset) +{ + free (subset->ps_name); + free (subset->widths); + free (subset->data); +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-clip-private.h b/libs/cairo/src/cairo-clip-private.h new file mode 100644 index 000000000..04d80afc7 --- /dev/null +++ b/libs/cairo/src/cairo-clip-private.h @@ -0,0 +1,120 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_CLIP_PRIVATE_H +#define CAIRO_CLIP_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-reference-count-private.h" + +extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; + +enum { + CAIRO_CLIP_PATH_HAS_REGION = 0x1, + CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2, + CAIRO_CLIP_PATH_IS_BOX = 0x4 +}; + +struct _cairo_clip_path { + cairo_reference_count_t ref_count; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; + cairo_clip_path_t *prev; + + cairo_rectangle_int_t extents; + + /* partial caches */ + unsigned int flags; + cairo_region_t *region; + cairo_surface_t *surface; +}; + +struct _cairo_clip { + /* can be used as a cairo_hash_entry_t for live clips */ + cairo_clip_path_t *path; + + cairo_bool_t all_clipped; + +}; + +cairo_private void +_cairo_clip_init (cairo_clip_t *clip); + +cairo_private_no_warn cairo_clip_t * +_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other); + +cairo_private cairo_status_t +_cairo_clip_init_copy_transformed (cairo_clip_t *clip, + cairo_clip_t *other, + const cairo_matrix_t *matrix); + +cairo_private void +_cairo_clip_reset (cairo_clip_t *clip); + +cairo_private cairo_bool_t +_cairo_clip_equal (const cairo_clip_t *clip_a, + const cairo_clip_t *clip_b); + +#define _cairo_clip_fini(clip) _cairo_clip_reset (clip) + +cairo_private cairo_status_t +_cairo_clip_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rectangle); + +cairo_private cairo_status_t +_cairo_clip_clip (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias); + +cairo_private cairo_status_t +_cairo_clip_apply_clip (cairo_clip_t *clip, + const cairo_clip_t *other); + +cairo_private const cairo_rectangle_int_t * +_cairo_clip_get_extents (const cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty); + +cairo_private cairo_status_t +_cairo_clip_combine_with_surface (cairo_clip_t *clip, + cairo_surface_t *dst, + int dst_x, int dst_y); + +cairo_private cairo_int_status_t +_cairo_clip_get_region (cairo_clip_t *clip, + cairo_region_t **region); + +cairo_private cairo_int_status_t +_cairo_clip_get_boxes (cairo_clip_t *clip, + cairo_box_t **boxes, + int *count); + +cairo_private cairo_status_t +_cairo_clip_to_boxes (cairo_clip_t **clip, + cairo_composite_rectangles_t *extents, + cairo_box_t **boxes, + int *num_boxes); + +cairo_private cairo_bool_t +_cairo_clip_contains_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect); + +cairo_private cairo_bool_t +_cairo_clip_contains_extents (cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents); + +cairo_private void +_cairo_clip_drop_cache (cairo_clip_t *clip); + +cairo_private cairo_rectangle_list_t* +_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate); + +#endif /* CAIRO_CLIP_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-clip.c b/libs/cairo/src/cairo-clip.c new file mode 100644 index 000000000..cbbf4d2ce --- /dev/null +++ b/libs/cairo/src/cairo-clip.c @@ -0,0 +1,1553 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" + +#if HAS_FREED_POOL +static freed_pool_t clip_path_pool; +#endif + +static cairo_clip_path_t * +_cairo_clip_path_create (cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path; + + clip_path = _freed_pool_get (&clip_path_pool); + if (unlikely (clip_path == NULL)) { + clip_path = malloc (sizeof (cairo_clip_path_t)); + if (unlikely (clip_path == NULL)) + return NULL; + } + + CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); + + clip_path->flags = 0; + clip_path->region = NULL; + clip_path->surface = NULL; + + clip_path->prev = clip->path; + clip->path = clip_path; + + return clip_path; +} + +static cairo_clip_path_t * +_cairo_clip_path_reference (cairo_clip_path_t *clip_path) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); + + _cairo_reference_count_inc (&clip_path->ref_count); + + return clip_path; +} + +static void +_cairo_clip_path_destroy (cairo_clip_path_t *clip_path) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) + return; + + _cairo_path_fixed_fini (&clip_path->path); + if (clip_path->region != NULL) + cairo_region_destroy (clip_path->region); + if (clip_path->surface != NULL) + cairo_surface_destroy (clip_path->surface); + + if (clip_path->prev != NULL) + _cairo_clip_path_destroy (clip_path->prev); + + _freed_pool_put (&clip_path_pool, clip_path); +} + +void +_cairo_clip_init (cairo_clip_t *clip) +{ + clip->all_clipped = FALSE; + clip->path = NULL; +} + +static void +_cairo_clip_set_all_clipped (cairo_clip_t *clip) +{ + clip->all_clipped = TRUE; + if (clip->path != NULL) { + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + } +} + +static cairo_status_t +_cairo_clip_intersect_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) +{ + cairo_clip_path_t *clip_path; + cairo_status_t status; + + if (clip->path != NULL) { + if (rect->x <= clip->path->extents.x && + rect->y <= clip->path->extents.y && + rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width && + rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_path_fixed_init (&clip_path->path); + + status = _cairo_path_fixed_move_to (&clip_path->path, + _cairo_fixed_from_int (rect->x), + _cairo_fixed_from_int (rect->y)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (rect->height)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (&clip_path->path, + _cairo_fixed_from_int (-rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_close_path (&clip_path->path); + assert (status == CAIRO_STATUS_SUCCESS); + + clip_path->fill_rule = CAIRO_FILL_RULE_WINDING; + clip_path->tolerance = 1; + clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT; + clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; + + clip_path->extents = *rect; + if (clip_path->prev != NULL) { + if (! _cairo_rectangle_intersect (&clip_path->extents, + &clip_path->prev->extents)) + { + _cairo_clip_set_all_clipped (clip); + } + } + + /* could preallocate the region if it proves worthwhile */ + + return CAIRO_STATUS_SUCCESS; +} + +cairo_clip_t * +_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) +{ + if (other != NULL) { + clip->all_clipped = other->all_clipped; + if (other->path == NULL) { + clip->path = NULL; + if (! clip->all_clipped) + clip = NULL; + } else { + clip->path = _cairo_clip_path_reference (other->path); + } + } else { + _cairo_clip_init (clip); + clip = NULL; + } + + return clip; +} + +void +_cairo_clip_reset (cairo_clip_t *clip) +{ + clip->all_clipped = FALSE; + if (clip->path != NULL) { + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + } +} + +static cairo_status_t +_cairo_clip_intersect_path (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_clip_path_t *clip_path; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_box_t box; + cairo_bool_t is_box = FALSE; + + if (clip->path != NULL) { + if (clip->path->fill_rule == fill_rule && + (path->is_rectilinear || tolerance == clip->path->tolerance) && + antialias == clip->path->antialias && + _cairo_path_fixed_equal (&clip->path->path, path)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + _cairo_path_fixed_approximate_clip_extents (path, &extents); + if (extents.width == 0 || extents.height == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + is_box = _cairo_path_fixed_is_box (path, &box); + if (clip->path != NULL) { + if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + /* does this clip wholly subsume the others? */ + if (is_box && + box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) && + box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) && + box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) && + box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_path_fixed_init_copy (&clip_path->path, path); + if (unlikely (status)) { + clip->path = clip->path->prev; + _cairo_clip_path_destroy (clip_path); + return status; + } + + clip_path->extents = extents; + clip_path->fill_rule = fill_rule; + clip_path->tolerance = tolerance; + clip_path->antialias = antialias; + if (is_box) + clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_clip_equal (const cairo_clip_t *clip_a, + const cairo_clip_t *clip_b) +{ + const cairo_clip_path_t *clip_path_a, *clip_path_b; + + clip_path_a = clip_a->path; + clip_path_b = clip_b->path; + + while (clip_path_a && clip_path_b) { + if (clip_path_a == clip_path_b) + return TRUE; + + if (clip_path_a->fill_rule != clip_path_b->fill_rule) + return FALSE; + + if (clip_path_a->tolerance != clip_path_b->tolerance) + return FALSE; + + if (clip_path_a->antialias != clip_path_b->antialias) + return FALSE; + + if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path)) + return FALSE; + + clip_path_a = clip_path_a->prev; + clip_path_b = clip_path_b->prev; + } + + return clip_path_a == clip_path_b; /* ie both NULL */ +} + +cairo_status_t +_cairo_clip_clip (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + /* catch the empty clip path */ + if (_cairo_path_fixed_fill_is_empty (path)) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_clip_intersect_path (clip, + path, fill_rule, tolerance, + antialias); +} + +cairo_status_t +_cairo_clip_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rectangle) +{ + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (rectangle->width == 0 || rectangle->height == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + /* if a smaller clip has already been set, ignore the new path */ + if (clip->path != NULL) { + if (rectangle->x <= clip->path->extents.x && + rectangle->y <= clip->path->extents.y && + rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width && + rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + return _cairo_clip_intersect_rectangle (clip, rectangle); +} + +static cairo_status_t +_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip, + cairo_clip_path_t *other_path, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + cairo_clip_path_t *clip_path; + cairo_bool_t is_empty; + + if (other_path->prev != NULL) { + status = _cairo_clip_path_reapply_clip_path_transform (clip, + other_path->prev, + matrix); + if (unlikely (status)) + return status; + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_path_fixed_init_copy (&clip_path->path, + &other_path->path); + if (unlikely (status)) { + clip->path = clip->path->prev; + _cairo_clip_path_destroy (clip_path); + return status; + } + + _cairo_path_fixed_transform (&clip_path->path, matrix); + _cairo_path_fixed_approximate_clip_extents (&clip_path->path, + &clip_path->extents); + if (clip_path->prev != NULL) { + is_empty = _cairo_rectangle_intersect (&clip_path->extents, + &clip_path->prev->extents); + } + + clip_path->fill_rule = other_path->fill_rule; + clip_path->tolerance = other_path->tolerance; + clip_path->antialias = other_path->antialias; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip, + cairo_clip_path_t *other_path, + int tx, int ty) +{ + cairo_status_t status; + cairo_clip_path_t *clip_path; + + if (other_path->prev != NULL) { + status = _cairo_clip_path_reapply_clip_path_translate (clip, + other_path->prev, + tx, ty); + if (unlikely (status)) + return status; + } + + clip_path = _cairo_clip_path_create (clip); + if (unlikely (clip_path == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_path_fixed_init_copy (&clip_path->path, + &other_path->path); + if (unlikely (status)) { + clip->path = clip->path->prev; + _cairo_clip_path_destroy (clip_path); + return status; + } + + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (tx), + _cairo_fixed_from_int (ty)); + + clip_path->fill_rule = other_path->fill_rule; + clip_path->tolerance = other_path->tolerance; + clip_path->antialias = other_path->antialias; + + clip_path->flags = other_path->flags; + if (other_path->region != NULL) { + clip_path->region = cairo_region_copy (other_path->region); + status = clip_path->region->status; + if (unlikely (status)) { + clip->path = clip->path->prev; + _cairo_clip_path_destroy (clip_path); + return status; + } + + cairo_region_translate (clip_path->region, tx, ty); + } + clip_path->surface = cairo_surface_reference (other_path->surface); + + clip_path->extents = other_path->extents; + clip_path->extents.x += tx; + clip_path->extents.y += ty; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_clip_init_copy_transformed (cairo_clip_t *clip, + cairo_clip_t *other, + const cairo_matrix_t *matrix) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int tx, ty; + + if (other == NULL) { + _cairo_clip_init (clip); + return CAIRO_STATUS_SUCCESS; + } + + if (other->all_clipped) { + _cairo_clip_init (clip); + clip->all_clipped = TRUE; + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_matrix_is_identity (matrix)) { + _cairo_clip_init_copy (clip, other); + return CAIRO_STATUS_SUCCESS; + } + + if (other->path != NULL) { + _cairo_clip_init (clip); + + /* if we only need to translate, so we can reuse the caches... */ + /* XXX we still loose the benefit of constructs when the copy is + * deleted though. Indirect clip_paths? + */ + if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) { + status = _cairo_clip_path_reapply_clip_path_translate (clip, + other->path, + tx, ty); + } else { + status = _cairo_clip_path_reapply_clip_path_transform (clip, + other->path, + matrix); + if (clip->path->extents.width == 0 && + clip->path->extents.height == 0) + { + _cairo_clip_set_all_clipped (clip); + } + } + } + + return status; +} + +static cairo_status_t +_cairo_clip_apply_clip_path (cairo_clip_t *clip, + const cairo_clip_path_t *path) +{ + cairo_status_t status; + + if (path->prev != NULL) + status = _cairo_clip_apply_clip_path (clip, path->prev); + + return _cairo_clip_intersect_path (clip, + &path->path, + path->fill_rule, + path->tolerance, + path->antialias); +} + +cairo_status_t +_cairo_clip_apply_clip (cairo_clip_t *clip, + const cairo_clip_t *other) +{ + cairo_status_t status; + + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (other->all_clipped) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_STATUS_SUCCESS; + } + + status = CAIRO_STATUS_SUCCESS; + if (other->path != NULL) + status = _cairo_clip_apply_clip_path (clip, other->path); + + return status; +} + +static inline cairo_bool_t +_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path) +{ + while (clip_path != NULL) { + if (! clip_path->path.is_rectilinear) + return FALSE; + + clip_path = clip_path->prev; + } + + return TRUE; +} + +static cairo_int_status_t +_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) +{ + cairo_traps_t traps; + cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; + cairo_box_t *boxes = stack_boxes; + cairo_status_t status; + int n; + + /* If we have nothing to intersect with this path, then it cannot + * magically be reduced into a region. + */ + if (clip_path->prev == NULL) + goto UNSUPPORTED; + + /* Start simple... Intersect some boxes with an arbitrary path. */ + if (! clip_path->path.is_rectilinear) + goto UNSUPPORTED; + if (clip_path->prev->prev != NULL) + goto UNSUPPORTED; + + _cairo_traps_init (&traps); + _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); + _cairo_traps_limit (&traps, boxes, 1); + + status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, + clip_path->fill_rule, + &traps); + if (unlikely (_cairo_status_is_error (status))) + return status; + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + goto UNSUPPORTED; + + if (unlikely (traps.num_traps == 0)) { + clip_path->region = cairo_region_create (); + clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; + return CAIRO_STATUS_SUCCESS; + } + + if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { + boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); + if (unlikely (boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (n = 0; n < traps.num_traps; n++) { + boxes[n].p1.x = traps.traps[n].left.p1.x; + boxes[n].p1.y = traps.traps[n].top; + boxes[n].p2.x = traps.traps[n].right.p1.x; + boxes[n].p2.y = traps.traps[n].bottom; + } + + _cairo_traps_clear (&traps); + _cairo_traps_limit (&traps, boxes, n); + status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, + clip_path->prev->fill_rule, + clip_path->prev->tolerance, + &traps); + if (boxes != stack_boxes) + free (boxes); + + if (unlikely (status)) + return status; + + status = _cairo_traps_extract_region (&traps, &clip_path->region); + _cairo_traps_fini (&traps); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + goto UNSUPPORTED; + if (unlikely (status)) + return status; + + clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; + return CAIRO_STATUS_SUCCESS; + +UNSUPPORTED: + clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_clip_path_to_region (cairo_clip_path_t *clip_path) +{ + cairo_int_status_t status; + cairo_region_t *prev = NULL; + + if (clip_path->flags & + (CAIRO_CLIP_PATH_HAS_REGION | + CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED)) + { + return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ? + CAIRO_INT_STATUS_UNSUPPORTED : + CAIRO_STATUS_SUCCESS; + } + + if (! clip_path->path.maybe_fill_region) + return _cairo_clip_path_to_region_geometric (clip_path); + + /* first retrieve the region for our antecedents */ + if (clip_path->prev != NULL) { + status = _cairo_clip_path_to_region (clip_path->prev); + if (status) { + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_clip_path_to_region_geometric (clip_path); + + return status; + } + + prev = clip_path->prev->region; + } + + /* now extract the region for ourselves */ + clip_path->region = + _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path, + clip_path->fill_rule, + &clip_path->extents); + assert (clip_path->region != NULL); + + status = clip_path->region->status; + if (unlikely (status)) + return status; + + if (prev != NULL) { + status = cairo_region_intersect (clip_path->region, prev); + if (unlikely (status)) + return status; + } + + clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; + return CAIRO_STATUS_SUCCESS; +} + +static inline int +pot (int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +/* XXX there is likely a faster method! ;-) */ +static cairo_status_t +_region_clip_to_boxes (const cairo_region_t *region, + cairo_box_t **boxes, + int *num_boxes, + int *size_boxes) +{ + cairo_traps_t traps; + cairo_status_t status; + int n, num_rects; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, *boxes, *num_boxes); + traps.is_rectilinear = TRUE; + traps.is_rectangular = TRUE; + + num_rects = cairo_region_num_rectangles (region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + cairo_point_t p1, p2; + + cairo_region_get_rectangle (region, n, &rect); + + p1.x = _cairo_fixed_from_int (rect.x); + p1.y = _cairo_fixed_from_int (rect.y); + p2.x = _cairo_fixed_from_int (rect.x + rect.width); + p2.y = _cairo_fixed_from_int (rect.y + rect.height); + + status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2); + if (unlikely (status)) + goto CLEANUP; + } + + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP; + + n = *size_boxes; + if (n < 0) + n = -n; + + if (traps.num_traps > n) { + cairo_box_t *new_boxes; + + new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (*size_boxes > 0) + free (*boxes); + + *boxes = new_boxes; + *size_boxes = traps.num_traps; + } + + for (n = 0; n < traps.num_traps; n++) { + (*boxes)[n].p1.x = traps.traps[n].left.p1.x; + (*boxes)[n].p1.y = traps.traps[n].top; + (*boxes)[n].p2.x = traps.traps[n].right.p1.x; + (*boxes)[n].p2.y = traps.traps[n].bottom; + } + *num_boxes = n; + + CLEANUP: + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_status_t +_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_box_t **boxes, + int *num_boxes, + int *size_boxes) +{ + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_status_t status; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, *boxes, *num_boxes); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, *boxes, *num_boxes); + + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP; + if (status == CAIRO_STATUS_SUCCESS) + goto BOXES; + + /* tolerance will be ignored as the path is rectilinear */ + status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) { + *num_boxes = 0; + } else { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + int i; + + BOXES: + i = *size_boxes; + if (i < 0) + i = -i; + + if (traps.num_traps > i) { + cairo_box_t *new_boxes; + int new_size; + + new_size = pot (traps.num_traps); + new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (*size_boxes > 0) + free (*boxes); + + *boxes = new_boxes; + *size_boxes = new_size; + } + + for (i = 0; i < traps.num_traps; i++) { + (*boxes)[i].p1.x = traps.traps[i].left.p1.x; + (*boxes)[i].p1.y = traps.traps[i].top; + (*boxes)[i].p2.x = traps.traps[i].right.p1.x; + (*boxes)[i].p2.y = traps.traps[i].bottom; + } + *num_boxes = i; + } + } + + CLEANUP: + _cairo_polygon_fini (&polygon); + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_int_status_t +_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path, + cairo_box_t **boxes, + int *count) +{ + int size = -*count; + int num_boxes = 0; + cairo_status_t status; + + if (clip_path->region != NULL) { + int num_rects, n; + + num_rects = cairo_region_num_rectangles (clip_path->region); + if (num_rects > -size) { + cairo_box_t *new_boxes; + + new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t)); + if (unlikely (new_boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *boxes = new_boxes; + } + + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_path->region, n, &rect); + (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x); + (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y); + (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width); + (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height); + } + + *count = num_rects; + return CAIRO_STATUS_SUCCESS; + } + + /* keep it simple at first */ + if (! _clip_paths_are_rectilinear (clip_path)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + assert (-size >= 1); + if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) { + num_boxes = 1; + } else { + status = _rectilinear_clip_to_boxes (&clip_path->path, + clip_path->fill_rule, + boxes, &num_boxes, &size); + if (unlikely (status)) + return status; + } + + while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) { + cairo_box_t box; + + if (clip_path->region != NULL) { + status = _region_clip_to_boxes (clip_path->region, + boxes, &num_boxes, &size); + if (unlikely (status)) + return status; + + break; + } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) { + int i, j; + + for (i = j = 0; i < num_boxes; i++) { + if (j != i) + (*boxes)[j] = (*boxes)[i]; + + if (box.p1.x > (*boxes)[j].p1.x) + (*boxes)[j].p1.x = box.p1.x; + if (box.p2.x < (*boxes)[j].p2.x) + (*boxes)[j].p2.x = box.p2.x; + + if (box.p1.y > (*boxes)[j].p1.y) + (*boxes)[j].p1.y = box.p1.y; + if (box.p2.y < (*boxes)[j].p2.y) + (*boxes)[j].p2.y = box.p2.y; + + j += (*boxes)[j].p2.x > (*boxes)[j].p1.x && + (*boxes)[j].p2.y > (*boxes)[j].p1.y; + } + + num_boxes = j; + } else { + status = _rectilinear_clip_to_boxes (&clip_path->path, + clip_path->fill_rule, + boxes, &num_boxes, &size); + if (unlikely (status)) + return status; + } + } + + *count = num_boxes; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path, + cairo_surface_t *target, + int *tx, int *ty) +{ + const cairo_rectangle_int_t *clip_extents = &clip_path->extents; + cairo_bool_t need_translate; + cairo_surface_t *surface; + cairo_clip_path_t *prev; + cairo_status_t status; + + while (clip_path->prev != NULL && + clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && + clip_path->path.maybe_fill_region) + { + clip_path = clip_path->prev; + } + + clip_extents = &clip_path->extents; + if (clip_path->surface != NULL && + clip_path->surface->backend == target->backend) + { + *tx = clip_extents->x; + *ty = clip_extents->y; + return clip_path->surface; + } + + surface = _cairo_surface_create_similar_scratch (target, + CAIRO_CONTENT_ALPHA, + clip_extents->width, + clip_extents->height); + if (surface == NULL) { + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, + clip_extents->width, + clip_extents->height); + } + if (unlikely (surface->status)) + return surface; + + need_translate = clip_extents->x | clip_extents->y; + if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && + clip_path->path.maybe_fill_region) + { + status = _cairo_surface_paint (surface, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + NULL); + if (unlikely (status)) + goto BAIL; + } + else + { + status = _cairo_surface_paint (surface, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + NULL); + if (unlikely (status)) + goto BAIL; + + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (-clip_extents->x), + _cairo_fixed_from_int (-clip_extents->y)); + } + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (clip_extents->x), + _cairo_fixed_from_int (clip_extents->y)); + } + + if (unlikely (status)) + goto BAIL; + } + + prev = clip_path->prev; + while (prev != NULL) { + if (prev->flags & CAIRO_CLIP_PATH_IS_BOX && + prev->path.maybe_fill_region) + { + /* a simple box only affects the extents */ + } + else if (prev->path.is_rectilinear || + prev->surface == NULL || + prev->surface->backend != target->backend) + { + if (need_translate) { + _cairo_path_fixed_translate (&prev->path, + _cairo_fixed_from_int (-clip_extents->x), + _cairo_fixed_from_int (-clip_extents->y)); + } + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + &prev->path, + prev->fill_rule, + prev->tolerance, + prev->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&prev->path, + _cairo_fixed_from_int (clip_extents->x), + _cairo_fixed_from_int (clip_extents->y)); + } + + if (unlikely (status)) + goto BAIL; + } + else + { + cairo_surface_pattern_t pattern; + cairo_surface_t *prev_surface; + int prev_tx, prev_ty; + + prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty); + status = prev_surface->status; + if (unlikely (status)) + goto BAIL; + + _cairo_pattern_init_for_surface (&pattern, prev_surface); + pattern.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&pattern.base.matrix, + clip_extents->x - prev_tx, + clip_extents->y - prev_ty); + status = _cairo_surface_paint (surface, + CAIRO_OPERATOR_IN, + &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) + goto BAIL; + + break; + } + + prev = prev->prev; + } + + *tx = clip_extents->x; + *ty = clip_extents->y; + cairo_surface_destroy (clip_path->surface); + return clip_path->surface = surface; + + BAIL: + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); +} + +cairo_bool_t +_cairo_clip_contains_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) +{ + cairo_clip_path_t *clip_path; + + if (clip == NULL) + return FALSE; + + clip_path = clip->path; + if (clip_path->extents.x > rect->x || + clip_path->extents.y > rect->y || + clip_path->extents.x + clip_path->extents.width < rect->x + rect->width || + clip_path->extents.y + clip_path->extents.height < rect->y + rect->height) + { + return FALSE; + } + + do { + cairo_box_t box; + + if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) + return FALSE; + + if (! _cairo_path_fixed_is_box (&clip_path->path, &box)) + return FALSE; + + if (box.p1.x > _cairo_fixed_from_int (rect->x) || + box.p1.y > _cairo_fixed_from_int (rect->y) || + box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) || + box.p2.y < _cairo_fixed_from_int (rect->y + rect->height)) + { + return FALSE; + } + } while ((clip_path = clip_path->prev) != NULL); + + return TRUE; +} + +cairo_bool_t +_cairo_clip_contains_extents (cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + const cairo_rectangle_int_t *rect; + + if (clip == NULL) + return FALSE; + + rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; + return _cairo_clip_contains_rectangle (clip, rect); +} + +void +_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path; + + if (clip == NULL) { + fprintf (stream, "no clip\n"); + return; + } + + if (clip->all_clipped) { + fprintf (stream, "clip: all-clipped\n"); + return; + } + + if (clip->path == NULL) { + fprintf (stream, "clip: empty\n"); + return; + } + + fprintf (stream, "clip:\n"); + + clip_path = clip->path; + do { + fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ", + clip_path->region == NULL ? "no" : "yes", + clip_path->surface == NULL ? "no" : "yes", + clip_path->antialias, + clip_path->tolerance, + clip_path->fill_rule); + _cairo_debug_print_path (stream, &clip_path->path); + fprintf (stream, "\n"); + } while ((clip_path = clip_path->prev) != NULL); +} + +cairo_surface_t * +_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty) +{ + /* XXX is_clear -> all_clipped */ + assert (clip->path != NULL); + return _cairo_clip_path_get_surface (clip->path, target, tx, ty); +} + +cairo_status_t +_cairo_clip_combine_with_surface (cairo_clip_t *clip, + cairo_surface_t *dst, + int dst_x, int dst_y) +{ + cairo_clip_path_t *clip_path = clip->path; + cairo_bool_t need_translate; + cairo_status_t status; + + assert (clip_path != NULL); + + need_translate = dst_x | dst_y; + do { + if (clip_path->surface != NULL && + clip_path->surface->backend == dst->backend) + { + cairo_surface_pattern_t pattern; + + _cairo_pattern_init_for_surface (&pattern, clip_path->surface); + cairo_matrix_init_translate (&pattern.base.matrix, + dst_x - clip_path->extents.x, + dst_y - clip_path->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (dst, + CAIRO_OPERATOR_IN, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + return status; + } + + if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && + clip_path->path.maybe_fill_region) + { + continue; + } + + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (-dst_x), + _cairo_fixed_from_int (-dst_y)); + } + status = _cairo_surface_fill (dst, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + if (need_translate) { + _cairo_path_fixed_translate (&clip_path->path, + _cairo_fixed_from_int (dst_x), + _cairo_fixed_from_int (dst_y)); + } + + if (unlikely (status)) + return status; + } while ((clip_path = clip_path->prev) != NULL); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 }; + +const cairo_rectangle_int_t * +_cairo_clip_get_extents (const cairo_clip_t *clip) +{ + if (clip->all_clipped) + return &_cairo_empty_rectangle_int; + + if (clip->path == NULL) + return NULL; + + return &clip->path->extents; +} + +void +_cairo_clip_drop_cache (cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path; + + if (clip->path == NULL) + return; + + clip_path = clip->path; + do { + if (clip_path->region != NULL) { + cairo_region_destroy (clip_path->region); + clip_path->region = NULL; + } + + if (clip_path->surface != NULL) { + cairo_surface_destroy (clip_path->surface); + clip_path->surface = NULL; + } + + clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION; + } while ((clip_path = clip_path->prev) != NULL); +} + +const cairo_rectangle_list_t _cairo_rectangles_nil = + { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; +static const cairo_rectangle_list_t _cairo_rectangles_not_representable = + { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 }; + +static cairo_bool_t +_cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, + cairo_rectangle_int_t *clip_rect, + cairo_rectangle_t *user_rect) +{ + cairo_bool_t is_tight; + + double x1 = clip_rect->x; + double y1 = clip_rect->y; + double x2 = clip_rect->x + (int) clip_rect->width; + double y2 = clip_rect->y + (int) clip_rect->height; + + _cairo_gstate_backend_to_user_rectangle (gstate, + &x1, &y1, &x2, &y2, + &is_tight); + + user_rect->x = x1; + user_rect->y = y1; + user_rect->width = x2 - x1; + user_rect->height = y2 - y1; + + return is_tight; +} + +cairo_int_status_t +_cairo_clip_get_region (cairo_clip_t *clip, + cairo_region_t **region) +{ + cairo_int_status_t status; + + if (clip->all_clipped) + goto CLIPPED; + + assert (clip->path != NULL); + + status = _cairo_clip_path_to_region (clip->path); + if (status) + return status; + + if (cairo_region_is_empty (clip->path->region)) { + _cairo_clip_set_all_clipped (clip); + goto CLIPPED; + } + + if (region) + *region = clip->path->region; + return CAIRO_STATUS_SUCCESS; + + CLIPPED: + if (region) + *region = NULL; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +cairo_int_status_t +_cairo_clip_get_boxes (cairo_clip_t *clip, + cairo_box_t **boxes, + int *count) +{ + cairo_int_status_t status; + + if (clip->all_clipped) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + assert (clip->path != NULL); + + status = _cairo_clip_path_to_boxes (clip->path, boxes, count); + if (status) + return status; + + if (*count == 0) { + _cairo_clip_set_all_clipped (clip); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +box_is_aligned (const cairo_box_t *box) +{ + return + _cairo_fixed_is_integer (box->p1.x) && + _cairo_fixed_is_integer (box->p1.y) && + _cairo_fixed_is_integer (box->p2.x) && + _cairo_fixed_is_integer (box->p2.y); +} + +static void +intersect_with_boxes (cairo_composite_rectangles_t *extents, + cairo_box_t *boxes, + int num_boxes) +{ + cairo_rectangle_int_t rect; + cairo_box_t box; + cairo_bool_t is_empty; + + box.p1.x = box.p1.y = INT_MIN; + box.p2.x = box.p2.y = INT_MAX; + while (num_boxes--) { + if (boxes->p1.x < box.p1.x) + box.p1.x = boxes->p1.x; + if (boxes->p1.y < box.p1.y) + box.p1.y = boxes->p1.y; + + if (boxes->p2.x > box.p2.x) + box.p2.x = boxes->p2.x; + if (boxes->p2.y > box.p2.y) + box.p2.y = boxes->p2.y; + } + + _cairo_box_round_to_rectangle (&box, &rect); + is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect); + is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect); +} + +cairo_status_t +_cairo_clip_to_boxes (cairo_clip_t **clip, + cairo_composite_rectangles_t *extents, + cairo_box_t **boxes, + int *num_boxes) +{ + cairo_status_t status; + const cairo_rectangle_int_t *rect; + + rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; + + if (*clip == NULL) + goto EXTENTS; + + status = _cairo_clip_rectangle (*clip, rect); + if (unlikely (status)) + return status; + + status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); + switch ((int) status) { + case CAIRO_STATUS_SUCCESS: + intersect_with_boxes (extents, *boxes, *num_boxes); + if (rect->width == 0 || rect->height == 0 || + extents->is_bounded || + (*num_boxes == 1 && box_is_aligned (*boxes))) + { + *clip = NULL; + } + goto DONE; + + case CAIRO_INT_STATUS_UNSUPPORTED: + goto EXTENTS; + + default: + return status; + } + + EXTENTS: + status = CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&(*boxes)[0], rect); + *num_boxes = 1; + DONE: + return status; +} + + +static cairo_rectangle_list_t * +_cairo_rectangle_list_create_in_error (cairo_status_t status) +{ + cairo_rectangle_list_t *list; + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) + return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + + list = malloc (sizeof (*list)); + if (unlikely (list == NULL)) { + _cairo_error_throw (status); + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + } + + list->status = status; + list->rectangles = NULL; + list->num_rectangles = 0; + + return list; +} + +cairo_rectangle_list_t * +_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) +{ +#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)) + + cairo_rectangle_list_t *list; + cairo_rectangle_t *rectangles = NULL; + cairo_region_t *region = NULL; + cairo_int_status_t status; + int n_rects = 0; + int i; + + if (clip->all_clipped) + goto DONE; + + if (!clip->path) + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + + status = _cairo_clip_get_region (clip, ®ion); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + goto DONE; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + } else if (unlikely (status)) { + return ERROR_LIST (status); + } + + n_rects = cairo_region_num_rectangles (region); + if (n_rects) { + rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); + if (unlikely (rectangles == NULL)) { + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_rects; ++i) { + cairo_rectangle_int_t clip_rect; + + cairo_region_get_rectangle (region, i, &clip_rect); + + if (! _cairo_clip_int_rect_to_user (gstate, + &clip_rect, + &rectangles[i])) + { + free (rectangles); + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + } + } + } + + DONE: + list = malloc (sizeof (cairo_rectangle_list_t)); + if (unlikely (list == NULL)) { + free (rectangles); + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); + } + + list->status = CAIRO_STATUS_SUCCESS; + list->rectangles = rectangles; + list->num_rectangles = n_rects; + return list; + +#undef ERROR_LIST +} + +/** + * cairo_rectangle_list_destroy: + * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles() + * + * Unconditionally frees @rectangle_list and all associated + * references. After this call, the @rectangle_list pointer must not + * be dereferenced. + * + * Since: 1.4 + **/ +void +cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) +{ + if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil || + rectangle_list == &_cairo_rectangles_not_representable) + return; + + free (rectangle_list->rectangles); + free (rectangle_list); +} + +void +_cairo_clip_reset_static_data (void) +{ + _freed_pool_reset (&clip_path_pool); +} diff --git a/libs/cairo/src/cairo-color.c b/libs/cairo/src/cairo-color.c new file mode 100644 index 000000000..b9dae237e --- /dev/null +++ b/libs/cairo/src/cairo-color.c @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +static cairo_color_t const cairo_color_white = { + 1.0, 1.0, 1.0, 1.0, + 0xffff, 0xffff, 0xffff, 0xffff +}; + +static cairo_color_t const cairo_color_black = { + 0.0, 0.0, 0.0, 1.0, + 0x0, 0x0, 0x0, 0xffff +}; + +static cairo_color_t const cairo_color_transparent = { + 0.0, 0.0, 0.0, 0.0, + 0x0, 0x0, 0x0, 0x0 +}; + +static cairo_color_t const cairo_color_magenta = { + 1.0, 0.0, 1.0, 1.0, + 0xffff, 0x0, 0xffff, 0xffff +}; + +const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock) +{ + switch (stock) { + case CAIRO_STOCK_WHITE: + return &cairo_color_white; + case CAIRO_STOCK_BLACK: + return &cairo_color_black; + case CAIRO_STOCK_TRANSPARENT: + return &cairo_color_transparent; + + case CAIRO_STOCK_NUM_COLORS: + default: + ASSERT_NOT_REACHED; + /* If the user can get here somehow, give a color that indicates a + * problem. */ + return &cairo_color_magenta; + } +} + +void +_cairo_color_init (cairo_color_t *color) +{ + *color = cairo_color_white; +} + +void +_cairo_color_init_rgb (cairo_color_t *color, + double red, double green, double blue) +{ + _cairo_color_init_rgba (color, red, green, blue, 1.0); +} + +/* Convert a double in [0.0, 1.0] to an integer in [0, 65535] + * The conversion is designed to divide the input range into 65536 + * equally-sized regions. This is achieved by multiplying by 65536 and + * then special-casing the result of an input value of 1.0 so that it + * maps to 65535 instead of 65536. + */ +uint16_t +_cairo_color_double_to_short (double d) +{ + uint32_t i; + i = (uint32_t) (d * 65536); + i -= (i >> 16); + return i; +} + +static void +_cairo_color_compute_shorts (cairo_color_t *color) +{ + color->red_short = _cairo_color_double_to_short (color->red * color->alpha); + color->green_short = _cairo_color_double_to_short (color->green * color->alpha); + color->blue_short = _cairo_color_double_to_short (color->blue * color->alpha); + color->alpha_short = _cairo_color_double_to_short (color->alpha); +} + +void +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha) +{ + color->red = red; + color->green = green; + color->blue = blue; + color->alpha = alpha; + + _cairo_color_compute_shorts (color); +} + +void +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha) +{ + color->alpha *= alpha; + + _cairo_color_compute_shorts (color); +} + +void +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) +{ + *red = color->red; + *green = color->green; + *blue = color->blue; + *alpha = color->alpha; +} + +void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) +{ + *red = color->red * color->alpha; + *green = color->green * color->alpha; + *blue = color->blue * color->alpha; + *alpha = color->alpha; +} + +/* NB: This function works both for unmultiplied and premultiplied colors */ +cairo_bool_t +_cairo_color_equal (const cairo_color_t *color_a, + const cairo_color_t *color_b) +{ + if (color_a == color_b) + return TRUE; + + if (color_a->alpha_short != color_b->alpha_short) + return FALSE; + + if (color_a->alpha_short == 0) + return TRUE; + + return color_a->red_short == color_b->red_short && + color_a->green_short == color_b->green_short && + color_a->blue_short == color_b->blue_short; +} + +cairo_bool_t +_cairo_color_stop_equal (const cairo_color_stop_t *color_a, + const cairo_color_stop_t *color_b) +{ + if (color_a == color_b) + return TRUE; + + return color_a->alpha_short == color_b->alpha_short && + color_a->red_short == color_b->red_short && + color_a->green_short == color_b->green_short && + color_a->blue_short == color_b->blue_short; +} + +cairo_content_t +_cairo_color_get_content (const cairo_color_t *color) +{ + if (CAIRO_COLOR_IS_OPAQUE (color)) + return CAIRO_CONTENT_COLOR; + + if (color->red_short == 0 && + color->green_short == 0 && + color->blue_short == 0) + { + return CAIRO_CONTENT_ALPHA; + } + + return CAIRO_CONTENT_COLOR_ALPHA; +} diff --git a/libs/cairo/src/cairo-combsort-private.h b/libs/cairo/src/cairo-combsort-private.h new file mode 100644 index 000000000..3400a681c --- /dev/null +++ b/libs/cairo/src/cairo-combsort-private.h @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This fragment implements a comb sort (specifically combsort11) */ +#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP +#define _HAVE_CAIRO_COMBSORT_NEWGAP +static inline unsigned int +_cairo_combsort_newgap (unsigned int gap) +{ + gap = 10 * gap / 13; + if (gap == 9 || gap == 10) + gap = 11; + if (gap < 1) + gap = 1; + return gap; +} +#endif + +#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \ +static void \ +NAME (TYPE *base, unsigned int nmemb) \ +{ \ + unsigned int gap = nmemb; \ + unsigned int i, j; \ + int swapped; \ + do { \ + gap = _cairo_combsort_newgap (gap); \ + swapped = gap > 1; \ + for (i = 0; i < nmemb-gap ; i++) { \ + j = i + gap; \ + if (CMP (base[i], base[j]) > 0 ) { \ + TYPE tmp; \ + tmp = base[i]; \ + base[i] = base[j]; \ + base[j] = tmp; \ + swapped = 1; \ + } \ + } \ + } while (swapped); \ +} diff --git a/libs/cairo/src/cairo-compiler-private.h b/libs/cairo/src/cairo-compiler-private.h new file mode 100644 index 000000000..34cfe0956 --- /dev/null +++ b/libs/cairo/src/cairo-compiler-private.h @@ -0,0 +1,244 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_COMPILER_PRIVATE_H +#define CAIRO_COMPILER_PRIVATE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/* Size in bytes of buffer to use off the stack per functions. + * Mostly used by text functions. For larger allocations, they'll + * malloc(). */ +#ifndef CAIRO_STACK_BUFFER_SIZE +#define CAIRO_STACK_BUFFER_SIZE (512 * sizeof (int)) +#endif + +#define CAIRO_STACK_ARRAY_LENGTH(T) (CAIRO_STACK_BUFFER_SIZE / sizeof(T)) + +/* + * The goal of this block is to define the following macros for + * providing faster linkage to functions in the public API for calls + * from within cairo. + * + * slim_hidden_proto(f) + * slim_hidden_proto_no_warn(f) + * + * Declares `f' as a library internal function and hides the + * function from the global symbol table. This macro must be + * expanded after `f' has been declared with a prototype but before + * any calls to the function are seen by the compiler. The no_warn + * variant inhibits warnings about the return value being unused at + * call sites. The macro works by renaming `f' to an internal name + * in the symbol table and hiding that. As far as cairo internal + * calls are concerned they're calling a library internal function + * and thus don't need to bounce via the PLT. + * + * slim_hidden_def(f) + * + * Exports `f' back to the global symbol table. This macro must be + * expanded right after the function definition and only for symbols + * hidden previously with slim_hidden_proto(). The macro works by + * adding a global entry to the symbol table which points at the + * internal name of `f' created by slim_hidden_proto(). + * + * Functions in the public API which aren't called by the library + * don't need to be hidden and re-exported using the slim hidden + * macros. + */ +#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun) +# define slim_hidden_proto(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private +# define slim_hidden_proto_no_warn(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private_no_warn +# define slim_hidden_def(name) slim_hidden_def1(name, slim_hidden_int_name(name)) +# define slim_hidden_int_name(name) INT_##name +# define slim_hidden_proto1(name, internal) \ + extern __typeof (name) name \ + __asm__ (slim_hidden_asmname (internal)) +# define slim_hidden_def1(name, internal) \ + extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \ + __attribute__((__alias__(slim_hidden_asmname(internal)))) +# define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__) +# define slim_hidden_ulp1(x) slim_hidden_ulp2(x) +# define slim_hidden_ulp2(x) #x +# define slim_hidden_asmname(name) slim_hidden_asmname1(name) +# define slim_hidden_asmname1(name) slim_hidden_ulp #name +#else +# define slim_hidden_proto(name) int _cairo_dummy_prototype(void) +# define slim_hidden_proto_no_warn(name) int _cairo_dummy_prototype(void) +# define slim_hidden_def(name) int _cairo_dummy_prototype(void) +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) \ + __attribute__((__format__(__printf__, fmt_index, va_index))) +#else +#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) +#endif + +/* slim_internal.h */ +#define CAIRO_HAS_HIDDEN_SYMBOLS 1 +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun) +#define cairo_private_no_warn __attribute__((__visibility__("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define cairo_private_no_warn __hidden +#else /* not gcc >= 3.3 and not Sun Studio >= 8 */ +#define cairo_private_no_warn +#undef CAIRO_HAS_HIDDEN_SYMBOLS +#endif + +#ifndef WARN_UNUSED_RESULT +#define WARN_UNUSED_RESULT +#endif +/* Add attribute(warn_unused_result) if supported */ +#define cairo_warn WARN_UNUSED_RESULT +#define cairo_private cairo_private_no_warn cairo_warn + +/* This macro allow us to deprecate a function by providing an alias + for the old function name to the new function name. With this + macro, binary compatibility is preserved. The macro only works on + some platforms --- tough. + + Meanwhile, new definitions in the public header file break the + source code so that it will no longer link against the old + symbols. Instead it will give a descriptive error message + indicating that the old function has been deprecated by the new + function. +*/ +#if __GNUC__ >= 2 && defined(__ELF__) +# define CAIRO_FUNCTION_ALIAS(old, new) \ + extern __typeof (new) old \ + __asm__ ("" #old) \ + __attribute__((__alias__("" #new))) +#else +# define CAIRO_FUNCTION_ALIAS(old, new) +#endif + +/* + * Cairo uses the following function attributes in order to improve the + * generated code (effectively by manual inter-procedural analysis). + * + * 'cairo_pure': The function is only allowed to read from its arguments + * and global memory (i.e. following a pointer argument or + * accessing a shared variable). The return value should + * only depend on its arguments, and for an identical set of + * arguments should return the same value. + * + * 'cairo_const': The function is only allowed to read from its arguments. + * It is not allowed to access global memory. The return + * value should only depend its arguments, and for an + * identical set of arguments should return the same value. + * This is currently the most strict function attribute. + * + * Both these function attributes allow gcc to perform CSE and + * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents + * do not change across the function call. + */ +#if __GNUC__ >= 3 +#define cairo_pure __attribute__((pure)) +#define cairo_const __attribute__((const)) +#define cairo_always_inline inline __attribute__((always_inline)) +#else +#define cairo_pure +#define cairo_const +#define cairo_always_inline inline +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _CAIRO_BOOLEAN_EXPR(expr) \ + __extension__ ({ \ + int _cairo_boolean_var_; \ + if (expr) \ + _cairo_boolean_var_ = 1; \ + else \ + _cairo_boolean_var_ = 0; \ + _cairo_boolean_var_; \ +}) +#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1)) +#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +/* + * clang-cl supports __attribute__, but MSVC doesn't, so we need to make sure + * we do this if not GNUC but also if not clang either. + */ +#if !defined(__GNUC__) && !defined(__clang__) +#undef __attribute__ +#define __attribute__(x) +#endif + +#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) +#define snprintf _snprintf +#define popen _popen +#define pclose _pclose +#define hypot _hypot +#endif + +#ifdef _MSC_VER + +#define HAVE_WIN32_ATOMIC_PRIMITIVES 1 + +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif + +/* there are currently linkage problems that arise when trying to include intrin.h in c++: + * D:\sdks\v7.0\include\winnt.h(3674) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed + * so avoid defining ffs in c++ code for now */ +#ifndef __cplusplus +/* Add a definition of ffs */ +#include +#pragma intrinsic(_BitScanForward) +static __forceinline int +ffs (int x) +{ + unsigned long i; + + if (_BitScanForward(&i, x) != 0) + return i + 1; + + return 0; +} +#endif + +#elif defined(__WIN32__) && defined(__GNUC__) + +#define ffs(x) __builtin_ffs(x) + +#endif + +#if defined(_MSC_VER) && defined(_M_IX86) +/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. + The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and + will never be folded into another one. Something like this might eventually + be needed for GCC but it seems fine for now. */ +#define CAIRO_ENSURE_UNIQUE \ + do { \ + char file[] = __FILE__; \ + __asm { \ + __asm jmp __internal_skip_line_no \ + __asm _emit (__COUNTER__ & 0xff) \ + __asm _emit ((__COUNTER__>>8) & 0xff) \ + __asm _emit ((__COUNTER__>>16) & 0xff)\ + __asm _emit ((__COUNTER__>>24) & 0xff)\ + __asm lea eax, dword ptr file \ + __asm __internal_skip_line_no: \ + }; \ + } while (0) +#else +#define CAIRO_ENSURE_UNIQUE do { } while (0) +#endif + +#ifdef __STRICT_ANSI__ +#undef inline +#define inline __inline__ +#endif + +#endif diff --git a/libs/cairo/src/cairo-composite-rectangles-private.h b/libs/cairo/src/cairo-composite-rectangles-private.h new file mode 100644 index 000000000..ddbe6bb1a --- /dev/null +++ b/libs/cairo/src/cairo-composite-rectangles-private.h @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H +#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H + +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +/* Rectangles that take part in a composite operation. + * + * The source and mask track the extents of the respective patterns in device + * space. The unbounded rectangle is essentially the clip rectangle. And the + * intersection of all is the bounded rectangle, which is the minimum extents + * the operation may require. Whether or not the operation is actually bounded + * is tracked in the is_bounded boolean. + * + */ +struct _cairo_composite_rectangles { + cairo_rectangle_int_t source; + cairo_rectangle_int_t mask; + cairo_rectangle_int_t bounded; /* dst */ + cairo_rectangle_int_t unbounded; /* clip */ + uint32_t is_bounded; +}; + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip, + cairo_bool_t *overlap); + +#endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-composite-rectangles.c b/libs/cairo/src/cairo-composite-rectangles.c new file mode 100644 index 000000000..a7b499cf4 --- /dev/null +++ b/libs/cairo/src/cairo-composite-rectangles.c @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-composite-rectangles-private.h" + +/* A collection of routines to facilitate writing compositors. */ + +static inline cairo_bool_t +_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + extents->unbounded = *surface_extents; + + if (clip != NULL) { + const cairo_rectangle_int_t *clip_extents; + + clip_extents = _cairo_clip_get_extents (clip); + if (clip_extents == NULL) + return FALSE; + + if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents)) + return FALSE; + } + + extents->bounded = extents->unbounded; + extents->is_bounded = _cairo_operator_bounded_by_either (op); + + _cairo_pattern_get_extents (source, &extents->source); + if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) { + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source)) + return FALSE; + } + + return TRUE; +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface_extents, + op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + extents->mask = extents->bounded; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents) +{ + cairo_bool_t ret; + + ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); + if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface_extents, + op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_pattern_get_extents (mask, &extents->mask); + + return _cairo_composite_rectangles_intersect (extents); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface_extents, + op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); + + return _cairo_composite_rectangles_intersect (extents); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface_extents, + op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_path_fixed_approximate_fill_extents (path, &extents->mask); + + return _cairo_composite_rectangles_intersect (extents); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, + const cairo_rectangle_int_t *surface_extents, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip, + cairo_bool_t *overlap) +{ + cairo_status_t status; + + if (! _cairo_composite_rectangles_init (extents, + surface_extents, + op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, num_glyphs, + &extents->mask, + overlap); + if (unlikely (status)) + return status; + + return _cairo_composite_rectangles_intersect (extents); +} diff --git a/libs/cairo/src/cairo-d2d-private-fx.h b/libs/cairo/src/cairo-d2d-private-fx.h new file mode 100644 index 000000000..1756c57f6 --- /dev/null +++ b/libs/cairo/src/cairo-d2d-private-fx.h @@ -0,0 +1,1164 @@ +#if 0 +// +// FX Version: fx_4_0 +// Child effect (requires effect pool): false +// +// 1 local buffer(s) +// +cbuffer cb0 +{ + float4 QuadDesc; // Offset: 0, size: 16 + float4 TexCoords; // Offset: 16, size: 16 + float4 TextColor; // Offset: 32, size: 16 +} + +// +// 3 local object(s) +// +Texture2D tex; +BlendState bTextBlend +{ + AlphaToCoverageEnable = bool(FALSE /* 0 */); + BlendEnable[0] = bool(TRUE /* 1 */); + SrcBlend[0] = uint(SRC1_COLOR /* 16 */); + DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */); + BlendOp[0] = uint(ADD /* 1 */); + SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */); + DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */); + BlendOpAlpha[0] = uint(ADD /* 1 */); + RenderTargetWriteMask[0] = byte(0x0f); +}; +SamplerState sSampler +{ + Texture = tex; + AddressU = uint(CLAMP /* 3 */); + AddressV = uint(CLAMP /* 3 */); +}; + +// +// 2 technique(s) +// +technique10 SampleTexture +{ + pass P0 + { + VertexShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 + // float4 TexCoords; // Offset: 16 Size: 16 + // float4 TextColor; // Offset: 32 Size: 16 [unused] + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // POSITION 0 xyz 0 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float xyzw + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) + // + // + // Runtime generated constant mappings: + // + // Target Reg Constant Description + // ---------- -------------------------------------------------- + // c0 Vertex Shader position offset + // + // + // Level9 shader bytecode: + // + vs_2_x + def c3, 0, 1, 0, 0 + dcl_texcoord v0 + mad oT0.xy, v0, c2.zwzw, c2 + mad r0.xy, v0, c1.zwzw, c1 + add oPos.xy, r0, c0 + mov oPos.zw, c3.xyxy + + // approximately 4 instruction slots used + vs_4_0 + dcl_constantbuffer cb0[2], immediateIndexed + dcl_input v0.xy + dcl_output_siv o0.xyzw, position + dcl_output o1.xy + mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx + mov o0.zw, l(0,0,0,1.000000) + mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx + ret + // Approximately 4 instruction slots used + + }; + GeometryShader = NULL; + PixelShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // sSampler sampler NA NA 0 1 + // tex texture float4 2d 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Target 0 xyzw 0 TARGET float xyzw + // + // + // Sampler/Resource to DX9 shader sampler mappings: + // + // Target Sampler Source Sampler Source Resource + // -------------- --------------- ---------------- + // s0 s0 t0 + // + // + // Level9 shader bytecode: + // + ps_2_x + dcl t0.xy + dcl_2d s0 + texld r0, t0, s0 + mov oC0, r0 + + // approximately 2 instruction slots used (1 texture, 1 arithmetic) + ps_4_0 + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + sample o0.xyzw, v1.xyxx, t0.xyzw, s0 + ret + // Approximately 2 instruction slots used + + }; + } + +} + +technique10 SampleTextTexture +{ + pass P0 + { + AB_BlendFactor = float4(0, 0, 0, 0); + AB_SampleMask = uint(0xffffffff); + BlendState = bTextBlend; + VertexShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 + // float4 TexCoords; // Offset: 16 Size: 16 + // float4 TextColor; // Offset: 32 Size: 16 [unused] + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // POSITION 0 xyz 0 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float xyzw + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) + // + // + // Runtime generated constant mappings: + // + // Target Reg Constant Description + // ---------- -------------------------------------------------- + // c0 Vertex Shader position offset + // + // + // Level9 shader bytecode: + // + vs_2_x + def c3, 0, 1, 0, 0 + dcl_texcoord v0 + mad oT0.xy, v0, c2.zwzw, c2 + mad r0.xy, v0, c1.zwzw, c1 + add oPos.xy, r0, c0 + mov oPos.zw, c3.xyxy + + // approximately 4 instruction slots used + vs_4_0 + dcl_constantbuffer cb0[2], immediateIndexed + dcl_input v0.xy + dcl_output_siv o0.xyzw, position + dcl_output o1.xy + mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx + mov o0.zw, l(0,0,0,1.000000) + mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx + ret + // Approximately 4 instruction slots used + + }; + GeometryShader = NULL; + PixelShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 [unused] + // float4 TexCoords; // Offset: 16 Size: 16 [unused] + // float4 TextColor; // Offset: 32 Size: 16 + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // sSampler sampler NA NA 0 1 + // tex texture float4 2d 0 1 + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Target 0 xyzw 0 TARGET float xyzw + // SV_Target 1 xyzw 1 TARGET float xyzw + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c0 cb0 2 1 ( FLT, FLT, FLT, FLT) + // + // + // Sampler/Resource to DX9 shader sampler mappings: + // + // Target Sampler Source Sampler Source Resource + // -------------- --------------- ---------------- + // s0 s0 t0 + // + // + // Level9 shader bytecode: + // + ps_2_x + dcl t0.xy + dcl_2d s0 + mov oC0, c0 + texld r0, t0, s0 + mul r0, r0.zyxy, c0.w + mov oC1, r0 + + // approximately 4 instruction slots used (1 texture, 3 arithmetic) + ps_4_0 + dcl_constantbuffer cb0[3], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_output o1.xyzw + dcl_temps 1 + mov o0.xyzw, cb0[2].xyzw + sample r0.xyzw, v1.xyxx, t0.xyzw, s0 + mul o1.xyzw, r0.zyxy, cb0[2].wwww + ret + // Approximately 4 instruction slots used + + }; + } + +} + +#endif + +const BYTE g_main[] = +{ + 68, 88, 66, 67, 53, 137, + 246, 90, 48, 255, 136, 62, + 98, 150, 163, 150, 147, 186, + 203, 53, 1, 0, 0, 0, + 225, 18, 0, 0, 1, 0, + 0, 0, 36, 0, 0, 0, + 70, 88, 49, 48, 181, 18, + 0, 0, 1, 16, 255, 254, + 1, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 57, 16, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 99, 98, + 48, 0, 102, 108, 111, 97, + 116, 52, 0, 8, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 10, 33, 0, + 0, 81, 117, 97, 100, 68, + 101, 115, 99, 0, 84, 101, + 120, 67, 111, 111, 114, 100, + 115, 0, 84, 101, 120, 116, + 67, 111, 108, 111, 114, 0, + 84, 101, 120, 116, 117, 114, + 101, 50, 68, 0, 72, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 0, + 0, 0, 116, 101, 120, 0, + 66, 108, 101, 110, 100, 83, + 116, 97, 116, 101, 0, 114, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 98, 84, 101, + 120, 116, 66, 108, 101, 110, + 100, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 16, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 17, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 18, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 19, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 15, 0, + 0, 0, 83, 97, 109, 112, + 108, 101, 114, 83, 116, 97, + 116, 101, 0, 16, 1, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 0, 0, + 0, 115, 83, 97, 109, 112, + 108, 101, 114, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 83, 97, + 109, 112, 108, 101, 84, 101, + 120, 116, 117, 114, 101, 0, + 80, 48, 0, 188, 3, 0, + 0, 68, 88, 66, 67, 211, + 96, 210, 105, 17, 130, 48, + 194, 178, 234, 96, 122, 215, + 146, 217, 132, 1, 0, 0, + 0, 188, 3, 0, 0, 6, + 0, 0, 0, 56, 0, 0, + 0, 228, 0, 0, 0, 168, + 1, 0, 0, 36, 2, 0, + 0, 48, 3, 0, 0, 100, + 3, 0, 0, 65, 111, 110, + 57, 164, 0, 0, 0, 164, + 0, 0, 0, 0, 2, 254, + 255, 112, 0, 0, 0, 52, + 0, 0, 0, 1, 0, 36, + 0, 0, 0, 48, 0, 0, + 0, 48, 0, 0, 0, 36, + 0, 1, 0, 48, 0, 0, + 0, 0, 0, 2, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 254, + 255, 81, 0, 0, 5, 3, + 0, 15, 160, 0, 0, 0, + 0, 0, 0, 128, 63, 0, + 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 2, 5, + 0, 0, 128, 0, 0, 15, + 144, 4, 0, 0, 4, 0, + 0, 3, 224, 0, 0, 228, + 144, 2, 0, 238, 160, 2, + 0, 228, 160, 4, 0, 0, + 4, 0, 0, 3, 128, 0, + 0, 228, 144, 1, 0, 238, + 160, 1, 0, 228, 160, 2, + 0, 0, 3, 0, 0, 3, + 192, 0, 0, 228, 128, 0, + 0, 228, 160, 1, 0, 0, + 2, 0, 0, 12, 192, 3, + 0, 68, 160, 255, 255, 0, + 0, 83, 72, 68, 82, 188, + 0, 0, 0, 64, 0, 1, + 0, 47, 0, 0, 0, 89, + 0, 0, 4, 70, 142, 32, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 95, 0, 0, + 3, 50, 16, 16, 0, 0, + 0, 0, 0, 103, 0, 0, + 4, 242, 32, 16, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 101, 0, 0, 3, 50, + 32, 16, 0, 1, 0, 0, + 0, 50, 0, 0, 11, 50, + 32, 16, 0, 0, 0, 0, + 0, 70, 16, 16, 0, 0, + 0, 0, 0, 230, 138, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 70, 128, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, + 8, 194, 32, 16, 0, 0, + 0, 0, 0, 2, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 63, 50, + 0, 0, 11, 50, 32, 16, + 0, 1, 0, 0, 0, 70, + 16, 16, 0, 0, 0, 0, + 0, 230, 138, 32, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 70, 128, 32, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 62, 0, 0, 1, 83, + 84, 65, 84, 116, 0, 0, + 0, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 68, 69, + 70, 4, 1, 0, 0, 1, + 0, 0, 0, 64, 0, 0, + 0, 1, 0, 0, 0, 28, + 0, 0, 0, 0, 4, 254, + 255, 0, 1, 0, 0, 208, + 0, 0, 0, 60, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 99, + 98, 48, 0, 60, 0, 0, + 0, 3, 0, 0, 0, 88, + 0, 0, 0, 48, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 0, 0, + 0, 0, 0, 0, 0, 16, + 0, 0, 0, 2, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 188, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 2, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 198, 0, 0, + 0, 32, 0, 0, 0, 16, + 0, 0, 0, 0, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 81, 117, 97, + 100, 68, 101, 115, 99, 0, + 171, 171, 171, 1, 0, 3, + 0, 1, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 84, 101, 120, 67, 111, + 111, 114, 100, 115, 0, 84, + 101, 120, 116, 67, 111, 108, + 111, 114, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 57, 46, 50, 57, + 46, 57, 53, 50, 46, 51, + 49, 49, 49, 0, 171, 171, + 171, 73, 83, 71, 78, 44, + 0, 0, 0, 1, 0, 0, + 0, 8, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 7, 3, 0, 0, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 171, 171, 171, 79, + 83, 71, 78, 80, 0, 0, + 0, 2, 0, 0, 0, 8, + 0, 0, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 68, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 1, 0, 0, 0, 3, + 12, 0, 0, 83, 86, 95, + 80, 111, 115, 105, 116, 105, + 111, 110, 0, 84, 69, 88, + 67, 79, 79, 82, 68, 0, + 171, 171, 171, 107, 1, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 188, + 2, 0, 0, 68, 88, 66, + 67, 57, 173, 135, 37, 0, + 15, 237, 50, 142, 80, 59, + 160, 81, 240, 60, 171, 1, + 0, 0, 0, 188, 2, 0, + 0, 6, 0, 0, 0, 56, + 0, 0, 0, 164, 0, 0, + 0, 16, 1, 0, 0, 140, + 1, 0, 0, 48, 2, 0, + 0, 136, 2, 0, 0, 65, + 111, 110, 57, 100, 0, 0, + 0, 100, 0, 0, 0, 0, + 2, 255, 255, 60, 0, 0, + 0, 40, 0, 0, 0, 0, + 0, 40, 0, 0, 0, 40, + 0, 0, 0, 40, 0, 1, + 0, 36, 0, 0, 0, 40, + 0, 0, 0, 0, 0, 1, + 2, 255, 255, 31, 0, 0, + 2, 0, 0, 0, 128, 0, + 0, 3, 176, 31, 0, 0, + 2, 0, 0, 0, 144, 0, + 8, 15, 160, 66, 0, 0, + 3, 0, 0, 15, 128, 0, + 0, 228, 176, 0, 8, 228, + 160, 1, 0, 0, 2, 0, + 8, 15, 128, 0, 0, 228, + 128, 255, 255, 0, 0, 83, + 72, 68, 82, 100, 0, 0, + 0, 64, 0, 0, 0, 25, + 0, 0, 0, 90, 0, 0, + 3, 0, 96, 16, 0, 0, + 0, 0, 0, 88, 24, 0, + 4, 0, 112, 16, 0, 0, + 0, 0, 0, 85, 85, 0, + 0, 98, 16, 0, 3, 50, + 16, 16, 0, 1, 0, 0, + 0, 101, 0, 0, 3, 242, + 32, 16, 0, 0, 0, 0, + 0, 69, 0, 0, 9, 242, + 32, 16, 0, 0, 0, 0, + 0, 70, 16, 16, 0, 1, + 0, 0, 0, 70, 126, 16, + 0, 0, 0, 0, 0, 0, + 96, 16, 0, 0, 0, 0, + 0, 62, 0, 0, 1, 83, + 84, 65, 84, 116, 0, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 68, 69, + 70, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 28, + 0, 0, 0, 0, 4, 255, + 255, 0, 1, 0, 0, 105, + 0, 0, 0, 92, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 101, + 0, 0, 0, 2, 0, 0, + 0, 5, 0, 0, 0, 4, + 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 1, + 0, 0, 0, 12, 0, 0, + 0, 115, 83, 97, 109, 112, + 108, 101, 114, 0, 116, 101, + 120, 0, 77, 105, 99, 114, + 111, 115, 111, 102, 116, 32, + 40, 82, 41, 32, 72, 76, + 83, 76, 32, 83, 104, 97, + 100, 101, 114, 32, 67, 111, + 109, 112, 105, 108, 101, 114, + 32, 57, 46, 50, 57, 46, + 57, 53, 50, 46, 51, 49, + 49, 49, 0, 171, 171, 73, + 83, 71, 78, 80, 0, 0, + 0, 2, 0, 0, 0, 8, + 0, 0, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 68, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 1, 0, 0, 0, 3, + 3, 0, 0, 83, 86, 95, + 80, 111, 115, 105, 116, 105, + 111, 110, 0, 84, 69, 88, + 67, 79, 79, 82, 68, 0, + 171, 171, 171, 79, 83, 71, + 78, 44, 0, 0, 0, 1, + 0, 0, 0, 8, 0, 0, + 0, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 15, 0, 0, + 0, 83, 86, 95, 84, 97, + 114, 103, 101, 116, 0, 171, + 171, 63, 5, 0, 0, 0, + 0, 0, 0, 83, 97, 109, + 112, 108, 101, 84, 101, 120, + 116, 84, 101, 120, 116, 117, + 114, 101, 0, 4, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 3, 0, 0, 0, 255, + 255, 255, 255, 188, 3, 0, + 0, 68, 88, 66, 67, 211, + 96, 210, 105, 17, 130, 48, + 194, 178, 234, 96, 122, 215, + 146, 217, 132, 1, 0, 0, + 0, 188, 3, 0, 0, 6, + 0, 0, 0, 56, 0, 0, + 0, 228, 0, 0, 0, 168, + 1, 0, 0, 36, 2, 0, + 0, 48, 3, 0, 0, 100, + 3, 0, 0, 65, 111, 110, + 57, 164, 0, 0, 0, 164, + 0, 0, 0, 0, 2, 254, + 255, 112, 0, 0, 0, 52, + 0, 0, 0, 1, 0, 36, + 0, 0, 0, 48, 0, 0, + 0, 48, 0, 0, 0, 36, + 0, 1, 0, 48, 0, 0, + 0, 0, 0, 2, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 254, + 255, 81, 0, 0, 5, 3, + 0, 15, 160, 0, 0, 0, + 0, 0, 0, 128, 63, 0, + 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 2, 5, + 0, 0, 128, 0, 0, 15, + 144, 4, 0, 0, 4, 0, + 0, 3, 224, 0, 0, 228, + 144, 2, 0, 238, 160, 2, + 0, 228, 160, 4, 0, 0, + 4, 0, 0, 3, 128, 0, + 0, 228, 144, 1, 0, 238, + 160, 1, 0, 228, 160, 2, + 0, 0, 3, 0, 0, 3, + 192, 0, 0, 228, 128, 0, + 0, 228, 160, 1, 0, 0, + 2, 0, 0, 12, 192, 3, + 0, 68, 160, 255, 255, 0, + 0, 83, 72, 68, 82, 188, + 0, 0, 0, 64, 0, 1, + 0, 47, 0, 0, 0, 89, + 0, 0, 4, 70, 142, 32, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 95, 0, 0, + 3, 50, 16, 16, 0, 0, + 0, 0, 0, 103, 0, 0, + 4, 242, 32, 16, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 101, 0, 0, 3, 50, + 32, 16, 0, 1, 0, 0, + 0, 50, 0, 0, 11, 50, + 32, 16, 0, 0, 0, 0, + 0, 70, 16, 16, 0, 0, + 0, 0, 0, 230, 138, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 70, 128, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, + 8, 194, 32, 16, 0, 0, + 0, 0, 0, 2, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 63, 50, + 0, 0, 11, 50, 32, 16, + 0, 1, 0, 0, 0, 70, + 16, 16, 0, 0, 0, 0, + 0, 230, 138, 32, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 70, 128, 32, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 62, 0, 0, 1, 83, + 84, 65, 84, 116, 0, 0, + 0, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 68, 69, + 70, 4, 1, 0, 0, 1, + 0, 0, 0, 64, 0, 0, + 0, 1, 0, 0, 0, 28, + 0, 0, 0, 0, 4, 254, + 255, 0, 1, 0, 0, 208, + 0, 0, 0, 60, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 99, + 98, 48, 0, 60, 0, 0, + 0, 3, 0, 0, 0, 88, + 0, 0, 0, 48, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 0, 0, + 0, 0, 0, 0, 0, 16, + 0, 0, 0, 2, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 188, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 2, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 198, 0, 0, + 0, 32, 0, 0, 0, 16, + 0, 0, 0, 0, 0, 0, + 0, 172, 0, 0, 0, 0, + 0, 0, 0, 81, 117, 97, + 100, 68, 101, 115, 99, 0, + 171, 171, 171, 1, 0, 3, + 0, 1, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 84, 101, 120, 67, 111, + 111, 114, 100, 115, 0, 84, + 101, 120, 116, 67, 111, 108, + 111, 114, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 57, 46, 50, 57, + 46, 57, 53, 50, 46, 51, + 49, 49, 49, 0, 171, 171, + 171, 73, 83, 71, 78, 44, + 0, 0, 0, 1, 0, 0, + 0, 8, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 7, 3, 0, 0, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 171, 171, 171, 79, + 83, 71, 78, 80, 0, 0, + 0, 2, 0, 0, 0, 8, + 0, 0, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 68, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 1, 0, 0, 0, 3, + 12, 0, 0, 83, 86, 95, + 80, 111, 115, 105, 116, 105, + 111, 110, 0, 84, 69, 88, + 67, 79, 79, 82, 68, 0, + 171, 171, 171, 73, 8, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 16, + 4, 0, 0, 68, 88, 66, + 67, 175, 129, 196, 242, 129, + 85, 126, 90, 106, 179, 87, + 12, 194, 2, 170, 102, 1, + 0, 0, 0, 16, 4, 0, + 0, 6, 0, 0, 0, 56, + 0, 0, 0, 204, 0, 0, + 0, 148, 1, 0, 0, 16, + 2, 0, 0, 108, 3, 0, + 0, 196, 3, 0, 0, 65, + 111, 110, 57, 140, 0, 0, + 0, 140, 0, 0, 0, 0, + 2, 255, 255, 88, 0, 0, + 0, 52, 0, 0, 0, 1, + 0, 40, 0, 0, 0, 52, + 0, 0, 0, 52, 0, 1, + 0, 36, 0, 0, 0, 52, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, + 2, 255, 255, 31, 0, 0, + 2, 0, 0, 0, 128, 0, + 0, 3, 176, 31, 0, 0, + 2, 0, 0, 0, 144, 0, + 8, 15, 160, 1, 0, 0, + 2, 0, 8, 15, 128, 0, + 0, 228, 160, 66, 0, 0, + 3, 0, 0, 15, 128, 0, + 0, 228, 176, 0, 8, 228, + 160, 5, 0, 0, 3, 0, + 0, 15, 128, 0, 0, 70, + 128, 0, 0, 255, 160, 1, + 0, 0, 2, 1, 8, 15, + 128, 0, 0, 228, 128, 255, + 255, 0, 0, 83, 72, 68, + 82, 192, 0, 0, 0, 64, + 0, 0, 0, 48, 0, 0, + 0, 89, 0, 0, 4, 70, + 142, 32, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 90, + 0, 0, 3, 0, 96, 16, + 0, 0, 0, 0, 0, 88, + 24, 0, 4, 0, 112, 16, + 0, 0, 0, 0, 0, 85, + 85, 0, 0, 98, 16, 0, + 3, 50, 16, 16, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 242, 32, 16, 0, 0, + 0, 0, 0, 101, 0, 0, + 3, 242, 32, 16, 0, 1, + 0, 0, 0, 104, 0, 0, + 2, 1, 0, 0, 0, 54, + 0, 0, 6, 242, 32, 16, + 0, 0, 0, 0, 0, 70, + 142, 32, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 69, + 0, 0, 9, 242, 0, 16, + 0, 0, 0, 0, 0, 70, + 16, 16, 0, 1, 0, 0, + 0, 70, 126, 16, 0, 0, + 0, 0, 0, 0, 96, 16, + 0, 0, 0, 0, 0, 56, + 0, 0, 8, 242, 32, 16, + 0, 1, 0, 0, 0, 102, + 4, 16, 0, 0, 0, 0, + 0, 246, 143, 32, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 62, 0, 0, 1, 83, + 84, 65, 84, 116, 0, 0, + 0, 4, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 68, 69, + 70, 84, 1, 0, 0, 1, + 0, 0, 0, 144, 0, 0, + 0, 3, 0, 0, 0, 28, + 0, 0, 0, 0, 4, 255, + 255, 0, 1, 0, 0, 32, + 1, 0, 0, 124, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 133, + 0, 0, 0, 2, 0, 0, + 0, 5, 0, 0, 0, 4, + 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 1, + 0, 0, 0, 12, 0, 0, + 0, 137, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 115, 83, 97, + 109, 112, 108, 101, 114, 0, + 116, 101, 120, 0, 99, 98, + 48, 0, 171, 171, 171, 137, + 0, 0, 0, 3, 0, 0, + 0, 168, 0, 0, 0, 48, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 240, + 0, 0, 0, 0, 0, 0, + 0, 16, 0, 0, 0, 0, + 0, 0, 0, 252, 0, 0, + 0, 0, 0, 0, 0, 12, + 1, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 0, + 0, 0, 0, 252, 0, 0, + 0, 0, 0, 0, 0, 22, + 1, 0, 0, 32, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 252, 0, 0, + 0, 0, 0, 0, 0, 81, + 117, 97, 100, 68, 101, 115, + 99, 0, 171, 171, 171, 1, + 0, 3, 0, 1, 0, 4, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 101, 120, + 67, 111, 111, 114, 100, 115, + 0, 84, 101, 120, 116, 67, + 111, 108, 111, 114, 0, 77, + 105, 99, 114, 111, 115, 111, + 102, 116, 32, 40, 82, 41, + 32, 72, 76, 83, 76, 32, + 83, 104, 97, 100, 101, 114, + 32, 67, 111, 109, 112, 105, + 108, 101, 114, 32, 57, 46, + 50, 57, 46, 57, 53, 50, + 46, 51, 49, 49, 49, 0, + 171, 171, 171, 73, 83, 71, + 78, 80, 0, 0, 0, 2, + 0, 0, 0, 8, 0, 0, + 0, 56, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 15, 0, 0, + 0, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 1, + 0, 0, 0, 3, 3, 0, + 0, 83, 86, 95, 80, 111, + 115, 105, 116, 105, 111, 110, + 0, 84, 69, 88, 67, 79, + 79, 82, 68, 0, 171, 171, + 171, 79, 83, 71, 78, 68, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 56, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 15, 0, 0, 0, 83, + 86, 95, 84, 97, 114, 103, + 101, 116, 0, 171, 171, 29, + 12, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 48, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, + 0, 43, 0, 0, 0, 15, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 52, + 0, 0, 0, 15, 0, 0, + 0, 0, 0, 0, 0, 16, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 62, 0, 0, + 0, 15, 0, 0, 0, 0, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 110, 0, 0, 0, 82, + 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, + 0, 0, 0, 153, 0, 0, + 0, 125, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, + 255, 9, 0, 0, 0, 36, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 164, + 0, 0, 0, 37, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 176, 0, 0, + 0, 38, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 188, 0, 0, 0, 39, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 200, + 0, 0, 0, 40, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 212, 0, 0, + 0, 41, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 224, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 236, + 0, 0, 0, 43, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 248, 0, 0, + 0, 44, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 4, 1, 0, 0, 0, + 0, 0, 0, 57, 1, 0, + 0, 29, 1, 0, 0, 0, + 0, 0, 0, 255, 255, 255, + 255, 3, 0, 0, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 110, + 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 66, 1, 0, + 0, 47, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 78, 1, 0, 0, 0, + 0, 0, 0, 90, 1, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 104, 1, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 7, + 0, 0, 0, 43, 5, 0, + 0, 8, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 51, 5, 0, 0, 7, + 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 255, + 7, 0, 0, 7, 8, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 104, 1, 0, + 0, 6, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 25, 8, 0, + 0, 11, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 61, 8, 0, 0, 2, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 153, + 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 7, + 0, 0, 0, 9, 12, 0, + 0, 8, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 17, 12, 0, 0, 7, + 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 49, + 16, 0, 0 +}; diff --git a/libs/cairo/src/cairo-d2d-private.fx b/libs/cairo/src/cairo-d2d-private.fx new file mode 100644 index 000000000..8f05d693b --- /dev/null +++ b/libs/cairo/src/cairo-d2d-private.fx @@ -0,0 +1,96 @@ +// We store vertex coordinates and the quad shape in a constant buffer, this is +// easy to update and allows us to use a single call to set the x, y, w, h of +// the quad. +// The QuadDesc and TexCoords both work as follows: +// The x component is the quad left point, the y component is the top point +// the z component is the width, and the w component is the height. The quad +// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f } +// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right +// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture +// space <0, 1.0f> left to right and top to bottom. The input vertices of the +// shader stage always form a rectangle from {0, 0} - {1, 1} +cbuffer cb0 +{ + float4 QuadDesc; + float4 TexCoords; + float4 TextColor; +} + +struct VS_OUTPUT +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; +}; + +struct PS_OUTPUT +{ + float4 color; + float4 alpha; +}; + +Texture2D tex; + +BlendState bTextBlend +{ + AlphaToCoverageEnable = FALSE; + BlendEnable[0] = TRUE; + SrcBlend = Src1_Color; + DestBlend = Inv_Src1_Color; + BlendOp = Add; + SrcBlendAlpha = Src1_Alpha; + DestBlendAlpha = Inv_Src1_Alpha; + BlendOpAlpha = Add; + RenderTargetWriteMask[0] = 0x0F; // All +}; + +sampler sSampler = sampler_state { + Texture = tex; + AddressU = Clamp; + AddressV = Clamp; +}; + +VS_OUTPUT SampleTextureVS(float3 pos : POSITION) +{ + VS_OUTPUT Output; + Output.Position.w = 1.0f; + Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x; + Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y; + Output.Position.z = 0; + Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x; + Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y; + return Output; +} + +float4 SampleTexturePS( VS_OUTPUT In) : SV_Target +{ + return tex.Sample(sSampler, In.TexCoord); +}; + +PS_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target +{ + PS_OUTPUT output; + output.color = TextColor; + output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a; + return output; +}; + +technique10 SampleTexture +{ + pass P0 + { + SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); + SetGeometryShader(NULL); + SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS())); + } +} + +technique10 SampleTextTexture +{ + pass P0 + { + SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); + SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); + SetGeometryShader(NULL); + SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS())); + } +} diff --git a/libs/cairo/src/cairo-d2d-private.h b/libs/cairo/src/cairo-d2d-private.h new file mode 100644 index 000000000..00244b497 --- /dev/null +++ b/libs/cairo/src/cairo-d2d-private.h @@ -0,0 +1,160 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_D2D_PRIVATE_H +#define CAIRO_D2D_PRIVATE_H + +#ifdef CAIRO_HAS_D2D_SURFACE + +#include +#include +#include +#include + +#include "cairoint.h" +#include "cairo-surface-clipper-private.h" + +#include "cairo-win32-refptr.h" +#include "cairo-d2d-private-fx.h" +#include "cairo-win32.h" +#include "cairo-list-private.h" + +/* describes the type of the currently applied clip so that we can pop it */ +struct d2d_clip_t; + +#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1 + +struct _cairo_d2d_device +{ + cairo_device_t base; + + HMODULE mD3D10_1; + RefPtr mD3D10Device; + RefPtr mSampleEffect; + RefPtr mInputLayout; + RefPtr mQuadBuffer; + RefPtr mRasterizerState; + RefPtr mBlendStates[MAX_OPERATORS]; + /** Texture used for manual glyph rendering */ + RefPtr mTextTexture; + RefPtr mTextTextureView; + int mVRAMUsage; +}; + +const unsigned int TEXT_TEXTURE_WIDTH = 2048; +const unsigned int TEXT_TEXTURE_HEIGHT = 512; +typedef struct _cairo_d2d_device cairo_d2d_device_t; + +struct _cairo_d2d_surface { + _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), + textRenderingState(TEXT_RENDERING_UNINITIALIZED) + { + _cairo_clip_init (&this->clip); + cairo_list_init(&this->dependent_surfaces); + } + + ~_cairo_d2d_surface(); + + + cairo_surface_t base; + /* Device used by this surface + * NOTE: In upstream cairo this is in the surface base class */ + cairo_d2d_device_t *device; + + /** Render target of the texture we render to */ + RefPtr rt; + /** Surface containing our backstore */ + RefPtr surface; + /** + * Surface used to temporarily store our surface if a bitmap isn't available + * straight from our render target surface. + */ + RefPtr bufferTexture; + /** Backbuffer surface hwndrt renders to (NULL if not a window surface) */ + RefPtr backBuf; + /** Bitmap shared with texture and rendered to by rt */ + RefPtr surfaceBitmap; + /** Swap chain holding our backbuffer (NULL if not a window surface) */ + RefPtr dxgiChain; + /** Window handle of the window we belong to */ + HWND hwnd; + /** Format of the surface */ + cairo_format_t format; + + cairo_clip_t clip; + d2d_clip_t *d2d_clip; + + + /** Mask layer used by surface_mask to push opacity masks */ + RefPtr maskLayer; + /** + * Layer used for clipping when tiling, and also for clearing out geometries + * - lazily initialized + */ + RefPtr helperLayer; + /** If this layer currently is clipping, used to prevent excessive push/pops */ + bool clipping; + /** Brush used for bitmaps */ + RefPtr bitmapBrush; + /** Brush used for solid colors */ + RefPtr solidColorBrush; + /** Indicates if our render target is currently in drawing mode */ + bool isDrawing; + /** Indicates if text rendering is initialized */ + enum TextRenderingState { + TEXT_RENDERING_UNINITIALIZED, + TEXT_RENDERING_NO_CLEARTYPE, + TEXT_RENDERING_NORMAL, + TEXT_RENDERING_GDI_CLASSIC + }; + TextRenderingState textRenderingState; + + RefPtr buffer_rt_view; + RefPtr buffer_sr_view; + + // Other d2d surfaces which depend on this one and need to be flushed if + // it is drawn to. This is required for situations where this surface is + // drawn to another surface, but may be modified before the other surface + // has flushed. When the flush of the other surface then happens and the + // drawing command is actually executed, the contents of this surface will + // no longer be what it was when the drawing command was issued. + cairo_list_t dependent_surfaces; + //cairo_surface_clipper_t clipper; +}; +typedef struct _cairo_d2d_surface cairo_d2d_surface_t; + +struct _cairo_d2d_surface_entry +{ + cairo_list_t link; + cairo_d2d_surface_t *surface; +}; + +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + +typedef HRESULT (WINAPI*D3D10CreateDevice1Func)( + IDXGIAdapter *pAdapter, + D3D10_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + D3D10_FEATURE_LEVEL1 HardwareLevel, + UINT SDKVersion, + ID3D10Device1 **ppDevice +); + +typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( + void *pData, + SIZE_T DataLength, + UINT FXFlags, + ID3D10Device *pDevice, + ID3D10EffectPool *pEffectPool, + ID3D10Effect **ppEffect +); + +#endif /* CAIRO_HAS_D2D_SURFACE */ +#endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-d2d-surface.cpp b/libs/cairo/src/cairo-d2d-surface.cpp new file mode 100644 index 000000000..6aa8a3503 --- /dev/null +++ b/libs/cairo/src/cairo-d2d-surface.cpp @@ -0,0 +1,4835 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define INITGUID + +#include "cairoint.h" +#include "cairo-d2d-private.h" +#include "cairo-dwrite-private.h" + +#include "cairo-win32.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" + +// Required for using placement new. +#include + +#include "d2d1_1.h" + +#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS + +struct Vertex +{ + float position[2]; +}; + +// This factory is not device dependent, we can store it. But will clear it +// if there are no devices left needing it. +static ID2D1Factory *sD2DFactory = NULL; +static HMODULE sD2DModule; + +static void +_cairo_d2d_release_factory() +{ + int refcnt = sD2DFactory->Release(); + if (!refcnt) { + // Once the last reference goes, free the library. + sD2DFactory = NULL; + FreeLibrary(sD2DModule); + } +} + +/** + * Set a blending mode for an operator. This will also return a boolean that + * reports if for this blend mode the entire surface needs to be blended. This + * is true whenever the DEST blend is not ONE when src alpha is 0. + */ +static cairo_int_status_t +_cairo_d2d_set_operator(cairo_d2d_device_t *device, + cairo_operator_t op) +{ + assert(op < MAX_OPERATORS); + if (op >= MAX_OPERATORS) { + // Eep! Someone forgot to update MAX_OPERATORS probably. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (device->mBlendStates[static_cast(op)]) { + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; + } + + D3D10_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.BlendEnable[0] = TRUE; + desc.AlphaToCoverageEnable = FALSE; + desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + + switch (op) { + case CAIRO_OPERATOR_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_ADD: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_XOR: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + }; + device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]); + + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_d2d_create_device_from_d3d10device(ID3D10Device1 *d3d10device) +{ + HRESULT hr; + D3D10_RASTERIZER_DESC rastDesc; + D3D10_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + D3D10_PASS_DESC passDesc; + ID3D10EffectTechnique *technique; + Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} }; + CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); + D3D10_SUBRESOURCE_DATA data; + CD3D10_TEXTURE2D_DESC textDesc(DXGI_FORMAT_B8G8R8A8_UNORM, + TEXT_TEXTURE_WIDTH, + TEXT_TEXTURE_HEIGHT, + 1, 1); + + cairo_d2d_device_t *device = new cairo_d2d_device_t; + + device->mD3D10Device = d3d10device; + + device->mD3D10_1 = LoadLibraryA("d3d10_1.dll"); + D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) + GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory"); + D2D1CreateFactoryFunc createD2DFactory; + + if (!createEffect) { + goto FAILED; + } + + if (!sD2DFactory) { + sD2DModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(sD2DModule, "D2D1CreateFactory"); + if (!createD2DFactory) { + goto FAILED; + } + D2D1_FACTORY_OPTIONS options; +#ifdef DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&sD2DFactory); + if (FAILED(hr)) { + goto FAILED; + } + } else { + sD2DFactory->AddRef(); + } + + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); + createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect); + + technique = device->mSampleEffect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->GetDesc(&passDesc); + + + hr = device->mD3D10Device->CreateInputLayout(layout, + sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), + passDesc.pIAInputSignature, + passDesc.IAInputSignatureSize, + &device->mInputLayout); + if (FAILED(hr)) { + goto FAILED; + } + + data.pSysMem = (void*)vertices; + hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer); + if (FAILED(hr)) { + goto FAILED; + } + + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.CullMode = D3D10_CULL_NONE; + rastDesc.FillMode = D3D10_FILL_SOLID; + rastDesc.DepthClipEnable = TRUE; + hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState); + if (FAILED(hr)) { + goto FAILED; + } + device->base.refcount = 1; + + // We start out with TEXT_TEXTURE roughly in VRAM usage. + device->mVRAMUsage = TEXT_TEXTURE_WIDTH * TEXT_TEXTURE_HEIGHT * 4; + + // We create this with USAGE_DEFAULT, our intention is to have VRAM reserved + // for text usage. We actually store glyph data in STAGING textures for the + // rendering pipeline to read and copy it to this VRAM texture. + textDesc.Usage = D3D10_USAGE_DEFAULT; + hr = device->mD3D10Device->CreateTexture2D(&textDesc, NULL, &device->mTextTexture); + if (FAILED(hr)) { + goto FAILED; + } + + hr = device->mD3D10Device->CreateShaderResourceView(device->mTextTexture, + NULL, + &device->mTextTextureView); + + return &device->base; +FAILED: + delete &device->base; + return NULL; +} + +cairo_device_t * +cairo_d2d_create_device() +{ + HMODULE d3d10module = LoadLibraryA("d3d10_1.dll"); + D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) + GetProcAddress(d3d10module, "D3D10CreateDevice1"); + + if (!createD3DDevice) { + return NULL; + } + + RefPtr d3ddevice; + + /** + * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: + * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + * can be misleading. In fact, that flag gives no such indication. I pointed this + * out to Bas in my email. However, Microsoft is in fact using this flag to + * indicate "light weight" DX applications. By light weight they are essentially + * referring to applications that are not games. The idea is that when you create + * a DX game, the driver assumes that you will pretty much have a single instance + * and therefore it doesn't try to hold back when it comes to GPU resource + * allocation as long as it can crank out performance. In other words, the + * priority in regular DX applications is to make that one application run as fast + * as you can. For "light weight" applications, including D2D applications, the + * priorities are a bit different. Now you are no longer going to have a single + * (or very few) instances. You can have a lot of them (say, for example, a + * separate DX context/device per browser tab). In such cases, the GPU resource + * allocation scheme changes. + */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_1, + D3D10_1_SDK_VERSION, + &d3ddevice); + if (FAILED(hr)) { + hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_0, + D3D10_1_SDK_VERSION, + &d3ddevice); + if (FAILED(hr)) { + /* This is not guaranteed to be too fast! */ + hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_9_3, + D3D10_1_SDK_VERSION, + &d3ddevice); + + } + } + if (FAILED(hr)) { + return NULL; + } + + cairo_device_t *device = cairo_d2d_create_device_from_d3d10device(d3ddevice); + + // Free our reference to the modules. The created device should have its own. + FreeLibrary(d3d10module); + return device; +} + +int +cairo_release_device(cairo_device_t *device) +{ + int newrefcnt = --device->refcount; + if (!newrefcnt) { + // Call the correct destructor + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + HMODULE d3d10_1 = d2d_device->mD3D10_1; + delete d2d_device; + _cairo_d2d_release_factory(); + FreeLibrary(d3d10_1); + } + return newrefcnt; +} + +int +cairo_addref_device(cairo_device_t *device) +{ + return ++device->refcount; +} + +void +cairo_d2d_finish_device(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + // Here it becomes interesting, this flush method is generally called when + // interop is going on between our device and another device. The + // synchronisation between these devices is not always that great. The + // device flush method may flush the device's command queue, but it gives + // no guarantee that the device will actually be done with those commands, + // and so the surface may still not be complete when the external device + // chooses to use it. The EVENT query will actually tell us when the GPU + // is completely done with our commands. + D3D10_QUERY_DESC queryDesc; + queryDesc.MiscFlags = 0; + queryDesc.Query = D3D10_QUERY_EVENT; + RefPtr query; + + d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query); + + // QUERY_EVENT does not use Begin(). It's disabled. + query->End(); + + BOOL done = FALSE; + while (!done) { + // This will return S_OK and done = FALSE when the GPU is not done, and + // S_OK and done = TRUE when the GPU is done. Any other return value + // means we need to break out or risk an infinite loop. + if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) { + break; + } + if (FAILED(d2d_device->mD3D10Device->GetDeviceRemovedReason())) { + break; + } + } +} + +ID3D10Device1* +cairo_d2d_device_get_device(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + return d2d_device->mD3D10Device; +} + +static void +_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) +{ + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device->mD3D10Device->IASetInputLayout(device->mInputLayout); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = device->mQuadBuffer; + device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + + device->mD3D10Device->RSSetState(device->mRasterizerState); +} + +// Contains our cache usage - perhaps this should be made threadsafe. +static int cache_usage = 0; + +/** + * Create a similar surface which will blend effectively to + * another surface. For D2D, this will create another texture. + * Within the types we use blending is always easy. + * + * \param surface Surface this needs to be similar to + * \param content Content type of the new surface + * \param width Width of the new surface + * \param height Height of the new surface + * \return New surface + */ +static cairo_surface_t* +_cairo_d2d_create_similar(void *surface, + cairo_content_t content, + int width, + int height); + +/** + * Release all the data held by a surface, the surface structure + * itsself will be freed by cairo. + * + * \param surface Surface to clean up + */ +static cairo_status_t +_cairo_d2d_finish(void *surface); + +/** + * Get a read-only image surface that contains the pixel data + * of a D2D surface. + * + * \param abstract_surface D2D surface to acquire the image from + * \param image_out Pointer to where we should store the image surface pointer + * \param image_extra Pointer where to store extra data we want to know about + * at the point of release. + * \return CAIRO_STATUS_SUCCESS for success + */ +static cairo_status_t +_cairo_d2d_acquire_source_image(void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); + +/** + * Release a read-only image surface that was obtained using acquire_source_image + * + * \param abstract_surface D2D surface to acquire the image from + * \param image_out Pointer to where we should store the image surface pointer + * \param image_extra Pointer where to store extra data we want to know about + * at the point of release. + * \return CAIRO_STATUS_SUCCESS for success + */ +static void +_cairo_d2d_release_source_image(void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra); + +/** + * Get a read-write image surface that contains the pixel data + * of a D2D surface. + * + * \param abstract_surface D2D surface to acquire the image from + * \param image_out Pointer to where we should store the image surface pointer + * \param image_extra Pointer where to store extra data we want to know about + * at the point of release. + * \return CAIRO_STATUS_SUCCESS for success + */ +static cairo_status_t +_cairo_d2d_acquire_dest_image(void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra); + +/** + * Release a read-write image surface that was obtained using acquire_source_image + * + * \param abstract_surface D2D surface to acquire the image from + * \param image_out Pointer to where we should store the image surface pointer + * \param image_extra Pointer where to store extra data we want to know about + * at the point of release. + * \return CAIRO_STATUS_SUCCESS for success + */ +static void +_cairo_d2d_release_dest_image(void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra); + +/** + * Flush this surface, only after this operation is the related hardware texture + * guaranteed to contain all the results of the executed drawing operations. + * + * \param surface D2D surface to flush + * \return CAIRO_STATUS_SUCCESS or CAIRO_SURFACE_TYPE_MISMATCH + */ +static cairo_status_t +_cairo_d2d_flush(void *surface); + +/** + * Fill a path on this D2D surface. + * + * \param surface The surface to apply this operation to, must be + * a D2D surface + * \param op The operator to use + * \param source The source pattern to fill this path with + * \param path The path to fill + * \param fill_rule The fill rule to uses on the path + * \param tolerance The tolerance applied to the filling + * \param antialias The anti-alias mode to use + * \param clip The clip of this operation + * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, + * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS + */ +static cairo_int_status_t +_cairo_d2d_fill(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +/** + * Paint this surface, applying the operation to the entire surface + * + * \param surface The surface to apply this operation to, must be + * a D2D surface + * \param op Operator to use when painting + * \param source The pattern to fill this surface with, source of the op + * \param clip The clip of this operation + * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, + * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS + */ +static cairo_int_status_t +_cairo_d2d_paint(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +/** + * Paint something on the surface applying a certain mask to that + * source. + * + * \param surface The surface to apply this oepration to, must be + * a D2D surface + * \param op Operator to use + * \param source Source for this operation + * \param mask Pattern to mask source with + * \param clip The clip of this operation + * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, + * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS + */ +static cairo_int_status_t +_cairo_d2d_mask(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +/** + * Show a glyph run on the target D2D surface. + * + * \param surface The surface to apply this oepration to, must be + * a D2D surface + * \param op Operator to use + * \param source Source for this operation + * \param glyphs Glyphs to draw + * \param num_gluphs Amount of glyphs stored at glyphs + * \param scaled_font Scaled font to draw + * \param remaining_glyphs Pointer to store amount of glyphs still + * requiring drawing. + * \param clip The clip of this operation + * \return CAIRO_ERROR_SURFACE_TYPE_MISMATCH, CAIRO_ERROR_FONT_TYPE_MISMATCH, + * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS + */ +static cairo_int_status_t +_cairo_d2d_show_glyphs (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +/** + * Get the extents of this surface. + * + * \param surface D2D surface to get the extents for + * \param extents Pointer to where to store the extents + * \param CAIRO_ERROR_SURFACE_TYPE_MISTMATCH or CAIRO_STATUS_SUCCESS + */ +static cairo_bool_t +_cairo_d2d_getextents(void *surface, + cairo_rectangle_int_t *extents); + + +/** + * Stroke a path on this D2D surface. + * + * \param surface The surface to apply this operation to, must be + * a D2D surface + * \param op The operator to use + * \param source The source pattern to fill this path with + * \param path The path to stroke + * \param style The style of the stroke + * \param ctm A logical to device matrix, since the path might be in + * device space the miter angle and such are not, hence we need to + * be aware of the transformation to apply correct stroking. + * \param ctm_inverse Inverse of ctm, used to transform the path back + * to logical space. + * \param tolerance Tolerance to stroke with + * \param antialias Antialias mode to use + * \param clip The clip of this operation + * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH, + * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS + */ +static cairo_int_status_t +_cairo_d2d_stroke(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +static const cairo_surface_backend_t cairo_d2d_surface_backend = { + CAIRO_SURFACE_TYPE_D2D, + _cairo_d2d_create_similar, /* create_similar */ + _cairo_d2d_finish, /* finish */ + _cairo_d2d_acquire_source_image, /* acquire_source_image */ + _cairo_d2d_release_source_image, /* release_source_image */ + _cairo_d2d_acquire_dest_image, /* acquire_dest_image */ + _cairo_d2d_release_dest_image, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_d2d_getextents, /* get_extents */ + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + _cairo_d2d_flush, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _cairo_d2d_paint, /* paint */ + _cairo_d2d_mask, /* mask */ + _cairo_d2d_stroke, /* stroke */ + _cairo_d2d_fill, /* fill */ + _cairo_d2d_show_glyphs, /* show_glyphs */ + NULL, /* snapshot */ + NULL +}; + +/* + * Helper functions. + */ + +/* Stack-based helper to manage region destruction. */ +struct cairo_region_auto_ptr +{ + cairo_region_auto_ptr() : region(NULL) + { } + cairo_region_auto_ptr(cairo_region_t *in_region) : region(in_region) + { } + + void set(cairo_region_t *in_region) { region = in_region; } + + ~cairo_region_auto_ptr() { if (region) cairo_region_destroy (region); } + + cairo_region_t *region; +}; + +/* This clears a new D2D surface in case the VRAM was reused from an existing surface + * and is therefor not empty, this must be called outside of drawing state! */ +static void +_d2d_clear_surface(cairo_d2d_surface_t *surf) +{ + surf->rt->BeginDraw(); + surf->rt->Clear(D2D1::ColorF(0, 0)); + surf->rt->EndDraw(); +} + +static cairo_rectangle_int_t +_cairo_rect_from_windows_rect(const RECT *rect) +{ + cairo_rectangle_int_t new_rect; + + new_rect.x = rect->left; + new_rect.y = rect->top; + new_rect.width = rect->right - rect->left; + new_rect.height = rect->bottom - rect->top; + + return new_rect; +} + +static D2D1_POINT_2F +_d2d_point_from_cairo_point(const cairo_point_t *point) +{ + return D2D1::Point2F(_cairo_fixed_to_float(point->x), + _cairo_fixed_to_float(point->y)); +} + +static D2D1_COLOR_F +_cairo_d2d_color_from_cairo_color(const cairo_color_t &color) +{ + return D2D1::ColorF((FLOAT)color.red, + (FLOAT)color.green, + (FLOAT)color.blue, + (FLOAT)color.alpha); +} + +static void +_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2) +{ + rect->x = (int)floor(x1); + rect->y = (int)floor(y1); + rect->width = (int)ceil(x2) - rect->x; + rect->height = (int)ceil(y2) - rect->y; +} + +static int +_cairo_d2d_compute_surface_mem_size(cairo_d2d_surface_t *surface) +{ + int size = surface->rt->GetPixelSize().width * surface->rt->GetPixelSize().height; + size *= surface->rt->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4; + return size; +} + +static D2D1_COLOR_F +_cairo_d2d_color_from_cairo_color_stop(const cairo_color_stop_t &color) +{ + return D2D1::ColorF((FLOAT)color.red, + (FLOAT)color.green, + (FLOAT)color.blue, + (FLOAT)color.alpha); +} + + +/** + * Gets the surface buffer texture for window surfaces whose backbuffer + * is not directly usable as a bitmap. + * + * \param surface D2D surface. + * \return Buffer texture + */ +static ID3D10Texture2D* +_cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface) +{ + if (!surface->bufferTexture) { + RefPtr surf; + DXGI_SURFACE_DESC surfDesc; + surface->surface->QueryInterface(&surf); + surf->GetDesc(&surfDesc); + CD3D10_TEXTURE2D_DESC softDesc(surfDesc.Format, surfDesc.Width, surfDesc.Height); + softDesc.MipLevels = 1; + softDesc.Usage = D3D10_USAGE_DEFAULT; + softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); + surface->device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(surface); + } + return surface->bufferTexture; +} + +/** + * Ensure that the surface has an up-to-date surface bitmap. Used for + * window surfaces which cannot have a surface bitmap directly related + * to their backbuffer for some reason. + * You cannot create a bitmap around a backbuffer surface for reason (it will + * fail with an E_INVALIDARG). Meaning they need a special texture to store + * their graphical data which is wrapped by a D2D bitmap if a window surface + * is ever used in a surface pattern. All other D2D surfaces use a texture as + * their backing store so can have a bitmap directly. + * + * \param surface D2D surface. + */ +static void _cairo_d2d_update_surface_bitmap(cairo_d2d_surface_t *d2dsurf) +{ + if (!d2dsurf->backBuf && d2dsurf->rt->GetPixelFormat().format != DXGI_FORMAT_A8_UNORM) { + return; + } + + if (!d2dsurf->surfaceBitmap) { + d2dsurf->rt->CreateBitmap(d2dsurf->rt->GetPixelSize(), + D2D1::BitmapProperties(d2dsurf->rt->GetPixelFormat()), + &d2dsurf->surfaceBitmap); + } + + d2dsurf->surfaceBitmap->CopyFromRenderTarget(NULL, d2dsurf->rt, NULL); +} + +/** + * Present the backbuffer for a surface create for an HWND. This needs + * to be called when the owner of the original window surface wants to + * actually present the executed drawing operations to the screen. + * + * \param surface D2D surface. + */ +void cairo_d2d_present_backbuffer(cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_D2D) { + return; + } + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + _cairo_d2d_flush(d2dsurf); + if (d2dsurf->dxgiChain) { + d2dsurf->dxgiChain->Present(0, 0); + d2dsurf->device->mD3D10Device->Flush(); + } +} + +struct d2d_clip_t +{ + enum clip_type {LAYER, AXIS_ALIGNED_CLIP}; + d2d_clip_t * const prev; + const enum clip_type type; + d2d_clip_t(d2d_clip_t *prev, clip_type type) : prev(prev), type(type) { } +}; + +static RefPtr +_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + D2D1_FIGURE_BEGIN type); + + +static cairo_bool_t +box_is_integer (cairo_box_t *box) +{ + return _cairo_fixed_is_integer(box->p1.x) && + _cairo_fixed_is_integer(box->p1.y) && + _cairo_fixed_is_integer(box->p2.x) && + _cairo_fixed_is_integer(box->p2.y); +} + +static cairo_status_t +push_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_path_t *clip_path) +{ + cairo_box_t box; + if (_cairo_path_fixed_is_box(&clip_path->path, &box)) { + + assert(box.p1.y < box.p2.y); + + D2D1_ANTIALIAS_MODE mode; + if (box_is_integer (&box)) { + mode = D2D1_ANTIALIAS_MODE_ALIASED; + } else { + mode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + } + d2dsurf->rt->PushAxisAlignedClip ( + D2D1::RectF( + _cairo_fixed_to_float(box.p1.x), + _cairo_fixed_to_float(box.p1.y), + _cairo_fixed_to_float(box.p2.x), + _cairo_fixed_to_float(box.p2.y)), + mode); + + d2dsurf->d2d_clip = new d2d_clip_t (d2dsurf->d2d_clip, d2d_clip_t::AXIS_ALIGNED_CLIP); + } else { + HRESULT hr; + RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&clip_path->path, + clip_path->fill_rule, + D2D1_FIGURE_BEGIN_FILLED); + RefPtr layer; + + hr = d2dsurf->rt->CreateLayer (&layer); + + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE; + + if (d2dsurf->base.content == CAIRO_CONTENT_COLOR) { + options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; + options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA; + options1 = D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + RefPtr dc; + hr = d2dsurf->rt->QueryInterface(IID_ID2D1DeviceContext, (void**)&dc); + + if (FAILED(hr)) { + d2dsurf->rt->PushLayer(D2D1::LayerParameters( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options), + layer); + } else { + dc->PushLayer(D2D1::LayerParameters1( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options1), + layer); + } + + d2dsurf->d2d_clip = new d2d_clip_t(d2dsurf->d2d_clip, d2d_clip_t::LAYER); + } + if (!d2dsurf->d2d_clip) + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + return CAIRO_STATUS_SUCCESS; +} + +static void +pop_clip (cairo_d2d_surface_t *d2dsurf) +{ + d2d_clip_t *current_clip = d2dsurf->d2d_clip; + + /* pop the clip from the render target */ + if (current_clip->type == d2d_clip_t::LAYER) { + d2dsurf->rt->PopLayer(); + } else if (current_clip->type == d2d_clip_t::AXIS_ALIGNED_CLIP) { + d2dsurf->rt->PopAxisAlignedClip(); + } + + /* pop it from our own stack */ + d2dsurf->d2d_clip = current_clip->prev; + delete current_clip; +} + +/* intersect clip_paths until we reach head */ +static cairo_status_t +clipper_intersect_clip_path_recursive (cairo_d2d_surface_t *d2dsurf, + cairo_clip_path_t *head, + cairo_clip_path_t *clip_path) +{ + cairo_status_t status; + + if (clip_path->prev != head) { + status = + clipper_intersect_clip_path_recursive (d2dsurf, + head, + clip_path->prev); + if (unlikely (status)) + return status; + } + return push_clip(d2dsurf, clip_path); + +} + +/* pop all of the clipping layers and reset the clip */ +static void +reset_clip (cairo_d2d_surface_t *d2dsurf) +{ + cairo_clip_path_t *current_clip_path = d2dsurf->clip.path; + while (current_clip_path != NULL) { + pop_clip (d2dsurf); + current_clip_path = current_clip_path->prev; + } + + _cairo_clip_reset (&d2dsurf->clip); +} + +/* finds the lowest common ancestor of a and b */ +static cairo_clip_path_t * +find_common_ancestor(cairo_clip_path_t *a, cairo_clip_path_t *b) +{ + int a_depth = 0, b_depth = 0; + + cairo_clip_path_t *x; + + /* find the depths of the clip_paths */ + x = a; + while (x) { + a_depth++; + x = x->prev; + } + + x = b; + while (x) { + b_depth++; + x = x->prev; + } + + /* rewind the deeper chain to the depth of the shallowest chain */ + while (b_depth < a_depth && a) { + a = a->prev; + a_depth--; + } + + while (a_depth < b_depth && b) { + b = b->prev; + b_depth--; + } + + /* walk back until we find a common ancesstor */ + + /* b will be null if and only if a is null because the depths + * must match at this point */ + while (a) { + if (a == b) + return a; + + a = a->prev; + b = b->prev; + } + + /* a will be NULL */ + return a; +} + +static cairo_status_t +_cairo_d2d_set_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip) +{ + if (clip == NULL) { + reset_clip (d2dsurf); + return CAIRO_STATUS_SUCCESS; + } + + if (clip != NULL && clip->path == d2dsurf->clip.path) + return CAIRO_STATUS_SUCCESS; + + cairo_clip_path_t *current_clip_path = d2dsurf->clip.path; + cairo_clip_path_t *new_clip_path = clip->path; + cairo_clip_path_t *ancestor = find_common_ancestor (current_clip_path, new_clip_path); + + /* adjust the clip to the common ancestor */ + while (current_clip_path != ancestor) { + pop_clip (d2dsurf); + current_clip_path = current_clip_path->prev; + } + + /* we now have a common parent (current_clip_path) for the clip */ + + /* replace the old clip */ + _cairo_clip_reset (&d2dsurf->clip); + _cairo_clip_init_copy (&d2dsurf->clip, clip); + + /* push the new clip paths up to current_clip_path */ + if (current_clip_path != clip->path) + return clipper_intersect_clip_path_recursive (d2dsurf, current_clip_path, clip->path); + else + return CAIRO_STATUS_SUCCESS; +} + +static void _cairo_d2d_add_dependent_surface(cairo_d2d_surface_t *surf, cairo_d2d_surface_t *user) +{ + _cairo_d2d_surface_entry *entry = new _cairo_d2d_surface_entry; + entry->surface = user; + cairo_surface_reference(&user->base); + cairo_list_add(&entry->link, &surf->dependent_surfaces); +}; + +static void _cairo_d2d_flush_dependent_surfaces(cairo_d2d_surface_t *surf) +{ + _cairo_d2d_surface_entry *entry, *next; + cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &surf->dependent_surfaces, link) { + _cairo_d2d_flush(entry->surface); + cairo_surface_destroy(&entry->surface->base); + delete entry; + } + cairo_list_init(&surf->dependent_surfaces); +} + +/** + * Enter the state where the surface is ready for drawing. This will guarantee + * the surface is in the correct state, and the correct clipping area is pushed. + * + * \param surface D2D surface + */ +static void _begin_draw_state(cairo_d2d_surface_t* surface) +{ + if (!surface->isDrawing) { + _cairo_d2d_flush_dependent_surfaces(surface); + surface->rt->BeginDraw(); + surface->isDrawing = true; + } +} + +/** + * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo + * uses column vectors. Hence the transposition. + * + * \param Cairo matrix + * \return D2D matrix + */ +static D2D1::Matrix3x2F +_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix) +{ + return D2D1::Matrix3x2F((FLOAT)matrix->xx, + (FLOAT)matrix->yx, + (FLOAT)matrix->xy, + (FLOAT)matrix->yy, + (FLOAT)matrix->x0, + (FLOAT)matrix->y0); +} + +/** + * Returns the inverse matrix for a D2D1 matrix. We cannot use the Invert function + * on the Matrix3x2F function class since it's statically linked and we'd have to + * lookup the symbol in the library. Doing this ourselves is easier. + * + * \param mat matrix + * \return inverse of matrix mat + */ +static D2D1::Matrix3x2F +_cairo_d2d_invert_matrix(const D2D1::Matrix3x2F &mat) +{ + float inv_det = (1 / mat.Determinant()); + + return D2D1::Matrix3x2F(mat._22 * inv_det, + -mat._12 * inv_det, + -mat._21 * inv_det, + mat._11 * inv_det, + (mat._21 * mat._32 - mat._22 * mat._31) * inv_det, + -(mat._11 * mat._32 - mat._12 * mat._31) * inv_det); +} + +/** + * Create a D2D stroke style interface for a cairo stroke style object. Must be + * released when the calling function is finished with it. + * + * \param style Cairo stroke style object + * \return D2D StrokeStyle interface + */ +static RefPtr +_cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style) +{ + D2D1_CAP_STYLE line_cap = D2D1_CAP_STYLE_FLAT; + switch (style->line_cap) { + case CAIRO_LINE_CAP_BUTT: + line_cap = D2D1_CAP_STYLE_FLAT; + break; + case CAIRO_LINE_CAP_SQUARE: + line_cap = D2D1_CAP_STYLE_SQUARE; + break; + case CAIRO_LINE_CAP_ROUND: + line_cap = D2D1_CAP_STYLE_ROUND; + break; + } + + D2D1_LINE_JOIN line_join = D2D1_LINE_JOIN_MITER; + switch (style->line_join) { + case CAIRO_LINE_JOIN_MITER: + line_join = D2D1_LINE_JOIN_MITER_OR_BEVEL; + break; + case CAIRO_LINE_JOIN_ROUND: + line_join = D2D1_LINE_JOIN_ROUND; + break; + case CAIRO_LINE_JOIN_BEVEL: + line_join = D2D1_LINE_JOIN_BEVEL; + break; + } + + FLOAT *dashes = NULL; + if (style->num_dashes) { + dashes = new FLOAT[style->num_dashes]; + for (unsigned int i = 0; i < style->num_dashes; i++) { + /* D2D seems to specify dash lengths in units of + * line width instead of the more traditional approach + * that cairo and many other APIs use where the unit + * is in pixels or someother constant unit. */ + dashes[i] = (FLOAT) (style->dash[i] / style->line_width); + } + } + + D2D1_DASH_STYLE dashStyle = D2D1_DASH_STYLE_SOLID; + if (dashes) { + dashStyle = D2D1_DASH_STYLE_CUSTOM; + } + + RefPtr strokeStyle; + sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, + line_cap, + line_cap, + line_join, + (FLOAT)style->miter_limit, + dashStyle, + (FLOAT)style->dash_offset), + dashes, + style->num_dashes, + &strokeStyle); + delete [] dashes; + return strokeStyle; +} + +static int _d2d_compute_bitmap_mem_size(ID2D1Bitmap *bitmap) +{ + D2D1_SIZE_U size = bitmap->GetPixelSize(); + int bytes_per_pixel = bitmap->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4; + return size.width * size.height * bytes_per_pixel; +} + +cairo_user_data_key_t bitmap_key_nonextend; +cairo_user_data_key_t bitmap_key_extend; +cairo_user_data_key_t bitmap_key_snapshot; + +struct cached_bitmap { + cached_bitmap() + { + sD2DFactory->AddRef(); + } + + ~cached_bitmap() + { + // Clear bitmap out first because it depends on the factory. + bitmap = NULL; + _cairo_d2d_release_factory(); + } + + /* Device this cached bitmap was created with, we should really have a per + * device cache, see bug 607408 */ + cairo_d2d_device_t *device; + /** The cached bitmap */ + RefPtr bitmap; + /** The cached bitmap is dirty and needs its data refreshed */ + bool dirty; + /** Order of snapshot detach/release bitmap called not guaranteed, single threaded refcount for now */ + int refs; +}; + +/** + * This is called when user data on a surface is replaced or the surface is + * destroyed. + */ +static void _d2d_release_bitmap(void *bitmap) +{ + cached_bitmap *existingBitmap = (cached_bitmap*)bitmap; + if (!--existingBitmap->refs) { + cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap); + delete existingBitmap; + } +} + +/** + * Via a little trick this is just used to determine when a surface has been + * modified. + */ +static void _d2d_snapshot_detached(cairo_surface_t *surface) +{ + cached_bitmap *existingBitmap = (cached_bitmap*)cairo_surface_get_user_data(surface, &bitmap_key_snapshot); + if (existingBitmap) { + existingBitmap->dirty = true; + } + if (!--existingBitmap->refs) { + cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap); + delete existingBitmap; + } + cairo_surface_destroy(surface); +} + +/** + * This function will calculate the part of srcSurf which will possibly be used within + * the boundaries of d2dsurf given the current transformation mat. This is used to + * determine what the minimal part of a surface is that needs to be uploaded. + * + * \param d2dsurf D2D surface + * \param srcSurf Source surface for operation + * \param mat Transformation matrix applied to source + */ +static void +_cairo_d2d_calculate_visible_rect(cairo_d2d_surface_t *d2dsurf, cairo_image_surface_t *srcSurf, + cairo_matrix_t *mat, + int *x, int *y, unsigned int *width, unsigned int *height) +{ + /** Leave room for extend_none space, 2 pixels */ + UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2; + + /* Transform this surface to image surface space */ + cairo_matrix_t invMat = *mat; + if (_cairo_matrix_is_invertible(mat)) { + /* If this is not invertible it will be rank zero, and invMat = mat is fine */ + cairo_matrix_invert(&invMat); + } + + RefPtr surf; + d2dsurf->surface->QueryInterface(&surf); + DXGI_SURFACE_DESC desc; + surf->GetDesc(&desc); + + double leftMost = 0; + double rightMost = desc.Width; + double topMost = 0; + double bottomMost = desc.Height; + + _cairo_matrix_transform_bounding_box(&invMat, &leftMost, &topMost, &rightMost, &bottomMost, NULL); + + leftMost -= 1; + topMost -= 1; + rightMost += 1; + bottomMost += 1; + + /* Calculate the offsets into the source image and the width of the part required */ + if ((UINT32)srcSurf->width > maxSize) { + *x = (int)MAX(0, floor(leftMost)); + /* Ensure that we get atleast 1 column of pixels as source, this will make EXTEND_PAD work */ + if (*x < srcSurf->width) { + *width = (unsigned int)MIN(MAX(1, ceil(rightMost - *x)), srcSurf->width - *x); + } else { + *x = srcSurf->width - 1; + *width = 1; + } + } else { + *x = 0; + *width = srcSurf->width; + } + + if ((UINT32)srcSurf->height > maxSize) { + *y = (int)MAX(0, floor(topMost)); + /* Ensure that we get atleast 1 row of pixels as source, this will make EXTEND_PAD work */ + if (*y < srcSurf->height) { + *height = (unsigned int)MIN(MAX(1, ceil(bottomMost - *y)), srcSurf->height - *y); + } else { + *y = srcSurf->height - 1; + *height = 1; + } + } else { + *y = 0; + *height = srcSurf->height; + } +} + +static double +_cairo_d2d_point_dist(const cairo_point_double_t &p1, const cairo_point_double_t &p2) +{ + return hypot(p2.x - p1.x, p2.y - p1.y); +} + +static void +_cairo_d2d_normalize_point(cairo_point_double_t *p) +{ + double length = hypot(p->x, p->y); + p->x /= length; + p->y /= length; +} + +static cairo_point_double_t +_cairo_d2d_subtract_point(const cairo_point_double_t &p1, const cairo_point_double_t &p2) +{ + cairo_point_double_t p = {p1.x - p2.x, p1.y - p2.y}; + return p; +} + +static double +_cairo_d2d_dot_product(const cairo_point_double_t &p1, const cairo_point_double_t &p2) +{ + return p1.x * p2.x + p1.y * p2.y; +} + +static RefPtr +_cairo_d2d_create_radial_gradient_brush(cairo_d2d_surface_t *d2dsurf, + cairo_radial_pattern_t *source_pattern) +{ + cairo_matrix_t inv_mat = source_pattern->base.base.matrix; + if (_cairo_matrix_is_invertible(&inv_mat)) { + /* If this is not invertible it will be rank zero, and invMat = mat is fine */ + cairo_matrix_invert(&inv_mat); + } + + D2D1_BRUSH_PROPERTIES brushProps = + D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat)); + + if ((source_pattern->c1.x != source_pattern->c2.x || + source_pattern->c1.y != source_pattern->c2.y) && + source_pattern->r1 != 0) { + /** + * In this particular case there's no way to deal with this! + * \todo Create an image surface with the gradient and use that. + */ + return NULL; + } + + D2D_POINT_2F center = + _d2d_point_from_cairo_point(&source_pattern->c2); + D2D_POINT_2F origin = + _d2d_point_from_cairo_point(&source_pattern->c1); + origin.x -= center.x; + origin.y -= center.y; + + float outer_radius = _cairo_fixed_to_float(source_pattern->r2); + float inner_radius = _cairo_fixed_to_float(source_pattern->r1); + int num_stops = source_pattern->base.n_stops; + int repeat_count = 1; + D2D1_GRADIENT_STOP *stops; + + if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + bool reflected = false; + bool reflect = source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT; + + RefPtr surf; + d2dsurf->surface->QueryInterface(&surf); + DXGI_SURFACE_DESC desc; + surf->GetDesc(&desc); + + // Calculate the largest distance the origin could be from the edge after inverse + // transforming by the pattern transformation. + cairo_point_double_t top_left, top_right, bottom_left, bottom_right, gradient_center; + top_left.x = bottom_left.x = top_left.y = top_right.y = 0; + top_right.x = bottom_right.x = desc.Width; + bottom_right.y = bottom_left.y = desc.Height; + + gradient_center.x = _cairo_fixed_to_float(source_pattern->c1.x); + gradient_center.y = _cairo_fixed_to_float(source_pattern->c1.y); + + // Transform surface corners into pattern coordinates. + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y); + + // Find the corner furthest away from the gradient center in pattern space. + double largest = MAX(_cairo_d2d_point_dist(top_left, gradient_center), _cairo_d2d_point_dist(top_right, gradient_center)); + largest = MAX(largest, _cairo_d2d_point_dist(bottom_left, gradient_center)); + largest = MAX(largest, _cairo_d2d_point_dist(bottom_right, gradient_center)); + + unsigned int minSize = (unsigned int)ceil(largest); + + // Calculate how often we need to repeat on the inside (for filling the inner radius) + // and on the outside (for covering the entire surface) and create the appropriate number + // of stops. + float gradient_length = outer_radius - inner_radius; + int inner_repeat = (int)ceil(inner_radius / gradient_length); + int outer_repeat = (int)MAX(1, ceil((minSize - inner_radius) / gradient_length)); + num_stops *= (inner_repeat + outer_repeat); + stops = new D2D1_GRADIENT_STOP[num_stops]; + + // Change outer_radius to the true outer radius after taking into account the needed + // repeats. + outer_radius = (inner_repeat + outer_repeat) * gradient_length; + + float stop_scale = 1.0f / (inner_repeat + outer_repeat); + + float inner_position = (inner_repeat * gradient_length) / outer_radius; + if (reflect) { + // We start out reflected (meaning reflected starts as false since it will + // immediately be inverted) if the inner_repeats are uneven. + reflected = !(inner_repeat & 0x1); + + for (int i = 0; i < num_stops; i++) { + if (!(i % source_pattern->base.n_stops)) { + // Reflect here + reflected = !reflected; + } + // Calculate the repeat count. + int repeat = i / source_pattern->base.n_stops; + // Take the stop that we're using in the pattern. + int stop = i % source_pattern->base.n_stops; + if (reflected) { + // Take the stop from the opposite side when reflected. + stop = source_pattern->base.n_stops - stop - 1; + // When reflected take 1 - offset as the offset. + stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale); + } else { + stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale); + } + stops[i].color = + _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color); + } + } else { + // Simple case, we don't need to reflect. + for (int i = 0; i < num_stops; i++) { + // Calculate which repeat this is. + int repeat = i / source_pattern->base.n_stops; + // Calculate which stop this would be in the original pattern + cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops]; + stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale); + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + } + } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) { + float offset_factor = (outer_radius - inner_radius) / outer_radius; + float global_offset = inner_radius / outer_radius; + + stops = new D2D1_GRADIENT_STOP[num_stops]; + + // If the inner radius is not 0 we need to scale and offset the stops. + for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) { + cairo_gradient_stop_t *stop = &source_pattern->base.stops[i]; + stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor); + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) { + float offset_factor = (outer_radius - inner_radius) / outer_radius; + float global_offset = inner_radius / outer_radius; + + num_stops++; // Add a stop on the outer radius. + if (inner_radius != 0) { + num_stops++; // Add a stop on the inner radius. + } + + stops = new D2D1_GRADIENT_STOP[num_stops]; + + // If the inner radius is not 0 we need to scale and offset the stops and put a stop before the inner_radius + // of a transparent color. + int i = 0; + if (inner_radius != 0) { + stops[i].position = global_offset; + stops[i].color = D2D1::ColorF(0, 0); + i++; + } + for (unsigned int j = 0; j < source_pattern->base.n_stops; j++, i++) { + cairo_gradient_stop_t *stop = &source_pattern->base.stops[j]; + stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor); + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + stops[i].position = 1.0f; + stops[i].color = D2D1::ColorF(0, 0); + } else { + return NULL; + } + + RefPtr stopCollection; + d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection); + RefPtr brush; + + d2dsurf->rt->CreateRadialGradientBrush(D2D1::RadialGradientBrushProperties(center, + origin, + outer_radius, + outer_radius), + brushProps, + stopCollection, + &brush); + delete [] stops; + return brush; +} + +static RefPtr +_cairo_d2d_create_linear_gradient_brush(cairo_d2d_surface_t *d2dsurf, + cairo_path_fixed_t *fill_path, + cairo_linear_pattern_t *source_pattern) +{ + if (source_pattern->p1.x == source_pattern->p2.x && + source_pattern->p1.y == source_pattern->p2.y) { + // Cairo behavior in this situation is to draw a solid color the size of the last stop. + RefPtr brush; + d2dsurf->rt->CreateSolidColorBrush( + _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[source_pattern->base.n_stops - 1].color), + &brush); + return brush; + } + + cairo_matrix_t inv_mat = source_pattern->base.base.matrix; + /** + * Cairo views this matrix as the transformation of the destination + * when the pattern is imposed. We see this differently, D2D transformation + * moves the pattern over the destination. + */ + if (_cairo_matrix_is_invertible(&inv_mat)) { + /* If this is not invertible it will be rank zero, and invMat = mat is fine */ + cairo_matrix_invert(&inv_mat); + } + D2D1_BRUSH_PROPERTIES brushProps = + D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat)); + cairo_point_double_t p1, p2; + p1.x = _cairo_fixed_to_float(source_pattern->p1.x); + p1.y = _cairo_fixed_to_float(source_pattern->p1.y); + p2.x = _cairo_fixed_to_float(source_pattern->p2.x); + p2.y = _cairo_fixed_to_float(source_pattern->p2.y); + + D2D1_GRADIENT_STOP *stops; + int num_stops = source_pattern->base.n_stops; + if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + // Get this when the points are not transformed yet. + double gradient_length = _cairo_d2d_point_dist(p1, p2); + cairo_point_double_t top_left, top_right, bottom_left, bottom_right; + + if (fill_path) { + // Calculate the repeat count needed; + cairo_box_t fill_extents; + _cairo_path_fixed_extents (fill_path, &fill_extents); + + top_left.x = bottom_left.x = _cairo_fixed_to_double (fill_extents.p1.x); + top_left.y = top_right.y = _cairo_fixed_to_double (fill_extents.p1.y); + top_right.x = bottom_right.x = _cairo_fixed_to_double (fill_extents.p2.x); + bottom_right.y = bottom_left.y = _cairo_fixed_to_double (fill_extents.p2.y); + } else { + RefPtr surf; + d2dsurf->surface->QueryInterface(&surf); + DXGI_SURFACE_DESC desc; + surf->GetDesc(&desc); + + top_left.x = bottom_left.x = 0; + top_left.y = top_right.y = 0; + top_right.x = bottom_right.x = desc.Width; + bottom_right.y = bottom_left.y = desc.Height; + } + + // Transform the corners of our surface to pattern space. + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y); + cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y); + + cairo_point_double_t u; + // Unit vector of the gradient direction. + u = _cairo_d2d_subtract_point(p2, p1); + _cairo_d2d_normalize_point(&u); + + // (corner - p1) . u = |corner - p1| cos(a) where a is the angle between the two vectors. + // Coincidentally |corner - p1| cos(a) is actually also the distance our gradient needs to cover since + // at this point on the gradient line it will be perpendicular to the line running from the gradient + // line through the corner. + + double max_dist, min_dist; + max_dist = MAX(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), + _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); + max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); + max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); + min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), + _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); + min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); + min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); + + min_dist = MAX(-min_dist, 0); + + // Repeats after gradient start. + // It's possible for max_dist and min_dist to both be zero, in which case + // we'll set num_stops to 0 and crash D2D. Let's just ensure after_repeat + // is at least 1. + int after_repeat = MAX((int)ceil(max_dist / gradient_length), 1); + int before_repeat = (int)ceil(min_dist / gradient_length); + num_stops *= (after_repeat + before_repeat); + + p2.x = p1.x + u.x * after_repeat * gradient_length; + p2.y = p1.y + u.y * after_repeat * gradient_length; + p1.x = p1.x - u.x * before_repeat * gradient_length; + p1.y = p1.y - u.y * before_repeat * gradient_length; + + float stop_scale = 1.0f / (float)(after_repeat + before_repeat); + float begin_position = (float)before_repeat / (float)(after_repeat + before_repeat); + + stops = new D2D1_GRADIENT_STOP[num_stops]; + if (source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + // We start out reflected (meaning reflected starts as false since it will + // immediately be inverted) if the inner_repeats are uneven. + bool reflected = !(before_repeat & 0x1); + + for (int i = 0; i < num_stops; i++) { + if (!(i % source_pattern->base.n_stops)) { + // Reflect here + reflected = !reflected; + } + // Calculate the repeat count. + int repeat = i / source_pattern->base.n_stops; + // Take the stop that we're using in the pattern. + int stop = i % source_pattern->base.n_stops; + if (reflected) { + // Take the stop from the opposite side when reflected. + stop = source_pattern->base.n_stops - stop - 1; + // When reflected take 1 - offset as the offset. + stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale); + } else { + stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale); + } + stops[i].color = + _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color); + } + } else { + // Simple case, we don't need to reflect. + for (int i = 0; i < num_stops; i++) { + // Calculate which repeat this is. + int repeat = i / source_pattern->base.n_stops; + // Calculate which stop this would be in the original pattern + cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops]; + stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale); + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + } + } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) { + stops = new D2D1_GRADIENT_STOP[source_pattern->base.n_stops]; + for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) { + cairo_gradient_stop_t *stop = &source_pattern->base.stops[i]; + stops[i].position = (FLOAT)stop->offset; + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) { + num_stops += 2; + stops = new D2D1_GRADIENT_STOP[num_stops]; + stops[0].position = 0; + stops[0].color = D2D1::ColorF(0, 0); + for (unsigned int i = 1; i < source_pattern->base.n_stops + 1; i++) { + cairo_gradient_stop_t *stop = &source_pattern->base.stops[i - 1]; + stops[i].position = (FLOAT)stop->offset; + stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color); + } + stops[source_pattern->base.n_stops + 1].position = 1.0f; + stops[source_pattern->base.n_stops + 1].color = D2D1::ColorF(0, 0); + } + RefPtr stopCollection; + d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection); + RefPtr brush; + d2dsurf->rt->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2D1::Point2F((FLOAT)p1.x, (FLOAT)p1.y), + D2D1::Point2F((FLOAT)p2.x, (FLOAT)p2.y)), + brushProps, + stopCollection, + &brush); + delete [] stops; + return brush; +} + +/** + * This creates an ID2D1Brush that will fill with the correct pattern. + * This function passes a -strong- reference to the caller, the brush + * needs to be released, even if it is not unique. + * + * \param d2dsurf Surface to create a brush for + * \param pattern The pattern to create a brush for + * \param unique We cache the bitmap/color brush for speed. If this + * needs a brush that is unique (i.e. when more than one is needed), + * this will make the function return a seperate brush. + * \return A brush object + */ +static RefPtr +_cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, + cairo_path_fixed_t *fill_path, + const cairo_pattern_t *pattern, + bool unique = false) +{ + HRESULT hr; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *sourcePattern = + (cairo_solid_pattern_t*)pattern; + D2D1_COLOR_F color = _cairo_d2d_color_from_cairo_color(sourcePattern->color); + if (unique) { + RefPtr brush; + d2dsurf->rt->CreateSolidColorBrush(color, + &brush); + return brush; + } else { + if (d2dsurf->solidColorBrush->GetColor().a != color.a || + d2dsurf->solidColorBrush->GetColor().r != color.r || + d2dsurf->solidColorBrush->GetColor().g != color.g || + d2dsurf->solidColorBrush->GetColor().b != color.b) { + d2dsurf->solidColorBrush->SetColor(color); + } + return d2dsurf->solidColorBrush; + } + + } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *source_pattern = + (cairo_linear_pattern_t*)pattern; + return _cairo_d2d_create_linear_gradient_brush(d2dsurf, fill_path, source_pattern); + } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { + cairo_radial_pattern_t *source_pattern = + (cairo_radial_pattern_t*)pattern; + return _cairo_d2d_create_radial_gradient_brush(d2dsurf, source_pattern); + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_matrix_t mat = pattern->matrix; + cairo_matrix_invert(&mat); + + cairo_surface_pattern_t *surfacePattern = + (cairo_surface_pattern_t*)pattern; + D2D1_EXTEND_MODE extendMode; + + cairo_user_data_key_t *key = &bitmap_key_extend; + + if (pattern->extend == CAIRO_EXTEND_NONE) { + extendMode = D2D1_EXTEND_MODE_CLAMP; + key = &bitmap_key_nonextend; + /** + * We create a slightly larger bitmap with a transparent border + * around it for this case. Need to translate for that. + */ + cairo_matrix_translate(&mat, -1.0, -1.0); + } else if (pattern->extend == CAIRO_EXTEND_REPEAT) { + extendMode = D2D1_EXTEND_MODE_WRAP; + } else if (pattern->extend == CAIRO_EXTEND_REFLECT) { + extendMode = D2D1_EXTEND_MODE_MIRROR; + } else { + extendMode = D2D1_EXTEND_MODE_CLAMP; + } + + RefPtr sourceBitmap; + bool partial = false; + int xoffset = 0; + int yoffset = 0; + unsigned int width; + unsigned int height; + unsigned char *data = NULL; + unsigned int stride = 0; + + if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_D2D) { + /** + * \todo We need to somehow get a rectangular transparent + * border here too!! + */ + cairo_d2d_surface_t *srcSurf = + reinterpret_cast(surfacePattern->surface); + + if (srcSurf == d2dsurf) { + /* D2D cannot deal with self-copy. We should add an optimized + * codepath for self-copy in the easy cases that does ping-pong like + * scroll does. See bug 579215. For now fallback. + */ + return NULL; + } + if (srcSurf->device != d2dsurf->device) { + /* This code does not work if the source surface does not use + * the same device. Some work could be done to do something + * fairly efficient here, for now, fallback. + */ + return NULL; + } + + _cairo_d2d_update_surface_bitmap(srcSurf); + _cairo_d2d_flush(srcSurf); + + // Mark a dependency on the source surface. + _cairo_d2d_add_dependent_surface(srcSurf, d2dsurf); + + if (pattern->extend == CAIRO_EXTEND_NONE) { + ID2D1Bitmap *srcSurfBitmap = srcSurf->surfaceBitmap; + d2dsurf->rt->CreateBitmap( + D2D1::SizeU(srcSurfBitmap->GetPixelSize().width + 2, + srcSurfBitmap->GetPixelSize().height + 2), + D2D1::BitmapProperties(srcSurfBitmap->GetPixelFormat()), + &sourceBitmap); + D2D1_POINT_2U point = D2D1::Point2U(1, 1); + sourceBitmap->CopyFromBitmap(&point, srcSurfBitmap, NULL); + } else { + sourceBitmap = srcSurf->surfaceBitmap; + } + + } else if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *srcSurf = + reinterpret_cast(surfacePattern->surface); + D2D1_ALPHA_MODE alpha; + if (srcSurf->format == CAIRO_FORMAT_ARGB32 || + srcSurf->format == CAIRO_FORMAT_A8) { + alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + } else { + alpha = D2D1_ALPHA_MODE_IGNORE; + } + + data = srcSurf->data; + stride = srcSurf->stride; + + /* This is used as a temporary surface for resampling surfaces larget than maxSize. */ + pixman_image_t *pix_image = NULL; + + DXGI_FORMAT format; + unsigned int Bpp; + if (srcSurf->format == CAIRO_FORMAT_ARGB32) { + format = DXGI_FORMAT_B8G8R8A8_UNORM; + Bpp = 4; + } else if (srcSurf->format == CAIRO_FORMAT_RGB24) { + format = DXGI_FORMAT_B8G8R8A8_UNORM; + Bpp = 4; + } else if (srcSurf->format == CAIRO_FORMAT_A8) { + format = DXGI_FORMAT_A8_UNORM; + Bpp = 1; + } else { + return NULL; + } + + /** Leave room for extend_none space, 2 pixels */ + UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2; + + if ((UINT32)srcSurf->width > maxSize || (UINT32)srcSurf->height > maxSize) { + if (pattern->extend == CAIRO_EXTEND_REPEAT || + pattern->extend == CAIRO_EXTEND_REFLECT) { + // XXX - we don't have code to deal with these yet. + return NULL; + } + + /* We cannot fit this image directly into a texture, start doing tricks to draw correctly anyway. */ + partial = true; + + /* First we check which part of the image is inside the viewable area. */ + _cairo_d2d_calculate_visible_rect(d2dsurf, srcSurf, &mat, &xoffset, &yoffset, &width, &height); + + cairo_matrix_translate(&mat, xoffset, yoffset); + + if (width > maxSize || height > maxSize) { + /* + * We cannot upload the required part of the surface directly, we're going to create + * a version which is downsampled to a smaller size by pixman and then uploaded. + * + * We need to size it to at least the diagonal size of this surface, in order to prevent ever + * upsampling this again when drawing it to the surface. We want the resized surface + * to be as small as possible to limit pixman required fill rate. + * + * Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and + * a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate + * this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination, + * the composition will require all 10 original columns to do the best possible sampling. + */ + RefPtr surf; + d2dsurf->surface->QueryInterface(&surf); + DXGI_SURFACE_DESC desc; + surf->GetDesc(&desc); + + unsigned int minSize = (unsigned int)ceil(sqrt(pow((float)desc.Width, 2) + pow((float)desc.Height, 2))); + + unsigned int newWidth = MIN(minSize, MIN(width, maxSize)); + unsigned int newHeight = MIN(minSize, MIN(height, maxSize)); + double xRatio = (double)width / newWidth; + double yRatio = (double)height / newHeight; + + if (newWidth > maxSize || newHeight > maxSize) { + /* + * Okay, the diagonal of our surface is big enough to require a sampling larger + * than the maximum texture size. This is where we give up. + */ + return NULL; + } + + /* Create a temporary surface to hold the downsampled image */ + pix_image = pixman_image_create_bits(srcSurf->pixman_format, + newWidth, + newHeight, + NULL, + -1); + + /* Set the transformation to downsample and call pixman_image_composite to downsample */ + pixman_transform_t transform; + pixman_transform_init_scale(&transform, pixman_double_to_fixed(xRatio), pixman_double_to_fixed(yRatio)); + pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(xoffset), pixman_int_to_fixed(yoffset)); + + pixman_image_set_transform(srcSurf->pixman_image, &transform); + pixman_image_composite(PIXMAN_OP_SRC, srcSurf->pixman_image, NULL, pix_image, 0, 0, 0, 0, 0, 0, newWidth, newHeight); + + /* Adjust the pattern transform to the used temporary surface */ + cairo_matrix_scale(&mat, xRatio, yRatio); + + data = (unsigned char*)pixman_image_get_data(pix_image); + stride = pixman_image_get_stride(pix_image); + + /* Into this image we actually have no offset */ + xoffset = 0; + yoffset = 0; + width = newWidth; + height = newHeight; + } + } else { + width = srcSurf->width; + height = srcSurf->height; + } + + cached_bitmap *cachebitmap = NULL; + + if (!partial) { + cachebitmap = + (cached_bitmap*)cairo_surface_get_user_data( + surfacePattern->surface, + key); + if (cachebitmap && cachebitmap->device != d2dsurf->device) { + cachebitmap = NULL; + } + } + + if (cachebitmap) { + sourceBitmap = cachebitmap->bitmap; + if (cachebitmap->dirty) { + D2D1_RECT_U rect; + /* No need to take partial uploading into account - partially uploaded surfaces are never cached. */ + if (pattern->extend == CAIRO_EXTEND_NONE) { + rect = D2D1::RectU(1, 1, srcSurf->width + 1, srcSurf->height + 1); + } else { + rect = D2D1::RectU(0, 0, srcSurf->width, srcSurf->height); + } + sourceBitmap->CopyFromMemory(&rect, + srcSurf->data, + srcSurf->stride); + cairo_surface_t *nullSurf = + cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA); + cachebitmap->refs++; + cachebitmap->dirty = false; + cairo_surface_set_user_data(nullSurf, + &bitmap_key_snapshot, + cachebitmap, + NULL); + cairo_surface_attach_snapshot(surfacePattern->surface, + nullSurf, + _d2d_snapshot_detached); + } + } else { + if (pattern->extend != CAIRO_EXTEND_NONE) { + hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(width, height), + data + yoffset * stride + xoffset * Bpp, + stride, + D2D1::BitmapProperties(D2D1::PixelFormat(format, + alpha)), + &sourceBitmap); + + if (FAILED(hr)) { + return NULL; + } + } else { + /** + * Trick here, we create a temporary rectangular + * surface with 1 pixel margin on each side. This + * provides a rectangular transparent border, that + * will ensure CLAMP acts as EXTEND_NONE. Perhaps + * this could be further optimized by not memsetting + * the whole array. + */ + unsigned int tmpWidth = width + 2; + unsigned int tmpHeight = height + 2; + unsigned char *tmp = new unsigned char[tmpWidth * tmpHeight * Bpp]; + memset(tmp, 0, tmpWidth * tmpHeight * Bpp); + for (unsigned int y = 0; y < height; y++) { + memcpy( + tmp + tmpWidth * Bpp * y + tmpWidth * Bpp + Bpp, + data + yoffset * stride + y * stride + xoffset * Bpp, + width * Bpp); + } + + hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(tmpWidth, tmpHeight), + tmp, + tmpWidth * Bpp, + D2D1::BitmapProperties(D2D1::PixelFormat(format, + D2D1_ALPHA_MODE_PREMULTIPLIED)), + &sourceBitmap); + + delete [] tmp; + if (FAILED(hr)) { + return NULL; + } + } + + if (!partial) { + cached_bitmap *cachebitmap = new cached_bitmap; + /* We can cache it if it isn't a partial bitmap */ + cachebitmap->dirty = false; + cachebitmap->bitmap = sourceBitmap; + cachebitmap->device = d2dsurf->device; + /* + * This will start out with two references, one on the snapshot + * and one more in the user data structure. + */ + cachebitmap->refs = 2; + cairo_surface_set_user_data(surfacePattern->surface, + key, + cachebitmap, + _d2d_release_bitmap); + cairo_surface_t *nullSurf = + cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA); + cairo_surface_set_user_data(nullSurf, + &bitmap_key_snapshot, + cachebitmap, + NULL); + cairo_surface_attach_snapshot(surfacePattern->surface, + nullSurf, + _d2d_snapshot_detached); + cache_usage += _d2d_compute_bitmap_mem_size(sourceBitmap); + } + if (pix_image) { + pixman_image_unref(pix_image); + } + } + } else { + return NULL; + } + D2D1_BITMAP_BRUSH_PROPERTIES bitProps; + + if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) { + bitProps = D2D1::BitmapBrushProperties(extendMode, + extendMode, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); + } else { + bitProps = D2D1::BitmapBrushProperties(extendMode, + extendMode, + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR); + } + if (unique) { + RefPtr bitBrush; + D2D1_BRUSH_PROPERTIES brushProps = + D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat)); + d2dsurf->rt->CreateBitmapBrush(sourceBitmap, + &bitProps, + &brushProps, + &bitBrush); + return bitBrush; + } else { + D2D1_MATRIX_3X2_F matrix = _cairo_d2d_matrix_from_matrix(&mat); + + if (d2dsurf->bitmapBrush) { + d2dsurf->bitmapBrush->SetTransform(matrix); + + if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) { + d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); + } else { + d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR); + } + + d2dsurf->bitmapBrush->SetBitmap(sourceBitmap); + d2dsurf->bitmapBrush->SetExtendModeX(extendMode); + d2dsurf->bitmapBrush->SetExtendModeY(extendMode); + } else { + D2D1_BRUSH_PROPERTIES brushProps = + D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat)); + d2dsurf->rt->CreateBitmapBrush(sourceBitmap, + &bitProps, + &brushProps, + &d2dsurf->bitmapBrush); + } + return d2dsurf->bitmapBrush; + } + } else { + return NULL; + } +} + + +/** Path Conversion */ + +/** + * Structure to use for the closure, containing all needed data. + */ +struct path_conversion { + /** Geometry sink that we need to write to */ + ID2D1GeometrySink *sink; + /** + * If this figure is active, cairo doesn't always send us a close. But + * we do need to end this figure if it didn't. + */ + bool figureActive; + /** + * Current point, D2D has no explicit move so we need to track moved for + * the next begin. + */ + cairo_point_t current_point; + /** The type of figure begin for this geometry instance */ + D2D1_FIGURE_BEGIN type; +}; + +static cairo_status_t +_cairo_d2d_path_move_to(void *closure, + const cairo_point_t *point) +{ + path_conversion *pathConvert = + static_cast(closure); + if (pathConvert->figureActive) { + pathConvert->sink->EndFigure(D2D1_FIGURE_END_OPEN); + pathConvert->figureActive = false; + } + + pathConvert->current_point = *point; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_d2d_path_line_to(void *closure, + const cairo_point_t *point) +{ + path_conversion *pathConvert = + static_cast(closure); + if (!pathConvert->figureActive) { + pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), + pathConvert->type); + pathConvert->figureActive = true; + } + + D2D1_POINT_2F d2dpoint = _d2d_point_from_cairo_point(point); + + pathConvert->sink->AddLine(d2dpoint); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_d2d_path_curve_to(void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + path_conversion *pathConvert = + static_cast(closure); + if (!pathConvert->figureActive) { + pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), + D2D1_FIGURE_BEGIN_FILLED); + pathConvert->figureActive = true; + } + + pathConvert->sink->AddBezier(D2D1::BezierSegment(_d2d_point_from_cairo_point(p0), + _d2d_point_from_cairo_point(p1), + _d2d_point_from_cairo_point(p2))); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_d2d_path_close(void *closure) +{ + path_conversion *pathConvert = + static_cast(closure); + + if (!pathConvert->figureActive) { + pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point), + pathConvert->type); + /** + * In this case we mean a single point. For D2D this means we need to add an infinitely + * small line here to get that effect. + */ + pathConvert->sink->AddLine(_d2d_point_from_cairo_point(&pathConvert->current_point)); + } + + pathConvert->sink->EndFigure(D2D1_FIGURE_END_CLOSED); + pathConvert->figureActive = false; + return CAIRO_STATUS_SUCCESS; +} + +/** + * Create an ID2D1PathGeometry for a cairo_path_fixed_t + * + * \param path Path to create a geometry for + * \param fill_rule Fill rule to use + * \param type Figure begin type to use + * \return A D2D geometry + */ +static RefPtr +_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + D2D1_FIGURE_BEGIN type) +{ + RefPtr d2dpath; + sD2DFactory->CreatePathGeometry(&d2dpath); + RefPtr sink; + d2dpath->Open(&sink); + D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING; + if (fill_rule == CAIRO_FILL_RULE_WINDING) { + fillMode = D2D1_FILL_MODE_WINDING; + } else if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD) { + fillMode = D2D1_FILL_MODE_ALTERNATE; + } + sink->SetFillMode(fillMode); + + path_conversion pathConvert; + pathConvert.type = type; + pathConvert.sink = sink; + pathConvert.figureActive = false; + _cairo_path_fixed_interpret(path, + CAIRO_DIRECTION_FORWARD, + _cairo_d2d_path_move_to, + _cairo_d2d_path_line_to, + _cairo_d2d_path_curve_to, + _cairo_d2d_path_close, + &pathConvert); + if (pathConvert.figureActive) { + sink->EndFigure(D2D1_FIGURE_END_OPEN); + } + sink->Close(); + return d2dpath; +} + +static cairo_bool_t +clip_contains_only_boxes (cairo_clip_t *clip) +{ + cairo_bool_t is_boxes = TRUE; + + if (clip) { + cairo_box_t clip_box; + cairo_clip_path_t *path = clip->path; + + while (path) { + is_boxes &= _cairo_path_fixed_is_box(&path->path, &clip_box); + path = path->prev; + } + } + return is_boxes; +} + +static cairo_int_status_t +_cairo_d2d_clear_box (cairo_d2d_surface_t *d2dsurf, + cairo_clip_t *clip, + cairo_box_t *box) +{ + if (clip_contains_only_boxes (clip)) { + /* clear the box using axis aligned clips */ + d2dsurf->rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(box->p1.x), + _cairo_fixed_to_float(box->p1.y), + _cairo_fixed_to_float(box->p2.x), + _cairo_fixed_to_float(box->p2.y)), + D2D1_ANTIALIAS_MODE_ALIASED); + d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); + d2dsurf->rt->PopAxisAlignedClip(); + + return CAIRO_INT_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf, + cairo_clip_t *clip) +{ + cairo_region_t *region; + cairo_int_status_t status; + + if (!clip) { + /* no clip so clear everything */ + _begin_draw_state(d2dsurf); + reset_clip(d2dsurf); + d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); + + return CAIRO_INT_STATUS_SUCCESS; + } + + status = _cairo_clip_get_region (clip, ®ion); + if (status) + return status; + + /* We now have a region, we'll clear it one rectangle at a time */ + _begin_draw_state(d2dsurf); + + reset_clip(d2dsurf); + + if (region) { + int num_rects; + int i; + + num_rects = cairo_region_num_rectangles (region); + + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + d2dsurf->rt->PushAxisAlignedClip( + D2D1::RectF((FLOAT)rect.x, + (FLOAT)rect.y, + (FLOAT)rect.x + rect.width, + (FLOAT)rect.y + rect.height), + D2D1_ANTIALIAS_MODE_ALIASED); + + d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); + + d2dsurf->rt->PopAxisAlignedClip(); + } + + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, + const cairo_pattern_t *source) +{ + if (op == CAIRO_OPERATOR_SOURCE) { + /** Operator over is easier for D2D! If the source if opaque, change */ + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surfpattern = + reinterpret_cast(source); + if (surfpattern->surface->content == CAIRO_CONTENT_COLOR) { + return CAIRO_OPERATOR_OVER; + } + } else if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solidpattern = + reinterpret_cast(source); + if (solidpattern->color.alpha == 1.0) { + return CAIRO_OPERATOR_OVER; + } + } + } + return op; +} + +void +_cairo_d2d_surface_init(cairo_d2d_surface_t *newSurf, cairo_d2d_device_t *d2d_device, cairo_format_t format) +{ + newSurf->format = format; + + newSurf->device = d2d_device; + cairo_addref_device(&d2d_device->base); + d2d_device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(newSurf); +} + +_cairo_d2d_surface::~_cairo_d2d_surface() +{ + _cairo_d2d_surface_entry *entry, *next; + cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &dependent_surfaces, link) { + // We do not need to flush, the contents of our texture has not changed, + // our users have their own reference and can just use it later. + cairo_surface_destroy(&entry->surface->base); + delete entry; + } + +} + +// Implementation +static cairo_surface_t* +_cairo_d2d_create_similar(void *surface, + cairo_content_t content, + int width, + int height) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + + new (newSurf) cairo_d2d_surface_t(); + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); + + + D2D1_SIZE_U sizePixels; + D2D1_SIZE_F size; + HRESULT hr; + + sizePixels.width = width; + sizePixels.height = height; + FLOAT dpiX; + FLOAT dpiY; + + d2dsurf->rt->GetDpi(&dpiX, &dpiY); + + D2D1_ALPHA_MODE alpha; + + if (content == CAIRO_CONTENT_COLOR) { + alpha = D2D1_ALPHA_MODE_IGNORE; + } else { + alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + } + + size.width = sizePixels.width * dpiX; + size.height = sizePixels.height * dpiY; + D2D1_BITMAP_PROPERTIES bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (sizePixels.width < 1) { + sizePixels.width = 1; + } + if (sizePixels.height < 1) { + sizePixels.height = 1; + } + RefPtr oldDxgiSurface; + d2dsurf->surface->QueryInterface(&oldDxgiSurface); + DXGI_SURFACE_DESC origDesc; + + oldDxgiSurface->GetDesc(&origDesc); + + CD3D10_TEXTURE2D_DESC desc(origDesc.Format, + sizePixels.width, + sizePixels.height); + + if (content == CAIRO_CONTENT_ALPHA) { + desc.Format = DXGI_FORMAT_A8_UNORM; + } + + desc.MipLevels = 1; + desc.Usage = D3D10_USAGE_DEFAULT; + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */ + if (desc.Format != DXGI_FORMAT_A8_UNORM) + desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; + + RefPtr texture; + RefPtr dxgiSurface; + + D2D1_RENDER_TARGET_USAGE usage = (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) ? + D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE + : D2D1_RENDER_TARGET_USAGE_NONE; + + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); + if (FAILED(hr)) { + goto FAIL_CREATESIMILAR; + } + + newSurf->surface = texture; + + // Create the DXGI surface. + hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); + if (FAILED(hr)) { + goto FAIL_CREATESIMILAR; + } + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha), + dpiX, + dpiY, + usage), + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATESIMILAR; + } + + if (desc.Format != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + if (FAILED(hr)) { + goto FAIL_CREATESIMILAR; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + _cairo_d2d_surface_init(newSurf, d2dsurf->device, _cairo_format_from_content(content)); + + return reinterpret_cast(newSurf); + +FAIL_CREATESIMILAR: + /** Ensure we call our surfaces desctructor */ + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); +} + +static cairo_status_t +_cairo_d2d_finish(void *surface) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + + d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf); + if (d2dsurf->bufferTexture) { + d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf); + } + + reset_clip(d2dsurf); + + // We need to release the device after calling the constructor, since the + // device destruction may release the D3D/D2D libraries. + cairo_device_t *device = &d2dsurf->device->base; + d2dsurf->~cairo_d2d_surface_t(); + cairo_release_device(device); + return CAIRO_STATUS_SUCCESS; +} + +/* The input types for src and dst don't match because in our particular use case, copying from a texture, + * those types don't match. */ +static void +_copy_data_to_different_stride(unsigned char *dst, int dst_stride, void *src, UINT src_stride, int height) +{ + + unsigned char *src_p = (unsigned char *)src; + int min_stride = MIN(dst_stride, src_stride); + while (height) { + memcpy(dst, src_p, min_stride); + height--; + dst += dst_stride; + src_p += src_stride; + } +} + +static cairo_status_t +_cairo_d2d_acquire_source_image(void *abstract_surface, + cairo_image_surface_t **image_out_ret, + void **image_extra) +{ + cairo_surface_t *image_out; + cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); + _cairo_d2d_flush(d2dsurf); + + HRESULT hr; + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + + RefPtr softTexture; + + RefPtr dxgiSurface; + d2dsurf->surface->QueryInterface(&dxgiSurface); + DXGI_SURFACE_DESC desc; + + dxgiSurface->GetDesc(&desc); + + CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height); + + /** + * We can't actually map our backing store texture, so we create one in CPU memory, and then + * tell D3D to copy the data from our surface there, readback is expensive, we -never- + * -ever- want this to happen. + */ + softDesc.MipLevels = 1; + softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; + softDesc.Usage = D3D10_USAGE_STAGING; + softDesc.BindFlags = 0; + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); + if (FAILED(hr)) { + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + } + + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); + + D3D10_MAPPED_TEXTURE2D data; + hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); + if (FAILED(hr)) { + return _cairo_error(CAIRO_STATUS_NO_DEVICE); + } + + if (_cairo_valid_stride_alignment(data.RowPitch)) { + image_out = cairo_image_surface_create_for_data((unsigned char*)data.pData, + d2dsurf->format, + size.width, + size.height, + data.RowPitch); + } else { + /* Slow path used when the stride doesn't match our requirements. + * This is possible on at least the Intel driver 8.15.10.2302. + * + * Create a new image surface and copy our data into it */ + image_out = cairo_image_surface_create(d2dsurf->format, + size.width, + size.height); + _copy_data_to_different_stride(cairo_image_surface_get_data(image_out), + cairo_image_surface_get_stride(image_out), + data.pData, + data.RowPitch, + size.height); + + } + /* these are the only surface statuses we expect */ + assert(cairo_surface_status(image_out) == CAIRO_STATUS_SUCCESS || + cairo_surface_status(image_out) == CAIRO_STATUS_NO_MEMORY); + + *image_extra = softTexture.forget().drop(); + *image_out_ret = (cairo_image_surface_t*)image_out; + + return cairo_surface_status(image_out); +} + +static void +_cairo_d2d_release_source_image(void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + if (((cairo_surface_t*)abstract_surface)->type != CAIRO_SURFACE_TYPE_D2D) { + return; + } + cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); + + cairo_surface_destroy(&image->base); + ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra; + + softTexture->Unmap(0); + softTexture->Release(); + softTexture = NULL; +} + +static cairo_status_t +_cairo_d2d_acquire_dest_image(void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); + _cairo_d2d_flush(d2dsurf); + + HRESULT hr; + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + + RefPtr softTexture; + + + RefPtr dxgiSurface; + d2dsurf->surface->QueryInterface(&dxgiSurface); + DXGI_SURFACE_DESC desc; + + dxgiSurface->GetDesc(&desc); + + CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height); + + image_rect->width = desc.Width; + image_rect->height = desc.Height; + image_rect->x = image_rect->y = 0; + + softDesc.MipLevels = 1; + softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; + softDesc.Usage = D3D10_USAGE_STAGING; + softDesc.BindFlags = 0; + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); + if (FAILED(hr)) { + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + } + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); + + D3D10_MAPPED_TEXTURE2D data; + hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); + if (FAILED(hr)) { + return _cairo_error(CAIRO_STATUS_NO_DEVICE); + } + *image_out = + (cairo_image_surface_t*)cairo_image_surface_create_for_data((unsigned char*)data.pData, + _cairo_format_from_content(d2dsurf->base.content), + size.width, + size.height, + data.RowPitch); + *image_extra = softTexture.forget().drop(); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_d2d_release_dest_image(void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(abstract_surface); + + ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra; + D2D1_POINT_2U point; + point.x = 0; + point.y = 0; + D2D1_RECT_U rect; + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + rect.left = rect.top = 0; + rect.right = size.width; + rect.bottom = size.height; + + cairo_surface_destroy(&image->base); + + softTexture->Unmap(0); + d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture); + softTexture->Release(); +} + + +static cairo_status_t +_cairo_d2d_flush(void *surface) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + + if (d2dsurf->isDrawing) { + reset_clip(d2dsurf); + HRESULT hr = d2dsurf->rt->EndDraw(); + d2dsurf->isDrawing = false; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + cairo_point_int_t *translation, + cairo_region_t *region) +{ + RefPtr dstSurface; + dst->surface->QueryInterface(&dstSurface); + RefPtr srcSurface; + src->surface->QueryInterface(&srcSurface); + DXGI_SURFACE_DESC srcDesc, dstDesc; + + srcSurface->GetDesc(&srcDesc); + dstSurface->GetDesc(&dstDesc); + + cairo_rectangle_int_t clip_rect; + clip_rect.x = 0; + clip_rect.y = 0; + clip_rect.width = dstDesc.Width; + clip_rect.height = dstDesc.Height; + + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _cairo_d2d_flush(dst); + ID3D10Resource *srcResource = src->surface; + if (src->surface.get() == dst->surface.get()) { + // Self-copy + srcResource = _cairo_d2d_get_buffer_texture(dst); + src->device->mD3D10Device->CopyResource(srcResource, src->surface); + } else { + // Need to flush the source too if it's a different surface. + _cairo_d2d_flush(src); + } + + // One copy for each rectangle in the final clipping region. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + D3D10_BOX rect; + cairo_rectangle_int_t area_to_copy; + + cairo_region_get_rectangle(region, i, &area_to_copy); + + cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, + area_to_copy.y + translation->y, + area_to_copy.width, area_to_copy.height }; + cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; + + + if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { + /* We cannot do any sort of extend, in the future a little bit of extra code could + * allow us to support EXTEND_NONE. + */ + rv = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + rect.front = 0; + rect.back = 1; + rect.left = transformed_rect.x; + rect.top = transformed_rect.y; + rect.right = transformed_rect.x + transformed_rect.width; + rect.bottom = transformed_rect.y + transformed_rect.height; + + src->device->mD3D10Device->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); + } + + return rv; +} + +static cairo_int_status_t +_cairo_d2d_blend_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + const cairo_matrix_t *transform, + cairo_box_t *box, + cairo_clip_t *clip, + cairo_filter_t filter, + float opacity) +{ + if (dst == src) { + // We cannot do self-blend. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _begin_draw_state(dst); + _cairo_d2d_set_clip(dst, clip); + _cairo_d2d_flush(src); + D2D1_SIZE_U sourceSize = src->surfaceBitmap->GetPixelSize(); + + + double x1, x2, y1, y2; + if (box) { + _cairo_box_to_doubles(box, &x1, &y1, &x2, &y2); + } else { + x1 = y1 = 0; + x2 = dst->rt->GetSize().width; + y2 = dst->rt->GetSize().height; + } + + if (clip) { + const cairo_rectangle_int_t *clipExtent = _cairo_clip_get_extents(clip); + x1 = MAX(x1, clipExtent->x); + x2 = MIN(x2, clipExtent->x + clipExtent->width); + y1 = MAX(y1, clipExtent->y); + y2 = MIN(y2, clipExtent->y + clipExtent->height); + } + + // We should be in drawing state for this. + _begin_draw_state(dst); + _cairo_d2d_set_clip (dst, clip); + D2D1_RECT_F rectSrc; + rectSrc.left = (float)(x1 * transform->xx + transform->x0); + rectSrc.top = (float)(y1 * transform->yy + transform->y0); + rectSrc.right = (float)(x2 * transform->xx + transform->x0); + rectSrc.bottom = (float)(y2 * transform->yy + transform->y0); + + if (rectSrc.left < 0 || rectSrc.top < 0 || rectSrc.right < 0 || rectSrc.bottom < 0 || + rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height || + rectSrc.left > sourceSize.width || rectSrc.top > sourceSize.height) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + D2D1_RECT_F rectDst; + rectDst.left = (float)x1; + rectDst.top = (float)y1; + rectDst.right = (float)x2; + rectDst.bottom = (float)y2; + + // Bug 599658 - if the src rect is inverted in either axis D2D is fine with + // this but it does not actually invert the bitmap. This is an easy way + // of doing that. + D2D1_MATRIX_3X2_F matrix = D2D1::IdentityMatrix(); + bool needsTransform = false; + if (rectSrc.left > rectSrc.right) { + rectDst.left = -rectDst.left; + rectDst.right = -rectDst.right; + matrix._11 = -1.0; + needsTransform = true; + } + if (rectSrc.top > rectSrc.bottom) { + rectDst.top = -rectDst.top; + rectDst.bottom = -rectDst.bottom; + matrix._22 = -1.0; + needsTransform = true; + } + + _cairo_d2d_add_dependent_surface(src, dst); + + D2D1_BITMAP_INTERPOLATION_MODE interpMode = + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; + + if (needsTransform) { + dst->rt->SetTransform(matrix); + } + + if (filter == CAIRO_FILTER_NEAREST) { + interpMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + } + + dst->rt->DrawBitmap(src->surfaceBitmap, + rectDst, + opacity, + interpMode, + rectSrc); + if (needsTransform) { + dst->rt->SetTransform(D2D1::IdentityMatrix()); + } + + return rv; +} +/** + * This function will text if we can use GPU mem cpy to execute an operation with + * a surface pattern. If box is NULL it will operate on the entire dst surface. + */ +static cairo_int_status_t +_cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst, + cairo_surface_t *src, + cairo_box_t *box, + const cairo_matrix_t *matrix, + cairo_clip_t *clip, + cairo_operator_t op, + cairo_filter_t filter, + float opacity = 1.0f) +{ + if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) { + op = CAIRO_OPERATOR_SOURCE; + } + if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* For now we do only D2D sources */ + if (src->type != CAIRO_SURFACE_TYPE_D2D) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + if (op == CAIRO_OPERATOR_OVER && matrix->xy == 0 && matrix->yx == 0) { + return _cairo_d2d_blend_surface(dst, d2dsrc, matrix, box, clip, filter, opacity); + } + + if (op == CAIRO_OPERATOR_OVER || opacity != 1.0f) { + // Past this point we will never get into a situation where we can + // support OVER. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_point_int_t translation; + if ((box && !box_is_integer(box)) || + !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t rect; + if (box) { + _cairo_box_round_to_rectangle(box, &rect); + } else { + rect.x = rect.y = 0; + rect.width = dst->rt->GetPixelSize().width; + rect.height = dst->rt->GetPixelSize().height; + } + + if (d2dsrc->device != dst->device) { + // This doesn't work between different devices. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Region we need to clip this operation to */ + cairo_region_t *clipping_region = NULL; + cairo_region_t *region; + cairo_region_auto_ptr region_ptr; + + if (clip) { + _cairo_clip_get_region(clip, &clipping_region); + + if (!clipping_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + region = cairo_region_copy(clipping_region); + region_ptr.set(region); + + cairo_region_intersect_rectangle(region, &rect); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + } else { + region = cairo_region_create_rectangle(&rect); + region_ptr.set(region); + + // Areas outside of the surface do not matter. + cairo_rectangle_int_t surface_rect = { 0, 0, + dst->rt->GetPixelSize().width, + dst->rt->GetPixelSize().height }; + cairo_region_intersect_rectangle(region, &surface_rect); + } + + cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); + + return rv; +} + +static RefPtr +_cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip) +{ + RefPtr texture = _cairo_d2d_get_buffer_texture(surf); + RefPtr new_rt; + RefPtr dxgiSurface; + texture->QueryInterface(&dxgiSurface); + HRESULT hr; + + _cairo_d2d_flush(surf); + + if (!surf) { + return NULL; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &new_rt); + + if (FAILED(hr)) { + return NULL; + } + + new_rt->BeginDraw(); + new_rt->Clear(D2D1::ColorF(0, 0)); + + // Since this is a fresh surface there's no point in doing clever things to + // keep the clip path around until a certain depth. So we just do a straight- + // forward push of all clip paths in the tree, similar to what the normal + // clip code does, but a little less clever. + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + cairo_box_t clip_box; + if (_cairo_path_fixed_is_box(&path->path, &clip_box)) { + // If this does not have a region it could be none-pixel aligned. + D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + if (box_is_integer(&clip_box)) { + aaMode = D2D1_ANTIALIAS_MODE_ALIASED; + } + new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x), + _cairo_fixed_to_float(clip_box.p1.y), + _cairo_fixed_to_float(clip_box.p2.x), + _cairo_fixed_to_float(clip_box.p2.y)), + aaMode); + } else { + HRESULT hr; + RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&path->path, + path->fill_rule, + D2D1_FIGURE_BEGIN_FILLED); + RefPtr layer; + + hr = new_rt->CreateLayer (&layer); + + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + new_rt->PushLayer(D2D1::LayerParameters( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options), + layer); + } + path = path->prev; + } + } + return new_rt; +} + +static cairo_int_status_t +_cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL) +{ + _cairo_d2d_flush_dependent_surfaces(surf); + + int numPaths = 0; + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + numPaths++; + path = path->prev; + } + + cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths]; + + numPaths = 0; + path = clip->path; + while (path) { + paths[numPaths++] = path; + path = path->prev; + } + + for (int i = numPaths - 1; i >= 0; i--) { + if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) { + rt->PopAxisAlignedClip(); + } else { + rt->PopLayer(); + } + } + delete [] paths; + } + rt->EndDraw(); + HRESULT hr; + + RefPtr srcTexture = _cairo_d2d_get_buffer_texture(surf); + RefPtr dstTexture; + + surf->surface->QueryInterface(&dstTexture); + ID3D10Device *device = surf->device->mD3D10Device; + + if (!surf->buffer_rt_view) { + hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (!surf->buffer_sr_view) { + hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + cairo_int_status_t status; + + status = _cairo_d2d_set_operator(surf->device, op); + + if (unlikely(status)) { + return status; + } + + D3D10_TEXTURE2D_DESC tDesc; + dstTexture->GetDesc(&tDesc); + D3D10_VIEWPORT vp; + vp.Height = tDesc.Height; + vp.MinDepth = 0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = tDesc.Width; + device->RSSetViewports(1, &vp); + + ID3D10Effect *effect = surf->device->mSampleEffect; + + ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view; + device->OMSetRenderTargets(1, &rtViewPtr, 0); + ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); + ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); + + float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f }; + float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f }; + if (bounds && _cairo_operator_bounded_by_mask(op)) { + quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f; + quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f; + quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f; + quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f; + texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width; + texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height; + texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width; + texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height; + } + quadDesc->SetFloatVector(quadDescVal); + texCoords->SetFloatVector(texCoordsVal); + + _cairo_d2d_setup_for_blend(surf->device); + ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->Apply(0); + + ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view; + device->PSSetShaderResources(0, 1, &srViewPtr); + + device->Draw(4, 0); + +#ifdef DEBUG + // Quiet down some info messages from D3D10 debug layer + srViewPtr = NULL; + device->PSSetShaderResources(0, 1, &srViewPtr); + rtViewPtr = NULL; + device->OMSetRenderTargets(1, &rtViewPtr, 0); +#endif + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_paint(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_int_status_t status; + + op = _cairo_d2d_simplify_operator(op, source); + + if (op == CAIRO_OPERATOR_SOURCE) { + if (!clip) { + _cairo_d2d_clear(d2dsurf, NULL); + op = CAIRO_OPERATOR_OVER; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (op == CAIRO_OPERATOR_CLEAR) { + return _cairo_d2d_clear(d2dsurf, clip); + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + + status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, + NULL, &source->matrix, clip, + op, source->filter); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, + source); + + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + D2D1_SIZE_F size = target_rt->GetSize(); + target_rt->FillRectangle(D2D1::RectF((FLOAT)0, + (FLOAT)0, + (FLOAT)size.width, + (FLOAT)size.height), + brush); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_mask(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_rectangle_int_t extents; + + cairo_clip_t *actual_clip = clip; + cairo_clip_t temporary_clip; + + cairo_int_status_t status; + + status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base, + op, source, + mask, + clip, &extents); + if (unlikely (status)) + return status; + + bool isSolidAlphaMask = false; + float solidAlphaValue = 1.0f; + + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solidPattern = + (cairo_solid_pattern_t*)mask; + if (_cairo_color_get_content (&solidPattern->color) == CAIRO_CONTENT_ALPHA) { + isSolidAlphaMask = true; + solidAlphaValue = solidPattern->color.alpha; + } + } + + cairo_box_t box; + _cairo_box_from_rectangle(&box, &extents); + + if (clip && isSolidAlphaMask) { + // We do some work here to try and avoid pushing and popping clips for rectangular areas, + // if we do this fill rects will occur without rectangular clips being pushed and popped. + // This is faster for non-axis aligned clips in general and allows more efficient batching + // of the pop-clip calls. + int num_boxes = 1; + cairo_box_t box_stack; + cairo_box_t *boxes; + boxes = &box_stack; + + // This function assumes atleast a single box resides at 'boxes' and the + // amount of boxes that reside there are passed in under num_boxes. + status = _cairo_clip_get_boxes(clip, &boxes, &num_boxes); + + if (!status && num_boxes == 1) { + box.p1.x = MAX(box.p1.x, boxes->p1.x); + box.p2.x = MIN(box.p2.x, boxes->p2.x); + box.p1.y = MAX(box.p1.y, boxes->p1.y); + box.p2.y = MIN(box.p2.y, boxes->p2.y); + + if (clip->path != d2dsurf->clip.path) { + // If we have a clip set, but it's not the right one. We want to + // pop as much as we need to, to be sure the area affected by + // the operation is not clipped. To do this we set the clip path + // to the common ancestor of the currently set clip path and the + // clip path for this operation. This will cause + // _cairo_d2d_set_clip to pop to that common ancestor, but not + // needlessly push the additional clips we're trying to avoid. + temporary_clip.path = find_common_ancestor(clip->path, d2dsurf->clip.path); + + // We're not going to be using this down the line so it doesn't + // really matter what the value is. If all -was- clipped this + // call shouldn't even have reached the surface backend. + temporary_clip.all_clipped = FALSE; + + actual_clip = &temporary_clip; + } + } + + if (boxes != &box_stack) { + // If the function changed the boxes pointer, we need to free it. + free(boxes); + } + } + + if (isSolidAlphaMask) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, + surf_pattern->surface, + &box, + &source->matrix, + clip, + op, + source->filter, + solidAlphaValue); + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } + } + + RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, source); + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, actual_clip); + if (unlikely(status)) + return status; + } +#endif + + D2D1_RECT_F rect = D2D1::RectF(_cairo_fixed_to_float(box.p1.x), + _cairo_fixed_to_float(box.p1.y), + _cairo_fixed_to_float(box.p2.x), + _cairo_fixed_to_float(box.p2.y)); + + if (isSolidAlphaMask) { + brush->SetOpacity(solidAlphaValue); + target_rt->FillRectangle(rect, + brush); + brush->SetOpacity(1.0); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); + } + return CAIRO_INT_STATUS_SUCCESS; + } + + RefPtr opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, mask, true); + if (!opacityBrush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (!d2dsurf->maskLayer) { + d2dsurf->rt->CreateLayer(&d2dsurf->maskLayer); + } + target_rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), + 0, + D2D1_ANTIALIAS_MODE_ALIASED, + D2D1::IdentityMatrix(), + 1.0, + opacityBrush), + d2dsurf->maskLayer); + + target_rt->FillRectangle(rect, + brush); + target_rt->PopLayer(); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); + } + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_stroke(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_int_status_t status; + + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + + op = _cairo_d2d_simplify_operator(op, source); + + if (op == CAIRO_OPERATOR_SOURCE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + if (antialias == CAIRO_ANTIALIAS_NONE) { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + } else { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + RefPtr strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style); + + if (!strokeStyle) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, + CAIRO_FILL_RULE_WINDING, + D2D1_FIGURE_BEGIN_FILLED); + + bool transformed = true; + + if (_cairo_matrix_is_identity(ctm)) { + transformed = false; + } + + RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, + source); + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + D2D1::Matrix3x2F mat; + if (transformed) { + // If we are transformed we will draw the geometry multiplied by the + // inverse transformation and apply the transform to our render target. + // This way the transformation will also be applied to the strokestyle. + mat = _cairo_d2d_matrix_from_matrix(ctm); + D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat); + + RefPtr trans_geom; + sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); + + // If we are setting a transform on the render target, we've multiplied + // the geometry by the inverse transform, we should also multiply the + // brush matrix by this inverse transform then to map the brush to the + // correct place. + D2D1_MATRIX_3X2_F brushMatrix; + brush->GetTransform(&brushMatrix); + brushMatrix = brushMatrix * inverse_mat; + brush->SetTransform(brushMatrix); + target_rt->SetTransform(mat); + d2dpath = trans_geom; + } else { + mat = D2D1::Matrix3x2F::Identity(); + } + + target_rt->DrawGeometry(d2dpath, brush, (FLOAT)style->line_width, strokeStyle); + + if (transformed) { + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); + } + + if (target_rt.get() != d2dsurf->rt.get()) { + D2D1_RECT_F bounds; + d2dpath->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, mat, &bounds); + cairo_rectangle_int_t bound_rect; + _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_fill(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_int_status_t status; + + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_box_t box; + bool is_box = _cairo_path_fixed_is_box(path, &box); + + if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, + &box, &source->matrix, clip, op, + source->filter); + + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } + + op = _cairo_d2d_simplify_operator(op, source); + + if (op == CAIRO_OPERATOR_SOURCE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + if (_cairo_path_fixed_is_box(path, &box)) { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + + return _cairo_d2d_clear_box (d2dsurf, clip, &box); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + RefPtr target_rt = d2dsurf->rt; + +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + if (antialias == CAIRO_ANTIALIAS_NONE) { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + } else { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + + if (is_box) { + float x1 = _cairo_fixed_to_float(box.p1.x); + float y1 = _cairo_fixed_to_float(box.p1.y); + float x2 = _cairo_fixed_to_float(box.p2.x); + float y2 = _cairo_fixed_to_float(box.p2.y); + RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, + path, source); + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + target_rt->FillRectangle(D2D1::RectF(x1, + y1, + x2, + y2), + brush); + } else { + RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED); + + RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, + path, source); + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + target_rt->FillGeometry(d2dpath, brush); + } + + if (target_rt.get() != d2dsurf->rt.get()) { + double x1, y1, x2, y2; + cairo_box_t box; + _cairo_path_fixed_extents (path, &box); + x1 = _cairo_fixed_to_double (box.p1.x); + y1 = _cairo_fixed_to_double (box.p1.y); + x2 = _cairo_fixed_to_double (box.p2.x); + y2 = _cairo_fixed_to_double (box.p2.y); + cairo_rectangle_int_t bounds; + _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface, + cairo_operator_t op, + const cairo_solid_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); + if (!dwritesf->manual_show_glyphs_allowed) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); + cairo_d2d_surface_t *dst = reinterpret_cast(surface); + + BOOL transform = FALSE; + HRESULT hr; + + cairo_region_t *clip_region = NULL; + + // We can only draw axis and pixel aligned rectangular quads, this means we + // can only support clips which form regions, since the intersection with + // our text area will then always be a set of rectangular axis and pixel + // aligned quads. + if (clip) { + _cairo_clip_get_region(clip, &clip_region); + if (!clip_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (!dst->isDrawing) { + _cairo_d2d_flush_dependent_surfaces(dst); + } + + _cairo_d2d_set_clip(dst, NULL); + dst->rt->Flush(); + + AutoDWriteGlyphRun run; + _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, scaled_font, &run, &transform); + + RefPtr analysis; + DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + + RefPtr params; + dst->rt->GetTextRenderingParams(¶ms); + + DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; + if (params) { + hr = dwriteff->dwriteface->GetRecommendedRenderingMode( + (FLOAT)scaled_font->base.font_matrix.yy, + 1.0f, + DWRITE_MEASURING_MODE_NATURAL, + params, + &renderMode); + if (FAILED(hr)) { + // this probably never happens, but let's play it safe + renderMode = DWRITE_RENDERING_MODE_DEFAULT; + } + } + + // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. + switch (renderMode) { + case DWRITE_RENDERING_MODE_ALIASED: + // ClearType texture creation will fail in this mode, so bail out + return CAIRO_INT_STATUS_UNSUPPORTED; + case DWRITE_RENDERING_MODE_DEFAULT: + // As per DWRITE_RENDERING_MODE documentation, pick Natural for font + // sizes under 16 ppem + if (scaled_font->base.font_matrix.yy < 16.0f) { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + } else { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + } + break; + case DWRITE_RENDERING_MODE_OUTLINE: + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + break; + default: + break; + } + + DWRITE_MEASURING_MODE measureMode = + renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : + renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : + DWRITE_MEASURING_MODE_NATURAL; + + hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + transform ? &dwmat : 0, + renderMode, + measureMode, + 0, + 0, + &analysis); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + RECT bounds; + hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds); + if (FAILED(hr) || + // with bitmap sizes of asian fonts, GetAlphaTextureBounds returns + // an empty rect, so we need to detect that and fall back + (bounds.top == 0 && bounds.bottom == 0 && num_glyphs > 0)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t cairo_bounds = + _cairo_rect_from_windows_rect(&bounds); + + cairo_region_t *region; + if (clip) { + region = cairo_region_copy(clip_region); + + cairo_region_intersect_rectangle(region, &cairo_bounds); + } else { + region = cairo_region_create_rectangle(&cairo_bounds); + } + + cairo_region_auto_ptr region_ptr(region); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + + int bufferSize = cairo_bounds.width * cairo_bounds.height * 3; + + if (!bufferSize) { + // width == 0 || height == 0 + return CAIRO_INT_STATUS_SUCCESS; + } + + // We add one byte so we can safely read an entire 32-bit int when copying + // the last 3 bytes of the alpha texture. + BYTE *texture = new BYTE[bufferSize + 1]; + hr = analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds, texture, bufferSize); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + RefPtr srView; + ID3D10Device1 *device = dst->device->mD3D10Device; + + int textureWidth, textureHeight; + + if (cairo_bounds.width < TEXT_TEXTURE_WIDTH && + cairo_bounds.height < TEXT_TEXTURE_HEIGHT) + { + // Use our cached TextTexture when it is big enough. + RefPtr tmpTexture; + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + cairo_bounds.width, cairo_bounds.height, + 1, 1, 0); + + desc.Usage = D3D10_USAGE_STAGING; + desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; + + hr = device->CreateTexture2D(&desc, NULL, &tmpTexture); + + D3D10_MAPPED_TEXTURE2D texMap; + hr = tmpTexture->Map(0, D3D10_MAP_WRITE, 0, &texMap); + + if (FAILED(hr)) { + delete [] texture; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + BYTE *alignedTextureData = (BYTE*)texMap.pData; + for (int y = 0; y < cairo_bounds.height; y++) { + for (int x = 0; x < cairo_bounds.width; x++) { + // Copy 3 Bpp source to 4 Bpp texture. + // + // Since we don't care what ends up in the alpha pixel of the + // destination, therefor we can simply copy a normal 32 bit + // integer each time, filling the alpha pixel of the destination + // with the first subpixel of the next pixel from the source. + *((int*)(alignedTextureData + (y * texMap.RowPitch) + x * 4)) = + *((int*)(texture + (y * cairo_bounds.width + x) * 3)); + } + } + + tmpTexture->Unmap(0); + + delete [] texture; + + D3D10_BOX box; + box.front = box.top = box.left = 0; + box.back = 1; + box.right = cairo_bounds.width; + box.bottom = cairo_bounds.height; + + device->CopySubresourceRegion(dst->device->mTextTexture, 0, 0, 0, 0, tmpTexture, 0, &box); + + srView = dst->device->mTextTextureView; + + textureWidth = TEXT_TEXTURE_WIDTH; + textureHeight = TEXT_TEXTURE_HEIGHT; + } else { + int alignedBufferSize = cairo_bounds.width * cairo_bounds.height * 4; + + // Create a one-off immutable texture from system memory. + BYTE *alignedTextureData = new BYTE[alignedBufferSize]; + for (int y = 0; y < cairo_bounds.height; y++) { + for (int x = 0; x < cairo_bounds.width; x++) { + // Copy 3 Bpp source to 4 Bpp destination memory used for + // texture creation. D3D10 has no 3 Bpp texture format we can + // use. + // + // Since we don't care what ends up in the alpha pixel of the + // destination, therefor we can simply copy a normal 32 bit + // integer each time, filling the alpha pixel of the destination + // with the first subpixel of the next pixel from the source. + *((int*)(alignedTextureData + (y * cairo_bounds.width + x) * 4)) = + *((int*)(texture + (y * cairo_bounds.width + x) * 3)); + } + } + + D3D10_SUBRESOURCE_DATA data; + + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + cairo_bounds.width, cairo_bounds.height, + 1, 1); + desc.Usage = D3D10_USAGE_IMMUTABLE; + + data.SysMemPitch = cairo_bounds.width * 4; + data.pSysMem = alignedTextureData; + + RefPtr tex; + hr = device->CreateTexture2D(&desc, &data, &tex); + + delete [] alignedTextureData; + delete [] texture; + + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + hr = device->CreateShaderResourceView(tex, NULL, &srView); + + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + textureWidth = cairo_bounds.width; + textureHeight = cairo_bounds.height; + } + + // Prepare destination surface for rendering. + RefPtr dstTexture; + + dst->surface->QueryInterface(&dstTexture); + + if (!dst->buffer_rt_view) { + hr = device->CreateRenderTargetView(dstTexture, NULL, &dst->buffer_rt_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + D3D10_TEXTURE2D_DESC tDesc; + dstTexture->GetDesc(&tDesc); + D3D10_VIEWPORT vp; + vp.Height = tDesc.Height; + vp.MinDepth = 0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = tDesc.Width; + device->RSSetViewports(1, &vp); + + ID3D10Effect *effect = dst->device->mSampleEffect; + + ID3D10RenderTargetView *rtViewPtr = dst->buffer_rt_view; + + device->OMSetRenderTargets(1, &rtViewPtr, 0); + + ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTextTexture"); + + ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); + ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); + ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector(); + + float colorVal[] = { float(source->color.red * source->color.alpha), + float(source->color.green * source->color.alpha), + float(source->color.blue * source->color.alpha), + float(source->color.alpha) }; + textColor->SetFloatVector(colorVal); + + float quadDescVal[4]; + float texCoordsVal[4]; + + // Draw a quad for each rectangle in the intersection of the clip and the + // text area. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + cairo_rectangle_int_t quad; + cairo_region_get_rectangle(region, i, &quad); + + quadDescVal[0] = -1.0f + ((float)quad.x / (float)tDesc.Width) * 2.0f; + quadDescVal[1] = 1.0f - ((float)quad.y / (float)tDesc.Height) * 2.0f; + quadDescVal[2] = ((float)quad.width / (float)tDesc.Width) * 2.0f; + quadDescVal[3] = -((float)quad.height / (float)tDesc.Height) * 2.0f; + + texCoordsVal[0] = (float)(quad.x - cairo_bounds.x) / textureWidth; + texCoordsVal[1] = (float)(quad.y - cairo_bounds.y) / textureHeight; + texCoordsVal[2] = (float)quad.width / textureWidth; + texCoordsVal[3] = (float)quad.height / textureHeight; + + quadDesc->SetFloatVector(quadDescVal); + texCoords->SetFloatVector(texCoordsVal); + + _cairo_d2d_setup_for_blend(dst->device); + technique->GetPassByIndex(0)->Apply(0); + + ID3D10ShaderResourceView *srViewPtr = srView; + device->PSSetShaderResources(0, 1, &srViewPtr); + + device->Draw(4, 0); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_int_status_t status; + + // TODO: Check font & surface for types. + cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); + cairo_d2d_surface_t *dst = reinterpret_cast(surface); + + /* We can only handle dwrite fonts */ + //XXX: this is checked by at least one caller + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + op = _cairo_d2d_simplify_operator(op, source); + + /* We cannot handle operator SOURCE or CLEAR */ + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (op == CAIRO_OPERATOR_OVER && source->type == CAIRO_PATTERN_TYPE_SOLID && + dst->base.content != CAIRO_CONTENT_COLOR && + dst->base.permit_subpixel_antialiasing && + dwritesf->antialias_mode == CAIRO_ANTIALIAS_SUBPIXEL) + { + // The D2D/DWrite drawing API's will not allow drawing subpixel AA to + // an RGBA surface. We do however want to do this if we know all text + // on a surface will be over opaque pixels, when this is the case + // we set the permit_subpixel_antialiasing flag on a surface. We then + // proceed to manually composite the glyphs to the surface. + + const cairo_solid_pattern_t *solid_src = + reinterpret_cast(source); + status = _cairo_dwrite_manual_show_glyphs_on_d2d_surface(surface, + op, + solid_src, + glyphs, + num_glyphs, + dwritesf, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } + + RefPtr target_rt = dst->rt; + cairo_rectangle_int_t fontArea; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(dst, clip); + + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(dst); + status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip); + + if (unlikely(status)) + return status; + } +#endif + + D2D1_TEXT_ANTIALIAS_MODE cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + + // If we're rendering to a temporary surface we cannot do sub-pixel AA. + if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) { + cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + } + + RefPtr params; + target_rt->GetTextRenderingParams(¶ms); + + DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; + if (params) { + HRESULT hr = dwriteff->dwriteface->GetRecommendedRenderingMode( + (FLOAT)dwritesf->base.font_matrix.yy, + 1.0f, + DWRITE_MEASURING_MODE_NATURAL, + params, + &renderMode); + if (FAILED(hr)) { + // this probably never happens, but let's play it safe + renderMode = DWRITE_RENDERING_MODE_DEFAULT; + } + } + + // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept + switch (renderMode) { + case DWRITE_RENDERING_MODE_DEFAULT: + // As per DWRITE_RENDERING_MODE documentation, pick Natural for font + // sizes under 16 ppem + if (dwritesf->base.font_matrix.yy < 16.0f) { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + } else { + renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + } + break; + case DWRITE_RENDERING_MODE_OUTLINE: + return CAIRO_INT_STATUS_UNSUPPORTED; + default: + break; + } + + switch (dwritesf->antialias_mode) { + case CAIRO_ANTIALIAS_NONE: + cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + case CAIRO_ANTIALIAS_GRAY: + cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + break; + } + + if (renderMode == DWRITE_RENDERING_MODE_ALIASED) { + cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + } + + target_rt->SetTextAntialiasMode(cleartype_quality); + + DWRITE_MEASURING_MODE measureMode = + renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : + renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : + DWRITE_MEASURING_MODE_NATURAL; + + cairo_bool_t transform = FALSE; + + AutoDWriteGlyphRun run; + _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform); + + D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat); + + if (transform) { + target_rt->SetTransform(mat); + } + + if (dst->rt.get() != target_rt.get()) { + RefPtr analysis; + DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + transform ? &dwmat : 0, + renderMode, + measureMode, + 0, + 0, + &analysis); + + RECT bounds; + analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ? + DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds); + fontArea.x = bounds.left; + fontArea.y = bounds.top; + fontArea.width = bounds.right - bounds.left; + fontArea.height = bounds.bottom - bounds.top; + } + + RefPtr brush = _cairo_d2d_create_brush_for_pattern(dst, NULL, + source); + + if (!brush) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (transform) { + D2D1::Matrix3x2F mat_inverse = _cairo_d2d_matrix_from_matrix(&dwritesf->mat_inverse); + D2D1::Matrix3x2F mat_brush; + + // The brush matrix needs to be multiplied with the inverted matrix + // as well, to move the brush into the space of the glyphs. Before + // the render target transformation. + brush->GetTransform(&mat_brush); + mat_brush = mat_brush * mat_inverse; + brush->SetTransform(&mat_brush); + } + + target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, measureMode); + + if (transform) { + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); + } + + if (target_rt.get() != dst->rt.get()) { + return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_d2d_show_glyphs (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D || + scaled_font->backend->type != CAIRO_FONT_TYPE_DWRITE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_d2d_surface_t::TextRenderingState textRenderingState = + reinterpret_cast(scaled_font)->rendering_mode; + if (d2dsurf->textRenderingState != textRenderingState) { + RefPtr params = + DWriteFactory::RenderingParams(textRenderingState); + d2dsurf->rt->SetTextRenderingParams(params); + d2dsurf->textRenderingState = textRenderingState; + } + cairo_int_status_t status = (cairo_int_status_t) + _cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); + + return status; +} + + +static cairo_bool_t +_cairo_d2d_getextents(void *surface, + cairo_rectangle_int_t *extents) +{ + cairo_d2d_surface_t *d2dsurf = static_cast(surface); + extents->x = 0; + extents->y = 0; + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + extents->width = size.width; + extents->height = size.height; + return TRUE; +} + + +/** Helper functions. */ + + + +cairo_surface_t* +cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device, + HWND wnd, + cairo_content_t content) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(cairo_device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); + + RECT rc; + HRESULT hr; + + newSurf->isDrawing = false; + ::GetClientRect(wnd, &rc); + + FLOAT dpiX; + FLOAT dpiY; + D2D1_SIZE_U sizePixels; + D2D1_SIZE_F size; + + dpiX = 96; + dpiY = 96; + + + sizePixels.width = rc.right - rc.left; + sizePixels.height = rc.bottom - rc.top; + + if (!sizePixels.width) { + sizePixels.width = 1; + } + if (!sizePixels.height) { + sizePixels.height = 1; + } + ID3D10Device1 *device = d2d_device->mD3D10Device; + RefPtr dxgiDevice; + RefPtr dxgiAdapter; + RefPtr dxgiFactory; + D2D1_RENDER_TARGET_PROPERTIES props; + D2D1_BITMAP_PROPERTIES bitProps; + + device->QueryInterface(&dxgiDevice); + dxgiDevice->GetAdapter(&dxgiAdapter); + dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)); + + DXGI_SWAP_CHAIN_DESC swapDesc; + ::ZeroMemory(&swapDesc, sizeof(swapDesc)); + + swapDesc.BufferDesc.Width = sizePixels.width; + swapDesc.BufferDesc.Height = sizePixels.height; + swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapDesc.BufferDesc.RefreshRate.Numerator = 60; + swapDesc.BufferDesc.RefreshRate.Denominator = 1; + swapDesc.SampleDesc.Count = 1; + swapDesc.SampleDesc.Quality = 0; + swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapDesc.BufferCount = 1; + swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE; + swapDesc.OutputWindow = wnd; + swapDesc.Windowed = TRUE; + + /** + * Create a swap chain, this swap chain will contain the backbuffer for + * the window we draw to. The front buffer is the full screen front + * buffer. + */ + hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, &newSurf->dxgiChain); + + /** + * We do not want DXGI to intercept alt-enter events and make the window go + * fullscreen! This shouldn't be in the cairo backend but controlled through + * the device. See comments on mozilla bug 553603. + */ + dxgiFactory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES); + + if (FAILED(hr)) { + goto FAIL_HWND; + } + /** Get the backbuffer surface from the swap chain */ + hr = newSurf->dxgiChain->GetBuffer(0, + IID_PPV_ARGS(&newSurf->surface)); + + if (FAILED(hr)) { + goto FAIL_HWND; + } + + newSurf->surface->QueryInterface(&newSurf->backBuf); + + size.width = sizePixels.width * dpiX; + size.height = sizePixels.height * dpiY; + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), + dpiX, + dpiY, + D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, + props, + &newSurf->rt); + if (FAILED(hr)) { + goto FAIL_HWND; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + D2D1_ALPHA_MODE_PREMULTIPLIED)); + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); + + return reinterpret_cast(newSurf); + +FAIL_HWND: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); +} + + + +cairo_surface_t * +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, + int width, + int height) +{ + if (width == 0 || height == 0) { + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)); + } + + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + DXGI_FORMAT dxgiformat = DXGI_FORMAT_B8G8R8A8_UNORM; + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + if (format == CAIRO_FORMAT_ARGB32) { + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR_ALPHA); + } else if (format == CAIRO_FORMAT_RGB24) { + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR); + alpha = D2D1_ALPHA_MODE_IGNORE; + } else { + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA); + dxgiformat = DXGI_FORMAT_A8_UNORM; + } + + + newSurf->format = format; + + D2D1_SIZE_U sizePixels; + HRESULT hr; + + sizePixels.width = width; + sizePixels.height = height; + + CD3D10_TEXTURE2D_DESC desc( + dxgiformat, + sizePixels.width, + sizePixels.height + ); + desc.MipLevels = 1; + desc.Usage = D3D10_USAGE_DEFAULT; + desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + + /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */ + if (desc.Format != DXGI_FORMAT_A8_UNORM) + desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; + + RefPtr texture; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + + hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); + + if (FAILED(hr)) { + goto FAIL_CREATE; + } + + newSurf->surface = texture; + + /** Create the DXGI surface. */ + hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); + if (FAILED(hr)) { + goto FAIL_CREATE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) + props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (dxgiformat != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + _cairo_d2d_surface_init(newSurf, d2d_device, format); + + return reinterpret_cast(newSurf); + +FAIL_CREATE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); +} + +cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content) +{ + if (!device) { + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); + } + + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + HRESULT hr; + RefPtr texture; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + DXGI_FORMAT format; + DXGI_SURFACE_DESC desc; + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + + hr = d2d_device->mD3D10Device->OpenSharedResource(handle, + __uuidof(ID3D10Resource), + (void**)&newSurf->surface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + hr = newSurf->surface->QueryInterface(&dxgiSurface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + dxgiSurface->GetDesc(&desc); + format = desc.Format; + + if (format == DXGI_FORMAT_B8G8R8A8_UNORM) { + if (content == CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); + if (content == CAIRO_CONTENT_COLOR) { + alpha = D2D1_ALPHA_MODE_IGNORE; + } + } else if (format == DXGI_FORMAT_A8_UNORM) { + if (content != CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA); + } else { + status = CAIRO_STATUS_INVALID_FORMAT; + // We don't know how to support this format! + goto FAIL_CREATEHANDLE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (format != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); + + return &newSurf->base; + +FAIL_CREATEHANDLE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(status)); +} + +cairo_surface_t * +cairo_d2d_surface_create_for_texture(cairo_device_t *device, + ID3D10Texture2D *texture, + cairo_content_t content) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + if (content == CAIRO_CONTENT_COLOR) { + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR); + alpha = D2D1_ALPHA_MODE_IGNORE; + } else { + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content); + } + + D2D1_SIZE_U sizePixels; + HRESULT hr; + + D3D10_TEXTURE2D_DESC desc; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + + texture->GetDesc(&desc); + + sizePixels.width = desc.Width; + sizePixels.height = desc.Height; + + newSurf->surface = texture; + + /** Create the DXGI surface. */ + hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface); + if (FAILED(hr)) { + goto FAIL_CREATE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) + props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (content != CAIRO_CONTENT_ALPHA) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content)); + + return reinterpret_cast(newSurf); + +FAIL_CREATE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); +} + +ID3D10Texture2D* +cairo_d2d_surface_get_texture(cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_D2D) { + return NULL; + } + + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + + RefPtr texture; + d2dsurf->surface->QueryInterface(&texture); + + return texture; +} + +void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) +{ + if (surface->type != CAIRO_SURFACE_TYPE_D2D) { + return; + } + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + + /** For now, we invalidate our storing texture with this operation. */ + D2D1_POINT_2U point; + D3D10_BOX rect; + rect.front = 0; + rect.back = 1; + + RefPtr dxgiSurface; + d2dsurf->surface->QueryInterface(&dxgiSurface); + DXGI_SURFACE_DESC desc; + + dxgiSurface->GetDesc(&desc); + + /** + * It's important we constrain the size of the clip region to the area of + * the surface. If we don't we might get a box that goes outside the + * surface, and CopySubresourceRegion will become very angry with us. + * It will cause a device failure and subsequent drawing will break. + */ + clip->x = MAX(clip->x, 0); + clip->y = MAX(clip->y, 0); + clip->width = MIN(clip->width, desc.Width - clip->x); + clip->height = MIN(clip->height, desc.Height - clip->y); + + if (x < 0) { + point.x = (UINT32)clip->x; + rect.left = (UINT)(clip->x - x); + rect.right = (UINT)(clip->x + clip->width); + } else { + point.x = (UINT32)(clip->x + x); + rect.left = (UINT)clip->x; + rect.right = (UINT32)(clip->x + clip->width - x); + } + if (y < 0) { + point.y = (UINT32)clip->y; + rect.top = (UINT)(clip->y - y); + rect.bottom = (UINT)(clip->y + clip->height); + } else { + point.y = (UINT32)(clip->y + y); + rect.top = (UINT)clip->y; + rect.bottom = (UINT)(clip->y + clip->height - y); + } + ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf); + + d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface, + 0, + point.x, + point.y, + 0, + texture, + 0, + &rect); + +} + +HDC +cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) +{ + if (surface->type != CAIRO_SURFACE_TYPE_D2D) { + return NULL; + } + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + + /* We'll pop the clip here manually so that we'll stay in drawing state if we + * already are, we need to ensure d2dsurf->isDrawing manually then though + */ + + /* Clips aren't allowed as per MSDN docs */ + reset_clip(d2dsurf); + + if (!d2dsurf->isDrawing) { + /* GetDC must be called between BeginDraw/EndDraw */ + d2dsurf->rt->BeginDraw(); + d2dsurf->isDrawing = true; + } + + RefPtr interopRT; + + /* This QueryInterface call will always succeed even if the + * the render target doesn't support the ID2D1GdiInteropRenderTarget + * interface */ + d2dsurf->rt->QueryInterface(&interopRT); + + HDC dc; + HRESULT rv; + + rv = interopRT->GetDC(retain_contents ? D2D1_DC_INITIALIZE_MODE_COPY : + D2D1_DC_INITIALIZE_MODE_CLEAR, &dc); + + if (FAILED(rv)) { + return NULL; + } + + return dc; +} + +void +cairo_d2d_release_dc(cairo_surface_t *surface, const cairo_rectangle_int_t *updated_rect) +{ + if (surface->type != CAIRO_SURFACE_TYPE_D2D) { + return; + } + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + + RefPtr interopRT; + + d2dsurf->rt->QueryInterface(&interopRT); + + if (!updated_rect) { + interopRT->ReleaseDC(NULL); + return; + } + + RECT r; + r.left = updated_rect->x; + r.top = updated_rect->y; + r.right = r.left + updated_rect->width; + r.bottom = r.top + updated_rect->height; + + interopRT->ReleaseDC(&r); +} + +int +cairo_d2d_get_image_surface_cache_usage() +{ + return _cairo_atomic_int_get(&cache_usage); +} + +int +cairo_d2d_get_surface_vram_usage(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + return d2d_device->mVRAMUsage; +} + +int +cairo_d2d_surface_get_width(cairo_surface_t *surface) +{ + if (surface->backend != &cairo_d2d_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + return size.width; +} + +int +cairo_d2d_surface_get_height(cairo_surface_t *surface) +{ + if (surface->backend != &cairo_d2d_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + cairo_d2d_surface_t *d2dsurf = reinterpret_cast(surface); + D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize(); + return size.height; +} diff --git a/libs/cairo/src/cairo-debug.c b/libs/cairo/src/cairo-debug.c new file mode 100644 index 000000000..49bf31594 --- /dev/null +++ b/libs/cairo/src/cairo-debug.c @@ -0,0 +1,221 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +/** + * cairo_debug_reset_static_data: + * + * Resets all static data within cairo to its original state, + * (ie. identical to the state at the time of program invocation). For + * example, all caches within cairo will be flushed empty. + * + * This function is intended to be useful when using memory-checking + * tools such as valgrind. When valgrind's memcheck analyzes a + * cairo-using program without a call to cairo_debug_reset_static_data(), + * it will report all data reachable via cairo's static objects as + * "still reachable". Calling cairo_debug_reset_static_data() just prior + * to program termination will make it easier to get squeaky clean + * reports from valgrind. + * + * WARNING: It is only safe to call this function when there are no + * active cairo objects remaining, (ie. the appropriate destroy + * functions have been called as necessary). If there are active cairo + * objects, this call is likely to cause a crash, (eg. an assertion + * failure due to a hash table being destroyed when non-empty). + **/ +void +cairo_debug_reset_static_data (void) +{ + CAIRO_MUTEX_INITIALIZE (); + + _cairo_scaled_font_map_destroy (); + + _cairo_toy_font_face_reset_static_data (); + +#if CAIRO_HAS_FT_FONT + _cairo_ft_font_reset_static_data (); +#endif + +#if CAIRO_HAS_WIN32_FONT + _cairo_win32_font_reset_static_data (); +#endif + + _cairo_intern_string_reset_static_data (); + + _cairo_scaled_font_reset_static_data (); + + _cairo_pattern_reset_static_data (); + + _cairo_clip_reset_static_data (); + + _cairo_image_reset_static_data (); + +#if CAIRO_HAS_DRM_SURFACE + _cairo_drm_device_reset_static_data (); +#endif + + _cairo_reset_static_data (); + + CAIRO_MUTEX_FINALIZE (); +} + +#if HAVE_VALGRIND +void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) +{ + const cairo_image_surface_t *image = (cairo_image_surface_t *) surface; + const uint8_t *bits; + int row, width; + + if (surface == NULL) + return; + + if (! RUNNING_ON_VALGRIND) + return; + + bits = image->data; + switch (image->format) { + case CAIRO_FORMAT_A1: + width = (image->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + width = image->width; + break; + case CAIRO_FORMAT_RGB16_565: + width = image->width*2; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + width = image->width*4; + break; + case CAIRO_FORMAT_INVALID: + default: + /* XXX compute width from pixman bpp */ + return; + } + + for (row = 0; row < image->height; row++) { + VALGRIND_CHECK_MEM_IS_DEFINED (bits, width); + /* and then silence any future valgrind warnings */ + VALGRIND_MAKE_MEM_DEFINED (bits, width); + bits += image->stride; + } +} +#endif + + +#if 0 +void +_cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn) +{ + char *fmt; + if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) + fmt = "P6"; + else if (isurf->format == CAIRO_FORMAT_A8) + fmt = "P5"; + else + return; + + FILE *fp = fopen(fn, "wb"); + if (!fp) + return; + + fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height); + for (int j = 0; j < isurf->height; j++) { + unsigned char *row = isurf->data + isurf->stride * j; + for (int i = 0; i < isurf->width; i++) { + if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) { + unsigned char r = *row++; + unsigned char g = *row++; + unsigned char b = *row++; + *row++; + putc(r, fp); + putc(g, fp); + putc(b, fp); + } else { + unsigned char a = *row++; + putc(a, fp); + } + } + } + + fclose (fp); + + fprintf (stderr, "Wrote %s\n", fn); +} +#endif + +static cairo_status_t +_print_move_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_line_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + fprintf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_close (void *closure) +{ + fprintf (closure, " h"); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) +{ + cairo_status_t status; + + printf ("path: extents=(%f, %f), (%f, %f)\n", + _cairo_fixed_to_double (path->extents.p1.x), + _cairo_fixed_to_double (path->extents.p1.y), + _cairo_fixed_to_double (path->extents.p2.x), + _cairo_fixed_to_double (path->extents.p2.y)); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _print_move_to, + _print_line_to, + _print_curve_to, + _print_close, + stream); + assert (status == CAIRO_STATUS_SUCCESS); + + printf ("\n"); +} diff --git a/libs/cairo/src/cairo-deflate-stream.c b/libs/cairo/src/cairo-deflate-stream.c new file mode 100644 index 000000000..b6d10b12a --- /dev/null +++ b/libs/cairo/src/cairo-deflate-stream.c @@ -0,0 +1,119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" +#include + +#define BUFFER_SIZE 16384 + +typedef struct _cairo_deflate_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + z_stream zlib_stream; + unsigned char input_buf[BUFFER_SIZE]; + unsigned char output_buf[BUFFER_SIZE]; +} cairo_deflate_stream_t; + +static void +cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush) +{ + int ret; + cairo_bool_t finished; + + do { + ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH); + if (flush || stream->zlib_stream.avail_out == 0) + { + _cairo_output_stream_write (stream->output, + stream->output_buf, + BUFFER_SIZE - stream->zlib_stream.avail_out); + stream->zlib_stream.next_out = stream->output_buf; + stream->zlib_stream.avail_out = BUFFER_SIZE; + } + + finished = TRUE; + if (stream->zlib_stream.avail_in != 0) + finished = FALSE; + if (flush && ret != Z_STREAM_END) + finished = FALSE; + + } while (!finished); + + stream->zlib_stream.next_in = stream->input_buf; +} + +static cairo_status_t +_cairo_deflate_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; + unsigned int count; + const unsigned char *p = data; + + while (length) { + count = length; + if (count > BUFFER_SIZE - stream->zlib_stream.avail_in) + count = BUFFER_SIZE - stream->zlib_stream.avail_in; + memcpy (stream->input_buf + stream->zlib_stream.avail_in, p, count); + p += count; + stream->zlib_stream.avail_in += count; + length -= count; + + if (stream->zlib_stream.avail_in == BUFFER_SIZE) + cairo_deflate_stream_deflate (stream, FALSE); + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_deflate_stream_close (cairo_output_stream_t *base) +{ + cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; + + cairo_deflate_stream_deflate (stream, TRUE); + deflateEnd (&stream->zlib_stream); + + return _cairo_output_stream_get_status (stream->output); +} + +cairo_output_stream_t * +_cairo_deflate_stream_create (cairo_output_stream_t *output) +{ + cairo_deflate_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + stream = malloc (sizeof (cairo_deflate_stream_t)); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + _cairo_deflate_stream_write, + NULL, + _cairo_deflate_stream_close); + stream->output = output; + + stream->zlib_stream.zalloc = Z_NULL; + stream->zlib_stream.zfree = Z_NULL; + stream->zlib_stream.opaque = Z_NULL; + + if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { + free (stream); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + stream->zlib_stream.next_in = stream->input_buf; + stream->zlib_stream.avail_in = 0; + stream->zlib_stream.next_out = stream->output_buf; + stream->zlib_stream.avail_out = BUFFER_SIZE; + + return &stream->base; +} diff --git a/libs/cairo/src/cairo-deprecated.h b/libs/cairo/src/cairo-deprecated.h new file mode 100644 index 000000000..04b5d264d --- /dev/null +++ b/libs/cairo/src/cairo-deprecated.h @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-device-private.h b/libs/cairo/src/cairo-device-private.h new file mode 100644 index 000000000..371f66714 --- /dev/null +++ b/libs/cairo/src/cairo-device-private.h @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CAIRO_DEVICE_PRIVATE_H_ +#define _CAIRO_DEVICE_PRIVATE_H_ + +#include "cairo-compiler-private.h" +#include "cairo-mutex-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-types-private.h" + +struct _cairo_device { + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + + const cairo_device_backend_t *backend; + + cairo_recursive_mutex_t mutex; + unsigned mutex_depth; + + cairo_bool_t finished; +}; + +struct _cairo_device_backend { + cairo_device_type_t type; + + void (*lock) (void *device); + void (*unlock) (void *device); + + cairo_warn cairo_status_t (*flush) (void *device); + void (*finish) (void *device); + void (*destroy) (void *device); +}; + +cairo_private cairo_device_t * +_cairo_device_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_device_init (cairo_device_t *device, + const cairo_device_backend_t *backend); + +cairo_private cairo_status_t +_cairo_device_set_error (cairo_device_t *device, + cairo_status_t error); + +slim_hidden_proto_no_warn (cairo_device_reference); +slim_hidden_proto (cairo_device_acquire); +slim_hidden_proto (cairo_device_release); +slim_hidden_proto (cairo_device_flush); +slim_hidden_proto (cairo_device_finish); +slim_hidden_proto (cairo_device_destroy); + +#endif /* _CAIRO_DEVICE_PRIVATE_H_ */ diff --git a/libs/cairo/src/cairo-device.c b/libs/cairo/src/cairo-device.c new file mode 100644 index 000000000..d24dba94c --- /dev/null +++ b/libs/cairo/src/cairo-device.c @@ -0,0 +1,502 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-device + * @Title: cairo_device_t + * @Short_Description: interface to underlying rendering system + * @See_Also: #cairo_surface_t + * + * Devices are the abstraction Cairo employs for the rendering system + * used by a #cairo_surface_t. You can get the device of a surface using + * cairo_surface_get_device(). + * + * Devices are created using custom functions specific to the rendering + * system you want to use. See the documentation for the surface types + * for those functions. + * + * An important function that devices fulfill is sharing access to the + * rendering system between Cairo and your application. If you want to + * access a device directly that you used to draw to with Cairo, you must + * first call cairo_device_flush() to ensure that Cairo finishes all + * operations on the device and resets it to a clean state. + * + * Cairo also provides the functions cairo_device_acquire() and + * cairo_device_release() to synchronize access to the rendering system + * in a multithreaded environment. This is done internally, but can also + * be used by applications. + * + * Putting this all together, a function that works with devices should + * look something like this: + * + * void + * my_device_modifying_function (cairo_device_t *device) + * { + * cairo_status_t status; + * + * // Ensure the device is properly reset + * cairo_device_flush (device); + * // Try to acquire the device + * status = cairo_device_acquire (device); + * if (status != CAIRO_STATUS_SUCCESS) { + * printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status)); + * return; + * } + * + * // Do the custom operations on the device here. + * // But do not call any Cairo functions that might acquire devices. + * + * // Release the device when done. + * cairo_device_release (device); + * } + * + * + * Please refer to the documentation of each backend for + * additional usage requirements, guarantees provided, and + * interactions with existing surface API of the device functions for + * surfaces of that type. + * + */ + +static const cairo_device_t _nil_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_NO_MEMORY, +}; + +static const cairo_device_t _mismatch_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_DEVICE_TYPE_MISMATCH, +}; + +static const cairo_device_t _invalid_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_DEVICE_ERROR, +}; + +cairo_device_t * +_cairo_device_create_in_error (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_device_t *) &_nil_device; + case CAIRO_STATUS_DEVICE_ERROR: + return (cairo_device_t *) &_invalid_device; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + return (cairo_device_t *) &_mismatch_device; + + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_STATUS: + case CAIRO_STATUS_INVALID_FORMAT: + case CAIRO_STATUS_INVALID_VISUAL: + case CAIRO_STATUS_READ_ERROR: + case CAIRO_STATUS_WRITE_ERROR: + case CAIRO_STATUS_FILE_NOT_FOUND: + case CAIRO_STATUS_TEMP_FILE_ERROR: + case CAIRO_STATUS_INVALID_STRIDE: + case CAIRO_STATUS_INVALID_SIZE: + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + case CAIRO_STATUS_INVALID_SLANT: + case CAIRO_STATUS_INVALID_WEIGHT: + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + case CAIRO_STATUS_INVALID_CONTENT: + default: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_device_t *) &_nil_device; + } +} + +void +_cairo_device_init (cairo_device_t *device, + const cairo_device_backend_t *backend) +{ + CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1); + device->status = CAIRO_STATUS_SUCCESS; + + device->backend = backend; + + CAIRO_RECURSIVE_MUTEX_INIT (device->mutex); + device->mutex_depth = 0; + + device->finished = FALSE; + + _cairo_user_data_array_init (&device->user_data); +} + +/** + * cairo_device_reference: + * @device: a #cairo_device_t + * + * Increases the reference count on @device by one. This prevents + * @device from being destroyed until a matching call to + * cairo_device_destroy() is made. + * + * The number of references to a #cairo_device_t can be get using + * cairo_device_get_reference_count(). + * + * Return value: the referenced #cairo_device_t. + * + * Since: 1.10 + **/ +cairo_device_t * +cairo_device_reference (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return device; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + _cairo_reference_count_inc (&device->ref_count); + + return device; +} +slim_hidden_def (cairo_device_reference); + +/** + * cairo_device_status: + * @device: a #cairo_device_t + * + * Checks whether an error has previously occurred for this + * device. + * + * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if + * the device is in an error state. + * + * Since: 1.10 + **/ +cairo_status_t +cairo_device_status (cairo_device_t *device) +{ + if (device == NULL) + return CAIRO_STATUS_NULL_POINTER; + + return device->status; +} + +/** + * cairo_device_flush: + * @device: a #cairo_device_t + * + * Finish any pending operations for the device and also restore any + * temporary modifications cairo has made to the device's state. + * This function must be called before switching from using the + * device with Cairo to operating on it directly with native APIs. + * If the device doesn't support direct access, then this function + * does nothing. + * + * This function may acquire devices. + * + * Since: 1.10 + **/ +void +cairo_device_flush (cairo_device_t *device) +{ + cairo_status_t status; + + if (device == NULL || device->status) + return; + + if (device->backend->flush != NULL) { + status = device->backend->flush (device); + if (unlikely (status)) + status = _cairo_device_set_error (device, status); + } +} +slim_hidden_def (cairo_device_flush); + +/** + * cairo_device_finish: + * @device: the #cairo_device_t to finish + * + * This function finishes the device and drops all references to + * external resources. All surfaces, fonts and other objects created + * for this @device will be finished, too. + * Further operations on the @device will not affect the @device but + * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error. + * + * When the last call to cairo_device_destroy() decreases the + * reference count to zero, cairo will call cairo_device_finish() if + * it hasn't been called already, before freeing the resources + * associated with the device. + * + * This function may acquire devices. + * + * Since: 1.10 + **/ +void +cairo_device_finish (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return; + } + + if (device->finished) + return; + + cairo_device_flush (device); + + device->finished = TRUE; + + if (device->backend->finish != NULL) + device->backend->finish (device); +} +slim_hidden_def (cairo_device_finish); + +/** + * cairo_device_destroy: + * @device: a #cairo_device_t + * + * Decreases the reference count on @device by one. If the result is + * zero, then @device and all associated resources are freed. See + * cairo_device_reference(). + * + * This function may acquire devices if the last reference was dropped. + * + * Since: 1.10 + **/ +void +cairo_device_destroy (cairo_device_t *device) +{ + cairo_user_data_array_t user_data; + + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + if (! _cairo_reference_count_dec_and_test (&device->ref_count)) + return; + + cairo_device_finish (device); + + assert (device->mutex_depth == 0); + CAIRO_MUTEX_FINI (device->mutex); + + user_data = device->user_data; + + device->backend->destroy (device); + + _cairo_user_data_array_fini (&user_data); + +} +slim_hidden_def (cairo_device_destroy); + +/** + * cairo_device_get_type: + * @device: a #cairo_device_t + * + * This function returns the type of the device. See #cairo_device_type_t + * for available types. + * + * Return value: The type of @device. + * + * Since: 1.10 + **/ +cairo_device_type_t +cairo_device_get_type (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return (cairo_device_type_t) -1; + } + + return device->backend->type; +} + +/** + * cairo_device_acquire: + * @device: a #cairo_device_t + * + * Acquires the @device for the current thread. This function will block + * until no other thread has acquired the device. + * + * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the + * device. From now on your thread owns the device and no other thread will be + * able to acquire it until a matching call to cairo_device_release(). It is + * allowed to recursively acquire the device multiple times from the same + * thread. + * + * You must never acquire two different devices at the same time + * unless this is explicitly allowed. Otherwise the possibility of deadlocks + * exist. + * + * As various Cairo functions can acquire devices when called, these functions + * may also cause deadlocks when you call them with an acquired device. So you + * must not have a device acquired when calling them. These functions are + * marked in the documentation. + * + * + * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if + * the device is in an error state and could not be + * acquired. After a successful call to cairo_device_acquire(), + * a matching call to cairo_device_release() is required. + * + * Since: 1.10 + **/ +cairo_status_t +cairo_device_acquire (cairo_device_t *device) +{ + if (device == NULL) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (device->status)) + return device->status; + + if (unlikely (device->finished)) + return _cairo_device_set_error (device, CAIRO_STATUS_SURFACE_FINISHED); /* XXX */ + + CAIRO_MUTEX_LOCK (device->mutex); + if (device->mutex_depth++ == 0) { + if (device->backend->lock != NULL) + device->backend->lock (device); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_device_acquire); + +/** + * cairo_device_release: + * @device: a #cairo_device_t + * + * Releases a @device previously acquired using cairo_device_acquire(). See + * that function for details. + * + * Since: 1.10 + **/ +void +cairo_device_release (cairo_device_t *device) +{ + if (device == NULL) + return; + + assert (device->mutex_depth > 0); + + if (--device->mutex_depth == 0) { + if (device->backend->unlock != NULL) + device->backend->unlock (device); + } + + CAIRO_MUTEX_UNLOCK (device->mutex); +} +slim_hidden_def (cairo_device_release); + +cairo_status_t +_cairo_device_set_error (cairo_device_t *device, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&device->status, status); + + return _cairo_error (status); +} + +/** + * cairo_device_get_reference_count: + * @device: a #cairo_device_t + * + * Returns the current reference count of @device. + * + * Return value: the current reference count of @device. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.10 + **/ +unsigned int +cairo_device_get_reference_count (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count); +} + +/** + * cairo_device_get_user_data: + * @device: a #cairo_device_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @device using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.10 + **/ +void * +cairo_device_get_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&device->user_data, + key); +} + +/** + * cairo_device_set_user_data: + * @device: a #cairo_device_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_device_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @device. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.10 + **/ +cairo_status_t +cairo_device_set_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + return device->status; + + return _cairo_user_data_array_set_data (&device->user_data, + key, user_data, destroy); +} diff --git a/libs/cairo/src/cairo-directfb-surface.c b/libs/cairo/src/cairo-directfb-surface.c new file mode 100644 index 000000000..6387fee94 --- /dev/null +++ b/libs/cairo/src/cairo-directfb-surface.c @@ -0,0 +1,1931 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-directfb.h" + +#include "cairo-clip-private.h" +#include "cairo-error-private.h" + +#include + +#include +#include +#include +#include +#include + +/* + * Rectangle works fine. + * Bugs 361377, 359553, 359243 in Gnome BTS are caused + * by GDK/DirectFB, not by Cairo/DirectFB. + */ +#define DFB_RECTANGLES 1 + +/* + * Composite works fine. + */ +#define DFB_COMPOSITE 1 + +/* + * CompositeTrapezoids works (without antialiasing). + */ +#define DFB_COMPOSITE_TRAPEZOIDS 1 + +/* + * ShowGlyphs works fine. + */ +#define DFB_SHOW_GLYPHS 1 + +#define PIXMAN_invalid (pixman_format_code_t) 0 + + +D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire"); +D_DEBUG_DOMAIN (CairoDFB_Clip, "CairoDFB/Clip", "Cairo DirectFB Clipping"); +D_DEBUG_DOMAIN (CairoDFB_Font, "CairoDFB/Font", "Cairo DirectFB Font Rendering"); +D_DEBUG_DOMAIN (CairoDFB_Render, "CairoDFB/Render", "Cairo DirectFB Rendering"); +D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface"); + +/*****************************************************************************/ + +typedef struct _cairo_directfb_surface { + cairo_surface_t base; + + pixman_format_code_t pixman_format; + cairo_bool_t supported_destination; + + IDirectFB *dfb; + IDirectFBSurface *dfbsurface; + IDirectFBSurface *tmpsurface; + pixman_format_code_t tmpformat; + + int width; + int height; + + unsigned local : 1; + unsigned blit_premultiplied : 1; +} cairo_directfb_surface_t; + + +typedef struct _cairo_directfb_font_cache { + IDirectFB *dfb; + IDirectFBSurface *dfbsurface; + + int width; + int height; + + /* coordinates within the surface + * of the last loaded glyph */ + int x; + int y; +} cairo_directfb_font_cache_t; + +static cairo_surface_backend_t _cairo_directfb_surface_backend; + +/*****************************************************************************/ + +static int _directfb_argb_font = 0; + +/*****************************************************************************/ + +#define RUN_CLIPPED(surface, clip_region, clip, func) {\ + if ((clip_region) != NULL) {\ + int n_clips = cairo_region_num_rectangles (clip_region), n; \ + for (n = 0; n < n_clips; n++) {\ + if (clip) {\ + DFBRegion reg, *cli = (clip); \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ + if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ + reg.x1 > cli->x2 || reg.y1 > cli->y2)\ + continue;\ + if (reg.x1 < cli->x1)\ + reg.x1 = cli->x1;\ + if (reg.y1 < cli->y1)\ + reg.y1 = cli->y1;\ + if (reg.x2 > cli->x2)\ + reg.x2 = cli->x2;\ + if (reg.y2 > cli->y2)\ + reg.y2 = cli->y2;\ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ + } else {\ + DFBRegion reg; \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ + }\ + func;\ + }\ + } else {\ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\ + func;\ + }\ +} + +#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \ + double _x = (x); \ + double _y = (y); \ + (ret_x) = (_x * (m).xx + (m).x0); \ + (ret_y) = (_y * (m).yy + (m).y0); \ +} while (0) + +#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \ + double _x = (x); \ + double _y = (y); \ + (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \ + (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \ +} while (0) + +/* XXX: A1 has a different bits ordering in cairo. + * Probably we should drop it. + */ + +static cairo_content_t +_directfb_format_to_content (DFBSurfacePixelFormat format) +{ + if (DFB_PIXELFORMAT_HAS_ALPHA (format)) { + if (DFB_COLOR_BITS_PER_PIXEL (format)) + return CAIRO_CONTENT_COLOR_ALPHA; + + return CAIRO_CONTENT_ALPHA; + } + + return CAIRO_CONTENT_COLOR; +} + +static inline DFBSurfacePixelFormat +_cairo_to_directfb_format (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_RGB24: + return DSPF_RGB32; + case CAIRO_FORMAT_ARGB32: + return DSPF_ARGB; + case CAIRO_FORMAT_A8: + return DSPF_A8; + case CAIRO_FORMAT_A1: + return DSPF_A1; + default: + break; + } + + return -1; +} + +static inline pixman_format_code_t +_directfb_to_pixman_format (DFBSurfacePixelFormat format) +{ + switch (format) { + case DSPF_UNKNOWN: return PIXMAN_invalid; + case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; + case DSPF_RGB16: return PIXMAN_r5g6b5; + case DSPF_RGB24: return PIXMAN_r8g8b8; + case DSPF_RGB32: return PIXMAN_x8r8g8b8; + case DSPF_ARGB: return PIXMAN_a8r8g8b8; + case DSPF_A8: return PIXMAN_a8; + case DSPF_YUY2: return PIXMAN_yuy2; + case DSPF_RGB332: return PIXMAN_r3g3b2; + case DSPF_UYVY: return PIXMAN_invalid; + case DSPF_I420: return PIXMAN_invalid; + case DSPF_YV12: return PIXMAN_yv12; + case DSPF_LUT8: return PIXMAN_invalid; + case DSPF_ALUT44: return PIXMAN_invalid; + case DSPF_AiRGB: return PIXMAN_invalid; + case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */ + case DSPF_NV12: return PIXMAN_invalid; + case DSPF_NV16: return PIXMAN_invalid; + case DSPF_ARGB2554: return PIXMAN_invalid; + case DSPF_ARGB4444: return PIXMAN_a4r4g4b4; + case DSPF_NV21: return PIXMAN_invalid; + case DSPF_AYUV: return PIXMAN_invalid; + case DSPF_A4: return PIXMAN_a4; + case DSPF_ARGB1666: return PIXMAN_invalid; + case DSPF_ARGB6666: return PIXMAN_invalid; + case DSPF_RGB18: return PIXMAN_invalid; + case DSPF_LUT2: return PIXMAN_invalid; + case DSPF_RGB444: return PIXMAN_x4r4g4b4; + case DSPF_RGB555: return PIXMAN_x1r5g5b5; +#if DFB_NUM_PIXELFORMATS >= 29 + case DSPF_BGR555: return PIXMAN_x1b5g5r5; +#endif + } + return PIXMAN_invalid; +} + +static inline DFBSurfacePixelFormat +_directfb_from_pixman_format (pixman_format_code_t format) +{ + switch ((int) format) { + case PIXMAN_a1r5g5b5: return DSPF_ARGB1555; + case PIXMAN_r5g6b5: return DSPF_RGB16; + case PIXMAN_r8g8b8: return DSPF_RGB24; + case PIXMAN_x8r8g8b8: return DSPF_RGB32; + case PIXMAN_a8r8g8b8: return DSPF_ARGB; + case PIXMAN_a8: return DSPF_A8; + case PIXMAN_yuy2: return DSPF_YUY2; + case PIXMAN_r3g3b2: return DSPF_RGB332; + case PIXMAN_yv12: return DSPF_YV12; + case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */ + case PIXMAN_a4r4g4b4: return DSPF_ARGB4444; + case PIXMAN_a4: return DSPF_A4; + case PIXMAN_x4r4g4b4: return DSPF_RGB444; + case PIXMAN_x1r5g5b5: return DSPF_RGB555; +#if DFB_NUM_PIXELFORMATS >= 29 + case PIXMAN_x1b5g5r5: return DSPF_BGR555; +#endif + default: return 0; + } +} + +static cairo_bool_t +_directfb_get_operator (cairo_operator_t operator, + DFBSurfaceBlendFunction *ret_srcblend, + DFBSurfaceBlendFunction *ret_dstblend) +{ + DFBSurfaceBlendFunction srcblend = DSBF_ONE; + DFBSurfaceBlendFunction dstblend = DSBF_ZERO; + + switch (operator) { + case CAIRO_OPERATOR_CLEAR: + srcblend = DSBF_ZERO; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_SOURCE: + srcblend = DSBF_ONE; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_OVER: + srcblend = DSBF_ONE; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_IN: + srcblend = DSBF_DESTALPHA; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_OUT: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_ATOP: + srcblend = DSBF_DESTALPHA; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_DEST: + srcblend = DSBF_ZERO; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_DEST_OVER: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_DEST_IN: + srcblend = DSBF_ZERO; + dstblend = DSBF_SRCALPHA; + break; + case CAIRO_OPERATOR_DEST_OUT: + srcblend = DSBF_ZERO; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_DEST_ATOP: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_SRCALPHA; + break; + case CAIRO_OPERATOR_XOR: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_ADD: + srcblend = DSBF_ONE; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_SATURATE: + /* XXX This does not work. */ +#if 0 + srcblend = DSBF_SRCALPHASAT; + dstblend = DSBF_ONE; + break; +#endif + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + default: + return FALSE; + } + + *ret_srcblend = srcblend; + *ret_dstblend = dstblend; + + return TRUE; +} + +static cairo_status_t +_directfb_buffer_surface_create (IDirectFB *dfb, + DFBSurfacePixelFormat format, + int width, + int height, + IDirectFBSurface **out) +{ + IDirectFBSurface *buffer; + DFBSurfaceDescription dsc; + DFBResult ret; + + dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT; + dsc.caps = DSCAPS_PREMULTIPLIED; + dsc.width = width; + dsc.height = height; + dsc.pixelformat = format; + + ret = dfb->CreateSurface (dfb, &dsc, &buffer); + if (ret) { + DirectFBError ("IDirectFB::CreateSurface()", ret); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *out = buffer; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_directfb_acquire_surface (cairo_directfb_surface_t *surface, + cairo_rectangle_int_t *intrest_rec, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra, + DFBSurfaceLockFlags lock_flags) +{ + IDirectFBSurface *buffer = NULL; + DFBRectangle source_rect; + cairo_surface_t *image; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + cairo_status_t status; + void *data; + int pitch; + + if (surface->pixman_format == PIXMAN_invalid) { + if (intrest_rec != NULL) { + source_rect.x = intrest_rec->x; + source_rect.y = intrest_rec->y; + source_rect.w = intrest_rec->width; + source_rect.h = intrest_rec->height; + } else { + source_rect.x = 0; + source_rect.y = 0; + surface->dfbsurface->GetSize (surface->dfbsurface, + &source_rect.w, &source_rect.h); + } + + if (surface->tmpsurface != NULL) { + int w, h; + + surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h); + if (w < source_rect.w || h < source_rect.h) { + surface->tmpsurface->Release (surface->tmpsurface); + surface->tmpsurface = NULL; + surface->tmpformat = PIXMAN_invalid; + } + } + + if (surface->tmpsurface == NULL) { + DFBSurfacePixelFormat format; + + D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface); + + format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content)); + status = + _directfb_buffer_surface_create (surface->dfb, format, + source_rect.w, source_rect.h, + &surface->tmpsurface); + if (unlikely (status)) + goto ERROR; + + surface->tmpformat = _directfb_to_pixman_format (format); + } + buffer = surface->tmpsurface; + pixman_format = surface->tmpformat; + + +/* surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps); + DFBSurfaceCapabilities caps; + if (caps & DSCAPS_FLIPPING) { + DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y, + .x2 = source_rect.x + source_rect.w - 1, + .y2 = source_rect.y + source_rect.h - 1 }; + surface->dfbsurface->Flip (surface->dfbsurface, ®ion, DSFLIP_BLIT); + } */ + buffer->Blit (buffer, surface->dfbsurface, &source_rect, 0, 0); + } else { + /*might be a subsurface get the offset*/ + surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect); + pixman_format = surface->pixman_format; + buffer = surface->dfbsurface; + } + + if (buffer->Lock (buffer, lock_flags, &data, &pitch)) { + D_DEBUG_AT (CairoDFB_Acquire, "Couldn't lock surface!\n"); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto ERROR; + } + + pixman_image = pixman_image_create_bits (pixman_format, + source_rect.w, source_rect.h, + data, pitch); + if (pixman_image == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto ERROR; + } + + image = _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + status = image->status; + if (status) + goto ERROR; + + if (image_rect_out) { + image_rect_out->x = source_rect.x; + image_rect_out->y = source_rect.y; + image_rect_out->width = source_rect.w; + image_rect_out->height = source_rect.h; + } else { + /* lock for read */ + /* might be a subsurface */ + if (buffer == surface->dfbsurface) { + cairo_surface_set_device_offset (image, + source_rect.x, source_rect.y); + } + } + + *image_extra = buffer; + *image_out = (cairo_image_surface_t *) image; + return CAIRO_STATUS_SUCCESS; + +ERROR: + if (buffer) { + buffer->Unlock (buffer); + if (buffer != surface->dfbsurface) + buffer->Release (buffer); + } + return status; +} + +static cairo_surface_t * +_cairo_directfb_surface_create_internal (IDirectFB *dfb, + DFBSurfacePixelFormat format, + cairo_content_t content, + int width, + int height) +{ + cairo_directfb_surface_t *surface; + cairo_status_t status; + + surface = calloc (1, sizeof (cairo_directfb_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->dfb = dfb; + + if (width < 8 || height < 8) { + IDirectFBSurface *tmp; + DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height }; + + /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */ + status = _directfb_buffer_surface_create (dfb, format, + MAX (width, 8), MAX (height, 8), + &tmp); + if (status) { + free (surface); + return _cairo_surface_create_in_error (status); + } + + tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface); + tmp->Release (tmp); + } else { + status = _directfb_buffer_surface_create (dfb, format, + width, height, + &surface->dfbsurface); + if (status) { + free (surface); + return _cairo_surface_create_in_error (status); + } + } + + _cairo_surface_init (&surface->base, + &_cairo_directfb_surface_backend, + NULL, /* device */ + content); + surface->pixman_format = _directfb_to_pixman_format (format); + surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); + + surface->width = width; + surface->height = height; + surface->local = TRUE; + surface->blit_premultiplied = TRUE; + + return &surface->base; +} + +static cairo_surface_t * +_cairo_directfb_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_directfb_surface_t *other = abstract_src; + DFBSurfacePixelFormat format; + + D_DEBUG_AT (CairoDFB_Surface, + "%s( src=%p, content=0x%x, width=%d, height=%d).\n", + __FUNCTION__, other, content, width, height); + + width = (width <= 0) ? 1 : width; + height = (height<= 0) ? 1 : height; + + format = _cairo_to_directfb_format (_cairo_format_from_content (content)); + return _cairo_directfb_surface_create_internal (other->dfb, format, + content, width, height); +} + +static cairo_status_t +_cairo_directfb_surface_finish (void *data) +{ + cairo_directfb_surface_t *surface = (cairo_directfb_surface_t *)data; + + D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface); + + if (surface->tmpsurface) { + surface->tmpsurface->Release (surface->tmpsurface); + surface->tmpsurface = NULL; + } + + if (surface->dfbsurface) { + surface->dfbsurface->Release (surface->dfbsurface); + surface->dfbsurface = NULL; + } + + if (surface->dfb) + surface->dfb = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_directfb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_directfb_surface_t *surface = abstract_surface; + + D_DEBUG_AT (CairoDFB_Acquire, + "%s( surface=%p ).\n", __FUNCTION__, surface); + + return _directfb_acquire_surface (surface, NULL, image_out, + NULL, image_extra, DSLF_READ); +} + +static void +_cairo_directfb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + IDirectFBSurface *buffer = image_extra; + + D_DEBUG_AT (CairoDFB_Acquire, + "%s( release=%p ).\n", __FUNCTION__, abstract_surface); + + buffer->Unlock (buffer); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_directfb_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_directfb_surface_t *surface = abstract_surface; + + D_DEBUG_AT (CairoDFB_Acquire, + "%s( surface=%p (%dx%d), interest_rect={ %u %u %u %u } ).\n", + __FUNCTION__, surface, surface->width, surface->height, + interest_rect ? interest_rect->x : 0, + interest_rect ? interest_rect->y : 0, + interest_rect ? interest_rect->width : (unsigned) surface->width, + interest_rect ? interest_rect->height : (unsigned) surface->height); + + return _directfb_acquire_surface (surface, interest_rect, image_out, + image_rect_out, image_extra, + DSLF_READ | DSLF_WRITE); +} + +static void +_cairo_directfb_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_directfb_surface_t *surface = abstract_surface; + IDirectFBSurface *buffer = image_extra; + + D_DEBUG_AT (CairoDFB_Acquire, + "%s( surface=%p ).\n", __FUNCTION__, surface); + + buffer->Unlock (buffer); + + if (surface->dfbsurface != buffer) { + DFBRegion region = { + .x1 = interest_rect->x, + .y1 = interest_rect->y, + .x2 = interest_rect->x + interest_rect->width - 1, + .y2 = interest_rect->y + interest_rect->height - 1 + }; + surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX); + surface->dfbsurface->SetClip (surface->dfbsurface, ®ion); + surface->dfbsurface->Blit (surface->dfbsurface, + buffer, NULL, + image_rect->x, image_rect->y); + } + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_directfb_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_directfb_surface_t *surface = abstract_surface; + cairo_directfb_surface_t *clone; + + D_DEBUG_AT (CairoDFB_Surface, + "%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src); + + if (src->backend == surface->base.backend) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; + DFBSurfacePixelFormat format; + DFBResult ret; + pixman_image_t *pixman_image; + void *data; + int pitch; + + format = _directfb_from_pixman_format (image_src->pixman_format); + if (format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + clone = (cairo_directfb_surface_t *) + _cairo_directfb_surface_create_internal (surface->dfb, format, + image_src->base.content, + width, height); + if (unlikely (clone->base.status)) + return clone->base.status; + + ret = clone->dfbsurface->Lock (clone->dfbsurface, + DSLF_WRITE, (void *)&data, &pitch); + if (ret) { + DirectFBError ("IDirectFBSurface::Lock()", ret); + cairo_surface_destroy (&clone->base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image = pixman_image_create_bits (clone->pixman_format, + width, height, + data, pitch); + if (unlikely (pixman_image == NULL)) { + DirectFBError ("IDirectFBSurface::Lock()", ret); + cairo_surface_destroy (&clone->base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + image_src->pixman_image, + NULL, + pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, height); + + pixman_image_unref (pixman_image); + + clone->dfbsurface->Unlock (clone->dfbsurface); + + *clone_offset_x = src_x; + *clone_offset_y = src_y; + *clone_out = &clone->base; + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +#if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS +static cairo_int_status_t +_directfb_prepare_composite (cairo_directfb_surface_t *dst, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + cairo_operator_t op, + int *src_x, int *src_y, + int *mask_x, int *mask_y, + unsigned int width, + unsigned int height, + cairo_directfb_surface_t **ret_src, + cairo_surface_attributes_t *ret_src_attr) +{ + cairo_directfb_surface_t *src; + cairo_surface_attributes_t src_attr; + cairo_status_t status; + DFBSurfaceBlittingFlags flags; + DFBSurfaceBlendFunction sblend; + DFBSurfaceBlendFunction dblend; + const cairo_color_t *color; + + /* XXX Unbounded operators are not handled correctly */ + if (! _cairo_operator_bounded_by_source (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _directfb_get_operator (op, &sblend, &dblend)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (mask_pattern) { + return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + const cairo_pattern_t *tmp; + int tmp_x, tmp_y; + + if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID || + sblend == DSBF_INVDESTALPHA) /* Doesn't work correctly */ + return CAIRO_INT_STATUS_UNSUPPORTED; + + D_DEBUG_AT (CairoDFB_Render, "Replacing src pattern by mask pattern.\n"); + + tmp = src_pattern; + tmp_x = *src_x; tmp_y = *src_y; + + src_pattern = mask_pattern; + *src_x = *mask_x; *src_y = *mask_y; + + mask_pattern = tmp; + *mask_x = tmp_x; *mask_y = tmp_y; + + if (sblend == DSBF_ONE) { + sblend = DSBF_SRCALPHA; + /*dblend = DSBF_INVSRCALPHA;*/ + } + } + + color = &((cairo_solid_pattern_t *) mask_pattern)->color; + } else { + color = _cairo_stock_color (CAIRO_STOCK_WHITE); + } + + status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + *src_x, *src_y, width, height, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT, + (cairo_surface_t **) &src, + &src_attr); + if (status) + return status; + + if (src->base.backend != &_cairo_directfb_surface_backend || + src->dfb != dst->dfb) + { + _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) { + if (sblend == DSBF_SRCALPHA) + sblend = DSBF_ONE; + else if (sblend == DSBF_INVSRCALPHA) + sblend = DSBF_ZERO; + + if (dblend == DSBF_SRCALPHA) + dblend = DSBF_ONE; + else if (dblend == DSBF_INVSRCALPHA) + dblend = DSBF_ZERO; + } + + if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { + if (sblend == DSBF_DESTALPHA) + sblend = DSBF_ONE; + else if (sblend == DSBF_INVDESTALPHA) + sblend = DSBF_ZERO; + + if (dblend == DSBF_DESTALPHA) + dblend = DSBF_ONE; + else if (dblend == DSBF_INVDESTALPHA) + dblend = DSBF_ZERO; + } + + flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) + ? DSBLIT_NOFX : DSBLIT_BLEND_ALPHACHANNEL; + if (! CAIRO_COLOR_IS_OPAQUE (color)) + flags |= DSBLIT_BLEND_COLORALPHA; + if (! _cairo_color_equal (color, _cairo_stock_color (CAIRO_STOCK_WHITE))) + flags |= DSBLIT_COLORIZE; + + dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); + + if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) { + dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); + dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); + } + + if (flags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE)) { + if (dst->blit_premultiplied) { + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red_short >> 8, + color->green_short >> 8, + color->blue_short >> 8, + color->alpha_short >> 8); + } else { + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red * 0xff, + color->green * 0xff, + color->blue * 0xff, + color->alpha * 0xff); + } + } + + *ret_src = src; + *ret_src_attr = src_attr; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_directfb_finish_composite (cairo_directfb_surface_t *dst, + const cairo_pattern_t *src_pattern, + cairo_surface_t *src, + cairo_surface_attributes_t *src_attr) +{ + _cairo_pattern_release_surface (src_pattern, src, src_attr); +} +#endif /* DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS */ + +#if DFB_COMPOSITE +static DFBAccelerationMask +_directfb_categorize_operation (cairo_surface_attributes_t *src_attr) +{ + cairo_matrix_t *m = &src_attr->matrix; + + if (m->xy != 0 || m->yx != 0 || m->xx < 0 || m->yy < 0) { + if (src_attr->extend != CAIRO_EXTEND_NONE) + return DFXL_NONE; + + return DFXL_TEXTRIANGLES; + } + + if (m->xx != 1 || m->yy != 1) { + if (src_attr->extend != CAIRO_EXTEND_NONE) + return DFXL_NONE; + + return DFXL_STRETCHBLIT; + } + + switch (src_attr->extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + if (_cairo_matrix_is_integer_translation (&src_attr->matrix, + NULL, NULL)) + { + return DFXL_BLIT; + } + else + { + return DFXL_STRETCHBLIT; + } + + default: + case CAIRO_EXTEND_REFLECT: + case CAIRO_EXTEND_PAD: + return DFXL_NONE; + } +} + +static cairo_int_status_t +_cairo_directfb_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, int src_y, + int mask_x, int mask_y, + int dst_x, int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_directfb_surface_t *dst = abstract_dst; + cairo_directfb_surface_t *src; + cairo_surface_attributes_t src_attr; + cairo_bool_t is_integer_translation; + DFBAccelerationMask accel, mask; + cairo_int_status_t status; + int tx, ty; + + D_DEBUG_AT (CairoDFB_Render, + "%s( op=%d, src_pattern=%p, mask_pattern=%p, dst=%p," + " src_x=%d, src_y=%d, mask_x=%d, mask_y=%d, dst_x=%d," + " dst_y=%d, width=%u, height=%u ).\n", + __FUNCTION__, op, src_pattern, mask_pattern, dst, + src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); + + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op, + &src_x, &src_y, &mask_x, &mask_y, + width, height, &src, &src_attr); + if (status) + return status; + + accel = _directfb_categorize_operation (&src_attr); + if (accel == DFXL_NONE) { + _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, + src->dfbsurface, + &mask); + if ((mask & accel) == 0) { + D_DEBUG_AT (CairoDFB_Render, "No acceleration (%08x)!\n", accel); + if (accel != DFXL_BLIT) { + _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + src_x += src_attr.x_offset; + src_y += src_attr.y_offset; + + switch ((int) accel) { + case DFXL_BLIT: + { + DFBRectangle sr; + + is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr.matrix, + &tx, &ty); + assert (is_integer_translation); + + sr.x = src_x + tx; + sr.y = src_y + ty; + sr.w = width; + sr.h = height; + + if (src_attr.extend == CAIRO_EXTEND_NONE) { + D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n"); + + RUN_CLIPPED (dst, clip_region, NULL, + dst->dfbsurface->Blit (dst->dfbsurface, + src->dfbsurface, + &sr, dst_x, dst_y)); + } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) { + DFBRegion clip; + + clip.x1 = dst_x; + clip.y1 = dst_y; + clip.x2 = dst_x + width - 1; + clip.y2 = dst_y + height - 1; + + D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n"); + + RUN_CLIPPED (dst, clip_region, &clip, + dst->dfbsurface->TileBlit (dst->dfbsurface, + src->dfbsurface, + &sr, dst_x, dst_y)); + } + break; + } + + case DFXL_STRETCHBLIT: + { + DFBRectangle sr, dr; + double x1, y1, x2, y2; + + TRANSFORM_POINT2X (src_attr.matrix, + src_x, src_y, x1, y1); + TRANSFORM_POINT2X (src_attr.matrix, + src_x+width, src_y+height, x2, y2); + + sr.x = floor (x1); + sr.y = floor (y1); + sr.w = ceil (x2) - sr.x; + sr.h = ceil (y2) - sr.y; + + dr.x = dst_x; + dr.y = dst_y; + dr.w = width; + dr.h = height; + + D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n"); + + RUN_CLIPPED (dst, clip_region, NULL, + dst->dfbsurface->StretchBlit (dst->dfbsurface, + src->dfbsurface, + &sr, &dr)); + break; + } + + case DFXL_TEXTRIANGLES: + { + DFBRegion clip; + DFBVertex v[4]; + float x1, y1, x2, y2; + int w, h; + + status = cairo_matrix_invert (&src_attr.matrix); + /* guaranteed by cairo_pattern_set_matrix (); */ + assert (status == CAIRO_STATUS_SUCCESS); + + x1 = src_x; + y1 = src_y; + x2 = width + x1; + y2 = height + y1; + + src->dfbsurface->GetSize (src->dfbsurface, &w, &h); + + TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y); + v[0].z = 0; + v[0].w = 1; + v[0].s = x1 / w; + v[0].t = y1 / h; + + TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y); + v[1].z = 0; + v[1].w = 1; + v[1].s = x2 / w; + v[1].t = y1 / h; + + TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y); + v[2].z = 0; + v[2].w = 1; + v[2].s = x2 / w; + v[2].t = y2 / h; + + TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y); + v[3].z = 0; + v[3].w = 1; + v[3].s = x1 / w; + v[3].t = y2 / h; + + clip.x1 = dst_x; + clip.y1 = dst_y; + clip.x2 = dst_x + width - 1; + clip.y2 = dst_y + height - 1; + + D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); + + RUN_CLIPPED (dst, clip_region, &clip, + dst->dfbsurface->TextureTriangles (dst->dfbsurface, + src->dfbsurface, + v, NULL, + 4, DTTF_FAN)); + break; + } + + default: + D_BUG ("Unexpected operation"); + break; + } + + _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); + + return CAIRO_STATUS_SUCCESS; +} +#endif /* DFB_COMPOSITE */ + +#if DFB_RECTANGLES +static cairo_int_status_t +_cairo_directfb_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int n_rects) +{ + cairo_directfb_surface_t *dst = abstract_surface; + DFBSurfaceDrawingFlags flags; + DFBSurfaceBlendFunction sblend; + DFBSurfaceBlendFunction dblend; + DFBRectangle r[n_rects]; + int i; + + D_DEBUG_AT (CairoDFB_Render, + "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n", + __FUNCTION__, dst, op, color, rects, n_rects); + + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _directfb_get_operator (op, &sblend, &dblend)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (CAIRO_COLOR_IS_OPAQUE (color)) { + if (sblend == DSBF_SRCALPHA) + sblend = DSBF_ONE; + else if (sblend == DSBF_INVSRCALPHA) + sblend = DSBF_ZERO; + + if (dblend == DSBF_SRCALPHA) + dblend = DSBF_ONE; + else if (dblend == DSBF_INVSRCALPHA) + dblend = DSBF_ZERO; + } + if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { + if (sblend == DSBF_DESTALPHA) + sblend = DSBF_ONE; + else if (sblend == DSBF_INVDESTALPHA) + sblend = DSBF_ZERO; + + if (dblend == DSBF_DESTALPHA) + dblend = DSBF_ONE; + else if (dblend == DSBF_INVDESTALPHA) + dblend = DSBF_ZERO; + } + + flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND; + dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags); + if (flags & DSDRAW_BLEND) { + dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); + dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); + } + + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red_short >> 8, + color->green_short >> 8, + color->blue_short >> 8, + color->alpha_short >> 8); + + for (i = 0; i < n_rects; i++) { + r[i].x = rects[i].x; + r[i].y = rects[i].y; + r[i].w = rects[i].width; + r[i].h = rects[i].height; + } + + RUN_CLIPPED (dst, NULL, NULL, + dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects)); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +#if DFB_COMPOSITE_TRAPEZOIDS +static cairo_int_status_t +_cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_directfb_surface_t *dst = abstract_dst; + cairo_directfb_surface_t *src; + cairo_surface_attributes_t src_attr; + cairo_status_t status; + DFBAccelerationMask accel; + + D_DEBUG_AT (CairoDFB_Render, + "%s( op=%d, pattern=%p, dst=%p, antialias=%d," + " src_x=%d, src_y=%d, dst_x=%d, dst_y=%d," + " width=%u, height=%u, traps=%p, num_traps=%d ).\n", + __FUNCTION__, op, pattern, dst, antialias, + src_x, src_y, dst_x, dst_y, width, height, traps, num_traps); + + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (antialias != CAIRO_ANTIALIAS_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Textures are not supported yet. */ + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _directfb_prepare_composite (dst, pattern, NULL, op, + &src_x, &src_y, NULL, NULL, + width, height, &src, &src_attr); + if (status) + return status; + + dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, + src->dfbsurface, + &accel); + + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (accel & DFXL_TEXTRIANGLES) { + DFBVertex vertex[6*num_traps]; + DFBVertex *v = &vertex[0]; + int n = 0; + +#define ADD_TRI_V(V, X, Y) do { \ + (V)->x = (X); (V)->y = (Y); (V)->w = 1; (V)->z = (V)->s = (V)->t = 0; \ +} while (0) +#define ADD_TRI(id, x1, y1, x2, y2, x3, y3) do {\ + const int p = (id)*3;\ + ADD_TRI_V (v+p+0, x1, y1); \ + ADD_TRI_V (v+p+1, x2, y2); \ + ADD_TRI_V (v+p+2, x3, y3); \ +} while (0) + while (num_traps--) { + double lx1, ly1, lx2, ly2; + double rx1, ry1, rx2, ry2; + + lx1 = _cairo_fixed_to_double (traps->left.p1.x); + ly1 = _cairo_fixed_to_double (traps->left.p1.y); + lx2 = _cairo_fixed_to_double (traps->left.p2.x); + ly2 = _cairo_fixed_to_double (traps->left.p2.y); + rx1 = _cairo_fixed_to_double (traps->right.p1.x); + ry1 = _cairo_fixed_to_double (traps->right.p1.y); + rx2 = _cairo_fixed_to_double (traps->right.p2.x); + ry2 = _cairo_fixed_to_double (traps->right.p2.y); + + if (traps->left.p1.y < traps->top) { + double y = _cairo_fixed_to_double (traps->top); + if (lx2 != lx1) + lx1 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; + ly1 = y; + } + if (traps->left.p2.y > traps->bottom) { + double y = _cairo_fixed_to_double (traps->bottom); + if (lx2 != lx1) + lx2 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; + ly2 = y; + } + + if (traps->right.p1.y < traps->top) { + double y = _cairo_fixed_to_double (traps->top); + if (rx2 != rx1) + rx1 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; + ry1 = y; + } + if (traps->right.p2.y > traps->bottom) { + double y = _cairo_fixed_to_double (traps->bottom); + if (rx2 != rx1) + rx2 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; + ry2 = y; + } + + if (lx1 == rx1 && ly1 == ry1) { + ADD_TRI (0, lx2, ly2, lx1, ly1, rx2, ry2); + v += 3; + n += 3; + } else if (lx2 == rx2 && ly2 == ry2) { + ADD_TRI (0, lx1, ly1, lx2, ly2, rx1, ry1); + v += 3; + n += 3; + } else { + ADD_TRI (0, lx1, ly1, rx1, ry1, lx2, ly2); + ADD_TRI (1, lx2, ly2, rx1, ry1, rx2, ry2); + v += 6; + n += 6; + } + + traps++; + } +#undef ADD_TRI +#undef ADD_TRI_V + + D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); + + RUN_CLIPPED (dst, clip_region, NULL, + dst->dfbsurface->TextureTriangles (dst->dfbsurface, + src->dfbsurface, + vertex, NULL, n, + DTTF_LIST)); + + status = CAIRO_STATUS_SUCCESS; + } + + _directfb_finish_composite (dst, pattern, &src->base, &src_attr); + + return status; +} +#endif /* DFB_COMPOSITE_TRAPEZOIDS */ + +static cairo_bool_t +_cairo_directfb_abstract_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_directfb_surface_t *surface = abstract_surface; + + D_DEBUG_AT (CairoDFB_Surface, + "%s( surface=%p, rectangle=%p ).\n", + __FUNCTION__, surface, rectangle); + + if (!surface->local) { + surface->dfbsurface->GetSize (surface->dfbsurface, + &surface->width, &surface->height); + } + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +#if DFB_SHOW_GLYPHS +static cairo_status_t +_directfb_allocate_font_cache (IDirectFB *dfb, + int width, int height, + cairo_directfb_font_cache_t **out) +{ + cairo_directfb_font_cache_t *cache; + cairo_status_t status; + + cache = calloc (1, sizeof (cairo_directfb_font_cache_t)); + if (cache == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cache->dfb = dfb; + status = _directfb_buffer_surface_create (dfb, + _directfb_argb_font ? DSPF_ARGB : DSPF_A8, + width, height, + &cache->dfbsurface); + if (status) { + free (cache); + return status; + } + + cache->width = width; + cache->height = height; + *out = cache; + return CAIRO_STATUS_SUCCESS; +} + +static void +_directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache) +{ + cache->dfbsurface->Release (cache->dfbsurface); + free (cache); +} + +/* XXX hook into rtree font cache from drm */ +static cairo_status_t +_directfb_acquire_font_cache (cairo_directfb_surface_t *surface, + cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_directfb_font_cache_t **ret_cache, + DFBRectangle *rects, + DFBPoint *points, + int *ret_num) +{ + cairo_status_t status; + cairo_scaled_glyph_t *chars[num_glyphs]; + int num_chars = 0; + cairo_directfb_font_cache_t *cache = NULL; + int n = 0; + int x = 0; + int y = 0; + int w = 8; + int h = 8; + int i; + + D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs ); + + _cairo_scaled_font_freeze_cache (scaled_font); + + if (scaled_font->surface_private) { + cache = scaled_font->surface_private; + x = cache->x; + y = cache->y; + } + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_image_surface_t *img; + + D_DEBUG_AT (CairoDFB_Font, " -> [%2d] = %4lu\n", i, glyphs[i].index ); + + status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (status) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + + img = scaled_glyph->surface; + switch (img->format) { + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_ARGB32: + break; + case CAIRO_FORMAT_RGB24: + default: + D_DEBUG_AT (CairoDFB_Font, + " -> Unsupported font format %d!\n", img->format); + _cairo_scaled_font_thaw_cache (scaled_font); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + points[n].x = _cairo_lround (glyphs[i].x - img->base.device_transform.x0); + points[n].y = _cairo_lround (glyphs[i].y - img->base.device_transform.y0); + + // D_DEBUG_AT (CairoDFB_Font, " (%4d,%4d) [%2d]\n", points[n].x, points[n].y, n ); + + if (points[n].x >= surface->width || + points[n].y >= surface->height || + points[n].x+img->width <= 0 || + points[n].y+img->height <= 0) + { + continue; + } + + if (scaled_glyph->surface_private == NULL) { + DFBRectangle *rect; + + if (x+img->width > 2048) { + x = 0; + y = h; + h = 0; + } + + rects[n].x = x; + rects[n].y = y; + rects[n].w = img->width; + rects[n].h = img->height; + + x += img->width; + h = MAX (h, img->height); + w = MAX (w, x); + + /* Remember glyph location */ + rect = malloc (sizeof (DFBRectangle)); + if (rect == NULL) { + _cairo_scaled_font_thaw_cache (scaled_font); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + *rect = rects[n]; + + scaled_glyph->surface_private = rect; + chars[num_chars++] = scaled_glyph; + + D_DEBUG_AT (CairoDFB_Font, " -> loading at %4d,%2d <- rect %p, img %p, entry %p\n", + rects[n].x, rects[n].y, rect, scaled_glyph->surface, scaled_glyph); + } else { + rects[n] = *(DFBRectangle *) scaled_glyph->surface_private; + + D_DEBUG_AT (CairoDFB_Font, " -> exists at %4d,%2d\n", rects[n].x, rects[n].y); + } + + n++; + } + + if (n == 0) { + _cairo_scaled_font_thaw_cache (scaled_font); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + h += y; + w = MAX (w, 8); + h = MAX (h, 8); + + /* XXX query maximum surface size */ + if (w > 2048 || h > 2048) { + _cairo_scaled_font_thaw_cache (scaled_font); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (cache) { + if (cache->width < w || cache->height < h) { + cairo_directfb_font_cache_t *new_cache; + + w = MAX (w, cache->width); + h = MAX (h, cache->height); + + D_DEBUG_AT (CairoDFB_Font, " -> Reallocating font cache (%dx%d).\n", w, h); + + status = _directfb_allocate_font_cache (surface->dfb, + w, h, + &new_cache); + if (status) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + + new_cache->dfbsurface->Blit (new_cache->dfbsurface, + cache->dfbsurface, NULL, 0, 0); + + _directfb_destroy_font_cache (cache); + scaled_font->surface_private = cache = new_cache; + } + } else { + D_DEBUG_AT (CairoDFB_Font, " -> Allocating font cache (%dx%d).\n", w, h); + + status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache); + if (status) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + + scaled_font->surface_backend = &_cairo_directfb_surface_backend; + scaled_font->surface_private = cache; + } + + if (num_chars) { + unsigned char *data; + int pitch; + + if (cache->dfbsurface->Lock (cache->dfbsurface, + DSLF_WRITE, (void *)&data, &pitch)) + { + _cairo_scaled_font_thaw_cache (scaled_font); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + D_DEBUG_AT (CairoDFB_Font, " => %d chars to load, cache %dx%d\n", num_chars, cache->width, cache->height); + + for (i = 0; i < num_chars; i++) { + cairo_image_surface_t *img = chars[i]->surface; + DFBRectangle *rect = chars[i]->surface_private; + unsigned char *dst = data; + unsigned char *src; + int j; + + D_DEBUG_AT (CairoDFB_Font, " -> loading [%2d] <- rect %p, img %p, entry %p\n", i, rect, img, chars[i]); + + src = img->data; + + D_DEBUG_AT (CairoDFB_Font, " from %p\n", src); + + dst += rect->y * pitch + (_directfb_argb_font ? (rect->x<<2) : rect->x); + + D_DEBUG_AT (CairoDFB_Font, " to %4d,%2d (%p)\n", rect->x, rect->y, dst); + + if (img->format == CAIRO_FORMAT_A1) { + for (h = rect->h; h; h--) { + if (_directfb_argb_font) { + for (j = 0; j < rect->w; j++) + ((uint32_t *) dst)[j] = (src[j>>3] & (1 << (j&7))) ? 0xffffffff : 0; + } else { + for (j = 0; j < rect->w; j++) + dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0; + } + + dst += pitch; + src += img->stride; + } + } else if (img->format == CAIRO_FORMAT_A8) { + for (h = rect->h; h; h--) { + if (_directfb_argb_font) { + for (j = 0; j < rect->w; j++) + ((uint32_t *) dst)[j] = src[j] * 0x01010101; + } else { + direct_memcpy (dst, src, rect->w); + } + + dst += pitch; + src += img->stride; + } + } else { /* ARGB32 */ + for (h = rect->h; h; h--) { + if (_directfb_argb_font) { + direct_memcpy (dst, src, rect->w<<2); + } else { + for (j = 0; j < rect->w; j++) + dst[j] = ((uint32_t *) src)[j] >> 24; + } + + dst += pitch; + src += img->stride; + } + } + } + + cache->dfbsurface->Unlock (cache->dfbsurface); + } + + _cairo_scaled_font_thaw_cache (scaled_font); + + cache->x = x; + cache->y = y; + + D_DEBUG_AT (CairoDFB_Font, " => cache %d,%d, %p [%d]\n", x, y, cache, n); + + *ret_cache = cache; + *ret_num = n; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_directfb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_directfb_font_cache_t *cache = scaled_font->surface_private; + + D_DEBUG_AT (CairoDFB_Font, + "%s( scaled_font=%p ).\n", __FUNCTION__, scaled_font); + + if (cache != NULL) { + _directfb_destroy_font_cache (cache); + scaled_font->surface_private = NULL; + } +} + +static void +_cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + D_DEBUG_AT (CairoDFB_Font, + "%s( scaled_glyph=%p, scaled_font=%p ).\n", + __FUNCTION__, scaled_glyph, scaled_font); + + if (scaled_glyph->surface_private != NULL) { + free (scaled_glyph->surface_private); + scaled_glyph->surface_private = NULL; + } +} + +static cairo_int_status_t +_cairo_directfb_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_directfb_surface_t *dst = abstract_dst; + cairo_directfb_font_cache_t *cache; + cairo_status_t status; + DFBSurfaceBlittingFlags flags; + DFBSurfaceBlendFunction sblend; + DFBSurfaceBlendFunction dblend; + DFBRectangle rects[num_glyphs]; + DFBPoint points[num_glyphs]; + int num; + const cairo_color_t *color; + cairo_region_t *clip_region = NULL; + + D_DEBUG_AT (CairoDFB_Font, + "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n", + __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font); + + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Fallback if we need to emulate clip regions */ + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + } + + /* XXX Unbounded operators are not handled correctly */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _directfb_get_operator (op, &sblend, &dblend) || + sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _directfb_acquire_font_cache (dst, scaled_font, glyphs, num_glyphs, + &cache, &rects[0], &points[0], &num); + if (status) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + return status; + } + + color = &((cairo_solid_pattern_t *) pattern)->color; + + flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE; + if (! CAIRO_COLOR_IS_OPAQUE (color)) + flags |= DSBLIT_BLEND_COLORALPHA; + + if (!_directfb_argb_font) { + if (sblend == DSBF_ONE) { + sblend = DSBF_SRCALPHA; + if (dblend == DSBF_ZERO) + dblend = DSBF_INVSRCALPHA; + } + } + + dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); + dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); + dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); + if (dst->blit_premultiplied) { + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red_short >> 8, + color->green_short >> 8, + color->blue_short >> 8, + color->alpha_short >> 8); + } else { + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red * 0xff, + color->green * 0xff, + color->blue * 0xff, + color->alpha * 0xff); + } + + D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n"); + + RUN_CLIPPED (dst, clip_region, NULL, + dst->dfbsurface->BatchBlit (dst->dfbsurface, + cache->dfbsurface, rects, points, num)); + + return CAIRO_STATUS_SUCCESS; +} +#endif /* DFB_SHOW_GLYPHS */ + + +static cairo_bool_t +_cairo_directfb_surface_is_similar (void *surface_a, void *surface_b) +{ + cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a; + cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b; + + return a->dfb == b->dfb; +} + +static cairo_surface_backend_t +_cairo_directfb_surface_backend = { + CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/ + _cairo_directfb_surface_create_similar,/*create_similar*/ + _cairo_directfb_surface_finish, /*finish*/ + _cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/ + _cairo_directfb_surface_release_source_image,/*release_source_image*/ + _cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/ + _cairo_directfb_surface_release_dest_image,/*release_dest_image*/ + _cairo_directfb_surface_clone_similar,/*clone_similar*/ +#if DFB_COMPOSITE + _cairo_directfb_surface_composite,/*composite*/ +#else + NULL,/*composite*/ +#endif +#if DFB_RECTANGLES + _cairo_directfb_surface_fill_rectangles,/*fill_rectangles*/ +#else + NULL,/*fill_rectangles*/ +#endif +#if DFB_COMPOSITE_TRAPEZOIDS + _cairo_directfb_surface_composite_trapezoids,/*composite_trapezoids*/ +#else + NULL,/*composite_trapezoids*/ +#endif + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_directfb_abstract_surface_get_extents,/* get_extents */ + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ +#if DFB_SHOW_GLYPHS + _cairo_directfb_surface_scaled_font_fini,/* scaled_font_fini */ + _cairo_directfb_surface_scaled_glyph_fini,/* scaled_glyph_fini */ +#else + NULL, + NULL, +#endif + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ +#if DFB_SHOW_GLYPHS + _cairo_directfb_surface_show_glyphs,/* show_glyphs */ +#else + NULL, /* show_glyphs */ +#endif + NULL, /* snapshot */ + _cairo_directfb_surface_is_similar, +}; + + +static void +cairo_directfb_surface_backend_init (IDirectFB *dfb) +{ + static int done = 0; + + if (done) + return; + + if (getenv ("CAIRO_DIRECTFB_NO_ACCEL")) { +#if DFB_RECTANGLES + _cairo_directfb_surface_backend.fill_rectangles = NULL; +#endif +#if DFB_COMPOSITE + _cairo_directfb_surface_backend.composite = NULL; +#endif +#if DFB_COMPOSITE_TRAPEZOIDS + _cairo_directfb_surface_backend.composite_trapezoids = NULL; +#endif +#if DFB_SHOW_GLYPHS + _cairo_directfb_surface_backend.scaled_font_fini = NULL; + _cairo_directfb_surface_backend.scaled_glyph_fini = NULL; + _cairo_directfb_surface_backend.show_glyphs = NULL; +#endif + D_DEBUG_AT (CairoDFB_Surface, "Acceleration disabled.\n"); + } else { + DFBGraphicsDeviceDescription dsc; + + dfb->GetDeviceDescription (dfb, &dsc); + +#if DFB_COMPOSITE + // if (!(dsc.acceleration_mask & DFXL_BLIT)) + // _cairo_directfb_surface_backend.composite = NULL; +#endif + +#if DFB_COMPOSITE_TRAPEZOIDS + // if (!(dsc.acceleration_mask & DFXL_TEXTRIANGLES)) + // _cairo_directfb_surface_backend.composite_trapezoids = NULL; +#endif + } + + if (getenv ("CAIRO_DIRECTFB_ARGB_FONT")) { + _directfb_argb_font = 1; + D_DEBUG_AT (CairoDFB_Surface, "Using ARGB fonts.\n"); + } + + done = 1; +} + +cairo_surface_t * +cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) +{ + cairo_directfb_surface_t *surface; + DFBSurfacePixelFormat format; + DFBSurfaceCapabilities caps; + + D_ASSERT (dfb != NULL); + D_ASSERT (dfbsurface != NULL); + + cairo_directfb_surface_backend_init (dfb); + + surface = calloc (1, sizeof (cairo_directfb_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + dfbsurface->AddRef (dfbsurface); + dfbsurface->GetPixelFormat (dfbsurface, &format); + dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height); + surface->dfb = dfb; + surface->dfbsurface = dfbsurface; + surface->pixman_format = _directfb_to_pixman_format (format); + surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); + + dfbsurface->GetCapabilities (dfbsurface, &caps); + if (caps & DSCAPS_PREMULTIPLIED) + surface->blit_premultiplied = TRUE; + + _cairo_surface_init (&surface->base, + &_cairo_directfb_surface_backend, + NULL, /* device */ + _directfb_format_to_content (format)); + + return &surface->base; +} diff --git a/libs/cairo/src/cairo-directfb.h b/libs/cairo/src/cairo-directfb.h new file mode 100644 index 000000000..029b43ef7 --- /dev/null +++ b/libs/cairo/src/cairo-directfb.h @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Environment variables affecting the backend: + * + * %CAIRO_DIRECTFB_NO_ACCEL (boolean) + * if found, disables acceleration at all + * + * %CAIRO_DIRECTFB_ARGB_FONT (boolean) + * if found, enables using ARGB fonts instead of A8 + */ + +#ifndef CAIRO_DIRECTFB_H +#define CAIRO_DIRECTFB_H + +#include "cairo.h" + +#if CAIRO_HAS_DIRECTFB_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_DIRECTFB_SURFACE*/ +# error Cairo was not compiled with support for the directfb backend +#endif /*CAIRO_HAS_DIRECTFB_SURFACE*/ + +#endif /*CAIRO_DIRECTFB_H*/ diff --git a/libs/cairo/src/cairo-drm.h b/libs/cairo/src/cairo-drm.h new file mode 100644 index 000000000..bbdb28ba2 --- /dev/null +++ b/libs/cairo/src/cairo-drm.h @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-dwrite-font.cpp b/libs/cairo/src/cairo-dwrite-font.cpp new file mode 100644 index 000000000..391f2e8a7 --- /dev/null +++ b/libs/cairo/src/cairo-dwrite-font.cpp @@ -0,0 +1,1590 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-win32-private.h" +#include "cairo-surface-private.h" +#include "cairo-clip-private.h" + +#include "cairo-d2d-private.h" +#include "cairo-dwrite-private.h" +#include "cairo-truetype-subset-private.h" +#include + +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + +#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS + +// Forward declarations +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area); + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area); + +class D2DFactory +{ +public: + static ID2D1Factory *Instance() + { + if (!mFactoryInstance) { + D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); + if (createD2DFactory) { + D2D1_FACTORY_OPTIONS options; + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&mFactoryInstance); + } + } + return mFactoryInstance; + } + + static ID2D1DCRenderTarget *RenderTarget() + { + if (!mRenderTarget) { + if (!Instance()) { + return NULL; + } + // Create a DC render target. + D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT + ); + + Instance()->CreateDCRenderTarget(&props, &mRenderTarget); + } + return mRenderTarget; + } + +private: + static ID2D1Factory *mFactoryInstance; + static ID2D1DCRenderTarget *mRenderTarget; +}; + +IDWriteFactory *DWriteFactory::mFactoryInstance = NULL; +IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL; +IDWriteRenderingParams *DWriteFactory::mDefaultRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mCustomClearTypeRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mForceGDIClassicRenderingParams = NULL; +FLOAT DWriteFactory::mGamma = -1.0; +FLOAT DWriteFactory::mEnhancedContrast = -1.0; +FLOAT DWriteFactory::mClearTypeLevel = -1.0; +int DWriteFactory::mPixelGeometry = -1; +int DWriteFactory::mRenderingMode = -1; + +ID2D1Factory *D2DFactory::mFactoryInstance = NULL; +ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL; + +/* Functions cairo_font_face_backend_t */ +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); +static void +_cairo_dwrite_font_face_destroy (void *font_face); + +static cairo_status_t +_cairo_dwrite_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font); + +const cairo_font_face_backend_t _cairo_dwrite_font_face_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_font_face_create_for_toy, + _cairo_dwrite_font_face_destroy, + _cairo_dwrite_font_face_scaled_font_create +}; + +/* Functions cairo_scaled_font_backend_t */ + +void _cairo_dwrite_scaled_font_fini(void *scaled_font); + +static cairo_warn cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + +cairo_warn cairo_int_status_t +_cairo_dwrite_scaled_show_glyphs(void *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region, + int *remaining_glyphs); + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length); + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4); + +const cairo_scaled_font_backend_t _cairo_dwrite_scaled_font_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_scaled_font_fini, + _cairo_dwrite_scaled_glyph_init, + NULL, + _cairo_dwrite_ucs4_to_index, + _cairo_dwrite_scaled_show_glyphs, + _cairo_dwrite_load_truetype_table, + NULL, +}; + +/* Helper conversion functions */ + +/** + * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo + * uses column vectors. Hence the transposition. + * + * \param Cairo matrix + * \return D2D matrix + */ +static D2D1::Matrix3x2F +_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix) +{ + return D2D1::Matrix3x2F((FLOAT)matrix->xx, + (FLOAT)matrix->yx, + (FLOAT)matrix->xy, + (FLOAT)matrix->yy, + (FLOAT)matrix->x0, + (FLOAT)matrix->y0); +} + + +/** + * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row + * vectors where cairo uses column vectors. Hence the transposition. + * + * \param Cairo matrix + * \return DirectWrite matrix + */ +DWRITE_MATRIX +_cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix) +{ + DWRITE_MATRIX dwmat; + dwmat.m11 = (FLOAT)matrix->xx; + dwmat.m12 = (FLOAT)matrix->yx; + dwmat.m21 = (FLOAT)matrix->xy; + dwmat.m22 = (FLOAT)matrix->yy; + dwmat.dx = (FLOAT)matrix->x0; + dwmat.dy = (FLOAT)matrix->y0; + return dwmat; +} + +/* Helper functions for cairo_dwrite_scaled_glyph_init */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + WCHAR *face_name; + int face_name_len; + + if (!DWriteFactory::Instance()) { + return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; + } + + face_name_len = MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, NULL, 0); + face_name = new WCHAR[face_name_len]; + MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); + + IDWriteFontFamily *family = DWriteFactory::FindSystemFontFamily(face_name); + delete face_name; + if (!family) { + *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; + return CAIRO_STATUS_FONT_TYPE_MISMATCH; + } + + DWRITE_FONT_WEIGHT weight; + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_BOLD: + weight = DWRITE_FONT_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + weight = DWRITE_FONT_WEIGHT_NORMAL; + break; + } + + DWRITE_FONT_STYLE style; + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_ITALIC: + style = DWRITE_FONT_STYLE_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + style = DWRITE_FONT_STYLE_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + style = DWRITE_FONT_STYLE_NORMAL; + break; + } + + cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t*)malloc(sizeof(cairo_dwrite_font_face_t)); + HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &face->font); + if (SUCCEEDED(hr)) { + // Cannot use C++ style new since cairo deallocates this. + *font_face = (cairo_font_face_t*)face; + _cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend); + } else { + free(face); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_dwrite_font_face_destroy (void *font_face) +{ + cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); + if (dwrite_font_face->dwriteface) + dwrite_font_face->dwriteface->Release(); + if (dwrite_font_face->font) + dwrite_font_face->font->Release(); +} + + +static inline unsigned short +read_short(const char *buf) +{ + return be16_to_cpu(*(unsigned short*)buf); +} + +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed) +{ + run->allocate(num_glyphs); + + UINT16 *indices = const_cast(run->glyphIndices); + FLOAT *advances = const_cast(run->glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); + + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); + + run->bidiLevel = 0; + run->fontFace = dwriteff->dwriteface; + run->glyphCount = num_glyphs; + run->isSideways = FALSE; + + if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 && + scaled_font->mat.xx == scaled_font->base.font_matrix.xx && + scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { + // Fast route, don't actually use a transform but just + // set the correct font size. + *transformed = 0; + + run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + + offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x); + advances[i] = 0.0; + } + } else { + *transformed = 1; + // Transforming positions by the inverse matrix, then by the original + // matrix later may introduce small errors, especially because the + // D2D matrix is single-precision whereas the cairo one is double. + // This is a problem when glyph positions were originally at exactly + // half-pixel locations, which eventually round to whole pixels for + // GDI rendering - the errors introduced here cause them to round in + // unpredictable directions, instead of all rounding in a consistent + // way, leading to poor glyph spacing (bug 675383). + // To mitigate this, nudge the positions by a tiny amount to try and + // ensure that after the two transforms, they'll still round in a + // consistent direction. + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x + EPSILON; + double y = glyphs[i].y; + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. Y-axis is + // inverted! Therefor the offset is -y. + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + // The font matrix takes care of the scaling if we have a transform, + // emSize should be 1. + run->fontEmSize = 1.0f; + } +} + +#define GASP_TAG 0x70736167 +#define GASP_DOGRAY 0x2 + +static cairo_bool_t +do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) +{ + void *tableContext; + char *tableData; + UINT32 tableSize; + BOOL exists; + dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); + + if (exists) { + if (tableSize < 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + struct gaspRange { + unsigned short maxPPEM; // Stored big-endian + unsigned short behavior; // Stored big-endian + }; + unsigned short numRanges = read_short(tableData + 2); + if (tableSize < (UINT)4 + numRanges * 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + gaspRange *ranges = (gaspRange *)(tableData + 4); + for (int i = 0; i < numRanges; i++) { + if (be16_to_cpu(ranges[i].maxPPEM) > ppem) { + if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) { + dwface->ReleaseFontTable(tableContext); + return false; + } + break; + } + } + dwface->ReleaseFontTable(tableContext); + } + return true; +} + +static cairo_status_t +_cairo_dwrite_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font) +{ + cairo_dwrite_font_face_t *font_face = static_cast(abstract_face); + + // Must do malloc and not C++ new, since Cairo frees this. + cairo_dwrite_scaled_font_t *dwriteFont = (cairo_dwrite_scaled_font_t*)malloc(sizeof(cairo_dwrite_scaled_font_t)); + *font = reinterpret_cast(dwriteFont); + _cairo_scaled_font_init(&dwriteFont->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend); + + cairo_font_extents_t extents; + + DWRITE_FONT_METRICS metrics; + font_face->dwriteface->GetMetrics(&metrics); + + extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; + extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.ascent + metrics.descent + metrics.lineGap) / metrics.designUnitsPerEm; + extents.max_x_advance = 14.0; + extents.max_y_advance = 0.0; + + dwriteFont->mat = dwriteFont->base.ctm; + cairo_matrix_multiply(&dwriteFont->mat, &dwriteFont->mat, font_matrix); + dwriteFont->mat_inverse = dwriteFont->mat; + cairo_matrix_invert (&dwriteFont->mat_inverse); + + cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + + // The following code detects the system quality at scaled_font creation time, + // this means that if cleartype settings are changed but the scaled_fonts + // are re-used, they might not adhere to the new system setting until re- + // creation. + switch (cairo_win32_get_system_text_quality()) { + case CLEARTYPE_QUALITY: + default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + break; + case ANTIALIASED_QUALITY: + default_quality = CAIRO_ANTIALIAS_GRAY; + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + case DEFAULT_QUALITY: + // _get_system_quality() seems to think aliased is default! + default_quality = CAIRO_ANTIALIAS_NONE; + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + } + + if (default_quality == CAIRO_ANTIALIAS_GRAY) { + if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { + default_quality = CAIRO_ANTIALIAS_NONE; + } + } + + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) { + dwriteFont->antialias_mode = default_quality; + } else { + dwriteFont->antialias_mode = options->antialias; + } + + dwriteFont->manual_show_glyphs_allowed = TRUE; + dwriteFont->rendering_mode = + default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? + cairo_d2d_surface_t::TEXT_RENDERING_NORMAL : cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE; + + return _cairo_scaled_font_set_metrics (*font, &extents); +} + +/* Implementation cairo_dwrite_scaled_font_backend_t */ +void +_cairo_dwrite_scaled_font_fini(void *scaled_font) +{ +} + +static cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast(scaled_font); + cairo_int_status_t status; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); + + UINT16 index; + face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); + return index; +} + +cairo_warn cairo_int_status_t +_cairo_dwrite_scaled_show_glyphs(void *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region, + int *remaining_glyphs) +{ + cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; + cairo_int_status_t status; + + if (width == 0 || height == 0) + return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_is_win32 (generic_surface) && + surface->format == CAIRO_FORMAT_RGB24 && + op == CAIRO_OPERATOR_OVER) { + + //XXX: we need to set the clip region here + + status = (cairo_int_status_t)_cairo_dwrite_show_glyphs_on_surface (surface, op, pattern, + glyphs, num_glyphs, + (cairo_scaled_font_t*)scaled_font, NULL); + + return status; + } else { + cairo_dwrite_scaled_font_t *dwritesf = + static_cast(scaled_font); + BOOL transform = FALSE; + + AutoDWriteGlyphRun run; + run.allocate(num_glyphs); + UINT16 *indices = const_cast(run.glyphIndices); + FLOAT *advances = const_cast(run.glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); + + run.bidiLevel = 0; + run.fontFace = ((cairo_dwrite_font_face_t*)dwritesf->base.font_face)->dwriteface; + run.isSideways = FALSE; + IDWriteGlyphRunAnalysis *analysis; + + if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && + dwritesf->mat.xx == dwritesf->base.font_matrix.xx && + dwritesf->mat.yy == dwritesf->base.font_matrix.yy) { + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. + offsets[i].ascenderOffset = (FLOAT)dest_y - (FLOAT)glyphs[i].y; + offsets[i].advanceOffset = (FLOAT)glyphs[i].x - dest_x; + advances[i] = 0.0; + } + run.fontEmSize = (FLOAT)dwritesf->base.font_matrix.yy; + } else { + transform = TRUE; + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x - dest_x; + double y = glyphs[i].y - dest_y; + cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + run.fontEmSize = 1.0f; + } + + HRESULT hr; + if (!transform) { + hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + NULL, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0, + 0, + &analysis); + } else { + DWRITE_MATRIX dwmatrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + &dwmatrix, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0, + 0, + &analysis); + } + + if (FAILED(hr) || !analysis) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + RECT r; + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + + BYTE *surface = new BYTE[width * height * 3]; + + analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &r, surface, width * height * 3); + + cairo_image_surface_t *mask_surface = + (cairo_image_surface_t*)cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + + cairo_surface_flush(&mask_surface->base); + + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + mask_surface->data[y * mask_surface->stride + x * 4] = surface[y * width * 3 + x * 3 + 1]; + mask_surface->data[y * mask_surface->stride + x * 4 + 1] = surface[y * width * 3 + x * 3 + 1]; + mask_surface->data[y * mask_surface->stride + x * 4 + 2] = surface[y * width * 3 + x * 3 + 1]; + mask_surface->data[y * mask_surface->stride + x * 4 + 3] = surface[y * width * 3 + x * 3 + 1]; + } + } + cairo_surface_mark_dirty(&mask_surface->base); + + pixman_image_set_component_alpha(mask_surface->pixman_image, 1); + + cairo_surface_pattern_t mask; + _cairo_pattern_init_for_surface (&mask, &mask_surface->base); + + status = (cairo_int_status_t)_cairo_surface_composite (op, pattern, + &mask.base, + generic_surface, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height, + clip_region); + + _cairo_pattern_fini (&mask.base); + + analysis->Release(); + delete [] surface; + + cairo_surface_destroy (&mask_surface->base); + *remaining_glyphs = 0; + + return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; + } +} + +/* cairo_dwrite_scaled_glyph_init helper function bodies */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + UINT16 charIndex = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); + cairo_dwrite_font_face_t *font_face = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + cairo_text_extents_t extents; + + DWRITE_GLYPH_METRICS metrics; + DWRITE_FONT_METRICS fontMetrics; + font_face->dwriteface->GetMetrics(&fontMetrics); + HRESULT hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // TODO: Treat swap_xy. + extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / + fontMetrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / + fontMetrics.designUnitsPerEm; + extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; + extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; + extents.y_advance = 0.0; + extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / + fontMetrics.designUnitsPerEm; + + // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics + // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, + // and therefore it does not always cover all pixels that will actually be touched. + if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && + extents.width > 0 && extents.height > 0) { + extents.width += scaled_font->mat_inverse.xx * 2; + extents.x_bearing -= scaled_font->mat_inverse.xx; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + return CAIRO_INT_STATUS_SUCCESS; +} + +/** + * Stack-based helper implementing IDWriteGeometrySink. + * Used to determine the path of the glyphs. + */ + +class GeometryRecorder : public IDWriteGeometrySink +{ +public: + GeometryRecorder(cairo_path_fixed_t *aCairoPath) + : mCairoPath(aCairoPath) {} + + // IUnknown interface + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) + { + if (iid != __uuidof(IDWriteGeometrySink)) + return E_NOINTERFACE; + + *ppObject = static_cast(this); + + return S_OK; + } + + IFACEMETHOD_(ULONG, AddRef)() + { + return 1; + } + + IFACEMETHOD_(ULONG, Release)() + { + return 1; + } + + IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) + { + return; + } + + STDMETHODIMP Close() + { + return S_OK; + } + + IFACEMETHODIMP_(void) SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) + { + return; + } + + cairo_fixed_t GetFixedX(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.x); + } + + cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.y); + } + + IFACEMETHODIMP_(void) BeginFigure( + D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN figureBegin) + { + mStartPoint = startPoint; + cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, + GetFixedX(startPoint), + GetFixedY(startPoint)); + } + + IFACEMETHODIMP_(void) EndFigure( + D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(mStartPoint), + GetFixedY(mStartPoint)); + } + } + + IFACEMETHODIMP_(void) AddBeziers( + const D2D1_BEZIER_SEGMENT *beziers, + UINT beziersCount) + { + for (unsigned int i = 0; i < beziersCount; i++) { + cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, + GetFixedX(beziers[i].point1), + GetFixedY(beziers[i].point1), + GetFixedX(beziers[i].point2), + GetFixedY(beziers[i].point2), + GetFixedX(beziers[i].point3), + GetFixedY(beziers[i].point3)); + } + } + + IFACEMETHODIMP_(void) AddLines( + const D2D1_POINT_2F *points, + UINT pointsCount) + { + for (unsigned int i = 0; i < pointsCount; i++) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(points[i]), + GetFixedY(points[i])); + } + } + +private: + cairo_path_fixed_t *mCairoPath; + D2D1_POINT_2F mStartPoint; +}; + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_path_fixed_t *path; + path = _cairo_path_fixed_create(); + GeometryRecorder recorder(path); + + DWRITE_GLYPH_OFFSET offset; + offset.advanceOffset = 0; + offset.ascenderOffset = 0; + UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); + FLOAT advance = 0.0; + cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + dwriteff->dwriteface->GetGlyphRunOutline((FLOAT)scaled_font->base.font_matrix.yy, + &glyphId, + &advance, + &offset, + 1, + FALSE, + FALSE, + &recorder); + _cairo_path_fixed_close_path(path); + + /* Now apply our transformation to the drawn path. */ + _cairo_path_fixed_transform(path, &scaled_font->base.ctm); + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Helper function also stolen from cairo-win32-font.c */ + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; + cairo_image_surface_t *image8; + int i, j; + + if (image24->base.status) + return cairo_surface_reference (&image24->base); + + image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, + image24->width, image24->height); + if (image8->base.status) + return &image8->base; + + for (i = 0; i < image24->height; i++) { + uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); + unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); + + for (j = 0; j < image24->width; j++) { + *q = 255 - ((*p & 0x0000ff00) >> 8); + p++; + q++; + } + } + + return &image8->base; +} + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status; + cairo_glyph_t glyph; + cairo_win32_surface_t *surface; + cairo_t *cr; + cairo_surface_t *image; + int width, height; + double x1, y1, x2, y2; + + x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + width = (int)(x2 - x1); + height = (int)(y2 - y1); + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = -x1; + glyph.y = -y1; + + DWRITE_GLYPH_RUN run; + FLOAT advance = 0; + UINT16 index = (UINT16)glyph.index; + DWRITE_GLYPH_OFFSET offset; + double x = glyph.x; + double y = glyph.y; + RECT area; + DWRITE_MATRIX matrix; + + surface = (cairo_win32_surface_t *) + cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); + + cr = cairo_create (&surface->base); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + status = (cairo_int_status_t)cairo_status (cr); + cairo_destroy(cr); + if (status) + goto FAIL; + + /** + * We transform by the inverse transformation here. This will put our glyph + * locations in the space in which we draw. Which is later transformed by + * the transformation matrix that we use. This will transform the + * glyph positions back to where they were before when drawing, but the + * glyph shapes will be transformed by the transformation matrix. + */ + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + offset.advanceOffset = (FLOAT)x; + /** Y-axis is inverted */ + offset.ascenderOffset = -(FLOAT)y; + + area.top = 0; + area.bottom = height; + area.left = 0; + area.right = width; + + run.glyphCount = 1; + run.glyphAdvances = &advance; + run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface; + run.fontEmSize = 1.0f; + run.bidiLevel = 0; + run.glyphIndices = &index; + run.isSideways = FALSE; + run.glyphOffsets = &offset; + + matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + + status = _dwrite_draw_glyphs_to_gdi_surface_gdi (surface, &matrix, &run, + RGB(0,0,0), scaled_font, area); + if (status) + goto FAIL; + + GdiFlush(); + + image = _compute_a8_mask (surface); + status = (cairo_int_status_t)image->status; + if (status) + goto FAIL; + + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image); + + FAIL: + cairo_surface_destroy (&surface->base); + + return status; +} + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); + + const void *data; + UINT32 size; + void *tableContext; + BOOL exists; + face->dwriteface->TryGetFontTable(be32_to_cpu (tag), + &data, + &size, + &tableContext, + &exists); + + if (!exists) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (buffer && *length && (UINT32)offset < size) { + size = MIN(size - (UINT32)offset, *length); + memcpy(buffer, (const char*)data + offset, size); + } + *length = size; + + if (tableContext) { + face->dwriteface->ReleaseFontTable(tableContext); + } + return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; +} + +// WIN32 Helper Functions +cairo_font_face_t* +cairo_dwrite_font_face_create_for_dwrite_fontface(void* dwrite_font, void* dwrite_font_face) +{ + IDWriteFont *dwritefont = static_cast(dwrite_font); + IDWriteFontFace *dwriteface = static_cast(dwrite_font_face); + cairo_dwrite_font_face_t *face = new cairo_dwrite_font_face_t; + cairo_font_face_t *font_face; + + dwriteface->AddRef(); + + face->dwriteface = dwriteface; + face->font = NULL; + + font_face = (cairo_font_face_t*)face; + + _cairo_font_face_init (&((cairo_dwrite_font_face_t*)font_face)->base, &_cairo_dwrite_font_face_backend); + + return font_face; +} + +void +cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); + font->manual_show_glyphs_allowed = allowed; +} + +void +cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); + if (force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL) { + font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC; + } else if (!force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) { + font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_NORMAL; + } +} + +cairo_bool_t +cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); + return font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC; +} + +void +cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, + int geometry, int mode) +{ + DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode); +} + +int +cairo_dwrite_get_cleartype_rendering_mode() +{ + return DWriteFactory::GetClearTypeRenderingMode(); +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area) +{ + IDWriteGdiInterop *gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + IDWriteBitmapRenderTarget *rt; + HRESULT rv; + + cairo_d2d_surface_t::TextRenderingState renderingState = + scaled_font->rendering_mode; + + rv = gdiInterop->CreateBitmapRenderTarget(surface->dc, + area.right - area.left, + area.bottom - area.top, + &rt); + + if (FAILED(rv)) { + if (rv == E_OUTOFMEMORY) { + return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if ((renderingState == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL || + renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) && + !surface->base.permit_subpixel_antialiasing) { + renderingState = cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE; + IDWriteBitmapRenderTarget1* rt1; + rv = rt->QueryInterface(&rt1); + + if (SUCCEEDED(rv) && rt1) { + rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE); + rt1->Release(); + } + } + + IDWriteRenderingParams *params = + DWriteFactory::RenderingParams(renderingState); + + /** + * We set the number of pixels per DIP to 1.0. This is because we always want + * to draw in device pixels, and not device independent pixels. On high DPI + * systems this value will be higher than 1.0 and automatically upscale + * fonts, we don't want this since we do our own upscaling for various reasons. + */ + rt->SetPixelsPerDip(1.0); + + if (transform) { + rt->SetCurrentTransform(transform); + } + BitBlt(rt->GetMemoryDC(), + 0, 0, + area.right - area.left, area.bottom - area.top, + surface->dc, + area.left, area.top, + SRCCOPY | NOMIRRORBITMAP); + DWRITE_MEASURING_MODE measureMode; + switch (renderingState) { + case cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC: + case cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE: + measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + default: + measureMode = DWRITE_MEASURING_MODE_NATURAL; + break; + } + HRESULT hr = rt->DrawGlyphRun(0, 0, measureMode, run, params, color); + BitBlt(surface->dc, + area.left, area.top, + area.right - area.left, area.bottom - area.top, + rt->GetMemoryDC(), + 0, 0, + SRCCOPY | NOMIRRORBITMAP); + params->Release(); + rt->Release(); + gdiInterop->Release(); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area) +{ + HRESULT rv; + + ID2D1DCRenderTarget *rt = D2DFactory::RenderTarget(); + + // XXX don't we need to set RenderingParams on this RenderTarget? + + rv = rt->BindDC(surface->dc, &area); + + printf("Rendering to surface: %p\n", surface->dc); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF. + color = (color & 0xFF) << 16 | + (color & 0xFF00) | + (color & 0xFF0000) >> 16; + ID2D1SolidColorBrush *brush; + rv = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F(transform->m11, + transform->m12, + transform->m21, + transform->m22, + transform->dx, + transform->dy)); + } + rt->BeginDraw(); + rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush); + rt->EndDraw(); + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F::Identity()); + } + brush->Release(); + if (FAILED(rv)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Surface helper function */ +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + // TODO: Check font & surface for types. + cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); + cairo_win32_surface_t *dst = reinterpret_cast(surface); + cairo_int_status_t status; + /* We can only handle dwrite fonts */ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle opaque solid color sources */ + if (!_cairo_pattern_is_opaque_solid(source)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle operator SOURCE or OVER with the destination + * having no alpha */ + if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path */ + if (clip != NULL) { + if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { + cairo_region_t *clip_region; + cairo_int_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + + _cairo_win32_surface_set_clip_region (dst, clip_region); + } + } else { + _cairo_win32_surface_set_clip_region (surface, NULL); + } + + /* It is vital that dx values for dxy_buf are calculated from the delta of + * _logical_ x coordinates (not user x coordinates) or else the sum of all + * previous dx values may start to diverge from the current glyph's x + * coordinate due to accumulated rounding error. As a result strings could + * be painted shorter or longer than expected. */ + + AutoDWriteGlyphRun run; + run.allocate(num_glyphs); + + UINT16 *indices = const_cast(run.glyphIndices); + FLOAT *advances = const_cast(run.glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); + + BOOL transform = FALSE; + /* Needed to calculate bounding box for efficient blitting */ + INT32 smallestX = INT_MAX; + INT32 largestX = 0; + INT32 smallestY = INT_MAX; + INT32 largestY = 0; + for (int i = 0; i < num_glyphs; i++) { + if (glyphs[i].x < smallestX) { + smallestX = (INT32)glyphs[i].x; + } + if (glyphs[i].x > largestX) { + largestX = (INT32)glyphs[i].x; + } + if (glyphs[i].y < smallestY) { + smallestY = (INT32)glyphs[i].y; + } + if (glyphs[i].y > largestY) { + largestY = (INT32)glyphs[i].y; + } + } + /** + * Here we try to get a rough estimate of the area that this glyph run will + * cover on the surface. Since we use GDI interop to draw we will be copying + * data around the size of the area of the surface that we map. We will want + * to map an area as small as possible to prevent large surfaces to be + * copied around. We take the X/Y-size of the font as margin on the left/top + * twice the X/Y-size of the font as margin on the right/bottom. + * This should always cover the entire area where the glyphs are. + */ + RECT fontArea; + fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx); + fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2); + fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy); + fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2); + if (fontArea.left < 0) + fontArea.left = 0; + if (fontArea.top < 0) + fontArea.top = 0; + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.right <= fontArea.left || + fontArea.bottom <= fontArea.top) { + return CAIRO_INT_STATUS_SUCCESS; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + + run.bidiLevel = 0; + run.fontFace = dwriteff->dwriteface; + run.isSideways = FALSE; + if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && + dwritesf->mat.xx == scaled_font->font_matrix.xx && + dwritesf->mat.yy == scaled_font->font_matrix.yy) { + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. + offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); + advances[i] = 0.0; + } + run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; + } else { + transform = TRUE; + // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x - fontArea.left + EPSILON; + double y = glyphs[i].y - fontArea.top; + cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); + /** + * Since we will multiply by our ctm matrix later for rotation effects + * and such, adjust positions by the inverse matrix now. The Y-axis + * is inverted so the offset becomes negative. + */ + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + run.fontEmSize = 1.0f; + } + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; + COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + + DWRITE_MATRIX *mat; + if (transform) { + mat = &matrix; + } else { + mat = NULL; + } + + RECT area; + area.left = dst->extents.x; + area.top = dst->extents.y; + area.right = area.left + dst->extents.width; + area.bottom = area.top + dst->extents.height; + +#ifdef CAIRO_TRY_D2D_TO_GDI + status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst, + mat, + &run, + color, + fontArea); + + if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { +#endif + status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, + mat, + &run, + color, + dwritesf, + fontArea); + +#ifdef CAIRO_TRY_D2D_TO_GDI + } +#endif + + return status; +} + +#define ENHANCED_CONTRAST_REGISTRY_KEY \ + HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel" + +void +DWriteFactory::CreateRenderingParams() +{ + if (!Instance()) { + return; + } + + Instance()->CreateRenderingParams(&mDefaultRenderingParams); + + // For EnhancedContrast, we override the default if the user has not set it + // in the registry (by using the ClearType Tuner). + FLOAT contrast; + if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) { + contrast = mEnhancedContrast; + } else { + HKEY hKey; + if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY, + 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + contrast = mDefaultRenderingParams->GetEnhancedContrast(); + RegCloseKey(hKey); + } else { + contrast = 1.0; + } + } + + // For parameters that have not been explicitly set via the SetRenderingParams API, + // we copy values from default params (or our overridden value for contrast) + FLOAT gamma = + mGamma >= 1.0 && mGamma <= 2.2 ? + mGamma : mDefaultRenderingParams->GetGamma(); + FLOAT clearTypeLevel = + mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? + mClearTypeLevel : mDefaultRenderingParams->GetClearTypeLevel(); + DWRITE_PIXEL_GEOMETRY pixelGeometry = + mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ? + (DWRITE_PIXEL_GEOMETRY)mPixelGeometry : mDefaultRenderingParams->GetPixelGeometry(); + DWRITE_RENDERING_MODE renderingMode = + mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ? + (DWRITE_RENDERING_MODE)mRenderingMode : mDefaultRenderingParams->GetRenderingMode(); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, renderingMode, + &mCustomClearTypeRenderingParams); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, + &mForceGDIClassicRenderingParams); +} + +static cairo_bool_t +_name_tables_match (cairo_scaled_font_t *font1, + cairo_scaled_font_t *font2) +{ + unsigned long size1; + unsigned long size2; + cairo_int_status_t status1; + cairo_int_status_t status2; + unsigned char *buffer1; + unsigned char *buffer2; + cairo_bool_t result = false; + + if (!font1->backend || !font2->backend || + !font1->backend->load_truetype_table || + !font2->backend->load_truetype_table) + return false; + + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, NULL, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, NULL, &size2); + if (status1 || status2) + return false; + if (size1 != size2) + return false; + + buffer1 = (unsigned char*)malloc (size1); + buffer2 = (unsigned char*)malloc (size2); + + if (buffer1 && buffer2) { + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, buffer1, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, buffer2, &size2); + if (!status1 && !status2) { + result = memcmp (buffer1, buffer2, size1) == 0; + } + } + + free (buffer1); + free (buffer2); + return result; +} + +// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent +// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing +// paths or blitting glyph bitmaps. +cairo_int_status_t +_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_t **new_font) +{ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); + cairo_dwrite_font_face_t *dwface = reinterpret_cast(face); + + RefPtr gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + if (!gdiInterop) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + LOGFONTW logfont; + if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + // DW must have been using an outline font, so we want GDI to use the same, + // even if there's also a bitmap face available + logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; + + cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont); + if (!win32_face) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_matrix_t font_matrix; + cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); + + cairo_matrix_t ctm; + cairo_scaled_font_get_ctm (scaled_font, &ctm); + + cairo_font_options_t options; + cairo_scaled_font_get_font_options (scaled_font, &options); + + cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, + &font_matrix, + &ctm, + &options); + cairo_font_face_destroy (win32_face); + + if (!font) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (!_name_tables_match (font, scaled_font)) { + // If the font name tables aren't equal, then GDI may have failed to + // find the right font and substituted a different font. + cairo_scaled_font_destroy (font); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *new_font = font; + return CAIRO_INT_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-dwrite-private.h b/libs/cairo/src/cairo-dwrite-private.h new file mode 100644 index 000000000..1eaa46113 --- /dev/null +++ b/libs/cairo/src/cairo-dwrite-private.h @@ -0,0 +1,193 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +// DirectWrite is not available on all platforms. +typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + +/* cairo_scaled_font_t implementation */ +struct _cairo_dwrite_scaled_font { + cairo_scaled_font_t base; + cairo_matrix_t mat; + cairo_matrix_t mat_inverse; + cairo_antialias_t antialias_mode; + DWRITE_MEASURING_MODE measuring_mode; + cairo_bool_t manual_show_glyphs_allowed; + cairo_d2d_surface_t::TextRenderingState rendering_mode; +}; +typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; + +class DWriteFactory +{ +public: + static IDWriteFactory *Instance() + { + if (!mFactoryInstance) { + DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); + if (createDWriteFactory) { + HRESULT hr = createDWriteFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast(&mFactoryInstance)); + assert(SUCCEEDED(hr)); + } + } + return mFactoryInstance; + } + + static IDWriteFontCollection *SystemCollection() + { + if (!mSystemCollection) { + if (Instance()) { + HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection); + assert(SUCCEEDED(hr)); + } + } + return mSystemCollection; + } + + static IDWriteFontFamily *FindSystemFontFamily(const WCHAR *aFamilyName) + { + UINT32 idx; + BOOL found; + if (!SystemCollection()) { + return NULL; + } + SystemCollection()->FindFamilyName(aFamilyName, &idx, &found); + if (!found) { + return NULL; + } + + IDWriteFontFamily *family; + SystemCollection()->GetFontFamily(idx, &family); + return family; + } + + static IDWriteRenderingParams *RenderingParams(cairo_d2d_surface_t::TextRenderingState mode) + { + if (!mDefaultRenderingParams || + !mForceGDIClassicRenderingParams || + !mCustomClearTypeRenderingParams) + { + CreateRenderingParams(); + } + IDWriteRenderingParams *params; + if (mode == cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE) { + params = mDefaultRenderingParams; + } else if (mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC && mRenderingMode < 0) { + params = mForceGDIClassicRenderingParams; + } else { + params = mCustomClearTypeRenderingParams; + } + if (params) { + params->AddRef(); + } + return params; + } + + static void SetRenderingParams(FLOAT aGamma, + FLOAT aEnhancedContrast, + FLOAT aClearTypeLevel, + int aPixelGeometry, + int aRenderingMode) + { + mGamma = aGamma; + mEnhancedContrast = aEnhancedContrast; + mClearTypeLevel = aClearTypeLevel; + mPixelGeometry = aPixelGeometry; + mRenderingMode = aRenderingMode; + // discard any current RenderingParams objects + if (mCustomClearTypeRenderingParams) { + mCustomClearTypeRenderingParams->Release(); + mCustomClearTypeRenderingParams = NULL; + } + if (mForceGDIClassicRenderingParams) { + mForceGDIClassicRenderingParams->Release(); + mForceGDIClassicRenderingParams = NULL; + } + if (mDefaultRenderingParams) { + mDefaultRenderingParams->Release(); + mDefaultRenderingParams = NULL; + } + } + + static int GetClearTypeRenderingMode() { + return mRenderingMode; + } + +private: + static void CreateRenderingParams(); + + static IDWriteFactory *mFactoryInstance; + static IDWriteFontCollection *mSystemCollection; + static IDWriteRenderingParams *mDefaultRenderingParams; + static IDWriteRenderingParams *mCustomClearTypeRenderingParams; + static IDWriteRenderingParams *mForceGDIClassicRenderingParams; + static FLOAT mGamma; + static FLOAT mEnhancedContrast; + static FLOAT mClearTypeLevel; + static int mPixelGeometry; + static int mRenderingMode; +}; + +class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN +{ + static const int kNumAutoGlyphs = 256; + +public: + AutoDWriteGlyphRun() { + glyphCount = 0; + } + + ~AutoDWriteGlyphRun() { + if (glyphCount > kNumAutoGlyphs) { + delete[] glyphIndices; + delete[] glyphAdvances; + delete[] glyphOffsets; + } + } + + void allocate(int aNumGlyphs) { + glyphCount = aNumGlyphs; + if (aNumGlyphs <= kNumAutoGlyphs) { + glyphIndices = &mAutoIndices[0]; + glyphAdvances = &mAutoAdvances[0]; + glyphOffsets = &mAutoOffsets[0]; + } else { + glyphIndices = new UINT16[aNumGlyphs]; + glyphAdvances = new FLOAT[aNumGlyphs]; + glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; + } + } + +private: + DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; + FLOAT mAutoAdvances[kNumAutoGlyphs]; + UINT16 mAutoIndices[kNumAutoGlyphs]; +}; + +/* cairo_font_face_t implementation */ +struct _cairo_dwrite_font_face { + cairo_font_face_t base; + IDWriteFont *font; + IDWriteFontFace *dwriteface; +}; +typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t; + +DWRITE_MATRIX _cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix); + +// This will initialize a DWrite glyph run from cairo glyphs and a scaled_font. +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed); diff --git a/libs/cairo/src/cairo-eagle-context.c b/libs/cairo/src/cairo-eagle-context.c new file mode 100644 index 000000000..5f59f5239 --- /dev/null +++ b/libs/cairo/src/cairo-eagle-context.c @@ -0,0 +1,147 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include /* XXX dummy surface for glewInit() */ +#include + +typedef struct _cairo_eagle_context { + cairo_gl_context_t base; + + EGLDisplay display; + EGLContext context; +} cairo_eagle_context_t; + +typedef struct _cairo_eagle_surface { + cairo_gl_surface_t base; + + EGLSurface eagle; +} cairo_eagle_surface_t; + +static void +_eagle_make_current (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_eagle_context_t *ctx = abstract_ctx; + cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; + + eagleMakeCurrent (ctx->display, surface->eagle, surface->eagle, ctx->context); +} + +static void +_eagle_swap_buffers (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_eagle_context_t *ctx = abstract_ctx; + cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; + + eagleSwapBuffers (ctx->display, surface->eagle); +} + +static void +_eagle_destroy (void *abstract_ctx) +{ +} + +static cairo_bool_t +_eagle_init (EGLDisplay display, EGLContext context) +{ + const EGLint config_attribs[] = { + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_NONE + }; + const EGLint surface_attribs[] = { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE + }; + EGLConfig config; + EGLSurface dummy; + struct drm_i915_gem_create create; + struct drm_gem_flink flink; + int fd; + GLenum err; + + if (! eagleChooseConfig (display, config_attribs, &config, 1, NULL)) { + fprintf (stderr, "Unable to choose config\n"); + return FALSE; + } + + /* XXX */ + fd = eagleGetDisplayFD (display); + + create.size = 4096; + if (ioctl (fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) { + fprintf (stderr, "gem create failed: %m\n"); + return FALSE; + } + flink.handle = create.handle; + if (ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) { + fprintf (stderr, "gem flink failed: %m\n"); + return FALSE; + } + + dummy = eagleCreateSurfaceForName (display, config, flink.name, + 1, 1, 4, surface_attribs); + if (dummy == NULL) { + fprintf (stderr, "Failed to create dummy surface\n"); + return FALSE; + } + + eagleMakeCurrent (display, dummy, dummy, context); +} + +cairo_gl_context_t * +cairo_eagle_context_create (EGLDisplay display, EGLContext context) +{ + cairo_eagle_context_t *ctx; + cairo_status_t status; + + if (! _eagle_init (display, context)) { + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + ctx = calloc (1, sizeof (cairo_eagle_context_t)); + if (ctx == NULL) + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + + ctx->display = display; + ctx->context = context; + + ctx->base.make_current = _eagle_make_current; + ctx->base.swap_buffers = _eagle_swap_buffers; + ctx->base.destroy = _eagle_destroy; + + status = _cairo_gl_context_init (&ctx->base); + if (status) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + return &ctx->base; +} + +cairo_surface_t * +cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx, + EGLSurface eagle, + int width, + int height) +{ + cairo_eagle_surface_t *surface; + + if (ctx->status) + return _cairo_surface_create_in_error (ctx->status); + + surface = calloc (1, sizeof (cairo_eagle_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (ctx, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, width, height); + surface->eagle = eagle; + + return &surface->base.base; +} diff --git a/libs/cairo/src/cairo-error-private.h b/libs/cairo/src/cairo-error-private.h new file mode 100644 index 000000000..c7a9f7098 --- /dev/null +++ b/libs/cairo/src/cairo-error-private.h @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CAIRO_ERROR_PRIVATE_H_ +#define _CAIRO_ERROR_PRIVATE_H_ + +#include "cairo.h" +#include "cairo-compiler-private.h" + +CAIRO_BEGIN_DECLS + +#define _cairo_status_is_error(status) \ + (status != CAIRO_STATUS_SUCCESS && status <= CAIRO_STATUS_LAST_STATUS) + +cairo_private cairo_status_t +_cairo_error (cairo_status_t status); + +/* hide compiler warnings when discarding the return value */ +#define _cairo_error_throw(status) do { \ + cairo_status_t status__ = _cairo_error (status); \ + (void) status__; \ +} while (0) + +CAIRO_END_DECLS + +#endif /* _CAIRO_ERROR_PRIVATE_H_ */ diff --git a/libs/cairo/src/cairo-features-win32.h b/libs/cairo/src/cairo-features-win32.h new file mode 100644 index 000000000..ef202fc14 --- /dev/null +++ b/libs/cairo/src/cairo-features-win32.h @@ -0,0 +1,16 @@ +/* Generated by configure. Do not edit. */ +#ifndef CAIRO_FEATURES_H +#define CAIRO_FEATURES_H + +#define HAVE_WINDOWS_H 1 + +#define CAIRO_HAS_WIN32_SURFACE 1 +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_PNG_FUNCTIONS 1 +#define CAIRO_HAS_PS_SURFACE 1 +#define CAIRO_HAS_PDF_SURFACE 1 +#define CAIRO_HAS_SVG_SURFACE 1 +#define CAIRO_HAS_IMAGE_SURFACE 1 +#define CAIRO_HAS_USER_FONT 1 + +#endif diff --git a/libs/cairo/src/cairo-features.h.in b/libs/cairo/src/cairo-features.h.in new file mode 100644 index 000000000..9692c7cb7 --- /dev/null +++ b/libs/cairo/src/cairo-features.h.in @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FEATURES_H +#define CAIRO_FEATURES_H + +#include "cairo-platform.h" + +#ifdef __cplusplus +# define CAIRO_BEGIN_DECLS extern "C" { +# define CAIRO_END_DECLS } +#else +# define CAIRO_BEGIN_DECLS +# define CAIRO_END_DECLS +#endif + +#ifndef cairo_public +# define cairo_public +#endif + +#define CAIRO_VERSION_MAJOR 1 +#define CAIRO_VERSION_MINOR 10 +#define CAIRO_VERSION_MICRO 28 + +@PS_SURFACE_FEATURE@ + +@PDF_SURFACE_FEATURE@ + +@SVG_SURFACE_FEATURE@ + +@XLIB_SURFACE_FEATURE@ + +@XLIB_XRENDER_SURFACE_FEATURE@ + +@QUARTZ_SURFACE_FEATURE@ + +@QUARTZ_IMAGE_SURFACE_FEATURE@ + +@WIN32_SURFACE_FEATURE@ + +@OS2_SURFACE_FEATURE@ + +@DIRECTFB_SURFACE_FEATURE@ + +@QT_SURFACE_FEATURE@ + +@FT_FONT_FEATURE@ + +@WIN32_FONT_FEATURE@ + +@WIN32_DWRITE_FONT_FEATURE@ + +@WIN32_D2D_SURFACE_FEATURE@ + +@QUARTZ_FONT_FEATURE@ + +@TEE_SURFACE_FEATURE@ + +@PNG_FUNCTIONS_FEATURE@ + +@FC_FONT_FEATURE@ +#endif diff --git a/libs/cairo/src/cairo-fixed-private.h b/libs/cairo/src/cairo-fixed-private.h new file mode 100644 index 000000000..a00e99cf3 --- /dev/null +++ b/libs/cairo/src/cairo-fixed-private.h @@ -0,0 +1,326 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FIXED_PRIVATE_H +#define CAIRO_FIXED_PRIVATE_H + +#include "cairo-fixed-type-private.h" + +#include "cairo-wideint-private.h" + +/* Implementation */ + +#if (CAIRO_FIXED_BITS != 32) +# error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type. +# error To remove this limitation, you will have to fix the tesselator. +#endif + +#define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS)) +#define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS)) +#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS)) +#define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1)) + +#define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS))) +#define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK) + +static inline cairo_fixed_t +_cairo_fixed_from_int (int i) +{ + return i << CAIRO_FIXED_FRAC_BITS; +} + +/* This is the "magic number" approach to converting a double into fixed + * point as described here: + * + * http://www.stereopsis.com/sree/fpu2006.html (an overview) + * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail) + * + * The basic idea is to add a large enough number to the double that the + * literal floating point is moved up to the extent that it forces the + * double's value to be shifted down to the bottom of the mantissa (to make + * room for the large number being added in). Since the mantissa is, at a + * given moment in time, a fixed point integer itself, one can convert a + * float to various fixed point representations by moving around the point + * of a floating point number through arithmetic operations. This behavior + * is reliable on most modern platforms as it is mandated by the IEEE-754 + * standard for floating point arithmetic. + * + * For our purposes, a "magic number" must be carefully selected that is + * both large enough to produce the desired point-shifting effect, and also + * has no lower bits in its representation that would interfere with our + * value at the bottom of the mantissa. The magic number is calculated as + * follows: + * + * (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5 + * + * where in our case: + * - MANTISSA_SIZE for 64-bit doubles is 52 + * - FRACTIONAL_SIZE for 16.16 fixed point is 16 + * + * Although this approach provides a very large speedup of this function + * on a wide-array of systems, it does come with two caveats: + * + * 1) It uses banker's rounding as opposed to arithmetic rounding. + * 2) It doesn't function properly if the FPU is in single-precision + * mode. + */ + +/* The 16.16 number must always be available */ +#define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0) + +#if CAIRO_FIXED_BITS <= 32 +#define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5) + +/* For 32-bit fixed point numbers */ +static inline cairo_fixed_t +_cairo_fixed_from_double (double d) +{ + union { + double d; + int32_t i[2]; + } u; + + u.d = d + CAIRO_MAGIC_NUMBER_FIXED; +#ifdef FLOAT_WORDS_BIGENDIAN + return u.i[1]; +#else + return u.i[0]; +#endif +} + +#else +# error Please define a magic number for your fixed point type! +# error See cairo-fixed-private.h for details. +#endif + +static inline cairo_fixed_t +_cairo_fixed_from_26_6 (uint32_t i) +{ +#if CAIRO_FIXED_FRAC_BITS > 6 + return i << (CAIRO_FIXED_FRAC_BITS - 6); +#else + return i >> (6 - CAIRO_FIXED_FRAC_BITS); +#endif +} + +static inline cairo_fixed_t +_cairo_fixed_from_16_16 (uint32_t i) +{ +#if CAIRO_FIXED_FRAC_BITS > 16 + return i << (CAIRO_FIXED_FRAC_BITS - 16); +#else + return i >> (16 - CAIRO_FIXED_FRAC_BITS); +#endif +} + +static inline double +_cairo_fixed_to_double (cairo_fixed_t f) +{ + return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; +} + +static inline float +_cairo_fixed_to_float (cairo_fixed_t f) +{ + return ((float) f) / CAIRO_FIXED_ONE_FLOAT; +} + +static inline int +_cairo_fixed_is_integer (cairo_fixed_t f) +{ + return (f & CAIRO_FIXED_FRAC_MASK) == 0; +} + +static inline cairo_fixed_t +_cairo_fixed_floor (cairo_fixed_t f) +{ + return f & ~CAIRO_FIXED_FRAC_MASK; +} + +static inline cairo_fixed_t +_cairo_fixed_round (cairo_fixed_t f) +{ + return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2); +} + +static inline cairo_fixed_t +_cairo_fixed_round_down (cairo_fixed_t f) +{ + return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2); +} + +static inline int +_cairo_fixed_integer_part (cairo_fixed_t f) +{ + return f >> CAIRO_FIXED_FRAC_BITS; +} + +static inline int +_cairo_fixed_integer_round (cairo_fixed_t f) +{ + return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2); +} + +static inline int +_cairo_fixed_integer_round_down (cairo_fixed_t f) +{ + return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2); +} + +static inline int +_cairo_fixed_fractional_part (cairo_fixed_t f) +{ + return f & CAIRO_FIXED_FRAC_MASK; +} + +static inline int +_cairo_fixed_integer_floor (cairo_fixed_t f) +{ + if (f >= 0) + return f >> CAIRO_FIXED_FRAC_BITS; + else + return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1; +} + +static inline int +_cairo_fixed_integer_ceil (cairo_fixed_t f) +{ + if (f > 0) + return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1; + else + return - (-f >> CAIRO_FIXED_FRAC_BITS); +} + +/* A bunch of explicit 16.16 operators; we need these + * to interface with pixman and other backends that require + * 16.16 fixed point types. + */ +static inline cairo_fixed_16_16_t +_cairo_fixed_to_16_16 (cairo_fixed_t f) +{ +#if (CAIRO_FIXED_FRAC_BITS == 16) && (CAIRO_FIXED_BITS == 32) + return f; +#elif CAIRO_FIXED_FRAC_BITS > 16 + /* We're just dropping the low bits, so we won't ever got over/underflow here */ + return f >> (CAIRO_FIXED_FRAC_BITS - 16); +#else + cairo_fixed_16_16_t x; + + /* Handle overflow/underflow by clamping to the lowest/highest + * value representable as 16.16 + */ + if ((f >> CAIRO_FIXED_FRAC_BITS) < INT16_MIN) { + x = INT32_MIN; + } else if ((f >> CAIRO_FIXED_FRAC_BITS) > INT16_MAX) { + x = INT32_MAX; + } else { + x = f << (16 - CAIRO_FIXED_FRAC_BITS); + } + + return x; +#endif +} + +static inline cairo_fixed_16_16_t +_cairo_fixed_16_16_from_double (double d) +{ + union { + double d; + int32_t i[2]; + } u; + + u.d = d + CAIRO_MAGIC_NUMBER_FIXED_16_16; +#ifdef FLOAT_WORDS_BIGENDIAN + return u.i[1]; +#else + return u.i[0]; +#endif +} + +static inline int +_cairo_fixed_16_16_floor (cairo_fixed_16_16_t f) +{ + if (f >= 0) + return f >> 16; + else + return -((-f - 1) >> 16) - 1; +} + +static inline double +_cairo_fixed_16_16_to_double (cairo_fixed_16_16_t f) +{ + return ((double) f) / (double) (1 << 16); +} + +#if CAIRO_FIXED_BITS == 32 + +static inline cairo_fixed_t +_cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b) +{ + cairo_int64_t temp = _cairo_int32x32_64_mul (a, b); + return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS)); +} + +/* computes round (a * b / c) */ +static inline cairo_fixed_t +_cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) +{ + cairo_int64_t ab = _cairo_int32x32_64_mul (a, b); + cairo_int64_t c64 = _cairo_int32_to_int64 (c); + return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo); +} + +/* computes floor (a * b / c) */ +static inline cairo_fixed_t +_cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) +{ + return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c); +} + + +static inline cairo_fixed_t +_cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t x) +{ + cairo_fixed_t y, dx; + + if (x == p1->x) + return p1->y; + if (x == p2->x) + return p2->y; + + y = p1->y; + dx = p2->x - p1->x; + if (dx != 0) + y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx); + + return y; +} + +static inline cairo_fixed_t +_cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == p1->y) + return p1->x; + if (y == p2->y) + return p2->x; + + x = p1->x; + dy = p2->y - p1->y; + if (dy != 0) + x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy); + + return x; +} + +#else +# error Please define multiplication and other operands for your fixed-point type size +#endif + +#endif /* CAIRO_FIXED_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-fixed-type-private.h b/libs/cairo/src/cairo-fixed-type-private.h new file mode 100644 index 000000000..d2bf6cb66 --- /dev/null +++ b/libs/cairo/src/cairo-fixed-type-private.h @@ -0,0 +1,43 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-fixed.c b/libs/cairo/src/cairo-fixed.c new file mode 100644 index 000000000..75805f3d3 --- /dev/null +++ b/libs/cairo/src/cairo-fixed.c @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-fixed-private.h" diff --git a/libs/cairo/src/cairo-font-face-twin-data.c b/libs/cairo/src/cairo-font-face-twin-data.c new file mode 100644 index 000000000..ff09cb2be --- /dev/null +++ b/libs/cairo/src/cairo-font-face-twin-data.c @@ -0,0 +1,1072 @@ +/* See cairo-font-face-twin.c for copyright info */ + +#include "cairoint.h" + +const int8_t _cairo_twin_outlines[] = { +/* 0x0 '\0' offset 0 */ + 0, 24, 42, 0, 2, 2, + 0, 24, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 24, -42, + 'l', 24, 0, + 'l', 0, 0, + 'e', + 'X', 'X', +/* 0x20 ' ' offset 28 */ + 0, 4, 0, 0, 0, 0, + /* snap_x */ + /* snap_y */ + 'e', + 'X', 'X', 'X', + 'X', 'X', +/* 0x21 '!' offset 40 */ + 0, 0, 42, 0, 1, 3, + 0, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -14, + 'm', 0, 0, + 'l', 0, 0, + 'e', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x22 '"' offset 90 */ + 0, 16, 42, -28, 2, 2, + 0, 16, /* snap_x */ + -42, -28, /* snap_y */ + 'm', 0, -42, + 'l', 0, -28, + 'm', 16, -42, + 'l', 16, -28, + 'e', + 'X', +/* 0x23 '#' offset 114 */ + 0, 30, 50, 14, 2, 5, + 0, 30, /* snap_x */ + -24, -21, -15, -12, 0, /* snap_y */ + 'm', 16, -50, + 'l', 2, 14, + 'm', 28, -50, + 'l', 14, 14, + 'm', 2, -24, + 'l', 30, -24, + 'm', 0, -12, + 'l', 28, -12, + 'e', +/* 0x24 '$' offset 152 */ + 0, 28, 50, 8, 4, 4, + 0, 10, 18, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 10, -50, + 'l', 10, 8, + 'm', 18, -50, + 'l', 18, 8, + 'm', 28, -36, + 'c', 24, -42, 18, -42, 14, -42, + 'c', 10, -42, 0, -42, 0, -34, + 'c', 0, -25, 8, -24, 14, -22, + 'c', 20, -20, 28, -19, 28, -9, + 'c', 28, 0, 18, 0, 14, 0, + 'c', 10, 0, 4, 0, 0, -6, + 'e', +/* 0x25 '%' offset 224 */ + 0, 36, 42, 0, 4, 7, + 0, 14, 22, 36, /* snap_x */ + -42, -38, -28, -21, -15, -14, 0, /* snap_y */ + 'm', 10, -42, + 'c', 12, -41, 14, -40, 14, -36, + 'c', 14, -30, 11, -28, 6, -28, + 'c', 2, -28, 0, -30, 0, -34, + 'c', 0, -39, 3, -42, 8, -42, + 'l', 10, -42, + 'c', 18, -37, 28, -37, 36, -42, + 'l', 0, 0, + 'm', 28, -14, + 'c', 24, -14, 22, -11, 22, -6, + 'c', 22, -2, 24, 0, 28, 0, + 'c', 33, 0, 36, -2, 36, -8, + 'c', 36, -12, 34, -14, 30, -14, + 'l', 28, -14, + 'e', + 'X', 'X', 'X', +/* 0x26 '&' offset 323 */ + 0, 40, 42, 0, 4, 4, + 0, 10, 22, 40, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 40, -24, + 'c', 40, -27, 39, -28, 37, -28, + 'c', 29, -28, 32, 0, 12, 0, + 'c', 0, 0, 0, -8, 0, -10, + 'c', 0, -24, 22, -20, 22, -34, + 'c', 22, -45, 10, -45, 10, -34, + 'c', 10, -27, 25, 0, 36, 0, + 'c', 39, 0, 40, -1, 40, -4, + 'e', +/* 0x27 ''' offset 390 */ + 0, 4, 42, -30, 2, 2, + 0, 4, /* snap_x */ + -42, -28, /* snap_y */ + 'm', 2, -38, + 'c', -1, -38, -1, -42, 2, -42, + 'c', 6, -42, 5, -33, 0, -30, + 'e', + 'X', +/* 0x28 '(' offset 419 */ + 0, 14, 50, 14, 2, 2, + 0, 14, /* snap_x */ + -50, 14, /* snap_y */ + 'm', 14, -50, + 'c', -5, -32, -5, -5, 14, 14, + 'e', + 'X', +/* 0x29 ')' offset 441 */ + 0, 14, 50, 14, 2, 2, + 0, 14, /* snap_x */ + -15, 14, /* snap_y */ + 'm', 0, -50, + 'c', 19, -34, 19, -2, 0, 14, + 'e', + 'X', +/* 0x2a '*' offset 463 */ + 0, 20, 30, -6, 3, 3, + 0, 10, 20, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 10, -30, + 'l', 10, -6, + 'm', 0, -24, + 'l', 20, -12, + 'm', 20, -24, + 'l', 0, -12, + 'e', +/* 0x2b '+' offset 494 */ + 0, 36, 36, 0, 3, 4, + 0, 18, 36, /* snap_x */ + -21, -18, -15, 0, /* snap_y */ + 'm', 18, -36, + 'l', 18, 0, + 'm', 0, -18, + 'l', 36, -18, + 'e', +/* 0x2c ',' offset 520 */ + 0, 4, 4, 8, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 4, -2, + 'c', 4, 1, 0, 1, 0, -2, + 'c', 0, -5, 4, -5, 4, -2, + 'c', 4, 4, 2, 6, 0, 8, + 'e', +/* 0x2d '-' offset 556 */ + 0, 36, 18, -18, 2, 4, + 0, 36, /* snap_x */ + -21, -18, -15, 0, /* snap_y */ + 'm', 0, -18, + 'l', 36, -18, + 'e', +/* 0x2e '.' offset 575 */ + 0, 4, 4, 0, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -4, + 'c', -1, -4, -1, 0, 2, 0, + 'c', 5, 0, 5, -4, 2, -4, + 'e', +/* 0x2f '/' offset 604 */ + 0, 36, 50, 14, 2, 3, + 0, 36, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 36, -50, + 'l', 0, 14, + 'e', +/* 0x30 '0' offset 622 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'c', 9, -42, 0, -42, 0, -21, + 'c', 0, 0, 9, 0, 14, 0, + 'c', 19, 0, 28, 0, 28, -21, + 'c', 28, -42, 19, -42, 14, -42, + 'E', +/* 0x31 '1' offset 666 */ + 0, 28, 42, 0, 2, 3, + 0, 17, 28 /* snap_x */ + -42, -34, 0, /* snap_y */ + 'm', 7, -34, + 'c', 11, -35, 15, -38, 17, -42, + 'l', 17, 0, + 'e', +/* 0x32 '2' offset 691 */ + 0, 28, 42, 0, 4, 4, + 0, 2, 26, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 2, -32, + 'c', 2, -34, 2, -42, 14, -42, + 'c', 26, -42, 26, -34, 26, -32, + 'c', 26, -30, 25, -25, 10, -10, + 'l', 0, 0, + 'l', 28, 0, + 'e', +/* 0x33 '3' offset 736 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -26, -21, -15, 0, /* snap_y */ + 'm', 4, -42, + 'l', 26, -42, + 'l', 14, -26, + 'c', 21, -26, 28, -26, 28, -14, + 'c', 28, 0, 17, 0, 13, 0, + 'c', 8, 0, 3, -1, 0, -8, + 'e', +/* 0x34 '4' offset 780 */ + 0, 28, 42, 0, 3, 3, + 0, 20, 30, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 20, 0, + 'l', 20, -42, + 'l', 0, -14, + 'l', 30, -14, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x35 '5' offset 809 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -28, -21, -15, 0, /* snap_y */ + 'm', 24, -42, + 'l', 4, -42, + 'l', 2, -24, + 'c', 5, -27, 10, -28, 13, -28, + 'c', 16, -28, 28, -28, 28, -14, + 'c', 28, 0, 16, 0, 13, 0, + 'c', 10, 0, 3, 0, 0, -8, + 'e', +/* 0x36 '6' offset 860 */ + 0, 28, 42, 0, 2, 5, + 0, 26, /* snap_x */ + -42, -26, -21, -15, 0, /* snap_y */ + 'm', 24, -36, + 'c', 22, -41, 19, -42, 14, -42, + 'c', 9, -42, 0, -41, 0, -19, + 'c', 0, -1, 9, 0, 13, 0, + 'c', 18, 0, 26, -3, 26, -13, + 'c', 26, -18, 23, -26, 13, -26, + 'c', 10, -26, 1, -24, 0, -14, + 'e', +/* 0x37 '7' offset 919 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 28, -42, + 'l', 8, 0, + 'e', + 'X', 'X', 'X', +/* 0x38 '8' offset 944 */ + 0, 28, 42, 0, 4, 4, + 0, 2, 26, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'c', 5, -42, 2, -40, 2, -34, + 'c', 2, -18, 28, -32, 28, -11, + 'c', 28, 0, 18, 0, 14, 0, + 'c', 10, 0, 0, 0, 0, -11, + 'c', 0, -32, 26, -18, 26, -34, + 'c', 26, -40, 23, -42, 14, -42, + 'E', +/* 0x39 '9' offset 1004 */ + 0, 28, 42, 0, 2, 5, + 0, 26, /* snap_x */ + -42, -21, -16, -15, 0, /* snap_y */ + 'm', 26, -28, + 'c', 25, -16, 13, -16, 13, -16, + 'c', 8, -16, 0, -19, 0, -29, + 'c', 0, -34, 3, -42, 13, -42, + 'c', 24, -42, 26, -32, 26, -23, + 'c', 26, -14, 24, 0, 12, 0, + 'c', 7, 0, 4, -2, 2, -6, + 'e', +/* 0x3a ':' offset 1063 */ + 0, 4, 28, 0, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -28, + 'c', -1, -28, -1, -24, 2, -24, + 'c', 5, -24, 5, -28, 2, -28, + 'm', 2, -4, + 'c', -1, -4, -1, 0, 2, 0, + 'c', 5, 0, 5, -4, 2, -4, + 'e', +/* 0x3b ';' offset 1109 */ + 0, 4, 28, 8, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -28, + 'c', -1, -28, -1, -24, 2, -24, + 'c', 5, -24, 5, -28, 2, -28, + 'm', 4, -2, + 'c', 4, 1, 0, 1, 0, -2, + 'c', 0, -5, 4, -5, 4, -2, + 'c', 4, 3, 2, 6, 0, 8, + 'e', +/* 0x3c '<' offset 1162 */ + 0, 32, 36, 0, 2, 3, + 0, 32, /* snap_x */ + -36, -18, 0, /* snap_y */ + 'm', 32, -36, + 'l', 0, -18, + 'l', 32, 0, + 'e', +/* 0x3d '=' offset 1183 */ + 0, 36, 24, -12, 2, 2, + 0, 36, /* snap_x */ + -24, -15, /* snap_y */ + 'm', 0, -24, + 'l', 36, -24, + 'm', 0, -12, + 'l', 36, -12, + 'e', + 'X', 'X', 'X', +/* 0x3e '>' offset 1209 */ + 0, 32, 36, 0, 2, 3, + 0, 32, /* snap_x */ + -36, -18, 0, /* snap_y */ + 'm', 0, -36, + 'l', 32, -18, + 'l', 0, 0, + 'e', +/* 0x3f '?' offset 1230 */ + 0, 24, 42, 0, 3, 4, + 0, 12, 24, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 0, -32, + 'c', 0, -34, 0, -42, 12, -42, + 'c', 24, -42, 24, -34, 24, -32, + 'c', 24, -29, 24, -24, 12, -20, + 'l', 12, -14, + 'm', 12, 0, + 'l', 12, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x40 '@' offset 1288 */ + 0, 42, 42, 0, 1, 6, + 30, /* snap_x */ + -42, -32, -21, -15, -10, 0, /* snap_y */ + 'm', 30, -26, + 'c', 28, -31, 24, -32, 21, -32, + 'c', 10, -32, 10, -23, 10, -19, + 'c', 10, -13, 11, -10, 19, -10, + 'c', 30, -10, 28, -21, 30, -32, + 'c', 27, -10, 30, -10, 34, -10, + 'c', 41, -10, 42, -19, 42, -22, + 'c', 42, -34, 34, -42, 21, -42, + 'c', 9, -42, 0, -34, 0, -21, + 'c', 0, -9, 8, 0, 21, 0, + 'c', 30, 0, 34, -3, 36, -6, + 'e', +/* 0x41 'A' offset 1375 */ + 0, 32, 42, 0, 2, 3, + 0, 32, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 0, 0, + 'l', 16, -42, + 'l', 32, 0, + 'm', 6, -14, + 'l', 26, -14, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x42 'B' offset 1406 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 32, -22, 18, -22, + 'l', 0, -22, + 'l', 18, -22, + 'c', 32, -22, 32, 0, 18, 0, + 'E', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x43 'C' offset 1455 */ + 0, 30, 42, 0, 2, 4, + 0, 30, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 30, -32, + 'c', 26, -42, 21, -42, 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 21, 0, 26, 0, 30, -10, + 'e', +/* 0x44 'D' offset 1499 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 14, -42, + 'c', 33, -42, 33, 0, 14, 0, + 'E', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x45 'E' offset 1534 */ + 0, 26, 42, 0, 2, 3, + 0, 26, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 26, -42, + 'l', 0, -42, + 'l', 0, 0, + 'l', 26, 0, + 'm', 0, -22, + 'l', 16, -22, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x46 'F' offset 1572 */ + 0, 26, 42, 0, 2, 3, + 0, 26, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 26, -42, + 'm', 0, -22, + 'l', 16, -22, + 'e', + 'X', 'X', 'X', + 'X', 'X', +/* 0x47 'G' offset 1604 */ + 0, 30, 42, 0, 2, 5, + 0, 30, /* snap_x */ + -42, -21, -16, -15, 0, /* snap_y */ + 'm', 30, -32, + 'c', 26, -42, 21, -42, 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 28, 0, 30, -7, 30, -16, + 'l', 20, -16, + 'e', + 'X', 'X', 'X', +/* 0x48 'H' offset 1655 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 28, -42, + 'l', 28, 0, + 'm', 0, -22, + 'l', 28, -22, + 'e', + 'X', +/* 0x49 'I' offset 1686 */ + 0, 0, 42, 0, 1, 2, + 0, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x4a 'J' offset 1703 */ + 0, 20, 42, 0, 2, 3, + 0, 20, /* snap_x */ + -42, -15, 0, /* snap_y */ + 'm', 20, -42, + 'l', 20, -10, + 'c', 20, 3, 0, 3, 0, -10, + 'l', 0, -14, + 'e', +/* 0x4b 'K' offset 1731 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 28, -42, + 'l', 0, -14, + 'm', 10, -24, + 'l', 28, 0, + 'e', +/* 0x4c 'L' offset 1761 */ + 0, 24, 42, 0, 2, 2, + 0, 24, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'l', 24, 0, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x4d 'M' offset 1785 */ + 0, 32, 42, 0, 2, 2, + 0, 32, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 16, 0, + 'l', 32, -42, + 'l', 32, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x4e 'N' offset 1821 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 28, 0, + 'l', 28, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x4f 'O' offset 1851 */ + 0, 32, 42, 0, 2, 4, + 0, 32, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 30, 0, 32, -13, 32, -21, + 'c', 32, -29, 30, -42, 16, -42, + 'E', +/* 0x50 'P' offset 1895 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -21, -20, -15, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 32, -20, 18, -20, + 'l', 0, -20, + 'e', + 'X', 'X', 'X', +/* 0x51 'Q' offset 1931 */ + 0, 32, 42, 4, 2, 4, + 0, 32, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 30, 0, 32, -13, 32, -21, + 'c', 32, -29, 30, -42, 16, -42, + 'M', 18, -8, + 'l', 30, 4, + 'e', +/* 0x52 'R' offset 1981 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -22, -21, -15, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 31, -22, 18, -22, + 'l', 0, -22, + 'm', 14, -22, + 'l', 28, 0, + 'e', + 'X', 'X', 'X', +/* 0x53 'S' offset 2023 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 28, -36, + 'c', 25, -41, 21, -42, 14, -42, + 'c', 10, -42, 0, -42, 0, -34, + 'c', 0, -17, 28, -28, 28, -9, + 'c', 28, 0, 19, 0, 14, 0, + 'c', 7, 0, 3, -1, 0, -6, + 'e', +/* 0x54 'T' offset 2074 */ + 0, 28, 42, 0, 3, 4, + 0, 14, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'l', 14, 0, + 'm', 0, -42, + 'l', 28, -42, + 'e', +/* 0x55 'U' offset 2100 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -12, + 'c', 0, 4, 28, 4, 28, -12, + 'l', 28, -42, + 'e', + 'X', +/* 0x56 'V' offset 2128 */ + 0, 32, 42, 0, 2, 2, + 0, 32, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 16, 0, + 'l', 32, -42, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x57 'W' offset 2152 */ + 0, 40, 42, 0, 2, 2, + 0, 40, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 10, 0, + 'l', 20, -42, + 'l', 30, 0, + 'l', 40, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x58 'X' offset 2188 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 28, 0, + 'm', 28, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x59 'Y' offset 2212 */ + 0, 32, 42, 0, 3, 3, + 0, 16, 32, /* snap_x */ + -42, -21, 0, /* snap_y */ + 'm', 0, -42, + 'l', 16, -22, + 'l', 16, 0, + 'm', 32, -42, + 'l', 16, -22, + 'e', +/* 0x5a 'Z' offset 2240 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 28, 0, + 'l', 0, 0, + 'l', 28, -42, + 'l', 0, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x5b '[' offset 2271 */ + 0, 14, 44, 0, 2, 4, + 0, 14, /* snap_x */ + -44, -21, -15, 0, /* snap_y */ + 'm', 14, -44, + 'l', 0, -44, + 'l', 0, 0, + 'l', 14, 0, + 'e', +/* 0x5c '\' offset 2296 */ + 0, 36, 50, 14, 2, 3, + 0, 36, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 0, -50, + 'l', 36, 14, + 'e', +/* 0x5d ']' offset 2314 */ + 0, 14, 44, 0, 2, 4, + 0, 14, /* snap_x */ + -44, -21, -15, 0, /* snap_y */ + 'm', 0, -44, + 'l', 14, -44, + 'l', 14, 0, + 'l', 0, 0, + 'e', +/* 0x5e '^' offset 2339 */ + 0, 32, 46, -18, 2, 3, + 0, 32, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 0, -18, + 'l', 16, -46, + 'l', 32, -18, + 'e', + 'X', 'X', 'X', +/* 0x5f '_' offset 2363 */ + 0, 36, 0, 0, 2, 1, + 0, 36, /* snap_x */ + 0, /* snap_y */ + 'm', 0, 0, + 'l', 36, 0, + 'e', + 'X', 'X', +/* 0x60 '`' offset 2381 */ + 0, 4, 42, -30, 2, 2, + 0, 4, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 4, -42, + 'c', 2, -40, 0, -39, 0, -32, + 'c', 0, -31, 1, -30, 2, -30, + 'c', 5, -30, 5, -34, 2, -34, + 'e', + 'X', +/* 0x61 'a' offset 2417 */ + 0, 24, 28, 0, 2, 4, + 0, 24, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 24, -28, + 'l', 24, 0, + 'm', 24, -22, + 'c', 21, -27, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -1, 24, -6, + 'e', +/* 0x62 'b' offset 2467 */ + 0, 24, 42, 0, 2, 4, + 0, 24, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 0, -22, + 'c', 3, -26, 6, -28, 11, -28, + 'c', 22, -28, 24, -19, 24, -14, + 'c', 24, -9, 22, 0, 11, 0, + 'c', 6, 0, 3, -2, 0, -6, + 'e', +/* 0x63 'c' offset 2517 */ + 0, 24, 28, 0, 2, 4, + 0, 24, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x64 'd' offset 2561 */ + 0, 24, 42, 0, 2, 4, + 0, 24, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 24, -42, + 'l', 24, 0, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x65 'e' offset 2611 */ + 0, 24, 28, 0, 2, 5, + 0, 24, /* snap_x */ + -28, -21, -16, -15, 0, /* snap_y */ + 'm', 0, -16, + 'l', 24, -16, + 'c', 24, -20, 24, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x66 'f' offset 2659 */ + 0, 16, 42, 0, 3, 5, + 0, 6, 16, /* snap_x */ + -42, -28, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 8, -42, 6, -40, 6, -34, + 'l', 6, 0, + 'm', 0, -28, + 'l', 14, -28, + 'e', +/* 0x67 'g' offset 2693 */ + 0, 24, 28, 14, 2, 5, + 0, 24, /* snap_x */ + -28, -21, -15, 0, 14, /* snap_y */ + 'm', 24, -28, + 'l', 24, 4, + 'c', 23, 14, 16, 14, 13, 14, + 'c', 10, 14, 8, 14, 6, 12, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x68 'h' offset 2758 */ + 0, 22, 42, 0, 2, 4, + 0, 22, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 0, -20, + 'c', 8, -32, 22, -31, 22, -20, + 'l', 22, 0, + 'e', +/* 0x69 'i' offset 2790 */ + 0, 0, 44, 0, 1, 3, + 0, /* snap_x */ + -42, -28, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -42, + 'm', 0, -28, + 'l', 0, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', + 'X', 'X', +/* 0x6a 'j' offset 2826 */ + -8, 4, 44, 14, 3, 5, + -8, 2, 4, /* snap_x */ + -42, -21, -15, 0, 14, /* snap_y */ + 'm', 2, -42, + 'l', 2, -42, + 'm', 2, -28, + 'l', 2, 6, + 'c', 2, 13, -1, 14, -8, 14, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x6b 'k' offset 2870 */ + 0, 22, 42, 0, 2, 3, + 0, 22, /* snap_x */ + -42, -28, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 20, -28, + 'l', 0, -8, + 'm', 8, -16, + 'l', 22, 0, + 'e', +/* 0x6c 'l' offset 2900 */ + 0, 0, 42, 0, 1, 2, + 0, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x6d 'm' offset 2917 */ + 0, 44, 28, 0, 3, 3, + 0, 22, 44, /* snap_x */ + -28, -21, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -20, + 'c', 5, -29, 22, -33, 22, -20, + 'l', 22, 0, + 'm', 22, -20, + 'c', 27, -29, 44, -33, 44, -20, + 'l', 44, 0, + 'e', + 'X', +/* 0x6e 'n' offset 2963 */ + 0, 22, 28, 0, 2, 3, + 0, 22, /* snap_x */ + -28, -21, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -20, + 'c', 4, -28, 22, -34, 22, -20, + 'l', 22, 0, + 'e', + 'X', +/* 0x6f 'o' offset 2995 */ + 0, 26, 28, 0, 2, 4, + 0, 26, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 24, 0, 26, -9, 26, -14, + 'c', 26, -19, 24, -28, 13, -28, + 'E', +/* 0x70 'p' offset 3039 */ + 0, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -21, 0, 14, /* snap_y */ + 'm', 0, -28, + 'l', 0, 14, + 'm', 0, -22, + 'c', 3, -26, 6, -28, 11, -28, + 'c', 22, -28, 24, -19, 24, -14, + 'c', 24, -9, 22, 0, 11, 0, + 'c', 6, 0, 3, -2, 0, -6, + 'e', +/* 0x71 'q' offset 3089 */ + 0, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -21, 0, 14, /* snap_y */ + 'm', 24, -28, + 'l', 24, 14, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x72 'r' offset 3139 */ + 0, 16, 28, 0, 2, 4, + 0, 16, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -16, + 'c', 2, -27, 7, -28, 16, -28, + 'e', +/* 0x73 's' offset 3168 */ + 0, 22, 28, 0, 2, 4, + 0, 22, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 22, -22, + 'c', 22, -27, 16, -28, 11, -28, + 'c', 4, -28, 0, -26, 0, -22, + 'c', 0, -11, 22, -20, 22, -7, + 'c', 22, 0, 17, 0, 11, 0, + 'c', 6, 0, 0, -1, 0, -6, + 'e', +/* 0x74 't' offset 3219 */ + 0, 16, 42, 0, 3, 4, + 0, 6, 16, /* snap_x */ + -42, -28, -21, 0, /* snap_y */ + 'm', 6, -42, + 'l', 6, -8, + 'c', 6, -2, 8, 0, 16, 0, + 'm', 0, -28, + 'l', 14, -28, + 'e', +/* 0x75 'u' offset 3252 */ + 0, 22, 28, 0, 2, 3, + 0, 22, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, -8, + 'c', 0, 6, 18, 0, 22, -8, + 'm', 22, -28, + 'l', 22, 0, + 'e', +/* 0x76 'v' offset 3283 */ + 0, 24, 28, 0, 2, 3, + 0, 24, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 12, 0, + 'l', 24, -28, + 'e', + 'X', 'X', 'X', +/* 0x77 'w' offset 3307 */ + 0, 32, 28, 0, 2, 3, + 0, 32, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 8, 0, + 'l', 16, -28, + 'l', 24, 0, + 'l', 32, -28, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x78 'x' offset 3343 */ + 0, 22, 28, 0, 2, 2, + 0, 22, /* snap_x */ + -28, 0, /* snap_y */ + 'm', 0, -28, + 'l', 22, 0, + 'm', 22, -28, + 'l', 0, 0, + 'e', + 'X', +/* 0x79 'y' offset 3367 */ + -2, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -15, 0, 14, /* snap_y */ + 'm', 0, -28, + 'l', 12, 0, + 'm', 24, -28, + 'l', 12, 0, + 'c', 6, 13, 0, 14, -2, 14, + 'e', +/* 0x7a 'z' offset 3399 */ + 0, 22, 28, 0, 2, 4, + 0, 22, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 22, 0, + 'l', 0, 0, + 'l', 22, -28, + 'l', 0, -28, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x7b '{' offset 3430 */ + 0, 16, 44, 0, 3, 5, + 0, 6, 16, /* snap_x */ + -44, -24, -21, -15, 0, /* snap_y */ + 'm', 16, -44, + 'c', 10, -44, 6, -42, 6, -36, + 'l', 6, -24, + 'l', 0, -24, + 'l', 6, -24, + 'l', 6, -8, + 'c', 6, -2, 10, 0, 16, 0, + 'e', +/* 0x7c '|' offset 3474 */ + 0, 0, 50, 14, 1, 2, + 0, /* snap_x */ + -50, 14, /* snap_y */ + 'm', 0, -50, + 'l', 0, 14, + 'e', + 'X', +/* 0x7d '}' offset 3491 */ + 0, 16, 44, 0, 3, 5, + 0, 10, 16, /* snap_x */ + -44, -24, -21, -15, 0, /* snap_y */ + 'm', 0, -44, + 'c', 6, -44, 10, -42, 10, -36, + 'l', 10, -24, + 'l', 16, -24, + 'l', 10, -24, + 'l', 10, -8, + 'c', 10, -2, 6, 0, 0, 0, + 'e', +/* 0x7e '~' offset 3535 */ + 0, 36, 24, -12, 2, 5, + 0, 36, /* snap_x */ + -24, -21, -15, -12, 0, /* snap_y */ + 'm', 0, -14, + 'c', 1, -21, 4, -24, 8, -24, + 'c', 18, -24, 18, -12, 28, -12, + 'c', 32, -12, 35, -15, 36, -22, + 'e', +}; + +const uint16_t _cairo_twin_charmap[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 28, 40, 90, 114, 152, 224, 323, 390, + 419, 441, 463, 494, 520, 556, 575, 604, + 622, 666, 691, 736, 780, 809, 860, 919, + 944, 1004, 1063, 1109, 1162, 1183, 1209, 1230, + 1288, 1375, 1406, 1455, 1499, 1534, 1572, 1604, + 1655, 1686, 1703, 1731, 1761, 1785, 1821, 1851, + 1895, 1931, 1981, 2023, 2074, 2100, 2128, 2152, + 2188, 2212, 2240, 2271, 2296, 2314, 2339, 2363, + 2381, 2417, 2467, 2517, 2561, 2611, 2659, 2693, + 2758, 2790, 2826, 2870, 2900, 2917, 2963, 2995, + 3039, 3089, 3139, 3168, 3219, 3252, 3283, 3307, + 3343, 3367, 3399, 3430, 3474, 3491, 3535, 0, +}; + diff --git a/libs/cairo/src/cairo-font-face-twin.c b/libs/cairo/src/cairo-font-face-twin.c new file mode 100644 index 000000000..da85cb08e --- /dev/null +++ b/libs/cairo/src/cairo-font-face-twin.c @@ -0,0 +1,729 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +#include + +/* + * This file implements a user-font rendering the descendant of the Hershey + * font coded by Keith Packard for use in the Twin window system. + * The actual font data is in cairo-font-face-twin-data.c + * + * Ported to cairo user font and extended by Behdad Esfahbod. + */ + + + +static cairo_user_data_key_t twin_properties_key; + + +/* + * Face properties + */ + +/* We synthesize multiple faces from the twin data. Here is the parameters. */ + +/* The following tables and matching code are copied from Pango */ + +/* CSS weight */ +typedef enum { + TWIN_WEIGHT_THIN = 100, + TWIN_WEIGHT_ULTRALIGHT = 200, + TWIN_WEIGHT_LIGHT = 300, + TWIN_WEIGHT_BOOK = 380, + TWIN_WEIGHT_NORMAL = 400, + TWIN_WEIGHT_MEDIUM = 500, + TWIN_WEIGHT_SEMIBOLD = 600, + TWIN_WEIGHT_BOLD = 700, + TWIN_WEIGHT_ULTRABOLD = 800, + TWIN_WEIGHT_HEAVY = 900, + TWIN_WEIGHT_ULTRAHEAVY = 1000 +} twin_face_weight_t; + +/* CSS stretch */ +typedef enum { + TWIN_STRETCH_ULTRA_CONDENSED, + TWIN_STRETCH_EXTRA_CONDENSED, + TWIN_STRETCH_CONDENSED, + TWIN_STRETCH_SEMI_CONDENSED, + TWIN_STRETCH_NORMAL, + TWIN_STRETCH_SEMI_EXPANDED, + TWIN_STRETCH_EXPANDED, + TWIN_STRETCH_EXTRA_EXPANDED, + TWIN_STRETCH_ULTRA_EXPANDED +} twin_face_stretch_t; + +typedef struct +{ + int value; + const char str[16]; +} FieldMap; + +static const FieldMap slant_map[] = { + { CAIRO_FONT_SLANT_NORMAL, "" }, + { CAIRO_FONT_SLANT_NORMAL, "Roman" }, + { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, + { CAIRO_FONT_SLANT_ITALIC, "Italic" } +}; + +static const FieldMap smallcaps_map[] = { + { FALSE, "" }, + { TRUE, "Small-Caps" } +}; + +static const FieldMap weight_map[] = { + { TWIN_WEIGHT_THIN, "Thin" }, + { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, + { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, + { TWIN_WEIGHT_LIGHT, "Light" }, + { TWIN_WEIGHT_BOOK, "Book" }, + { TWIN_WEIGHT_NORMAL, "" }, + { TWIN_WEIGHT_NORMAL, "Regular" }, + { TWIN_WEIGHT_MEDIUM, "Medium" }, + { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, + { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, + { TWIN_WEIGHT_BOLD, "Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, + { TWIN_WEIGHT_HEAVY, "Heavy" }, + { TWIN_WEIGHT_HEAVY, "Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } +}; + +static const FieldMap stretch_map[] = { + { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, + { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, + { TWIN_STRETCH_CONDENSED, "Condensed" }, + { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, + { TWIN_STRETCH_NORMAL, "" }, + { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, + { TWIN_STRETCH_EXPANDED, "Expanded" }, + { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, + { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } +}; + +static const FieldMap monospace_map[] = { + { FALSE, "" }, + { TRUE, "Mono" }, + { TRUE, "Monospace" } +}; + + +typedef struct _twin_face_properties { + cairo_font_slant_t slant; + twin_face_weight_t weight; + twin_face_stretch_t stretch; + + /* lets have some fun */ + cairo_bool_t monospace; + cairo_bool_t smallcaps; +} twin_face_properties_t; + +static cairo_bool_t +field_matches (const char *s1, + const char *s2, + int len) +{ + int c1, c2; + + while (len && *s1 && *s2) + { +#define TOLOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) + + c1 = TOLOWER (*s1); + c2 = TOLOWER (*s2); + if (c1 != c2) { + if (c1 == '-') { + s1++; + continue; + } + return FALSE; + } + s1++; s2++; + len--; + } + + return len == 0 && *s1 == '\0'; +} + +static cairo_bool_t +parse_int (const char *word, + size_t wordlen, + int *out) +{ + char *end; + long val = strtol (word, &end, 10); + int i = val; + + if (end != word && (end == word + wordlen) && val >= 0 && val == i) + { + if (out) + *out = i; + + return TRUE; + } + + return FALSE; +} + +static cairo_bool_t +find_field (const char *what, + const FieldMap *map, + int n_elements, + const char *str, + int len, + int *val) +{ + int i; + cairo_bool_t had_prefix = FALSE; + + if (what) + { + i = strlen (what); + if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') + { + str += i + 1; + len -= i + 1; + had_prefix = TRUE; + } + } + + for (i=0; iNAME)) \ + return; \ + + FIELD (weight); + FIELD (slant); + FIELD (stretch); + FIELD (smallcaps); + FIELD (monospace); + +#undef FIELD +} + +static void +face_props_parse (twin_face_properties_t *props, + const char *s) +{ + const char *start, *end; + + for (start = end = s; *end; end++) { + if (*end != ' ' && *end != ':') + continue; + + if (start < end) + parse_field (props, start, end - start); + start = end + 1; + } + if (start < end) + parse_field (props, start, end - start); +} + +static cairo_status_t +twin_font_face_create_properties (cairo_font_face_t *twin_face, + twin_face_properties_t **props_out) +{ + twin_face_properties_t *props; + cairo_status_t status; + + props = malloc (sizeof (twin_face_properties_t)); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + props->stretch = TWIN_STRETCH_NORMAL; + props->slant = CAIRO_FONT_SLANT_NORMAL; + props->weight = TWIN_WEIGHT_NORMAL; + props->monospace = FALSE; + props->smallcaps = FALSE; + + status = cairo_font_face_set_user_data (twin_face, + &twin_properties_key, + props, free); + if (unlikely (status)) { + free (props); + return status; + } + + if (props_out) + *props_out = props; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, + cairo_toy_font_face_t *toy_face) +{ + cairo_status_t status; + twin_face_properties_t *props; + + status = twin_font_face_create_properties (twin_face, &props); + if (unlikely (status)) + return status; + + props->slant = toy_face->slant; + props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? + TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; + face_props_parse (props, toy_face->family); + + return CAIRO_STATUS_SUCCESS; +} + + +/* + * Scaled properties + */ + +typedef struct _twin_scaled_properties { + twin_face_properties_t *face_props; + + cairo_bool_t snap; /* hint outlines */ + + double weight; /* unhinted pen width */ + double penx, peny; /* hinted pen width */ + double marginl, marginr; /* hinted side margins */ + + double stretch; /* stretch factor */ +} twin_scaled_properties_t; + +static void +compute_hinting_scale (cairo_t *cr, + double x, double y, + double *scale, double *inv) +{ + cairo_user_to_device_distance (cr, &x, &y); + *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); + *inv = 1 / *scale; +} + +static void +compute_hinting_scales (cairo_t *cr, + double *x_scale, double *x_scale_inv, + double *y_scale, double *y_scale_inv) +{ + double x, y; + + x = 1; y = 0; + compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); + + x = 0; y = 1; + compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); +} + +#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) +#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) + +/* This controls the global font size */ +#define F(g) ((g) / 72.) + +static void +twin_hint_pen_and_margins(cairo_t *cr, + double *penx, double *peny, + double *marginl, double *marginr) +{ + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; + double margin; + + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + + *penx = SNAPXI (*penx); + if (*penx < x_scale_inv) + *penx = x_scale_inv; + + *peny = SNAPYI (*peny); + if (*peny < y_scale_inv) + *peny = y_scale_inv; + + margin = *marginl + *marginr; + *marginl = SNAPXI (*marginl); + if (*marginl < x_scale_inv) + *marginl = x_scale_inv; + + *marginr = margin - *marginl; + if (*marginr < 0) + *marginr = 0; + *marginr = SNAPXI (*marginr); +} + +static cairo_status_t +twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, + cairo_t *cr) +{ + cairo_status_t status; + twin_scaled_properties_t *props; + + props = malloc (sizeof (twin_scaled_properties_t)); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + + props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &twin_properties_key); + + props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; + + /* weight */ + props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); + + /* pen & margins */ + props->penx = props->peny = props->weight; + props->marginl = props->marginr = F (4); + if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) + twin_hint_pen_and_margins(cr, + &props->penx, &props->peny, + &props->marginl, &props->marginr); + + /* stretch */ + props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); + + + /* Save it */ + status = cairo_scaled_font_set_user_data (scaled_font, + &twin_properties_key, + props, free); + if (unlikely (status)) + goto FREE_PROPS; + + return CAIRO_STATUS_SUCCESS; + +FREE_PROPS: + free (props); + return status; +} + + +/* + * User-font implementation + */ + +static cairo_status_t +twin_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *metrics) +{ + metrics->ascent = F (54); + metrics->descent = 1 - metrics->ascent; + + return twin_scaled_font_compute_properties (scaled_font, cr); +} + +#define TWIN_GLYPH_MAX_SNAP_X 4 +#define TWIN_GLYPH_MAX_SNAP_Y 7 + +typedef struct { + int n_snap_x; + int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; + double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; + int n_snap_y; + int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; + double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; +} twin_snap_info_t; + +#define twin_glyph_left(g) ((g)[0]) +#define twin_glyph_right(g) ((g)[1]) +#define twin_glyph_ascent(g) ((g)[2]) +#define twin_glyph_descent(g) ((g)[3]) + +#define twin_glyph_n_snap_x(g) ((g)[4]) +#define twin_glyph_n_snap_y(g) ((g)[5]) +#define twin_glyph_snap_x(g) (&g[6]) +#define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) +#define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) + +static void +twin_compute_snap (cairo_t *cr, + twin_snap_info_t *info, + const signed char *b) +{ + int s, n; + const signed char *snap; + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; + + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + + snap = twin_glyph_snap_x (b); + n = twin_glyph_n_snap_x (b); + info->n_snap_x = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_X); + for (s = 0; s < n; s++) { + info->snap_x[s] = snap[s]; + info->snapped_x[s] = SNAPXI (F (snap[s])); + } + + snap = twin_glyph_snap_y (b); + n = twin_glyph_n_snap_y (b); + info->n_snap_y = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_Y); + for (s = 0; s < n; s++) { + info->snap_y[s] = snap[s]; + info->snapped_y[s] = SNAPYI (F (snap[s])); + } +} + +static double +twin_snap (int8_t v, int n, int8_t *snap, double *snapped) +{ + int s; + + if (!n) + return F(v); + + if (snap[0] == v) + return snapped[0]; + + for (s = 0; s < n - 1; s++) + { + if (snap[s+1] == v) + return snapped[s+1]; + + if (snap[s] <= v && v <= snap[s+1]) + { + int before = snap[s]; + int after = snap[s+1]; + int dist = after - before; + double snap_before = snapped[s]; + double snap_after = snapped[s+1]; + double dist_before = v - before; + return snap_before + (snap_after - snap_before) * dist_before / dist; + } + } + return F(v); +} + +#define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) +#define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) + +static cairo_status_t +twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + double x1, y1, x2, y2, x3, y3; + double marginl; + twin_scaled_properties_t *props; + twin_snap_info_t info; + const int8_t *b; + const int8_t *g; + int8_t w; + double gw; + + props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); + + /* Save glyph space, we need it when stroking */ + cairo_save (cr); + + /* center the pen */ + cairo_translate (cr, props->penx * .5, -props->peny * .5); + + /* small-caps */ + if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { + glyph += 'A' - 'a'; + /* 28 and 42 are small and capital letter heights of the glyph data */ + cairo_scale (cr, 1, 28. / 42); + } + + /* slant */ + if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { + cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; + cairo_transform (cr, &shear); + } + + b = _cairo_twin_outlines + + _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; + g = twin_glyph_draw(b); + w = twin_glyph_right(b); + gw = F(w); + + marginl = props->marginl; + + /* monospace */ + if (props->face_props->monospace) { + double monow = F(24); + double extra = props->penx + props->marginl + props->marginr; + cairo_scale (cr, (monow + extra) / (gw + extra), 1); + gw = monow; + + /* resnap margin for new transform */ + { + double x, y, x_scale, x_scale_inv; + x = 1; y = 0; + compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); + marginl = SNAPXI (marginl); + } + } + + cairo_translate (cr, marginl, 0); + + /* stretch */ + cairo_scale (cr, props->stretch, 1); + + if (props->snap) + twin_compute_snap (cr, &info, b); + else + info.n_snap_x = info.n_snap_y = 0; + + /* advance width */ + metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; + + /* glyph shape */ + for (;;) { + switch (*g++) { + case 'M': + cairo_close_path (cr); + /* fall through */ + case 'm': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + cairo_move_to (cr, x1, y1); + continue; + case 'L': + cairo_close_path (cr); + /* fall through */ + case 'l': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + cairo_line_to (cr, x1, y1); + continue; + case 'C': + cairo_close_path (cr); + /* fall through */ + case 'c': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + x2 = SNAPX(*g++); + y2 = SNAPY(*g++); + x3 = SNAPX(*g++); + y3 = SNAPY(*g++); + cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); + continue; + case 'E': + cairo_close_path (cr); + /* fall through */ + case 'e': + cairo_restore (cr); /* restore glyph space */ + cairo_set_tolerance (cr, 0.01); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr, 1); + cairo_scale (cr, props->penx, props->peny); + cairo_stroke (cr); + break; + case 'X': + /* filler */ + continue; + } + break; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph) +{ + /* We use an identity charmap. Which means we could live + * with no unicode_to_glyph method too. But we define this + * to map all unknown chars to a single unknown glyph to + * reduce pressure on cache. */ + + if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) + *glyph = unicode; + else + *glyph = 0; + + return CAIRO_STATUS_SUCCESS; +} + + +/* + * Face constructor + */ + +static cairo_font_face_t * +_cairo_font_face_twin_create_internal (void) +{ + cairo_font_face_t *twin_font_face; + + twin_font_face = cairo_user_font_face_create (); + cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); + cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); + cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); + + return twin_font_face; +} + +cairo_font_face_t * +_cairo_font_face_twin_create_fallback (void) +{ + cairo_font_face_t *twin_font_face; + cairo_status_t status; + + twin_font_face = _cairo_font_face_twin_create_internal (); + status = twin_font_face_create_properties (twin_font_face, NULL); + if (status) { + cairo_font_face_destroy (twin_font_face); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } + + return twin_font_face; +} + +cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + cairo_status_t status; + cairo_font_face_t *twin_font_face; + + twin_font_face = _cairo_font_face_twin_create_internal (); + status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); + if (status) { + cairo_font_face_destroy (twin_font_face); + return status; + } + + *font_face = twin_font_face; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-font-face.c b/libs/cairo/src/cairo-font-face.c new file mode 100644 index 000000000..ab17a8cb2 --- /dev/null +++ b/libs/cairo/src/cairo-font-face.c @@ -0,0 +1,272 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-font-face + * @Title: cairo_font_face_t + * @Short_Description: Base class for font faces + * @See_Also: #cairo_scaled_font_t + * + * #cairo_font_face_t represents a particular font at a particular weight, + * slant, and other characteristic but no size, transformation, or size. + * + * Font faces are created using font-backend-specific + * constructors, typically of the form + * cairo_backend_font_face_create(), or implicitly + * using the toy text API by way of + * cairo_select_font_face(). The resulting face can be accessed using + * cairo_get_font_face(). + */ + +/* #cairo_font_face_t */ + +const cairo_font_face_t _cairo_font_face_nil = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NO_MEMORY, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +cairo_status_t +_cairo_font_face_set_error (cairo_font_face_t *font_face, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&font_face->status, status); + + return _cairo_error (status); +} + +void +_cairo_font_face_init (cairo_font_face_t *font_face, + const cairo_font_face_backend_t *backend) +{ + CAIRO_MUTEX_INITIALIZE (); + + font_face->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1); + font_face->backend = backend; + + _cairo_user_data_array_init (&font_face->user_data); +} + +/** + * cairo_font_face_reference: + * @font_face: a #cairo_font_face_t, (may be %NULL in which case this + * function does nothing). + * + * Increases the reference count on @font_face by one. This prevents + * @font_face from being destroyed until a matching call to + * cairo_font_face_destroy() is made. + * + * The number of references to a #cairo_font_face_t can be get using + * cairo_font_face_get_reference_count(). + * + * Return value: the referenced #cairo_font_face_t. + **/ +cairo_font_face_t * +cairo_font_face_reference (cairo_font_face_t *font_face) +{ + if (font_face == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return font_face; + + /* We would normally assert that we have a reference here but we + * can't get away with that due to the zombie case as documented + * in _cairo_ft_font_face_destroy. */ + + _cairo_reference_count_inc (&font_face->ref_count); + + return font_face; +} +slim_hidden_def (cairo_font_face_reference); + +/** + * cairo_font_face_destroy: + * @font_face: a #cairo_font_face_t + * + * Decreases the reference count on @font_face by one. If the result + * is zero, then @font_face and all associated resources are freed. + * See cairo_font_face_reference(). + **/ +void +cairo_font_face_destroy (cairo_font_face_t *font_face) +{ + if (font_face == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&font_face->ref_count)) + return; + + if (font_face->backend->destroy) + font_face->backend->destroy (font_face); + + /* We allow resurrection to deal with some memory management for the + * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t + * need to effectively mutually reference each other + */ + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)) + return; + + _cairo_user_data_array_fini (&font_face->user_data); + + free (font_face); +} +slim_hidden_def (cairo_font_face_destroy); + +/** + * cairo_font_face_get_type: + * @font_face: a font face + * + * This function returns the type of the backend used to create + * a font face. See #cairo_font_type_t for available types. + * + * Return value: The type of @font_face. + * + * Since: 1.2 + **/ +cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return CAIRO_FONT_TYPE_TOY; + + return font_face->backend->type; +} + +/** + * cairo_font_face_get_reference_count: + * @font_face: a #cairo_font_face_t + * + * Returns the current reference count of @font_face. + * + * Return value: the current reference count of @font_face. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_font_face_get_reference_count (cairo_font_face_t *font_face) +{ + if (font_face == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count); +} + +/** + * cairo_font_face_status: + * @font_face: a #cairo_font_face_t + * + * Checks whether an error has previously occurred for this + * font face + * + * Return value: %CAIRO_STATUS_SUCCESS or another error such as + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +cairo_font_face_status (cairo_font_face_t *font_face) +{ + return font_face->status; +} + +/** + * cairo_font_face_get_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @font_face using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +cairo_font_face_get_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&font_face->user_data, + key); +} +slim_hidden_def (cairo_font_face_get_user_data); + +/** + * cairo_font_face_set_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the font face + * @destroy: a #cairo_destroy_func_t which will be called when the + * font face is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @font_face. To remove user data from a font face, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +cairo_font_face_set_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return font_face->status; + + return _cairo_user_data_array_set_data (&font_face->user_data, + key, user_data, destroy); +} +slim_hidden_def (cairo_font_face_set_user_data); + +void +_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, + const cairo_unscaled_font_backend_t *backend) +{ + CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1); + unscaled_font->backend = backend; +} + +cairo_unscaled_font_t * +_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font) +{ + if (unscaled_font == NULL) + return NULL; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); + + _cairo_reference_count_inc (&unscaled_font->ref_count); + + return unscaled_font; +} + +void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) +{ + if (unscaled_font == NULL) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count)) + return; + + unscaled_font->backend->destroy (unscaled_font); + + free (unscaled_font); +} diff --git a/libs/cairo/src/cairo-font-options.c b/libs/cairo/src/cairo-font-options.c new file mode 100644 index 000000000..17a892160 --- /dev/null +++ b/libs/cairo/src/cairo-font-options.c @@ -0,0 +1,481 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-font-options + * @Title: cairo_font_options_t + * @Short_Description: How a font should be rendered + * @See_Also: #cairo_scaled_font_t + * + * The font options specify how fonts should be rendered. Most of the + * time the font options implied by a surface are just right and do not + * need any changes, but for pixel-based targets tweaking font options + * may result in superior output on a particular display. + */ + +static const cairo_font_options_t _cairo_font_options_nil = { + CAIRO_ANTIALIAS_DEFAULT, + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_LCD_FILTER_DEFAULT, + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_METRICS_DEFAULT, + CAIRO_ROUND_GLYPH_POS_DEFAULT +}; + +/** + * _cairo_font_options_init_default: + * @options: a #cairo_font_options_t + * + * Initializes all fields of the font options object to default values. + **/ +void +_cairo_font_options_init_default (cairo_font_options_t *options) +{ + options->antialias = CAIRO_ANTIALIAS_DEFAULT; + options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT; + options->hint_style = CAIRO_HINT_STYLE_DEFAULT; + options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT; + options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT; +} + +void +_cairo_font_options_init_copy (cairo_font_options_t *options, + const cairo_font_options_t *other) +{ + options->antialias = other->antialias; + options->subpixel_order = other->subpixel_order; + options->lcd_filter = other->lcd_filter; + options->hint_style = other->hint_style; + options->hint_metrics = other->hint_metrics; + options->round_glyph_positions = other->round_glyph_positions; +} + +/** + * cairo_font_options_create: + * + * Allocates a new font options object with all options initialized + * to default values. + * + * Return value: a newly allocated #cairo_font_options_t. Free with + * cairo_font_options_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_font_options_status(). + **/ +cairo_font_options_t * +cairo_font_options_create (void) +{ + cairo_font_options_t *options; + + options = malloc (sizeof (cairo_font_options_t)); + if (!options) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_options_t *) &_cairo_font_options_nil; + } + + _cairo_font_options_init_default (options); + + return options; +} + +/** + * cairo_font_options_copy: + * @original: a #cairo_font_options_t + * + * Allocates a new font options object copying the option values from + * @original. + * + * Return value: a newly allocated #cairo_font_options_t. Free with + * cairo_font_options_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_font_options_status(). + **/ +cairo_font_options_t * +cairo_font_options_copy (const cairo_font_options_t *original) +{ + cairo_font_options_t *options; + + if (cairo_font_options_status ((cairo_font_options_t *) original)) + return (cairo_font_options_t *) &_cairo_font_options_nil; + + options = malloc (sizeof (cairo_font_options_t)); + if (!options) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_options_t *) &_cairo_font_options_nil; + } + + _cairo_font_options_init_copy (options, original); + + return options; +} + +/** + * cairo_font_options_destroy: + * @options: a #cairo_font_options_t + * + * Destroys a #cairo_font_options_t object created with + * cairo_font_options_create() or cairo_font_options_copy(). + **/ +void +cairo_font_options_destroy (cairo_font_options_t *options) +{ + if (cairo_font_options_status (options)) + return; + + free (options); +} + +/** + * cairo_font_options_status: + * @options: a #cairo_font_options_t + * + * Checks whether an error has previously occurred for this + * font options object + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + **/ +cairo_status_t +cairo_font_options_status (cairo_font_options_t *options) +{ + if (options == NULL) + return CAIRO_STATUS_NULL_POINTER; + else if (options == (cairo_font_options_t *) &_cairo_font_options_nil) + return CAIRO_STATUS_NO_MEMORY; + else + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_font_options_status); + +/** + * cairo_font_options_merge: + * @options: a #cairo_font_options_t + * @other: another #cairo_font_options_t + * + * Merges non-default options from @other into @options, replacing + * existing values. This operation can be thought of as somewhat + * similar to compositing @other onto @options with the operation + * of %CAIRO_OPERATION_OVER. + **/ +void +cairo_font_options_merge (cairo_font_options_t *options, + const cairo_font_options_t *other) +{ + if (cairo_font_options_status (options)) + return; + + if (cairo_font_options_status ((cairo_font_options_t *) other)) + return; + + if (other->antialias != CAIRO_ANTIALIAS_DEFAULT) + options->antialias = other->antialias; + if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) + options->subpixel_order = other->subpixel_order; + if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) + options->lcd_filter = other->lcd_filter; + if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT) + options->hint_style = other->hint_style; + if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT) + options->hint_metrics = other->hint_metrics; + if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) + options->round_glyph_positions = other->round_glyph_positions; +} +slim_hidden_def (cairo_font_options_merge); + +/** + * cairo_font_options_equal: + * @options: a #cairo_font_options_t + * @other: another #cairo_font_options_t + * + * Compares two font options objects for equality. + * + * Return value: %TRUE if all fields of the two font options objects match. + * Note that this function will return %FALSE if either object is in + * error. + **/ +cairo_bool_t +cairo_font_options_equal (const cairo_font_options_t *options, + const cairo_font_options_t *other) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return FALSE; + if (cairo_font_options_status ((cairo_font_options_t *) other)) + return FALSE; + + if (options == other) + return TRUE; + + return (options->antialias == other->antialias && + options->subpixel_order == other->subpixel_order && + options->lcd_filter == other->lcd_filter && + options->hint_style == other->hint_style && + options->hint_metrics == other->hint_metrics && + options->round_glyph_positions == other->round_glyph_positions); +} +slim_hidden_def (cairo_font_options_equal); + +/** + * cairo_font_options_hash: + * @options: a #cairo_font_options_t + * + * Compute a hash for the font options object; this value will + * be useful when storing an object containing a #cairo_font_options_t + * in a hash table. + * + * Return value: the hash value for the font options object. + * The return value can be cast to a 32-bit type if a + * 32-bit hash value is needed. + **/ +unsigned long +cairo_font_options_hash (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + options = &_cairo_font_options_nil; /* force default values */ + + return ((options->antialias) | + (options->subpixel_order << 4) | + (options->lcd_filter << 8) | + (options->hint_style << 12) | + (options->hint_metrics << 16)); +} +slim_hidden_def (cairo_font_options_hash); + +/** + * cairo_font_options_set_antialias: + * @options: a #cairo_font_options_t + * @antialias: the new antialiasing mode + * + * Sets the antialiasing mode for the font options object. This + * specifies the type of antialiasing to do when rendering text. + **/ +void +cairo_font_options_set_antialias (cairo_font_options_t *options, + cairo_antialias_t antialias) +{ + if (cairo_font_options_status (options)) + return; + + options->antialias = antialias; +} +slim_hidden_def (cairo_font_options_set_antialias); + +/** + * cairo_font_options_get_antialias: + * @options: a #cairo_font_options_t + * + * Gets the antialiasing mode for the font options object. + * + * Return value: the antialiasing mode + **/ +cairo_antialias_t +cairo_font_options_get_antialias (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_ANTIALIAS_DEFAULT; + + return options->antialias; +} + +/** + * cairo_font_options_set_subpixel_order: + * @options: a #cairo_font_options_t + * @subpixel_order: the new subpixel order + * + * Sets the subpixel order for the font options object. The subpixel + * order specifies the order of color elements within each pixel on + * the display device when rendering with an antialiasing mode of + * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for + * #cairo_subpixel_order_t for full details. + **/ +void +cairo_font_options_set_subpixel_order (cairo_font_options_t *options, + cairo_subpixel_order_t subpixel_order) +{ + if (cairo_font_options_status (options)) + return; + + options->subpixel_order = subpixel_order; +} +slim_hidden_def (cairo_font_options_set_subpixel_order); + +/** + * cairo_font_options_get_subpixel_order: + * @options: a #cairo_font_options_t + * + * Gets the subpixel order for the font options object. + * See the documentation for #cairo_subpixel_order_t for full details. + * + * Return value: the subpixel order for the font options object + **/ +cairo_subpixel_order_t +cairo_font_options_get_subpixel_order (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_SUBPIXEL_ORDER_DEFAULT; + + return options->subpixel_order; +} + +/** + * _cairo_font_options_set_lcd_filter: + * @options: a #cairo_font_options_t + * @lcd_filter: the new LCD filter + * + * Sets the LCD filter for the font options object. The LCD filter + * specifies how pixels are filtered when rendered with an antialiasing + * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for + * #cairo_lcd_filter_t for full details. + * + * Since: 1.8 + **/ +void +_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, + cairo_lcd_filter_t lcd_filter) +{ + if (cairo_font_options_status (options)) + return; + + options->lcd_filter = lcd_filter; +} + +/** + * _cairo_font_options_get_lcd_filter: + * @options: a #cairo_font_options_t + * + * Gets the LCD filter for the font options object. + * See the documentation for #cairo_lcd_filter_t for full details. + * + * Return value: the LCD filter for the font options object + * + * Since: 1.8 + **/ +cairo_lcd_filter_t +_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_LCD_FILTER_DEFAULT; + + return options->lcd_filter; +} + +/** + * _cairo_font_options_set_round_glyph_positions: + * @options: a #cairo_font_options_t + * @round: the new rounding value + * + * Sets the rounding options for the font options object. If rounding is set, a + * glyph's position will be rounded to integer values. + * + * Since: 1.12 + **/ +void +_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, + cairo_round_glyph_positions_t round) +{ + if (cairo_font_options_status (options)) + return; + + options->round_glyph_positions = round; +} + +/** + * _cairo_font_options_get_round_glyph_positions: + * @options: a #cairo_font_options_t + * + * Gets the glyph position rounding option for the font options object. + * + * Return value: The round glyph posistions flag for the font options object. + * + * Since: 1.12 + **/ +cairo_round_glyph_positions_t +_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_ROUND_GLYPH_POS_DEFAULT; + + return options->round_glyph_positions; +} + +/** + * cairo_font_options_set_hint_style: + * @options: a #cairo_font_options_t + * @hint_style: the new hint style + * + * Sets the hint style for font outlines for the font options object. + * This controls whether to fit font outlines to the pixel grid, + * and if so, whether to optimize for fidelity or contrast. + * See the documentation for #cairo_hint_style_t for full details. + **/ +void +cairo_font_options_set_hint_style (cairo_font_options_t *options, + cairo_hint_style_t hint_style) +{ + if (cairo_font_options_status (options)) + return; + + options->hint_style = hint_style; +} +slim_hidden_def (cairo_font_options_set_hint_style); + +/** + * cairo_font_options_get_hint_style: + * @options: a #cairo_font_options_t + * + * Gets the hint style for font outlines for the font options object. + * See the documentation for #cairo_hint_style_t for full details. + * + * Return value: the hint style for the font options object + **/ +cairo_hint_style_t +cairo_font_options_get_hint_style (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_HINT_STYLE_DEFAULT; + + return options->hint_style; +} + +/** + * cairo_font_options_set_hint_metrics: + * @options: a #cairo_font_options_t + * @hint_metrics: the new metrics hinting mode + * + * Sets the metrics hinting mode for the font options object. This + * controls whether metrics are quantized to integer values in + * device units. + * See the documentation for #cairo_hint_metrics_t for full details. + **/ +void +cairo_font_options_set_hint_metrics (cairo_font_options_t *options, + cairo_hint_metrics_t hint_metrics) +{ + if (cairo_font_options_status (options)) + return; + + options->hint_metrics = hint_metrics; +} +slim_hidden_def (cairo_font_options_set_hint_metrics); + +/** + * cairo_font_options_get_hint_metrics: + * @options: a #cairo_font_options_t + * + * Gets the metrics hinting mode for the font options object. + * See the documentation for #cairo_hint_metrics_t for full details. + * + * Return value: the metrics hinting mode for the font options object + **/ +cairo_hint_metrics_t +cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_HINT_METRICS_DEFAULT; + + return options->hint_metrics; +} diff --git a/libs/cairo/src/cairo-fontconfig-private.h b/libs/cairo/src/cairo-fontconfig-private.h new file mode 100644 index 000000000..110304b18 --- /dev/null +++ b/libs/cairo/src/cairo-fontconfig-private.h @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CAIRO_FONTCONFIG_PRIVATE_H +#define _CAIRO_FONTCONFIG_PRIVATE_H + +#include "cairo.h" + +#if CAIRO_HAS_FC_FONT +#include +#include +#endif + +/* sub-pixel order */ +#ifndef FC_RGBA_UNKNOWN +#define FC_RGBA_UNKNOWN 0 +#define FC_RGBA_RGB 1 +#define FC_RGBA_BGR 2 +#define FC_RGBA_VRGB 3 +#define FC_RGBA_VBGR 4 +#define FC_RGBA_NONE 5 +#endif + +/* hinting style */ +#ifndef FC_HINT_NONE +#define FC_HINT_NONE 0 +#define FC_HINT_SLIGHT 1 +#define FC_HINT_MEDIUM 2 +#define FC_HINT_FULL 3 +#endif + +/* LCD filter */ +#ifndef FC_LCD_NONE +#define FC_LCD_NONE 0 +#define FC_LCD_DEFAULT 1 +#define FC_LCD_LIGHT 2 +#define FC_LCD_LEGACY 3 +#endif + +#endif /* _CAIRO_FONTCONFIG_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-freed-pool-private.h b/libs/cairo/src/cairo-freed-pool-private.h new file mode 100644 index 000000000..c23e5a03a --- /dev/null +++ b/libs/cairo/src/cairo-freed-pool-private.h @@ -0,0 +1,97 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FREED_POOL_H +#define CAIRO_FREED_POOL_H + +#include "cairoint.h" +#include "cairo-atomic-private.h" + +#if HAS_ATOMIC_OPS +/* Keep a stash of recently freed clip_paths, since we need to + * reallocate them frequently. + */ +#define MAX_FREED_POOL_SIZE 4 +typedef struct { + void *pool[MAX_FREED_POOL_SIZE]; + cairo_atomic_int_t top; +} freed_pool_t; + +static cairo_always_inline void * +_atomic_fetch (void **slot) +{ + void *ptr; + + do { + ptr = _cairo_atomic_ptr_get (slot); + } while (! _cairo_atomic_ptr_cmpxchg (slot, ptr, NULL)); + + return ptr; +} + +static cairo_always_inline cairo_bool_t +_atomic_store (void **slot, void *ptr) +{ + return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr); +} + +cairo_private void * +_freed_pool_get_search (freed_pool_t *pool); + +static inline void * +_freed_pool_get (freed_pool_t *pool) +{ + void *ptr; + int i; + + i = _cairo_atomic_int_get_relaxed (&pool->top) - 1; + if (i < 0) + i = 0; + + ptr = _atomic_fetch (&pool->pool[i]); + if (likely (ptr != NULL)) { + _cairo_atomic_int_set_relaxed (&pool->top, i); + return ptr; + } + + /* either empty or contended */ + return _freed_pool_get_search (pool); +} + +cairo_private void +_freed_pool_put_search (freed_pool_t *pool, void *ptr); + +static inline void +_freed_pool_put (freed_pool_t *pool, void *ptr) +{ + int i; + + i = _cairo_atomic_int_get_relaxed (&pool->top); + if (likely (i < ARRAY_LENGTH (pool->pool) && + _atomic_store (&pool->pool[i], ptr))) + { + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); + return; + } + + /* either full or contended */ + _freed_pool_put_search (pool, ptr); +} + +cairo_private void +_freed_pool_reset (freed_pool_t *pool); + +#define HAS_FREED_POOL 1 + +#else + +typedef int freed_pool_t; + +#define _freed_pool_get(pool) NULL +#define _freed_pool_put(pool, ptr) free(ptr) +#define _freed_pool_reset(ptr) + +#endif + +#endif /* CAIRO_FREED_POOL_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-freed-pool.c b/libs/cairo/src/cairo-freed-pool.c new file mode 100644 index 000000000..c65f4626d --- /dev/null +++ b/libs/cairo/src/cairo-freed-pool.c @@ -0,0 +1,60 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-freed-pool-private.h" + +#if HAS_FREED_POOL + +void * +_freed_pool_get_search (freed_pool_t *pool) +{ + void *ptr; + int i; + + for (i = ARRAY_LENGTH (pool->pool); i--;) { + ptr = _atomic_fetch (&pool->pool[i]); + if (ptr != NULL) { + _cairo_atomic_int_set_relaxed (&pool->top, i); + return ptr; + } + } + + /* empty */ + _cairo_atomic_int_set_relaxed (&pool->top, 0); + return NULL; +} + +void +_freed_pool_put_search (freed_pool_t *pool, void *ptr) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + if (_atomic_store (&pool->pool[i], ptr)) { + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); + return; + } + } + + /* full */ + _cairo_atomic_int_set_relaxed (&pool->top, i); + free (ptr); +} + +void +_freed_pool_reset (freed_pool_t *pool) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + free (pool->pool[i]); + pool->pool[i] = NULL; + } + + _cairo_atomic_int_set_relaxed (&pool->top, 0); +} + +#endif diff --git a/libs/cairo/src/cairo-freelist-private.h b/libs/cairo/src/cairo-freelist-private.h new file mode 100644 index 000000000..703181b56 --- /dev/null +++ b/libs/cairo/src/cairo-freelist-private.h @@ -0,0 +1,139 @@ +/* + * Copyright © 2006 Joonas Pihlaja + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#ifndef CAIRO_FREELIST_H +#define CAIRO_FREELIST_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-freelist-type-private.h" + +/* for stand-alone compilation*/ +#ifndef VG +#define VG(x) +#endif + +#ifndef NULL +#define NULL (void *) 0 +#endif + +/* Initialise a freelist that will be responsible for allocating + * nodes of size nodesize. */ +cairo_private void +_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize); + +/* Deallocate any nodes in the freelist. */ +cairo_private void +_cairo_freelist_fini (cairo_freelist_t *freelist); + +/* Allocate a new node from the freelist. If the freelist contains no + * nodes, a new one will be allocated using malloc(). The caller is + * responsible for calling _cairo_freelist_free() or free() on the + * returned node. Returns %NULL on memory allocation error. */ +cairo_private void * +_cairo_freelist_alloc (cairo_freelist_t *freelist); + +/* Allocate a new node from the freelist. If the freelist contains no + * nodes, a new one will be allocated using calloc(). The caller is + * responsible for calling _cairo_freelist_free() or free() on the + * returned node. Returns %NULL on memory allocation error. */ +cairo_private void * +_cairo_freelist_calloc (cairo_freelist_t *freelist); + +/* Return a node to the freelist. This does not deallocate the memory, + * but makes it available for later reuse by + * _cairo_freelist_alloc(). */ +cairo_private void +_cairo_freelist_free (cairo_freelist_t *freelist, void *node); + + +cairo_private void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize); + +cairo_private void +_cairo_freepool_fini (cairo_freepool_t *freepool); + +static inline void +_cairo_freepool_reset (cairo_freepool_t *freepool) +{ + while (freepool->pools != &freepool->embedded_pool) { + cairo_freelist_pool_t *pool = freepool->pools; + freepool->pools = pool->next; + pool->next = freepool->freepools; + freepool->freepools = pool; + } + + freepool->embedded_pool.rem = sizeof (freepool->embedded_data); + freepool->embedded_pool.data = freepool->embedded_data; +} + +cairo_private void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool); + +static inline void * +_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + uint8_t *ptr; + + pool = freepool->pools; + if (unlikely (freepool->nodesize > pool->rem)) + return _cairo_freepool_alloc_from_new_pool (freepool); + + ptr = pool->data; + pool->data += freepool->nodesize; + pool->rem -= freepool->nodesize; + VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize)); + return ptr; +} + +static inline void * +_cairo_freepool_alloc (cairo_freepool_t *freepool) +{ + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (unlikely (node == NULL)) + return _cairo_freepool_alloc_from_pool (freepool); + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + + return node; +} + +cairo_private cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array); + +static inline void +_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) +{ + cairo_freelist_node_t *node = ptr; + + node->next = freepool->first_free_node; + freepool->first_free_node = node; + VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize)); +} + +#endif /* CAIRO_FREELIST_H */ diff --git a/libs/cairo/src/cairo-freelist-type-private.h b/libs/cairo/src/cairo-freelist-type-private.h new file mode 100644 index 000000000..4dd056461 --- /dev/null +++ b/libs/cairo/src/cairo-freelist-type-private.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Joonas Pihlaja + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#ifndef CAIRO_FREELIST_TYPE_H +#define CAIRO_FREELIST_TYPE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +typedef struct _cairo_freelist_node cairo_freelist_node_t; +struct _cairo_freelist_node { + cairo_freelist_node_t *next; +}; + +typedef struct _cairo_freelist { + cairo_freelist_node_t *first_free_node; + unsigned nodesize; +} cairo_freelist_t; + +typedef struct _cairo_freelist_pool cairo_freelist_pool_t; +struct _cairo_freelist_pool { + cairo_freelist_pool_t *next; + unsigned size, rem; + uint8_t *data; +}; + +typedef struct _cairo_freepool { + cairo_freelist_node_t *first_free_node; + cairo_freelist_pool_t *pools; + cairo_freelist_pool_t *freepools; + unsigned nodesize; + cairo_freelist_pool_t embedded_pool; + uint8_t embedded_data[1000]; +} cairo_freepool_t; + +#endif /* CAIRO_FREELIST_TYPE_H */ diff --git a/libs/cairo/src/cairo-freelist.c b/libs/cairo/src/cairo-freelist.c new file mode 100644 index 000000000..d596eab81 --- /dev/null +++ b/libs/cairo/src/cairo-freelist.c @@ -0,0 +1,191 @@ +/* + * Copyright © 2006 Joonas Pihlaja + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" + +void +_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize) +{ + memset (freelist, 0, sizeof (cairo_freelist_t)); + freelist->nodesize = nodesize; +} + +void +_cairo_freelist_fini (cairo_freelist_t *freelist) +{ + cairo_freelist_node_t *node = freelist->first_free_node; + while (node) { + cairo_freelist_node_t *next; + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + next = node->next; + + free (node); + node = next; + } +} + +void * +_cairo_freelist_alloc (cairo_freelist_t *freelist) +{ + if (freelist->first_free_node) { + cairo_freelist_node_t *node; + + node = freelist->first_free_node; + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freelist->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); + + return node; + } + + return malloc (freelist->nodesize); +} + +void * +_cairo_freelist_calloc (cairo_freelist_t *freelist) +{ + void *node = _cairo_freelist_alloc (freelist); + if (node) + memset (node, 0, freelist->nodesize); + return node; +} + +void +_cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) +{ + cairo_freelist_node_t *node = voidnode; + if (node) { + node->next = freelist->first_free_node; + freelist->first_free_node = node; + VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize)); + } +} + +void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize) +{ + freepool->first_free_node = NULL; + freepool->pools = &freepool->embedded_pool; + freepool->freepools = NULL; + freepool->nodesize = nodesize; + + freepool->embedded_pool.next = NULL; + freepool->embedded_pool.size = sizeof (freepool->embedded_data); + freepool->embedded_pool.rem = sizeof (freepool->embedded_data); + freepool->embedded_pool.data = freepool->embedded_data; + + VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, sizeof (freepool->embedded_data))); +} + +void +_cairo_freepool_fini (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + + pool = freepool->pools; + while (pool != &freepool->embedded_pool) { + cairo_freelist_pool_t *next = pool->next; + free (pool); + pool = next; + } + + pool = freepool->freepools; + while (pool != NULL) { + cairo_freelist_pool_t *next = pool->next; + free (pool); + pool = next; + } + + VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool))); +} + +void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + int poolsize; + + if (freepool->freepools != NULL) { + pool = freepool->freepools; + freepool->freepools = pool->next; + + poolsize = pool->size; + } else { + if (freepool->pools != &freepool->embedded_pool) + poolsize = 2 * freepool->pools->size; + else + poolsize = (128 * freepool->nodesize + 8191) & -8192; + + pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize); + if (unlikely (pool == NULL)) + return pool; + + pool->size = poolsize; + } + + pool->next = freepool->pools; + freepool->pools = pool; + + pool->rem = poolsize - freepool->nodesize; + pool->data = (uint8_t *) (pool + 1) + freepool->nodesize; + + VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, pool->rem)); + + return pool + 1; +} + +cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array) +{ + int i; + + for (i = 0; i < count; i++) { + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (likely (node != NULL)) { + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + } else { + node = _cairo_freepool_alloc_from_pool (freepool); + if (unlikely (node == NULL)) + goto CLEANUP; + } + + array[i] = node; + } + + return CAIRO_STATUS_SUCCESS; + + CLEANUP: + while (i--) + _cairo_freepool_free (freepool, array[i]); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} diff --git a/libs/cairo/src/cairo-ft-font.c b/libs/cairo/src/cairo-ft-font.c new file mode 100644 index 000000000..1a2799b86 --- /dev/null +++ b/libs/cairo/src/cairo-ft-font.c @@ -0,0 +1,3319 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for strdup() */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-ft-private.h" + +#include + +#include "cairo-fontconfig-private.h" + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_IMAGE_H +#include FT_BITMAP_H +#include FT_TRUETYPE_TABLES_H +#if HAVE_FT_GLYPHSLOT_EMBOLDEN +#include FT_SYNTHESIS_H +#endif + +#if HAVE_FT_LIBRARY_SETLCDFILTER +#include FT_LCD_FILTER_H +#endif + +#define _GNU_SOURCE /* for RTLD_DEFAULT */ +#include + +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT ((void *) 0) +#endif + +/* Fontconfig version older than 2.6 didn't have these options */ +#ifndef FC_LCD_FILTER +#define FC_LCD_FILTER "lcdfilter" +#endif +/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */ +#ifndef FC_LCD_NONE +#define FC_LCD_NONE 0 +#define FC_LCD_DEFAULT 1 +#define FC_LCD_LIGHT 2 +#define FC_LCD_LEGACY 3 +#endif + +/* FreeType version older than 2.3.5(?) didn't have these options */ +#ifndef FT_LCD_FILTER_NONE +#define FT_LCD_FILTER_NONE 0 +#define FT_LCD_FILTER_DEFAULT 1 +#define FT_LCD_FILTER_LIGHT 2 +#define FT_LCD_FILTER_LEGACY 16 +#endif + +typedef FT_Error (*setLcdFilterFunc)(FT_Library, int); +static setLcdFilterFunc setLcdFilter; + +#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) +#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) +#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) +#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) + +/* This is the max number of FT_face objects we keep open at once + */ +#define MAX_OPEN_FACES 10 +/* This is the maximum font size we allow to be passed to FT_Set_Char_Size + */ +#define MAX_FONT_SIZE 1000 + +/** + * SECTION:cairo-ft + * @Title: FreeType Fonts + * @Short_Description: Font support for FreeType + * @See_Also: #cairo_font_face_t + * + * The FreeType font backend is primarily used to render text on GNU/Linux + * systems, but can be used on other platforms too. + */ + +/** + * CAIRO_HAS_FT_FONT: + * + * Defined if the FreeType font backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +/** + * CAIRO_HAS_FC_FONT: + * + * Defined if the Fontconfig-specific functions of the FreeType font backend + * are available. + * This macro can be used to conditionally compile backend-specific code. + */ + +/* + * The simple 2x2 matrix is converted into separate scale and shape + * factors so that hinting works right + */ + +typedef struct _cairo_ft_font_transform { + double x_scale, y_scale; + double shape[2][2]; +} cairo_ft_font_transform_t; + +/* + * We create an object that corresponds to a single font on the disk; + * (identified by a filename/id pair) these are shared between all + * fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we + * just create a one-off version with a permanent face value. + */ + +typedef struct _cairo_ft_font_face cairo_ft_font_face_t; + +struct _cairo_ft_unscaled_font { + cairo_unscaled_font_t base; + + cairo_bool_t from_face; /* was the FT_Face provided by user? */ + FT_Face face; /* provided or cached face */ + + /* only set if from_face is false */ + char *filename; + int id; + + /* We temporarily scale the unscaled font as needed */ + cairo_bool_t have_scale; + cairo_matrix_t current_scale; + double x_scale; /* Extracted X scale factor */ + double y_scale; /* Extracted Y scale factor */ + cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/ + cairo_matrix_t current_shape; + FT_Matrix Current_Shape; + + cairo_mutex_t mutex; + int lock_count; + + cairo_ft_font_face_t *faces; /* Linked list of faces for this font */ +}; + +static int +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b); + +static void +_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); + +typedef enum _cairo_ft_extra_flags { + CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0), + CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1) +} cairo_ft_extra_flags_t; + +typedef struct _cairo_ft_options { + cairo_font_options_t base; + int load_flags; /* flags for FT_Load_Glyph */ + cairo_ft_extra_flags_t extra_flags; /* other flags that affect results */ +} cairo_ft_options_t; + +struct _cairo_ft_font_face { + cairo_font_face_t base; + + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; + cairo_ft_font_face_t *next; + +#if CAIRO_HAS_FC_FONT + FcPattern *pattern; /* if pattern is set, the above fields will be NULL */ + cairo_font_face_t *resolved_font_face; + FcConfig *resolved_config; +#endif +}; + +static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); + +#endif + +/* + * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t. + * The hash table itself isn't limited in size. However, we limit the + * number of FT_Face objects we keep around; when we've exceeded that + * limit and need to create a new FT_Face, we dump the FT_Face from a + * random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if + * there are any). + */ + +typedef struct _cairo_ft_unscaled_font_map { + cairo_hash_table_t *hash_table; + FT_Library ft_library; + int num_open_faces; +} cairo_ft_unscaled_font_map_t; + +static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL; + + +static void +_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map, + cairo_ft_unscaled_font_t *unscaled) +{ + if (unscaled->face) { + FT_Done_Face (unscaled->face); + unscaled->face = NULL; + unscaled->have_scale = FALSE; + + font_map->num_open_faces--; + } +} + +static cairo_status_t +_cairo_ft_unscaled_font_map_create (void) +{ + cairo_ft_unscaled_font_map_t *font_map; + + /* This function is only intended to be called from + * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can + * detect some other call path. */ + assert (cairo_ft_unscaled_font_map == NULL); + + font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t)); + if (unlikely (font_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_map->hash_table = + _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal); + + if (unlikely (font_map->hash_table == NULL)) + goto FAIL; + + if (unlikely (FT_Init_FreeType (&font_map->ft_library))) + goto FAIL; + + font_map->num_open_faces = 0; + + cairo_ft_unscaled_font_map = font_map; + return CAIRO_STATUS_SUCCESS; + +FAIL: + if (font_map->hash_table) + _cairo_hash_table_destroy (font_map->hash_table); + free (font_map); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + + +static void +_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) +{ + cairo_ft_unscaled_font_t *unscaled = entry; + cairo_ft_unscaled_font_map_t *font_map = closure; + + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + + if (! unscaled->from_face) + _font_map_release_face_lock_held (font_map, unscaled); + + _cairo_ft_unscaled_font_fini (unscaled); + free (unscaled); +} + +static void +_cairo_ft_unscaled_font_map_destroy (void) +{ + cairo_ft_unscaled_font_map_t *font_map; + + CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); + font_map = cairo_ft_unscaled_font_map; + cairo_ft_unscaled_font_map = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); + + if (font_map != NULL) { + _cairo_hash_table_foreach (font_map->hash_table, + _cairo_ft_unscaled_font_map_pluck_entry, + font_map); + assert (font_map->num_open_faces == 0); + + FT_Done_FreeType (font_map->ft_library); + + _cairo_hash_table_destroy (font_map->hash_table); + + free (font_map); + } +} + +static cairo_ft_unscaled_font_map_t * +_cairo_ft_unscaled_font_map_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); + + if (unlikely (cairo_ft_unscaled_font_map == NULL)) { + if (unlikely (_cairo_ft_unscaled_font_map_create ())) { + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); + return NULL; + } + } + + return cairo_ft_unscaled_font_map; +} + +static void +_cairo_ft_unscaled_font_map_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); +} + +static void +_cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key, + cairo_bool_t from_face, + char *filename, + int id, + FT_Face face) +{ + unsigned long hash; + + key->from_face = from_face; + key->filename = filename; + key->id = id; + key->face = face; + + hash = _cairo_hash_string (filename); + /* the constants are just arbitrary primes */ + hash += ((unsigned long) id) * 1607; + hash += ((unsigned long) face) * 2137; + + key->base.hash_entry.hash = hash; +} + +/** + * _cairo_ft_unscaled_font_init: + * + * Initialize a #cairo_ft_unscaled_font_t. + * + * There are two basic flavors of #cairo_ft_unscaled_font_t, one + * created from an FT_Face and the other created from a filename/id + * pair. These two flavors are identified as from_face and !from_face. + * + * To initialize a from_face font, pass filename==%NULL, id=0 and the + * desired face. + * + * To initialize a !from_face font, pass the filename/id as desired + * and face==%NULL. + * + * Note that the code handles these two flavors in very distinct + * ways. For example there is a hash_table mapping + * filename/id->#cairo_unscaled_font_t in the !from_face case, but no + * parallel in the from_face case, (where the calling code would have + * to do its own mapping to ensure similar sharing). + **/ +static cairo_status_t +_cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled, + cairo_bool_t from_face, + const char *filename, + int id, + FT_Face face) +{ + _cairo_unscaled_font_init (&unscaled->base, + &cairo_ft_unscaled_font_backend); + + if (from_face) { + unscaled->from_face = TRUE; + _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face); + } else { + char *filename_copy; + + unscaled->from_face = FALSE; + unscaled->face = NULL; + + filename_copy = strdup (filename); + if (unlikely (filename_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL); + } + + unscaled->have_scale = FALSE; + CAIRO_MUTEX_INIT (unscaled->mutex); + unscaled->lock_count = 0; + + unscaled->faces = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_ft_unscaled_font_fini: + * + * Free all data associated with a #cairo_ft_unscaled_font_t. + * + * CAUTION: The unscaled->face field must be %NULL before calling this + * function. This is because the #cairo_ft_unscaled_font_t_map keeps a + * count of these faces (font_map->num_open_faces) so it maintains the + * unscaled->face field while it has its lock held. See + * _font_map_release_face_lock_held(). + **/ +static void +_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) +{ + assert (unscaled->face == NULL); + + if (unscaled->filename) { + free (unscaled->filename); + unscaled->filename = NULL; + } + + CAIRO_MUTEX_FINI (unscaled->mutex); +} + +static int +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_ft_unscaled_font_t *unscaled_a = key_a; + const cairo_ft_unscaled_font_t *unscaled_b = key_b; + + if (unscaled_a->id == unscaled_b->id && + unscaled_a->from_face == unscaled_b->from_face) + { + if (unscaled_a->from_face) + return unscaled_a->face == unscaled_b->face; + + if (unscaled_a->filename == NULL && unscaled_b->filename == NULL) + return TRUE; + else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL) + return FALSE; + else + return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0); + } + + return FALSE; +} + +/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from + * pattern. Returns a new reference to the unscaled font. + */ +static cairo_status_t +_cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, + char *filename, + int id, + FT_Face font_face, + cairo_ft_unscaled_font_t **out) +{ + cairo_ft_unscaled_font_t key, *unscaled; + cairo_ft_unscaled_font_map_t *font_map; + cairo_status_t status; + + font_map = _cairo_ft_unscaled_font_map_lock (); + if (unlikely (font_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); + + /* Return existing unscaled font if it exists in the hash table. */ + unscaled = _cairo_hash_table_lookup (font_map->hash_table, + &key.base.hash_entry); + if (unscaled != NULL) { + _cairo_unscaled_font_reference (&unscaled->base); + goto DONE; + } + + /* Otherwise create it and insert into hash table. */ + unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); + if (unlikely (unscaled == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto UNWIND_FONT_MAP_LOCK; + } + + status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); + if (unlikely (status)) + goto UNWIND_UNSCALED_MALLOC; + + assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (font_map->hash_table, + &unscaled->base.hash_entry); + if (unlikely (status)) + goto UNWIND_UNSCALED_FONT_INIT; + +DONE: + _cairo_ft_unscaled_font_map_unlock (); + *out = unscaled; + return CAIRO_STATUS_SUCCESS; + +UNWIND_UNSCALED_FONT_INIT: + _cairo_ft_unscaled_font_fini (unscaled); +UNWIND_UNSCALED_MALLOC: + free (unscaled); +UNWIND_FONT_MAP_LOCK: + _cairo_ft_unscaled_font_map_unlock (); + return status; +} + + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, + cairo_ft_unscaled_font_t **out) +{ + FT_Face font_face = NULL; + char *filename = NULL; + int id = 0; + FcResult ret; + + ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); + if (ret == FcResultMatch) + goto DONE; + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (ret == FcResultMatch) { + /* If FC_INDEX is not set, we just use 0 */ + ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + goto DONE; + } + + /* The pattern contains neither a face nor a filename, resolve it later. */ + *out = NULL; + return CAIRO_STATUS_SUCCESS; + +DONE: + return _cairo_ft_unscaled_font_create_internal (font_face != NULL, + filename, id, font_face, + out); +} +#endif + +static cairo_status_t +_cairo_ft_unscaled_font_create_from_face (FT_Face face, + cairo_ft_unscaled_font_t **out) +{ + return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out); +} + +static void +_cairo_ft_unscaled_font_destroy (void *abstract_font) +{ + cairo_ft_unscaled_font_t *unscaled = abstract_font; + cairo_ft_unscaled_font_map_t *font_map; + + if (unscaled == NULL) + return; + + font_map = _cairo_ft_unscaled_font_map_lock (); + /* All created objects must have been mapped in the font map. */ + assert (font_map != NULL); + + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_ft_unscaled_font_map_unlock (); + return; + } + + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + + if (unscaled->from_face) { + /* See comments in _ft_font_face_destroy about the "zombie" state + * for a _ft_font_face. + */ + if (unscaled->faces && unscaled->faces->unscaled == NULL) { + assert (unscaled->faces->next == NULL); + cairo_font_face_destroy (&unscaled->faces->base); + } + } else { + _font_map_release_face_lock_held (font_map, unscaled); + } + unscaled->face = NULL; + + _cairo_ft_unscaled_font_map_unlock (); + + _cairo_ft_unscaled_font_fini (unscaled); +} + +static cairo_bool_t +_has_unlocked_face (const void *entry) +{ + const cairo_ft_unscaled_font_t *unscaled = entry; + + return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face); +} + +/* Ensures that an unscaled font has a face object. If we exceed + * MAX_OPEN_FACES, try to close some. + * + * This differs from _cairo_ft_scaled_font_lock_face in that it doesn't + * set the scale on the face, but just returns it at the last scale. + */ +cairo_warn FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) +{ + cairo_ft_unscaled_font_map_t *font_map; + FT_Face face = NULL; + + CAIRO_MUTEX_LOCK (unscaled->mutex); + unscaled->lock_count++; + + if (unscaled->face) + return unscaled->face; + + /* If this unscaled font was created from an FT_Face then we just + * returned it above. */ + assert (!unscaled->from_face); + + font_map = _cairo_ft_unscaled_font_map_lock (); + { + assert (font_map != NULL); + + while (font_map->num_open_faces >= MAX_OPEN_FACES) + { + cairo_ft_unscaled_font_t *entry; + + entry = _cairo_hash_table_random_entry (font_map->hash_table, + _has_unlocked_face); + if (entry == NULL) + break; + + _font_map_release_face_lock_held (font_map, entry); + } + } + _cairo_ft_unscaled_font_map_unlock (); + + if (FT_New_Face (font_map->ft_library, + unscaled->filename, + unscaled->id, + &face) != FT_Err_Ok) + { + unscaled->lock_count--; + CAIRO_MUTEX_UNLOCK (unscaled->mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + unscaled->face = face; + + font_map->num_open_faces++; + + return face; +} + + +/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face + */ +void +_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled) +{ + assert (unscaled->lock_count > 0); + + unscaled->lock_count--; + + CAIRO_MUTEX_UNLOCK (unscaled->mutex); +} + + +static cairo_status_t +_compute_transform (cairo_ft_font_transform_t *sf, + cairo_matrix_t *scale, + cairo_ft_unscaled_font_t *unscaled) +{ + cairo_status_t status; + double x_scale, y_scale; + cairo_matrix_t normalized = *scale; + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. + */ + + status = _cairo_matrix_compute_basis_scale_factors (scale, + &x_scale, &y_scale, + 1); + if (unlikely (status)) + return status; + + /* FreeType docs say this about x_scale and y_scale: + * "A character width or height smaller than 1pt is set to 1pt;" + * So, we cap them from below at 1.0 and let the FT transform + * take care of sub-1.0 scaling. */ + if (x_scale < 1.0) + x_scale = 1.0; + if (y_scale < 1.0) + y_scale = 1.0; + + if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) { + double min_distance = DBL_MAX; + cairo_bool_t magnify = TRUE; + int i; + int best_i = 0; + double best_x_size = 0; + double best_y_size = 0; + + for (i = 0; i < unscaled->face->num_fixed_sizes; i++) { + double x_size = unscaled->face->available_sizes[i].x_ppem / 64.; + double y_size = unscaled->face->available_sizes[i].y_ppem / 64.; + double distance = y_size - y_scale; + + /* + * distance is positive if current strike is larger than desired + * size, and negative if smaller. + * + * We like to prefer down-scaling to upscaling. + */ + + if ((magnify && distance >= 0) || fabs (distance) <= min_distance) { + magnify = distance < 0; + min_distance = fabs (distance); + best_i = i; + best_x_size = x_size; + best_y_size = y_size; + } + } + + x_scale = best_x_size; + y_scale = best_y_size; + } + + sf->x_scale = x_scale; + sf->y_scale = y_scale; + + cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); + + _cairo_matrix_get_affine (&normalized, + &sf->shape[0][0], &sf->shape[0][1], + &sf->shape[1][0], &sf->shape[1][1], + NULL, NULL); + + return CAIRO_STATUS_SUCCESS; +} + +/* Temporarily scales an unscaled font to the give scale. We catch + * scaling to the same size, since changing a FT_Face is expensive. + */ +static cairo_status_t +_cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, + cairo_matrix_t *scale) +{ + cairo_status_t status; + cairo_ft_font_transform_t sf; + FT_Matrix mat; + FT_Error error; + double x_scale, y_scale; + + assert (unscaled->face != NULL); + + if (unscaled->have_scale && + scale->xx == unscaled->current_scale.xx && + scale->yx == unscaled->current_scale.yx && + scale->xy == unscaled->current_scale.xy && + scale->yy == unscaled->current_scale.yy) + return CAIRO_STATUS_SUCCESS; + + unscaled->have_scale = TRUE; + unscaled->current_scale = *scale; + + status = _compute_transform (&sf, scale, unscaled); + if (unlikely (status)) + return status; + + unscaled->x_scale = sf.x_scale; + unscaled->y_scale = sf.y_scale; + + mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); + mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); + mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); + mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); + + unscaled->have_shape = (mat.xx != 0x10000 || + mat.yx != 0x00000 || + mat.xy != 0x00000 || + mat.yy != 0x10000); + + unscaled->Current_Shape = mat; + cairo_matrix_init (&unscaled->current_shape, + sf.shape[0][0], sf.shape[0][1], + sf.shape[1][0], sf.shape[1][1], + 0.0, 0.0); + + FT_Set_Transform(unscaled->face, &mat, NULL); + + x_scale = MIN(sf.x_scale, MAX_FONT_SIZE); + y_scale = MIN(sf.y_scale, MAX_FONT_SIZE); + error = FT_Set_Char_Size (unscaled->face, + x_scale * 64.0 + .5, + y_scale * 64.0 + .5, + 0, 0); + if (error) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot + * into a different format. For example, we want to convert a + * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit + * ARGB or ABGR bitmap. + * + * this function prepares a target descriptor for this operation. + * + * input :: target bitmap descriptor. The function will set its + * 'width', 'rows' and 'pitch' fields, and only these + * + * slot :: the glyph slot containing the source bitmap. this + * function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP + * + * mode :: the requested final rendering mode. supported values are + * MONO, NORMAL (i.e. gray), LCD and LCD_V + * + * the function returns the size in bytes of the corresponding buffer, + * it's up to the caller to allocate the corresponding memory block + * before calling _fill_xrender_bitmap + * + * it also returns -1 in case of error (e.g. incompatible arguments, + * like trying to convert a gray bitmap into a monochrome one) + */ +static int +_compute_xrender_bitmap_size(FT_Bitmap *target, + FT_GlyphSlot slot, + FT_Render_Mode mode) +{ + FT_Bitmap *ftbit; + int width, height, pitch; + + if (slot->format != FT_GLYPH_FORMAT_BITMAP) + return -1; + + /* compute the size of the final bitmap */ + ftbit = &slot->bitmap; + + width = ftbit->width; + height = ftbit->rows; + pitch = (width + 3) & ~3; + + switch (ftbit->pixel_mode) { + case FT_PIXEL_MODE_MONO: + if (mode == FT_RENDER_MODE_MONO) { + pitch = (((width + 31) & ~31) >> 3); + break; + } + /* fall-through */ + + case FT_PIXEL_MODE_GRAY: + if (mode == FT_RENDER_MODE_LCD || + mode == FT_RENDER_MODE_LCD_V) + { + /* each pixel is replicated into a 32-bit ARGB value */ + pitch = width * 4; + } + break; + + case FT_PIXEL_MODE_LCD: + if (mode != FT_RENDER_MODE_LCD) + return -1; + + /* horz pixel triplets are packed into 32-bit ARGB values */ + width /= 3; + pitch = width * 4; + break; + + case FT_PIXEL_MODE_LCD_V: + if (mode != FT_RENDER_MODE_LCD_V) + return -1; + + /* vert pixel triplets are packed into 32-bit ARGB values */ + height /= 3; + pitch = width * 4; + break; + + default: /* unsupported source format */ + return -1; + } + + target->width = width; + target->rows = height; + target->pitch = pitch; + target->buffer = NULL; + + return pitch * height; +} + +/* this functions converts the glyph bitmap found in a FT_GlyphSlot + * into a different format (see _compute_xrender_bitmap_size) + * + * you should call this function after _compute_xrender_bitmap_size + * + * target :: target bitmap descriptor. Note that its 'buffer' pointer + * must point to memory allocated by the caller + * + * slot :: the glyph slot containing the source bitmap + * + * mode :: the requested final rendering mode + * + * bgr :: boolean, set if BGR or VBGR pixel ordering is needed + */ +static void +_fill_xrender_bitmap(FT_Bitmap *target, + FT_GlyphSlot slot, + FT_Render_Mode mode, + int bgr) +{ + FT_Bitmap *ftbit = &slot->bitmap; + unsigned char *srcLine = ftbit->buffer; + unsigned char *dstLine = target->buffer; + int src_pitch = ftbit->pitch; + int width = target->width; + int height = target->rows; + int pitch = target->pitch; + int subpixel; + int h; + + subpixel = (mode == FT_RENDER_MODE_LCD || + mode == FT_RENDER_MODE_LCD_V); + + if (src_pitch < 0) + srcLine -= src_pitch * (ftbit->rows - 1); + + target->pixel_mode = ftbit->pixel_mode; + + switch (ftbit->pixel_mode) { + case FT_PIXEL_MODE_MONO: + if (subpixel) { + /* convert mono to ARGB32 values */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + + for (x = 0; x < width; x++) { + if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) + ((unsigned int *) dstLine)[x] = 0xffffffffU; + } + } + target->pixel_mode = FT_PIXEL_MODE_LCD; + + } else if (mode == FT_RENDER_MODE_NORMAL) { + /* convert mono to 8-bit gray */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + + for (x = 0; x < width; x++) { + if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) + dstLine[x] = 0xff; + } + } + target->pixel_mode = FT_PIXEL_MODE_GRAY; + + } else { + /* copy mono to mono */ + + int bytes = (width + 7) >> 3; + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) + memcpy (dstLine, srcLine, bytes); + } + break; + + case FT_PIXEL_MODE_GRAY: + if (subpixel) { + /* convert gray to ARGB32 values */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++) { + unsigned int pix = srcLine[x]; + + pix |= (pix << 8); + pix |= (pix << 16); + + dst[x] = pix; + } + } + target->pixel_mode = FT_PIXEL_MODE_LCD; + } else { + /* copy gray into gray */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) + memcpy (dstLine, srcLine, width); + } + break; + + case FT_PIXEL_MODE_LCD: + if (!bgr) { + /* convert horizontal RGB into ARGB32 */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 3) { + unsigned int pix; + + pix = ((unsigned int)src[0] << 16) | + ((unsigned int)src[1] << 8) | + ((unsigned int)src[2] ) | + ((unsigned int)src[1] << 24) ; + + dst[x] = pix; + } + } + } else { + /* convert horizontal BGR into ARGB32 */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 3) { + unsigned int pix; + + pix = ((unsigned int)src[2] << 16) | + ((unsigned int)src[1] << 8) | + ((unsigned int)src[0] ) | + ((unsigned int)src[1] << 24) ; + + dst[x] = pix; + } + } + } + break; + + default: /* FT_PIXEL_MODE_LCD_V */ + /* convert vertical RGB into ARGB32 */ + if (!bgr) { + + for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) { + int x; + unsigned char* src = srcLine; + unsigned int* dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 1) { + unsigned int pix; + pix = ((unsigned int)src[0] << 16) | + ((unsigned int)src[src_pitch] << 8) | + ((unsigned int)src[src_pitch*2] ) | + ((unsigned int)src[src_pitch] << 24) ; + dst[x] = pix; + } + } + } else { + + for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) { + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 1) { + unsigned int pix; + + pix = ((unsigned int)src[src_pitch * 2] << 16) | + ((unsigned int)src[src_pitch] << 8) | + ((unsigned int)src[0] ) | + ((unsigned int)src[src_pitch] << 24) ; + + dst[x] = pix; + } + } + } + } +} + + +/* Fills in val->image with an image surface created from @bitmap + */ +static cairo_status_t +_get_bitmap_surface (FT_Bitmap *bitmap, + FT_Library library, + cairo_bool_t own_buffer, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + int width, height, stride; + unsigned char *data; + int format = CAIRO_FORMAT_A8; + cairo_image_surface_t *image; + cairo_bool_t component_alpha = FALSE; + + width = bitmap->width; + height = bitmap->rows; + + if (width == 0 || height == 0) { + *surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); + return (*surface)->base.status; + } + + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: + stride = (((width + 31) & ~31) >> 3); + if (own_buffer) { + data = bitmap->buffer; + assert (stride == bitmap->pitch); + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (stride == bitmap->pitch) { + memcpy (data, bitmap->buffer, stride * height); + } else { + int i; + unsigned char *source, *dest; + int copy_len = MIN (stride, bitmap->pitch); + int pad_len = stride - bitmap->pitch; + + source = bitmap->buffer; + dest = data; + for (i = height; i; i--) { + memcpy (dest, source, copy_len); + source += bitmap->pitch; + dest += stride; + } + /* do we really care about zeroing any extra row padding in dest? */ + if (pad_len > 0) { + dest = data + copy_len; + for (i = height; i; i--) { + memset (dest, '\0', pad_len); + dest += stride; + } + } + } + } + +#ifndef WORDS_BIGENDIAN + { + uint8_t *d = data; + int count = stride * height; + + while (count--) { + *d = CAIRO_BITSWAP8 (*d); + d++; + } + } +#endif + format = CAIRO_FORMAT_A1; + break; + + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_GRAY: + if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL || + bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) + { + stride = bitmap->pitch; + + /* We don't support stride not multiple of 4. */ + if (stride & 3) + { + assert (!own_buffer); + goto convert; + } + + if (own_buffer) { + data = bitmap->buffer; + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (data, bitmap->buffer, stride * height); + } + + format = CAIRO_FORMAT_A8; + } else { + data = bitmap->buffer; + stride = bitmap->pitch; + format = CAIRO_FORMAT_ARGB32; + component_alpha = TRUE; + } + break; +#ifdef FT_LOAD_COLOR + case FT_PIXEL_MODE_BGRA: + stride = width * 4; + if (own_buffer) { + data = bitmap->buffer; + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (data, bitmap->buffer, stride * height); + } + format = CAIRO_FORMAT_ARGB32; + break; +#endif + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + convert: + if (!own_buffer && library) + { + /* This is pretty much the only case that we can get in here. */ + /* Convert to 8bit grayscale. */ + + FT_Bitmap tmp; + FT_Int align; + + format = CAIRO_FORMAT_A8; + + align = cairo_format_stride_for_width (format, bitmap->width); + + FT_Bitmap_New( &tmp ); + + if (FT_Bitmap_Convert( library, bitmap, &tmp, align )) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + FT_Bitmap_Done( library, bitmap ); + *bitmap = tmp; + + stride = bitmap->pitch; + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (bitmap->num_grays != 256) + { + unsigned int x, y; + unsigned int mul = 255 / (bitmap->num_grays - 1); + FT_Byte *p = bitmap->buffer; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + p[x] *= mul; + p += bitmap->pitch; + } + } + + memcpy (data, bitmap->buffer, stride * height); + break; + } + /* These could be triggered by very rare types of TrueType fonts */ + default: + if (own_buffer) + free (bitmap->buffer); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* XXX */ + *surface = image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (data, + format, + width, height, stride); + if (image->base.status) { + free (data); + return (*surface)->base.status; + } + + if (component_alpha) + pixman_image_set_component_alpha (image->pixman_image, TRUE); + + _cairo_image_surface_assume_ownership_of_data (image); + + _cairo_debug_check_image_surface_is_defined (&image->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* Converts an outline FT_GlyphSlot into an image + * + * This could go through _render_glyph_bitmap as well, letting + * FreeType convert the outline to a bitmap, but doing it ourselves + * has two minor advantages: first, we save a copy of the bitmap + * buffer: we can directly use the buffer that FreeType renders + * into. + * + * Second, it may help when we add support for subpixel + * rendering: the Xft code does it this way. (Keith thinks that + * it may also be possible to get the subpixel rendering with + * FT_Render_Glyph: something worth looking into in more detail + * when we add subpixel support. If so, we may want to eliminate + * this version of the code path entirely. + */ +static cairo_status_t +_render_glyph_outline (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + int rgba = FC_RGBA_UNKNOWN; + int lcd_filter = FT_LCD_FILTER_LEGACY; + FT_GlyphSlot glyphslot = face->glyph; + FT_Outline *outline = &glyphslot->outline; + FT_Bitmap bitmap; + FT_BBox cbox; + unsigned int width, height; + cairo_status_t status; + FT_Error fterror; + FT_Library library = glyphslot->library; + FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; + + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + render_mode = FT_RENDER_MODE_MONO; + break; + + case CAIRO_ANTIALIAS_SUBPIXEL: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + render_mode = FT_RENDER_MODE_LCD; + break; + + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + render_mode = FT_RENDER_MODE_LCD_V; + break; + } + + switch (font_options->lcd_filter) { + case CAIRO_LCD_FILTER_NONE: + lcd_filter = FT_LCD_FILTER_NONE; + break; + case CAIRO_LCD_FILTER_DEFAULT: + case CAIRO_LCD_FILTER_INTRA_PIXEL: + lcd_filter = FT_LCD_FILTER_LEGACY; + break; + case CAIRO_LCD_FILTER_FIR3: + lcd_filter = FT_LCD_FILTER_LIGHT; + break; + case CAIRO_LCD_FILTER_FIR5: + lcd_filter = FT_LCD_FILTER_DEFAULT; + break; + } + + break; + + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + render_mode = FT_RENDER_MODE_NORMAL; + } + + FT_Outline_Get_CBox (outline, &cbox); + + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = (cbox.xMax + 63) & -64; + cbox.yMax = (cbox.yMax + 63) & -64; + + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); + height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); + + if (width * height == 0) { + cairo_format_t format; + /* Looks like fb handles zero-sized images just fine */ + switch (render_mode) { + case FT_RENDER_MODE_MONO: + format = CAIRO_FORMAT_A1; + break; + case FT_RENDER_MODE_LCD: + case FT_RENDER_MODE_LCD_V: + format= CAIRO_FORMAT_ARGB32; + break; + case FT_RENDER_MODE_LIGHT: + case FT_RENDER_MODE_NORMAL: + case FT_RENDER_MODE_MAX: + default: + format = CAIRO_FORMAT_A8; + break; + } + + (*surface) = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); + if ((*surface)->base.status) + return (*surface)->base.status; + } else { + + int bitmap_size; + static int initialized_setLcdFilter = 0; + + switch (render_mode) { + case FT_RENDER_MODE_LCD: + if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) { + rgba = FC_RGBA_BGR; + } else { + rgba = FC_RGBA_RGB; + } + break; + case FT_RENDER_MODE_LCD_V: + if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) { + rgba = FC_RGBA_VBGR; + } else { + rgba = FC_RGBA_VRGB; + } + break; + case FT_RENDER_MODE_MONO: + case FT_RENDER_MODE_LIGHT: + case FT_RENDER_MODE_NORMAL: + case FT_RENDER_MODE_MAX: + default: + break; + } + + if (!initialized_setLcdFilter) { + initialized_setLcdFilter = 1; +#ifdef HAVE_FT_LIBRARY_SETLCDFILTER + setLcdFilter = &FT_Library_SetLcdFilter; +#else + setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter"); +#endif + } + + if (setLcdFilter) + setLcdFilter (library, lcd_filter); + + fterror = FT_Render_Glyph (face->glyph, render_mode); + + if (setLcdFilter) + setLcdFilter (library, FT_LCD_FILTER_NONE); + + if (fterror != 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + bitmap_size = _compute_xrender_bitmap_size (&bitmap, + face->glyph, + render_mode); + if (bitmap_size < 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + bitmap.buffer = calloc (1, bitmap_size); + if (bitmap.buffer == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, + (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); + + /* Note: + * _get_bitmap_surface will free bitmap.buffer if there is an error + */ + status = _get_bitmap_surface (&bitmap, NULL, TRUE, font_options, surface); + if (unlikely (status)) + return status; + + /* Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. Moreover, device + * offsets are position of glyph origin relative to top left while xMin + * and yMax are offsets of top left relative to origin. Another negation. + */ + cairo_surface_set_device_offset (&(*surface)->base, + (double)-glyphslot->bitmap_left, + (double)+glyphslot->bitmap_top); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Converts a bitmap (or other) FT_GlyphSlot into an image */ +static cairo_status_t +_render_glyph_bitmap (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + FT_GlyphSlot glyphslot = face->glyph; + cairo_status_t status; + FT_Error error; + + /* According to the FreeType docs, glyphslot->format could be + * something other than FT_GLYPH_FORMAT_OUTLINE or + * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType + * the opportunity to convert such to + * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since + * we avoid the FT_LOAD_NO_RECURSE flag. + */ + error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + /* XXX ignoring all other errors for now. They are not fatal, typically + * just a glyph-not-found. */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _get_bitmap_surface (&glyphslot->bitmap, + glyphslot->library, + FALSE, font_options, + surface); + if (unlikely (status)) + return status; + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. Moreover, device + * offsets are position of glyph origin relative to top left while + * bitmap_left and bitmap_top are offsets of top left relative to origin. + * Another negation. + */ + cairo_surface_set_device_offset (&(*surface)->base, + -glyphslot->bitmap_left, + +glyphslot->bitmap_top); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_transform_glyph_bitmap (cairo_matrix_t * shape, + cairo_image_surface_t ** surface) +{ + cairo_matrix_t original_to_transformed; + cairo_matrix_t transformed_to_original; + cairo_image_surface_t *old_image; + cairo_surface_t *image; + double x[4], y[4]; + double origin_x, origin_y; + int orig_width, orig_height; + int i; + int x_min, y_min, x_max, y_max; + int width, height; + cairo_status_t status; + cairo_surface_pattern_t pattern; + + /* We want to compute a transform that takes the origin + * (device_x_offset, device_y_offset) to 0,0, then applies + * the "shape" portion of the font transform + */ + original_to_transformed = *shape; + + cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y); + orig_width = (*surface)->width; + orig_height = (*surface)->height; + + cairo_matrix_translate (&original_to_transformed, + -origin_x, -origin_y); + + /* Find the bounding box of the original bitmap under that + * transform + */ + x[0] = 0; y[0] = 0; + x[1] = orig_width; y[1] = 0; + x[2] = orig_width; y[2] = orig_height; + x[3] = 0; y[3] = orig_height; + + for (i = 0; i < 4; i++) + cairo_matrix_transform_point (&original_to_transformed, + &x[i], &y[i]); + + x_min = floor (x[0]); y_min = floor (y[0]); + x_max = ceil (x[0]); y_max = ceil (y[0]); + + for (i = 1; i < 4; i++) { + if (x[i] < x_min) + x_min = floor (x[i]); + else if (x[i] > x_max) + x_max = ceil (x[i]); + if (y[i] < y_min) + y_min = floor (y[i]); + else if (y[i] > y_max) + y_max = ceil (y[i]); + } + + /* Adjust the transform so that the bounding box starts at 0,0 ... + * this gives our final transform from original bitmap to transformed + * bitmap. + */ + original_to_transformed.x0 -= x_min; + original_to_transformed.y0 -= y_min; + + /* Create the transformed bitmap */ + width = x_max - x_min; + height = y_max - y_min; + + transformed_to_original = original_to_transformed; + status = cairo_matrix_invert (&transformed_to_original); + if (unlikely (status)) + return status; + + if (cairo_image_surface_get_format (*surface) == CAIRO_FORMAT_ARGB32 && + !pixman_image_get_component_alpha ((*surface)->pixman_image)) + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + else + image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (unlikely (image->status)) + return image->status; + + /* Draw the original bitmap transformed into the new bitmap + */ + _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); + cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); + + status = _cairo_surface_paint (image, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + /* Now update the cache entry for the new bitmap, recomputing + * the origin based on the final transform. + */ + cairo_matrix_transform_point (&original_to_transformed, + &origin_x, &origin_y); + + old_image = (*surface); + (*surface) = (cairo_image_surface_t *)image; + cairo_surface_destroy (&old_image->base); + + cairo_surface_set_device_offset (&(*surface)->base, + _cairo_lround (origin_x), + _cairo_lround (origin_y)); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { + _cairo_ft_unscaled_font_destroy, +#if 0 + _cairo_ft_unscaled_font_create_glyph +#endif +}; + +/* #cairo_ft_scaled_font_t */ + +typedef struct _cairo_ft_scaled_font { + cairo_scaled_font_t base; + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; +} cairo_ft_scaled_font_t; + +static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; + +#if CAIRO_HAS_FC_FONT +/* The load flags passed to FT_Load_Glyph control aspects like hinting and + * antialiasing. Here we compute them from the fields of a FcPattern. + */ +static void +_get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) +{ + FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden; + cairo_ft_options_t ft_options; + int rgba; +#ifdef FC_HINT_STYLE + int hintstyle; +#endif + + _cairo_font_options_init_default (&ft_options.base); + ft_options.load_flags = FT_LOAD_DEFAULT; + ft_options.extra_flags = 0; + +#ifndef FC_EMBEDDED_BITMAP +#define FC_EMBEDDED_BITMAP "embeddedbitmap" +#endif + + /* Check whether to force use of embedded bitmaps */ + if (FcPatternGetBool (pattern, + FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch) + bitmap = FcFalse; + + /* disable antialiasing if requested */ + if (FcPatternGetBool (pattern, + FC_ANTIALIAS, 0, &antialias) != FcResultMatch) + antialias = FcTrue; + + if (antialias) { + cairo_subpixel_order_t subpixel_order; + int lcd_filter; + + /* disable hinting if requested */ + if (FcPatternGetBool (pattern, + FC_HINTING, 0, &hinting) != FcResultMatch) + hinting = FcTrue; + + if (FcPatternGetInteger (pattern, + FC_RGBA, 0, &rgba) != FcResultMatch) + rgba = FC_RGBA_UNKNOWN; + + switch (rgba) { + case FC_RGBA_RGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; + break; + case FC_RGBA_BGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; + break; + case FC_RGBA_VRGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; + break; + case FC_RGBA_VBGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; + break; + case FC_RGBA_UNKNOWN: + case FC_RGBA_NONE: + default: + subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + break; + } + + if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) { + ft_options.base.subpixel_order = subpixel_order; + ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; + } + + if (FcPatternGetInteger (pattern, + FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch) + { + switch (lcd_filter) { + case FC_LCD_NONE: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE; + break; + case FC_LCD_DEFAULT: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5; + break; + case FC_LCD_LIGHT: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3; + break; + case FC_LCD_LEGACY: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; + break; + } + } + +#ifdef FC_HINT_STYLE + if (FcPatternGetInteger (pattern, + FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) + hintstyle = FC_HINT_FULL; + + if (!hinting) + hintstyle = FC_HINT_NONE; + + switch (hintstyle) { + case FC_HINT_NONE: + ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; + break; + case FC_HINT_SLIGHT: + ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT; + break; + case FC_HINT_MEDIUM: + default: + ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM; + break; + case FC_HINT_FULL: + ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL; + break; + } +#else /* !FC_HINT_STYLE */ + if (!hinting) { + ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; + } +#endif /* FC_HINT_STYLE */ + + /* Force embedded bitmaps off if no hinting requested */ + if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE) + bitmap = FcFalse; + + if (!bitmap) + ft_options.load_flags |= FT_LOAD_NO_BITMAP; + + } else { + ft_options.base.antialias = CAIRO_ANTIALIAS_NONE; + } + + /* force autohinting if requested */ + if (FcPatternGetBool (pattern, + FC_AUTOHINT, 0, &autohint) != FcResultMatch) + autohint = FcFalse; + + if (autohint) + ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT; + + if (FcPatternGetBool (pattern, + FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch) + vertical_layout = FcFalse; + + if (vertical_layout) + ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; + +#ifndef FC_EMBOLDEN +#define FC_EMBOLDEN "embolden" +#endif + if (FcPatternGetBool (pattern, + FC_EMBOLDEN, 0, &embolden) != FcResultMatch) + embolden = FcFalse; + + if (embolden) + ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN; + + *ret = ft_options; +} +#endif + +static void +_cairo_ft_options_merge (cairo_ft_options_t *options, + cairo_ft_options_t *other) +{ + int load_flags = other->load_flags; + int load_target = FT_LOAD_TARGET_NORMAL; + + /* clear load target mode */ + load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); + + if (load_flags & FT_LOAD_NO_HINTING) + other->base.hint_style = CAIRO_HINT_STYLE_NONE; + + if (other->base.antialias == CAIRO_ANTIALIAS_NONE || + options->base.antialias == CAIRO_ANTIALIAS_NONE) { + options->base.antialias = CAIRO_ANTIALIAS_NONE; + options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + } else if (options->base.antialias != CAIRO_ANTIALIAS_GRAY) { + /* The surface supports subpixel aa, so let the font face options + * choose whether to use subpixel aa. If the surface has + * CAIRO_ANTIALIAS_GRAY (e.g. PS, PDF, SVG, translucent part of a + * CONTENT_COLOR_ALPHA surface), then don't accept subpixel aa. */ + if (other->base.antialias != CAIRO_ANTIALIAS_DEFAULT) + options->base.antialias = other->base.antialias; + /* If the surface knows the subpixel order then use that. */ + if (options->base.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) + options->base.subpixel_order = other->base.subpixel_order; + } + + if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT) + options->base.hint_style = other->base.hint_style; + + if (other->base.hint_style == CAIRO_HINT_STYLE_NONE) + options->base.hint_style = CAIRO_HINT_STYLE_NONE; + + if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT) + options->base.lcd_filter = other->base.lcd_filter; + + if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE) + options->base.lcd_filter = CAIRO_LCD_FILTER_NONE; + + if (options->base.antialias == CAIRO_ANTIALIAS_NONE) { + if (options->base.hint_style == CAIRO_HINT_STYLE_NONE) + load_flags |= FT_LOAD_NO_HINTING; + else + load_target = FT_LOAD_TARGET_MONO; + load_flags |= FT_LOAD_MONOCHROME; + } else { + switch (options->base.hint_style) { + case CAIRO_HINT_STYLE_NONE: + load_flags |= FT_LOAD_NO_HINTING; + break; + case CAIRO_HINT_STYLE_SLIGHT: + load_target = FT_LOAD_TARGET_LIGHT; + break; + case CAIRO_HINT_STYLE_MEDIUM: + break; + case CAIRO_HINT_STYLE_FULL: + case CAIRO_HINT_STYLE_DEFAULT: + if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (options->base.subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + load_target = FT_LOAD_TARGET_LCD; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + load_target = FT_LOAD_TARGET_LCD_V; + break; + } + } + break; + } + } + + options->load_flags = load_flags | load_target; + options->extra_flags = other->extra_flags; + if (options->base.hint_metrics != CAIRO_HINT_METRICS_OFF) + options->extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS; +} + +static cairo_status_t +_cairo_ft_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) +{ + cairo_ft_font_face_t *font_face = abstract_font_face; + cairo_ft_scaled_font_t *scaled_font; + FT_Face face; + FT_Size_Metrics *metrics; + cairo_font_extents_t fs_metrics; + cairo_status_t status; + cairo_ft_unscaled_font_t *unscaled; + + assert (font_face->unscaled); + + face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled); + if (unlikely (face == NULL)) /* backend error */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + scaled_font = malloc (sizeof (cairo_ft_scaled_font_t)); + if (unlikely (scaled_font == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + scaled_font->unscaled = unscaled = font_face->unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + + _cairo_font_options_init_copy (&scaled_font->ft_options.base, options); + _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options); + + status = _cairo_scaled_font_init (&scaled_font->base, + &font_face->base, + font_matrix, ctm, options, + &_cairo_ft_scaled_font_backend); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + status = _cairo_ft_unscaled_font_set_scale (unscaled, + &scaled_font->base.scale); + if (unlikely (status)) { + /* This can only fail if we encounter an error with the underlying + * font, so propagate the error back to the font-face. */ + _cairo_ft_unscaled_font_unlock_face (unscaled); + _cairo_unscaled_font_destroy (&unscaled->base); + free (scaled_font); + return status; + } + + + metrics = &face->size->metrics; + + /* + * Get to unscaled metrics so that the upper level can get back to + * user space + * + * Also use this path for bitmap-only fonts. The other branch uses + * face members that are only relevant for scalable fonts. This is + * detected by simply checking for units_per_EM==0. + */ + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF || + face->units_per_EM == 0) { + double x_factor, y_factor; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; + fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; + fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; + if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor; + } + } else { + double scale = face->units_per_EM; + + fs_metrics.ascent = face->ascender / scale; + fs_metrics.descent = - face->descender / scale; + fs_metrics.height = face->height / scale; + if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = face->max_advance_width / scale; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = face->max_advance_height / scale; + } + } + + status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + *font_out = &scaled_font->base; + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SCALED_FONT: + _cairo_unscaled_font_destroy (&unscaled->base); + free (scaled_font); + FAIL: + _cairo_ft_unscaled_font_unlock_face (font_face->unscaled); + *font_out = _cairo_scaled_font_create_in_error (status); + return CAIRO_STATUS_SUCCESS; /* non-backend error */ +} + +cairo_bool_t +_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &_cairo_ft_scaled_font_backend; +} + +static void +_cairo_ft_scaled_font_fini (void *abstract_font) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font == NULL) + return; + + _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); +} + +static int +_move_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS) + return 1; + if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_line_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_conic_to (FT_Vector *control, FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + cairo_fixed_t x3, y3; + cairo_point_t conic; + + if (! _cairo_path_fixed_get_current_point (path, &x0, &y0)) + return 1; + + conic.x = _cairo_fixed_from_26_6 (control->x); + conic.y = _cairo_fixed_from_26_6 (control->y); + + x3 = _cairo_fixed_from_26_6 (to->x); + y3 = _cairo_fixed_from_26_6 (to->y); + + x1 = x0 + 2.0/3.0 * (conic.x - x0); + y1 = y0 + 2.0/3.0 * (conic.y - y0); + + x2 = x3 + 2.0/3.0 * (conic.x - x3); + y2 = y3 + 2.0/3.0 * (conic.y - y3); + + if (_cairo_path_fixed_curve_to (path, + x1, y1, + x2, y2, + x3, y3) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_cubic_to (FT_Vector *control1, FT_Vector *control2, + FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + + x0 = _cairo_fixed_from_26_6 (control1->x); + y0 = _cairo_fixed_from_26_6 (control1->y); + + x1 = _cairo_fixed_from_26_6 (control2->x); + y1 = _cairo_fixed_from_26_6 (control2->y); + + x2 = _cairo_fixed_from_26_6 (to->x); + y2 = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_curve_to (path, + x0, y0, + x1, y1, + x2, y2) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static cairo_status_t +_decompose_glyph_outline (FT_Face face, + cairo_font_options_t *options, + cairo_path_fixed_t **pathp) +{ + static const FT_Outline_Funcs outline_funcs = { + (FT_Outline_MoveToFunc)_move_to, + (FT_Outline_LineToFunc)_line_to, + (FT_Outline_ConicToFunc)_conic_to, + (FT_Outline_CubicToFunc)_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + static const FT_Matrix invert_y = { + DOUBLE_TO_16_16 (1.0), 0, + 0, DOUBLE_TO_16_16 (-1.0), + }; + + FT_GlyphSlot glyph; + cairo_path_fixed_t *path; + cairo_status_t status; + + path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + glyph = face->glyph; + + /* Font glyphs have an inverted Y axis compared to cairo. */ + FT_Outline_Transform (&glyph->outline, &invert_y); + if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) { + _cairo_path_fixed_destroy (path); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_path_fixed_close_path (path); + if (unlikely (status)) { + _cairo_path_fixed_destroy (path); + return status; + } + + *pathp = path; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Translate glyph to match its metrics. + */ +static void +_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, + FT_GlyphSlot glyph) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + FT_Vector vector; + + vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX; + vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape); + FT_Outline_Translate(&glyph->outline, vector.x, vector.y); + } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + glyph->bitmap_left += vector.x / 64; + glyph->bitmap_top += vector.y / 64; + } +} + +static cairo_int_status_t +_cairo_ft_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_text_extents_t fs_metrics; + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_GlyphSlot glyph; + FT_Face face; + FT_Error error; + int load_flags = scaled_font->ft_options.load_flags; + FT_Glyph_Metrics *metrics; + double x_factor, y_factor; + cairo_bool_t vertical_layout = FALSE; + cairo_status_t status; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, + &scaled_font->base.scale); + if (unlikely (status)) + goto FAIL; + + /* Ignore global advance unconditionally */ + load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && + (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) + load_flags |= FT_LOAD_NO_BITMAP; + + /* + * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as + * suggested by freetype people. + */ + if (load_flags & FT_LOAD_VERTICAL_LAYOUT) { + load_flags &= ~FT_LOAD_VERTICAL_LAYOUT; + vertical_layout = TRUE; + } + +#ifdef FT_LOAD_COLOR + /* Color-glyph support: + * + * This flags needs plumbing through fontconfig (does it?), and + * maybe we should cache color and grayscale bitmaps separately + * such that users of the font (ie. the surface) can choose which + * version to use based on target content type. + */ + + load_flags |= FT_LOAD_COLOR; +#endif + + error = FT_Load_Glyph (scaled_font->unscaled->face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags); + /* XXX ignoring all other errors for now. They are not fatal, typically + * just a glyph-not-found. */ + if (error == FT_Err_Out_Of_Memory) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + glyph = face->glyph; + +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + /* + * embolden glyphs if requested + */ + if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN) + FT_GlyphSlot_Embolden (glyph); +#endif + + if (vertical_layout) + _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); + + if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { + + cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; + /* + * Compute font-space metrics + */ + metrics = &glyph->metrics; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + /* + * Note: Y coordinates of the horizontal bearing need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + * + * If we want hinted metrics but aren't asking for hinted glyphs from + * FreeType, then we need to do the metric hinting ourselves. + */ + + if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) + { + FT_Pos x1, x2; + FT_Pos y1, y2; + FT_Pos advance; + + if (!vertical_layout) { + x1 = (metrics->horiBearingX) & -64; + x2 = (metrics->horiBearingX + metrics->width + 63) & -64; + y1 = (-metrics->horiBearingY) & -64; + y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; + + advance = ((metrics->horiAdvance + 32) & -64); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; + fs_metrics.y_advance = 0; + } else { + x1 = (metrics->vertBearingX) & -64; + x2 = (metrics->vertBearingX + metrics->width + 63) & -64; + y1 = (metrics->vertBearingY) & -64; + y2 = (metrics->vertBearingY + metrics->height + 63) & -64; + + advance = ((metrics->vertAdvance + 32) & -64); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = 0; + fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; + } + } else { + fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; + + if (!vertical_layout) { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; + else + fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; + fs_metrics.y_advance = 0 * y_factor; + } else { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + + fs_metrics.x_advance = 0 * x_factor; + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; + else + fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; + } + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &fs_metrics); + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { + cairo_image_surface_t *surface; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + status = _render_glyph_outline (face, &scaled_font->ft_options.base, + &surface); + } else { + status = _render_glyph_bitmap (face, &scaled_font->ft_options.base, + &surface); + if (likely (status == CAIRO_STATUS_SUCCESS) && + unscaled->have_shape) + { + status = _transform_glyph_bitmap (&unscaled->current_shape, + &surface); + if (unlikely (status)) + cairo_surface_destroy (&surface->base); + } + } + if (unlikely (status)) + goto FAIL; + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path = NULL; /* hide compiler warning */ + + /* + * A kludge -- the above code will trash the outline, + * so reload it. This will probably never occur though + */ + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { + error = FT_Load_Glyph (face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags | FT_LOAD_NO_BITMAP); + /* XXX ignoring all other errors for now. They are not fatal, typically + * just a glyph-not-found. */ + if (error == FT_Err_Out_Of_Memory) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + /* + * embolden glyphs if requested + */ + if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN) + FT_GlyphSlot_Embolden (glyph); +#endif + if (vertical_layout) + _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); + + } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, + &path); + else + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (status)) + goto FAIL; + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + FAIL: + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return status; +} + +static unsigned long +_cairo_ft_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_UInt index; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return 0; + +#if CAIRO_HAS_FC_FONT + index = FcFreeTypeCharIndex (face, ucs4); +#else + index = FT_Get_Char_Index (face, ucs4); +#endif + + _cairo_ft_unscaled_font_unlock_face (unscaled); + return index; +} + +static cairo_int_status_t +_cairo_ft_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) + return CAIRO_INT_STATUS_UNSUPPORTED; + +#if HAVE_FT_LOAD_SFNT_TABLE + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (FT_IS_SFNT (face) && + FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) + status = CAIRO_STATUS_SUCCESS; + + _cairo_ft_unscaled_font_unlock_face (unscaled); +#endif + + return status; +} + +static cairo_int_status_t +_cairo_ft_index_to_ucs4(void *abstract_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_ULong charcode; + FT_UInt gindex; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *ucs4 = (uint32_t) -1; + charcode = FT_Get_First_Char(face, &gindex); + while (gindex != 0) { + if (gindex == index) { + *ucs4 = charcode; + break; + } + charcode = FT_Get_Next_Char (face, charcode, &gindex); + } + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { + CAIRO_FONT_TYPE_FT, + _cairo_ft_scaled_font_fini, + _cairo_ft_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_ft_ucs4_to_index, + NULL, /* show_glyphs */ + _cairo_ft_load_truetype_table, + _cairo_ft_index_to_ucs4 +}; + +/* #cairo_ft_font_face_t */ + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, + cairo_font_face_t **out); + +static cairo_status_t +_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + FcPattern *pattern; + int fcslant; + int fcweight; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + pattern = FcPatternCreate (); + if (!pattern) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (!FcPatternAddString (pattern, + FC_FAMILY, (unsigned char *) toy_face->family)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->slant) + { + case CAIRO_FONT_SLANT_ITALIC: + fcslant = FC_SLANT_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + fcslant = FC_SLANT_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + fcslant = FC_SLANT_ROMAN; + break; + } + + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->weight) + { + case CAIRO_FONT_WEIGHT_BOLD: + fcweight = FC_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + fcweight = FC_WEIGHT_MEDIUM; + break; + } + + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + status = _cairo_ft_font_face_create_for_pattern (pattern, font_face); + + FREE_PATTERN: + FcPatternDestroy (pattern); + + return status; +} +#endif + +static void +_cairo_ft_font_face_destroy (void *abstract_face) +{ + cairo_ft_font_face_t *font_face = abstract_face; + + /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, + * we have a special "zombie" state for the face when the unscaled font + * is still alive but there are no other references to a font face with + * the same FT_Face. + * + * We go from: + * + * font_face ------> unscaled + * <-....weak....../ + * + * To: + * + * font_face <------- unscaled + */ + + if (font_face->unscaled && + font_face->unscaled->from_face && + font_face->next == NULL && + font_face->unscaled->faces == font_face && + CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) + { + cairo_font_face_reference (&font_face->base); + + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + + return; + } + + if (font_face->unscaled) { + cairo_ft_font_face_t *tmp_face = NULL; + cairo_ft_font_face_t *last_face = NULL; + + /* Remove face from linked list */ + for (tmp_face = font_face->unscaled->faces; + tmp_face; + tmp_face = tmp_face->next) + { + if (tmp_face == font_face) { + if (last_face) + last_face->next = tmp_face->next; + else + font_face->unscaled->faces = tmp_face->next; + } + + last_face = tmp_face; + } + + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + } + +#if CAIRO_HAS_FC_FONT + if (font_face->pattern) { + FcPatternDestroy (font_face->pattern); + cairo_font_face_destroy (font_face->resolved_font_face); + } +#endif +} + +static cairo_font_face_t * +_cairo_ft_font_face_get_implementation (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_ft_font_face_t *font_face = abstract_face; + + /* The handling of font options is different depending on how the + * font face was created. When the user creates a font face with + * cairo_ft_font_face_create_for_ft_face(), then the load flags + * passed in augment the load flags for the options. But for + * cairo_ft_font_face_create_for_pattern(), the load flags are + * derived from a pattern where the user has called + * cairo_ft_font_options_substitute(), so *just* use those load + * flags and ignore the options. + */ + +#if CAIRO_HAS_FC_FONT + /* If we have an unresolved pattern, resolve it and create + * unscaled font. Otherwise, use the ones stored in font_face. + */ + if (font_face->pattern) { + cairo_font_face_t *resolved; + + /* Cache the resolved font whilst the FcConfig remains consistent. */ + resolved = font_face->resolved_font_face; + if (resolved != NULL) { + if (! FcInitBringUptoDate ()) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } + + if (font_face->resolved_config == FcConfigGetCurrent ()) + return cairo_font_face_reference (resolved); + + cairo_font_face_destroy (resolved); + font_face->resolved_font_face = NULL; + } + + resolved = _cairo_ft_resolve_pattern (font_face->pattern, + font_matrix, + ctm, + options); + if (unlikely (resolved->status)) + return resolved; + + font_face->resolved_font_face = cairo_font_face_reference (resolved); + font_face->resolved_config = FcConfigGetCurrent (); + + return resolved; + } +#endif + + return abstract_face; +} + +const cairo_font_face_backend_t _cairo_ft_font_face_backend = { + CAIRO_FONT_TYPE_FT, +#if CAIRO_HAS_FC_FONT + _cairo_ft_font_face_create_for_toy, +#else + NULL, +#endif + _cairo_ft_font_face_destroy, + _cairo_ft_font_face_scaled_font_create, + _cairo_ft_font_face_get_implementation +}; + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, + cairo_font_face_t **out) +{ + cairo_ft_font_face_t *font_face; + + font_face = malloc (sizeof (cairo_ft_font_face_t)); + if (unlikely (font_face == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_face->unscaled = NULL; + font_face->next = NULL; + + font_face->pattern = FcPatternDuplicate (pattern); + if (unlikely (font_face->pattern == NULL)) { + free (font_face); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + font_face->resolved_font_face = NULL; + font_face->resolved_config = NULL; + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + + *out = &font_face->base; + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_font_face_t * +_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, + cairo_ft_options_t *ft_options) +{ + cairo_ft_font_face_t *font_face, **prev_font_face; + + /* Looked for an existing matching font face */ + for (font_face = unscaled->faces, prev_font_face = &unscaled->faces; + font_face; + prev_font_face = &font_face->next, font_face = font_face->next) + { + if (font_face->ft_options.load_flags == ft_options->load_flags && + font_face->ft_options.extra_flags == ft_options->extra_flags && + cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base)) + { + if (font_face->base.status) { + /* The font_face has been left in an error state, abandon it. */ + *prev_font_face = font_face->next; + break; + } + + if (font_face->unscaled == NULL) { + /* Resurrect this "zombie" font_face (from + * _cairo_ft_font_face_destroy), switching its unscaled_font + * from owner to ownee. */ + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + return &font_face->base; + } else + return cairo_font_face_reference (&font_face->base); + } + } + + /* No match found, create a new one */ + font_face = malloc (sizeof (cairo_ft_font_face_t)); + if (unlikely (!font_face)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + + font_face->ft_options = *ft_options; + + if (unscaled->faces && unscaled->faces->unscaled == NULL) { + /* This "zombie" font_face (from _cairo_ft_font_face_destroy) + * is no longer needed. */ + assert (unscaled->from_face && unscaled->faces->next == NULL); + cairo_font_face_destroy (&unscaled->faces->base); + unscaled->faces = NULL; + } + + font_face->next = unscaled->faces; + unscaled->faces = font_face; + +#if CAIRO_HAS_FC_FONT + font_face->pattern = NULL; +#endif + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + + return &font_face->base; +} + +/* implement the platform-specific interface */ + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern) +{ + FcValue v; + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) + { + if (! FcPatternAddBool (pattern, + FC_ANTIALIAS, + options->antialias != CAIRO_ANTIALIAS_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { + FcPatternDel (pattern, FC_RGBA); + if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + } + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch) + { + int rgba; + + if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + default: + rgba = FC_RGBA_RGB; + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + rgba = FC_RGBA_BGR; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + rgba = FC_RGBA_VRGB; + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + rgba = FC_RGBA_VBGR; + break; + } + } else { + rgba = FC_RGBA_NONE; + } + + if (! FcPatternAddInteger (pattern, FC_RGBA, rgba)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) + { + if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch) + { + int lcd_filter; + + switch (options->lcd_filter) { + case CAIRO_LCD_FILTER_NONE: + lcd_filter = FT_LCD_FILTER_NONE; + break; + case CAIRO_LCD_FILTER_DEFAULT: + case CAIRO_LCD_FILTER_INTRA_PIXEL: + lcd_filter = FT_LCD_FILTER_LEGACY; + break; + case CAIRO_LCD_FILTER_FIR3: + lcd_filter = FT_LCD_FILTER_LIGHT; + break; + default: + case CAIRO_LCD_FILTER_FIR5: + lcd_filter = FT_LCD_FILTER_DEFAULT; + break; + } + + if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT) + { + if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch) + { + if (! FcPatternAddBool (pattern, + FC_HINTING, + options->hint_style != CAIRO_HINT_STYLE_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + +#ifdef FC_HINT_STYLE + if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch) + { + int hint_style; + + switch (options->hint_style) { + case CAIRO_HINT_STYLE_NONE: + hint_style = FC_HINT_NONE; + break; + case CAIRO_HINT_STYLE_SLIGHT: + hint_style = FC_HINT_SLIGHT; + break; + case CAIRO_HINT_STYLE_MEDIUM: + hint_style = FC_HINT_MEDIUM; + break; + case CAIRO_HINT_STYLE_FULL: + case CAIRO_HINT_STYLE_DEFAULT: + default: + hint_style = FC_HINT_FULL; + break; + } + + if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } +#endif + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_ft_font_options_substitute: + * @options: a #cairo_font_options_t object + * @pattern: an existing #FcPattern + * + * Add options to a #FcPattern based on a #cairo_font_options_t font + * options object. Options that are already in the pattern, are not overridden, + * so you should call this function after calling FcConfigSubstitute() (the + * user's settings should override options based on the surface type), but + * before calling FcDefaultSubstitute(). + **/ +void +cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return; + + _cairo_ft_font_options_substitute (options, pattern); +} + +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *font_options) +{ + cairo_status_t status; + + cairo_matrix_t scale; + FcPattern *resolved; + cairo_ft_font_transform_t sf; + FcResult result; + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; + cairo_font_face_t *font_face; + + scale = *ctm; + scale.x0 = scale.y0 = 0; + cairo_matrix_multiply (&scale, + font_matrix, + &scale); + + status = _compute_transform (&sf, &scale, NULL); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + pattern = FcPatternDuplicate (pattern); + if (pattern == NULL) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + status = _cairo_ft_font_options_substitute (font_options, pattern); + if (status) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + FcDefaultSubstitute (pattern); + + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) { + /* We failed to find any font. Substitute twin so that the user can + * see something (and hopefully recognise that the font is missing) + * and not just receive a NO_MEMORY error during rendering. + */ + font_face = _cairo_font_face_twin_create_fallback (); + goto FREE_PATTERN; + } + + status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); + if (unlikely (status || unscaled == NULL)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_RESOLVED; + } + + _get_pattern_ft_options (resolved, &ft_options); + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_unscaled_font_destroy (&unscaled->base); + +FREE_RESOLVED: + FcPatternDestroy (resolved); + +FREE_PATTERN: + FcPatternDestroy (pattern); + + return font_face; +} + +/** + * cairo_ft_font_face_create_for_pattern: + * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern + * if it needs to. You are free to modify or free @pattern after this call. + * + * Creates a new font face for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_scaled_font_lock_face(). + * + * Font rendering options are represented both here and when you + * call cairo_scaled_font_create(). Font options that have a representation + * in a #FcPattern must be passed in here; to modify #FcPattern + * appropriately to reflect the options in a #cairo_font_options_t, call + * cairo_ft_font_options_substitute(). + * + * The pattern's FC_FT_FACE element is inspected first and if that is set, + * that will be the FreeType font face associated with the returned cairo + * font face. Otherwise the FC_FILE element is checked. If it's set, + * that and the value of the FC_INDEX element (defaults to zero) of @pattern + * are used to load a font face from file. + * + * If both steps from the previous paragraph fails, @pattern will be passed + * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch, + * and the resulting font pattern is used. + * + * If the FC_FT_FACE element of @pattern is set, the user is responsible + * for making sure that the referenced FT_Face remains valid for the life + * time of the returned #cairo_font_face_t. See + * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple + * the life time of the FT_Face to that of the cairo font-face. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern) +{ + cairo_ft_unscaled_font_t *unscaled; + cairo_font_face_t *font_face; + cairo_ft_options_t ft_options; + cairo_status_t status; + + status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); + if (unlikely (status)) + return (cairo_font_face_t *) &_cairo_font_face_nil; + if (unlikely (unscaled == NULL)) { + /* Store the pattern. We will resolve it and create unscaled + * font when creating scaled fonts */ + status = _cairo_ft_font_face_create_for_pattern (pattern, + &font_face); + if (unlikely (status)) + return (cairo_font_face_t *) &_cairo_font_face_nil; + + return font_face; + } + + _get_pattern_ft_options (pattern, &ft_options); + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_unscaled_font_destroy (&unscaled->base); + + return font_face; +} +#endif + +/** + * cairo_ft_font_face_create_for_ft_face: + * @face: A FreeType face object, already opened. This must + * be kept around until the face's ref_count drops to + * zero and it is freed. Since the face may be referenced + * internally to Cairo, the best way to determine when it + * is safe to free the face is to pass a + * #cairo_destroy_func_t to cairo_font_face_set_user_data() + * @load_flags: flags to pass to FT_Load_Glyph when loading + * glyphs from the font. These flags are OR'ed together with + * the flags derived from the #cairo_font_options_t passed + * to cairo_scaled_font_create(), so only a few values such + * as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT + * are useful. You should not pass any of the flags affecting + * the load target, such as %FT_LOAD_TARGET_LIGHT. + * + * Creates a new font face for the FreeType font backend from a + * pre-opened FreeType face. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference + * to the FT_Face alive in a font-cache and the exact lifetime of the reference + * depends highly upon the exact usage pattern and is subject to external + * factors. You must not call FT_Done_Face() before the last reference to the + * #cairo_font_face_t has been dropped. + * + * As an example, below is how one might correctly couple the lifetime of + * the FreeType face object to the #cairo_font_face_t. + * + * + * static const cairo_user_data_key_t key; + * + * font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); + * status = cairo_font_face_set_user_data (font_face, &key, + * ft_face, (cairo_destroy_func_t) FT_Done_Face); + * if (status) { + * cairo_font_face_destroy (font_face); + * FT_Done_Face (ft_face); + * return ERROR; + * } + * + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags) +{ + cairo_ft_unscaled_font_t *unscaled; + cairo_font_face_t *font_face; + cairo_ft_options_t ft_options; + cairo_status_t status; + + status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + ft_options.load_flags = load_flags; + ft_options.extra_flags = 0; + _cairo_font_options_init_default (&ft_options.base); + + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_unscaled_font_destroy (&unscaled->base); + + return font_face; +} + +/** + * cairo_ft_scaled_font_lock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_ft_face()). + * + * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType + * backend font and scales it appropriately for the font. You must + * release the face with cairo_ft_scaled_font_unlock_face() + * when you are done using it. Since the #FT_Face object can be + * shared between multiple #cairo_scaled_font_t objects, you must not + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_scaled_font_lock_face() is + * called. cairo_ft_scaled_font_unlock_face() must be called the same number + * of times. + * + * You must be careful when using this function in a library or in a + * threaded application, because freetype's design makes it unsafe to + * call freetype functions simultaneously from multiple threads, (even + * if using distinct FT_Face objects). Because of this, application + * code that acquires an FT_Face object with this call must add its + * own locking to protect any use of that object, (and which also must + * protect any other calls into cairo as almost any cairo function + * might result in a call into the freetype library). + * + * Return value: The #FT_Face object for @font, scaled appropriately, + * or %NULL if @scaled_font is in an error state (see + * cairo_scaled_font_status()) or there is insufficient memory. + **/ +FT_Face +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) +{ + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; + FT_Face face; + cairo_status_t status; + + if (! _cairo_scaled_font_is_ft (abstract_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return NULL; + } + + if (scaled_font->base.status) + return NULL; + + face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); + if (unlikely (face == NULL)) { + status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, + &scaled_font->base.scale); + if (unlikely (status)) { + _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + return NULL; + } + + /* Note: We deliberately release the unscaled font's mutex here, + * so that we are not holding a lock across two separate calls to + * cairo function, (which would give the application some + * opportunity for creating deadlock. This is obviously unsafe, + * but as documented, the user must add manual locking when using + * this function. */ + CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex); + + return face; +} + +/** + * cairo_ft_scaled_font_unlock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_ft_face()). + * + * Releases a face obtained with cairo_ft_scaled_font_lock_face(). + **/ +void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) +{ + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; + + if (! _cairo_scaled_font_is_ft (abstract_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return; + } + + if (scaled_font->base.status) + return; + + /* Note: We released the unscaled font's mutex at the end of + * cairo_ft_scaled_font_lock_face, so we have to acquire it again + * as _cairo_ft_unscaled_font_unlock_face expects it to be held + * when we call into it. */ + CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex); + + _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); +} + +/* We expose our unscaled font implementation internally for the the + * PDF backend, which needs to keep track of the the different + * fonts-on-disk used by a document, so it can embed them. + */ +cairo_unscaled_font_t * +_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *abstract_font) +{ + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; + + return &scaled_font->unscaled->base; +} + +cairo_bool_t +_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (!_cairo_scaled_font_is_ft (scaled_font)) + return FALSE; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT) + return TRUE; + return FALSE; +} + +unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (! _cairo_scaled_font_is_ft (scaled_font)) + return 0; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + return ft_scaled_font->ft_options.load_flags; +} + +void +_cairo_ft_font_reset_static_data (void) +{ + _cairo_ft_unscaled_font_map_destroy (); +} diff --git a/libs/cairo/src/cairo-ft-private.h b/libs/cairo/src/cairo-ft-private.h new file mode 100644 index 000000000..42a1776ed --- /dev/null +++ b/libs/cairo/src/cairo-ft-private.h @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FT_PRIVATE_H +#define CAIRO_FT_PRIVATE_H + +#include "cairo-ft.h" +#include "cairoint.h" + +#if CAIRO_HAS_FT_FONT + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_ft_unscaled_font cairo_ft_unscaled_font_t; + +cairo_private cairo_bool_t +_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); + +/* These functions are needed by the PDF backend, which needs to keep track of the + * the different fonts-on-disk used by a document, so it can embed them + */ +cairo_private cairo_unscaled_font_t * +_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font); + +cairo_private FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled); + +cairo_private void +_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); + +cairo_private cairo_bool_t +_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); + +cairo_private unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); + +CAIRO_END_DECLS + +#endif /* CAIRO_HAS_FT_FONT */ +#endif /* CAIRO_FT_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-ft.h b/libs/cairo/src/cairo-ft.h new file mode 100644 index 000000000..2f584066f --- /dev/null +++ b/libs/cairo/src/cairo-ft.h @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FT_H +#define CAIRO_FT_H + +#include "cairo.h" + +#if CAIRO_HAS_FT_FONT + +/* Fontconfig/Freetype platform-specific font interface */ + +#include +#include FT_FREETYPE_H + +#if CAIRO_HAS_FC_FONT +#include +#endif + +CAIRO_BEGIN_DECLS + +cairo_public cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags); + +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/libs/cairo/src/cairo-gl-glyphs.c b/libs/cairo/src/cairo-gl-glyphs.c new file mode 100644 index 000000000..883883fbf --- /dev/null +++ b/libs/cairo/src/cairo-gl-glyphs.c @@ -0,0 +1,571 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" + +#define GLYPH_CACHE_WIDTH 1024 +#define GLYPH_CACHE_HEIGHT 1024 +#define GLYPH_CACHE_MIN_SIZE 4 +#define GLYPH_CACHE_MAX_SIZE 128 + +typedef struct _cairo_gl_glyph_private { + cairo_rtree_node_t node; + cairo_gl_glyph_cache_t *cache; + struct { float x, y; } p1, p2; +} cairo_gl_glyph_private_t; + +static cairo_status_t +_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_gl_surface_t *cache_surface; + cairo_gl_glyph_private_t *glyph_private; + cairo_rtree_node_t *node = NULL; + cairo_status_t status; + int width, height; + + width = glyph_surface->width; + if (width < GLYPH_CACHE_MIN_SIZE) + width = GLYPH_CACHE_MIN_SIZE; + height = glyph_surface->height; + if (height < GLYPH_CACHE_MIN_SIZE) + height = GLYPH_CACHE_MIN_SIZE; + + /* search for an available slot */ + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + /* search for an unlocked slot */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_rtree_evict_random (&cache->rtree, + width, height, &node); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_rtree_node_insert (&cache->rtree, + node, width, height, &node); + } + } + if (status) + return status; + + cache_surface = (cairo_gl_surface_t *) cache->pattern.surface; + + /* XXX: Make sure we use the mask texture. This should work automagically somehow */ + glActiveTexture (GL_TEXTURE1); + status = _cairo_gl_surface_draw_image (cache_surface, + glyph_surface, + 0, 0, + glyph_surface->width, glyph_surface->height, + node->x, node->y); + if (unlikely (status)) + return status; + + scaled_glyph->surface_private = node; + node->owner = &scaled_glyph->surface_private; + + glyph_private = (cairo_gl_glyph_private_t *) node; + glyph_private->cache = cache; + + /* compute tex coords */ + glyph_private->p1.x = node->x; + glyph_private->p1.y = node->y; + glyph_private->p2.x = node->x + glyph_surface->width; + glyph_private->p2.y = node->y + glyph_surface->height; + if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { + glyph_private->p1.x /= cache_surface->width; + glyph_private->p1.y /= cache_surface->height; + glyph_private->p2.x /= cache_surface->width; + glyph_private->p2.y /= cache_surface->height; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_gl_glyph_private_t * +_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private); +} + +static cairo_status_t +cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, + cairo_format_t format, + cairo_gl_glyph_cache_t **cache_out) +{ + cairo_gl_glyph_cache_t *cache; + cairo_content_t content; + + switch (format) { + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + cache = &ctx->glyph_cache[0]; + content = CAIRO_CONTENT_COLOR_ALPHA; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + cache = &ctx->glyph_cache[1]; + content = CAIRO_CONTENT_ALPHA; + break; + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + if (unlikely (cache->pattern.surface == NULL)) { + cairo_surface_t *surface; + surface = cairo_gl_surface_create (&ctx->base, + content, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT); + if (unlikely (surface->status)) { + cairo_status_t status = surface->status; + cairo_surface_destroy (surface); + return status; + } + _cairo_surface_release_device_reference (surface); + _cairo_pattern_init_for_surface (&cache->pattern, surface); + cairo_surface_destroy (surface); + cache->pattern.base.has_component_alpha = (content == CAIRO_CONTENT_COLOR_ALPHA); + } + + *cache_out = cache; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_unpin (&cache->rtree); +} + +static cairo_bool_t +_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_device_t *font_private; + + font_private = scaled_font->surface_private; + if ((scaled_font->surface_backend != NULL && + scaled_font->surface_backend != &_cairo_gl_surface_backend) || + (font_private != NULL && font_private != surface->base.device)) + { + return FALSE; + } + + return TRUE; +} + +void +_cairo_gl_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_list_del (&scaled_font->link); +} + +void +_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_glyph_private_t *glyph_private; + + glyph_private = scaled_glyph->surface_private; + if (glyph_private != NULL) { + glyph_private->node.owner = NULL; + if (! glyph_private->node.pinned) { + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + _cairo_rtree_node_remove (&glyph_private->cache->rtree, + &glyph_private->node); + } + } +} + +static cairo_status_t +_render_glyphs (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_rectangle_int_t *glyph_extents, + cairo_scaled_font_t *scaled_font, + cairo_bool_t *has_component_alpha, + cairo_region_t *clip_region, + int *remaining_glyphs) +{ + cairo_format_t last_format = CAIRO_FORMAT_INVALID; + cairo_gl_glyph_cache_t *cache = NULL; + cairo_gl_context_t *ctx; + cairo_gl_composite_t setup; + cairo_status_t status; + int i = 0; + + *has_component_alpha = FALSE; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + _cairo_scaled_font_freeze_cache (scaled_font); + + status = _cairo_gl_composite_init (&setup, op, dst, + TRUE, glyph_extents); + + if (unlikely (status)) + goto FINISH; + + if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FINISH; + } + + status = _cairo_gl_composite_set_source (&setup, source, + glyph_extents->x, glyph_extents->y, + dst_x, dst_y, + glyph_extents->width, + glyph_extents->height); + if (unlikely (status)) + goto FINISH; + + if (scaled_font->surface_private == NULL) { + scaled_font->surface_private = ctx; + scaled_font->surface_backend = &_cairo_gl_surface_backend; + cairo_list_add (&scaled_font->link, &ctx->fonts); + } + + _cairo_gl_composite_set_clip_region (&setup, clip_region); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_gl_glyph_private_t *glyph; + double x_offset, y_offset; + double x1, x2, y1, y2; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + if (scaled_glyph->surface->width == 0 || + scaled_glyph->surface->height == 0) + { + continue; + } + if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || + scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FINISH; + } + + if (scaled_glyph->surface->format != last_format) { + status = cairo_gl_context_get_glyph_cache (ctx, + scaled_glyph->surface->format, + &cache); + if (unlikely (status)) + goto FINISH; + + last_format = scaled_glyph->surface->format; + + status = _cairo_gl_composite_set_mask (&setup, + &cache->pattern.base, + 0, 0, 0, 0, 0, 0); + if (unlikely (status)) + goto FINISH; + + *has_component_alpha |= cache->pattern.base.has_component_alpha; + + /* XXX: _cairo_gl_composite_begin() acquires the context a + * second time. Need to refactor this loop so this doesn't happen. + */ + status = _cairo_gl_composite_begin (&setup, &ctx); + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) + goto FINISH; + } + + if (scaled_glyph->surface_private == NULL) { + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* Cache is full, so flush existing prims and try again. */ + _cairo_gl_composite_flush (ctx); + _cairo_gl_glyph_cache_unlock (cache); + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + } + + if (unlikely (_cairo_status_is_error (status))) + goto FINISH; + } + + x_offset = scaled_glyph->surface->base.device_transform.x0; + y_offset = scaled_glyph->surface->base.device_transform.y0; + + x1 = _cairo_lround (glyphs[i].x - x_offset); + y1 = _cairo_lround (glyphs[i].y - y_offset); + x2 = x1 + scaled_glyph->surface->width; + y2 = y1 + scaled_glyph->surface->height; + + glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); + _cairo_gl_composite_emit_glyph (ctx, + x1, y1, x2, y2, + glyph->p1.x, glyph->p1.y, + glyph->p2.x, glyph->p2.y); + } + + status = CAIRO_STATUS_SUCCESS; + FINISH: + _cairo_scaled_font_thaw_cache (scaled_font); + + status = _cairo_gl_context_release (ctx, status); + + _cairo_gl_composite_fini (&setup); + + *remaining_glyphs = num_glyphs - i; + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_rectangle_int_t *glyph_extents, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_surface_t *mask; + cairo_status_t status; + cairo_bool_t has_component_alpha; + int i; + + /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ + mask = cairo_gl_surface_create (dst->base.device, + CAIRO_CONTENT_COLOR_ALPHA, + glyph_extents->width, + glyph_extents->height); + if (unlikely (mask->status)) + return mask->status; + + for (i = 0; i < num_glyphs; i++) { + glyphs[i].x -= glyph_extents->x; + glyphs[i].y -= glyph_extents->y; + } + + status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + glyphs, num_glyphs, glyph_extents, + scaled_font, &has_component_alpha, + NULL, remaining_glyphs); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask_pattern; + + mask->is_clear = FALSE; + _cairo_pattern_init_for_surface (&mask_pattern, mask); + mask_pattern.base.has_component_alpha = has_component_alpha; + cairo_matrix_init_translate (&mask_pattern.base.matrix, + -glyph_extents->x, -glyph_extents->y); + status = _cairo_surface_mask (&dst->base, op, + source, &mask_pattern.base, clip); + _cairo_pattern_fini (&mask_pattern.base); + } else { + for (i = 0; i < num_glyphs; i++) { + glyphs[i].x += glyph_extents->x; + glyphs[i].y += glyph_extents->y; + } + *remaining_glyphs = num_glyphs; + } + + cairo_surface_destroy (mask); + + return status; +} + +cairo_int_status_t +_cairo_gl_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_rectangle_int_t surface_extents; + cairo_rectangle_int_t extents; + cairo_region_t *clip_region = NULL; + cairo_bool_t overlap, use_mask = FALSE; + cairo_bool_t has_component_alpha; + cairo_status_t status; + int i; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + if (! _cairo_operator_bounded_by_mask (op)) + use_mask |= TRUE; + + /* If any of the glyphs are component alpha, we have to go through a mask, + * since only _cairo_gl_surface_composite() currently supports component + * alpha. + */ + if (!use_mask && op != CAIRO_OPERATOR_OVER) { + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (!_cairo_status_is_error (status) && + scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32) + { + use_mask = TRUE; + break; + } + } + } + + /* For CLEAR, cairo's rendering equation (quoting Owen's description in: + * http://lists.cairographics.org/archives/cairo/2005-August/004992.html) + * is: + * mask IN clip ? src OP dest : dest + * or more simply: + * mask IN CLIP ? 0 : dest + * + * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). + * + * The model we use in _cairo_gl_set_operator() is Render's: + * src IN mask IN clip OP dest + * which would boil down to: + * 0 (bounded by the extents of the drawing). + * + * However, we can do a Render operation using an opaque source + * and DEST_OUT to produce: + * 1 IN mask IN clip DEST_OUT dest + * which is + * mask IN clip ? 0 : dest + */ + if (op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + /* For SOURCE, cairo's rendering equation is: + * (mask IN clip) ? src OP dest : dest + * or more simply: + * (mask IN clip) ? src : dest. + * + * If we just used the Render equation, we would get: + * (src IN mask IN clip) OP dest + * or: + * (src IN mask IN clip) bounded by extents of the drawing. + * + * The trick is that for GL blending, we only get our 4 source values + * into the blender, and since we need all 4 components of source, we + * can't also get the mask IN clip into the blender. But if we did + * two passes we could make it work: + * dest = (mask IN clip) DEST_OUT dest + * dest = src IN mask IN clip ADD dest + * + * But for now, composite via an intermediate mask. + */ + if (op == CAIRO_OPERATOR_SOURCE) + use_mask |= TRUE; + + /* XXX we don't need ownership of the font as we use a global + * glyph cache -- but we do need scaled_glyph eviction notification. :-( + */ + if (! _cairo_gl_surface_owns_font (dst, scaled_font)) + return UNSUPPORTED ("do not control font"); + + /* If the glyphs overlap, we need to build an intermediate mask rather + * then perform the compositing directly. + */ + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, num_glyphs, + &extents, + &overlap); + if (unlikely (status)) + return status; + + use_mask |= overlap; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + /* the empty clip should never be propagated this far */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (unlikely (_cairo_status_is_error (status))) + return status; + + use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_rectangle_intersect (&extents, + _cairo_clip_get_extents (clip))) + goto EMPTY; + } + + surface_extents.x = surface_extents.y = 0; + surface_extents.width = dst->width; + surface_extents.height = dst->height; + if (! _cairo_rectangle_intersect (&extents, &surface_extents)) + goto EMPTY; + + if (use_mask) { + return _cairo_gl_surface_show_glyphs_via_mask (dst, op, + source, + glyphs, num_glyphs, + &extents, + scaled_font, + clip, + remaining_glyphs); + } + + return _render_glyphs (dst, extents.x, extents.y, + op, source, + glyphs, num_glyphs, &extents, + scaled_font, &has_component_alpha, + clip_region, remaining_glyphs); + +EMPTY: + *remaining_glyphs = 0; + if (! _cairo_operator_bounded_by_mask (op)) + return _cairo_surface_paint (&dst->base, op, source, clip); + else + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_init (&cache->rtree, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT, + GLYPH_CACHE_MIN_SIZE, + sizeof (cairo_gl_glyph_private_t)); +} + +void +_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_fini (&cache->rtree); + + if (cache->pattern.surface) { + _cairo_pattern_fini (&cache->pattern.base); + cache->pattern.surface = NULL; + } +} + diff --git a/libs/cairo/src/cairo-gl-private.h b/libs/cairo/src/cairo-gl-private.h new file mode 100644 index 000000000..96a2a1b5b --- /dev/null +++ b/libs/cairo/src/cairo-gl-private.h @@ -0,0 +1,448 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_GL_PRIVATE_H +#define CAIRO_GL_PRIVATE_H + +#include "cairoint.h" + +#include "cairo-gl-gradient-private.h" + +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" + +#include + +#include + +#include "cairo-gl.h" + +#include +#define GL_GLEXT_PROTOTYPES +#include + +#define DEBUG_GL 0 + +#if DEBUG_GL && __GNUC__ +#define UNSUPPORTED(reason) ({ \ + fprintf (stderr, \ + "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason); \ + CAIRO_INT_STATUS_UNSUPPORTED; \ +}) +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +/* maximal number of shaders we keep in the cache. + * Random number that is hopefully big enough to not cause many cache evictions. */ +#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 + +/* VBO size that we allocate, smaller size means we gotta flush more often */ +#define CAIRO_GL_VBO_SIZE 16384 + +typedef struct _cairo_gl_surface { + cairo_surface_t base; + + int width, height; + + GLuint tex; /* GL texture object containing our data. */ + GLuint fb; /* GL framebuffer object wrapping our data. */ + GLuint depth; /* GL framebuffer object holding depth */ + int owns_tex; +} cairo_gl_surface_t; + +typedef struct cairo_gl_glyph_cache { + cairo_rtree_t rtree; + cairo_surface_pattern_t pattern; +} cairo_gl_glyph_cache_t; + +typedef enum cairo_gl_tex { + CAIRO_GL_TEX_SOURCE = 0, + CAIRO_GL_TEX_MASK = 1, + CAIRO_GL_TEX_TEMP = 2 +} cairo_gl_tex_t; + +typedef enum cairo_gl_operand_type { + CAIRO_GL_OPERAND_NONE, + CAIRO_GL_OPERAND_CONSTANT, + CAIRO_GL_OPERAND_TEXTURE, + CAIRO_GL_OPERAND_LINEAR_GRADIENT, + CAIRO_GL_OPERAND_RADIAL_GRADIENT, + CAIRO_GL_OPERAND_SPANS, + + CAIRO_GL_OPERAND_COUNT +} cairo_gl_operand_type_t; + +typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t; + +typedef struct cairo_gl_shader { + GLuint fragment_shader; + GLuint program; +} cairo_gl_shader_t; + +typedef enum cairo_gl_shader_in { + CAIRO_GL_SHADER_IN_NORMAL, + CAIRO_GL_SHADER_IN_CA_SOURCE, + CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, + + CAIRO_GL_SHADER_IN_COUNT +} cairo_gl_shader_in_t; + +typedef enum cairo_gl_var_type { + CAIRO_GL_VAR_NONE, + CAIRO_GL_VAR_TEXCOORDS, + CAIRO_GL_VAR_COVERAGE +} cairo_gl_var_type_t; + +#define cairo_gl_var_type_hash(src,mask,dest) ((mask) << 2 | (src << 1) | (dest)) +#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_COVERAGE << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS) + +/* This union structure describes a potential source or mask operand to the + * compositing equation. + */ +typedef struct cairo_gl_operand { + cairo_gl_operand_type_t type; + union { + struct { + GLuint tex; + cairo_gl_surface_t *surface; + cairo_surface_attributes_t attributes; + } texture; + struct { + GLfloat color[4]; + } constant; + struct { + cairo_gl_gradient_t *gradient; + cairo_matrix_t m; + float segment_x; + float segment_y; + cairo_extend_t extend; + } linear; + struct { + cairo_gl_gradient_t *gradient; + cairo_matrix_t m; + float circle_1_x; + float circle_1_y; + float radius_0; + float radius_1; + cairo_extend_t extend; + } radial; + }; + unsigned int vertex_offset; +} cairo_gl_operand_t; + +struct _cairo_gl_context { + cairo_device_t base; + + GLuint dummy_tex; + GLuint texture_load_pbo; + GLuint vbo; + GLint max_framebuffer_size; + GLint max_texture_size; + GLint max_textures; + GLenum tex_target; + + const cairo_gl_shader_impl_t *shader_impl; + + GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1]; + cairo_gl_shader_t fill_rectangles_shader; + cairo_cache_t shaders; + + cairo_cache_t gradients; + + cairo_gl_glyph_cache_t glyph_cache[2]; + cairo_list_t fonts; + + cairo_gl_surface_t *current_target; + cairo_operator_t current_operator; + cairo_gl_shader_t *pre_shader; /* for component alpha */ + cairo_gl_shader_t *current_shader; + + cairo_gl_operand_t operands[2]; + + char *vb; + unsigned int vb_offset; + unsigned int vertex_size; + cairo_region_t *clip_region; + + void (*acquire) (void *ctx); + void (*release) (void *ctx); + + void (*make_current) (void *ctx, cairo_gl_surface_t *surface); + void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); + void (*destroy) (void *ctx); +}; + +typedef struct _cairo_gl_composite { + cairo_gl_surface_t *dst; + cairo_operator_t op; + cairo_region_t *clip_region; + + cairo_gl_operand_t src; + cairo_gl_operand_t mask; +} cairo_gl_composite_t; + +cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; + +static cairo_always_inline GLenum +_cairo_gl_get_error (void) +{ + GLenum err = glGetError(); + + if (unlikely (err)) + while (glGetError ()); + + return err; +} + +static inline cairo_device_t * +_cairo_gl_context_create_in_error (cairo_status_t status) +{ + return (cairo_device_t *) _cairo_device_create_in_error (status); +} + +cairo_private cairo_status_t +_cairo_gl_context_init (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_surface_init (cairo_device_t *device, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height); + +static cairo_always_inline cairo_bool_t cairo_warn +_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface) +{ + return surface->tex != 0; +} + +cairo_private cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y); + +static cairo_always_inline cairo_bool_t +_cairo_gl_device_has_glsl (cairo_device_t *device) +{ + return ((cairo_gl_context_t *) device)->shader_impl != NULL; +} + +static cairo_always_inline cairo_bool_t +_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device) +{ + return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE_EXT; +} + +static cairo_always_inline cairo_status_t cairo_warn +_cairo_gl_context_acquire (cairo_device_t *device, + cairo_gl_context_t **ctx) +{ + cairo_status_t status; + + status = cairo_device_acquire (device); + if (unlikely (status)) + return status; + + /* clear potential previous GL errors */ + _cairo_gl_get_error (); + + *ctx = (cairo_gl_context_t *) device; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_always_inline cairo_warn cairo_status_t +_cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) +{ + GLenum err; + + err = _cairo_gl_get_error (); + + if (unlikely (err)) { + cairo_status_t new_status; + new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + if (status == CAIRO_STATUS_SUCCESS) + status = new_status; + } + + cairo_device_release (&(ctx)->base); + + return status; +} + +cairo_private void +_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface); + +cairo_private void +_cairo_gl_context_activate (cairo_gl_context_t *ctx, + cairo_gl_tex_t tex_unit); + +cairo_private cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op); + +cairo_private cairo_status_t +_cairo_gl_composite_init (cairo_gl_composite_t *setup, + cairo_operator_t op, + cairo_gl_surface_t *dst, + cairo_bool_t has_component_alpha, + const cairo_rectangle_int_t *rect); + +cairo_private void +_cairo_gl_composite_fini (cairo_gl_composite_t *setup); + +cairo_private void +_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, + cairo_region_t *clip_region); + +cairo_private cairo_int_status_t +_cairo_gl_composite_set_source (cairo_gl_composite_t *setup, + const cairo_pattern_t *pattern, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height); + +cairo_private cairo_int_status_t +_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, + const cairo_pattern_t *pattern, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height); + +cairo_private void +_cairo_gl_composite_set_mask_spans (cairo_gl_composite_t *setup); + +cairo_private cairo_status_t +_cairo_gl_composite_begin (cairo_gl_composite_t *setup, + cairo_gl_context_t **ctx); + +cairo_private void +_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, + GLfloat x1, + GLfloat y1, + GLfloat x2, + GLfloat y2, + uint8_t alpha); + +cairo_private void +_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx, + GLfloat x1, + GLfloat y1, + GLfloat x2, + GLfloat y2, + GLfloat glyph_x1, + GLfloat glyph_y1, + GLfloat glyph_x2, + GLfloat glyph_y2); + +cairo_private void +_cairo_gl_composite_flush (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, + cairo_gl_tex_t tex_unit); + +cairo_private cairo_bool_t +_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha); + +cairo_private void +_cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); + +cairo_private void +_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache); + +cairo_private cairo_int_status_t +_cairo_gl_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +static inline int +_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y) +{ + if (surface->fb) + return y; + else + return (surface->height - 1) - y; +} + +cairo_private cairo_status_t +_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx); + +static cairo_always_inline cairo_bool_t +_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx) +{ + return ctx->vb == NULL; +} + +cairo_private cairo_status_t +_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, + cairo_gl_operand_type_t source, + cairo_gl_operand_type_t mask, + cairo_gl_shader_in_t in, + cairo_gl_shader_t **shader); + +cairo_private void +_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, + const char *name, + float value); + +cairo_private void +_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, + const char *name, + float value0, float value1); + +cairo_private void +_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, + const char *name, + float value0, + float value1, + float value2); + +cairo_private void +_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, + const char *name, + float value0, float value1, + float value2, float value3); + +cairo_private void +_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, + const char *name, + cairo_matrix_t* m); + +cairo_private void +_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx, + const char *name, + GLuint tex_unit); + +cairo_private void +_cairo_gl_set_shader (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader); + +cairo_private void +_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); + +slim_hidden_proto (cairo_gl_surface_create); +slim_hidden_proto (cairo_gl_surface_create_for_texture); + +#endif /* CAIRO_GL_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-gl-shaders.c b/libs/cairo/src/cairo-gl-shaders.c new file mode 100644 index 000000000..d0edffa88 --- /dev/null +++ b/libs/cairo/src/cairo-gl-shaders.c @@ -0,0 +1,960 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-gl-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +typedef struct cairo_gl_shader_impl { + void + (*compile_shader) (GLuint *shader, GLenum type, const char *text); + + void + (*link_shader) (GLuint *program, GLuint vert, GLuint frag); + + void + (*destroy_shader) (GLuint shader); + + void + (*destroy_program) (GLuint program); + + void + (*bind_float) (cairo_gl_shader_t *shader, + const char *name, + float value); + + void + (*bind_vec2) (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1); + + void + (*bind_vec3) (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1, + float value2); + + void + (*bind_vec4) (cairo_gl_shader_t *shader, + const char *name, + float value0, float value1, + float value2, float value3); + + void + (*bind_matrix) (cairo_gl_shader_t *shader, + const char *name, + cairo_matrix_t* m); + + void + (*bind_texture) (cairo_gl_shader_t *shader, + const char *name, + cairo_gl_tex_t tex_unit); + + void + (*use) (cairo_gl_shader_t *shader); +} shader_impl_t; + +static cairo_status_t +_cairo_gl_shader_compile (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader, + cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + const char *fragment_text); + +/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions + API. */ +static void +compile_shader_arb (GLuint *shader, GLenum type, const char *text) +{ + const char* strings[1] = { text }; + GLint gl_status; + + *shader = glCreateShaderObjectARB (type); + glShaderSourceARB (*shader, 1, strings, 0); + glCompileShaderARB (*shader); + glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB, &gl_status); + if (gl_status == GL_FALSE) { + GLint log_size; + glGetObjectParameterivARB (*shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); + if (0 < log_size) { + char *log = _cairo_malloc (log_size); + GLint chars; + + log[log_size - 1] = '\0'; + glGetInfoLogARB (*shader, log_size, &chars, log); + printf ("OpenGL shader compilation failed. Shader:\n" + "%s\n" + "OpenGL compilation log:\n" + "%s\n", + text, log); + + free (log); + } else { + printf ("OpenGL shader compilation failed.\n"); + } + + ASSERT_NOT_REACHED; + } +} + +static void +link_shader_arb (GLuint *program, GLuint vert, GLuint frag) +{ + GLint gl_status; + + *program = glCreateProgramObjectARB (); + glAttachObjectARB (*program, vert); + glAttachObjectARB (*program, frag); + glLinkProgramARB (*program); + glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB, &gl_status); + if (gl_status == GL_FALSE) { + GLint log_size; + glGetObjectParameterivARB (*program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); + if (0 < log_size) { + char *log = _cairo_malloc (log_size); + GLint chars; + + log[log_size - 1] = '\0'; + glGetInfoLogARB (*program, log_size, &chars, log); + printf ("OpenGL shader link failed:\n%s\n", log); + + free (log); + } else { + printf ("OpenGL shader link failed.\n"); + } + + ASSERT_NOT_REACHED; + } +} + +static void +destroy_shader_arb (GLuint shader) +{ + glDeleteObjectARB (shader); +} + +static void +destroy_program_arb (GLuint shader) +{ + glDeleteObjectARB (shader); +} + +static void +bind_float_arb (cairo_gl_shader_t *shader, + const char *name, + float value) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + assert (location != -1); + glUniform1fARB (location, value); +} + +static void +bind_vec2_arb (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + assert (location != -1); + glUniform2fARB (location, value0, value1); +} + +static void +bind_vec3_arb (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1, + float value2) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + assert (location != -1); + glUniform3fARB (location, value0, value1, value2); +} + +static void +bind_vec4_arb (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1, + float value2, + float value3) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + assert (location != -1); + glUniform4fARB (location, value0, value1, value2, value3); +} + +static void +bind_matrix_arb (cairo_gl_shader_t *shader, + const char *name, + cairo_matrix_t* m) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + float gl_m[9] = { + m->xx, m->xy, m->x0, + m->yx, m->yy, m->y0, + 0, 0, 1 + }; + assert (location != -1); + glUniformMatrix3fvARB (location, 1, GL_TRUE, gl_m); +} + +static void +bind_texture_arb (cairo_gl_shader_t *shader, + const char *name, + cairo_gl_tex_t tex_unit) +{ + GLint location = glGetUniformLocationARB (shader->program, name); + assert (location != -1); + glUniform1iARB (location, tex_unit); +} + +static void +use_program_arb (cairo_gl_shader_t *shader) +{ + if (shader) + glUseProgramObjectARB (shader->program); + else + glUseProgramObjectARB (0); +} + +/* OpenGL Core 2.0 API. */ +static void +compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text) +{ + const char* strings[1] = { text }; + GLint gl_status; + + *shader = glCreateShader (type); + glShaderSource (*shader, 1, strings, 0); + glCompileShader (*shader); + glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status); + if (gl_status == GL_FALSE) { + GLint log_size; + glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); + if (0 < log_size) { + char *log = _cairo_malloc (log_size); + GLint chars; + + log[log_size - 1] = '\0'; + glGetShaderInfoLog (*shader, log_size, &chars, log); + printf ("OpenGL shader compilation failed. Shader:\n" + "%s\n" + "OpenGL compilation log:\n" + "%s\n", + text, log); + + free (log); + } else { + printf ("OpenGL shader compilation failed.\n"); + } + + ASSERT_NOT_REACHED; + } +} + +static void +link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag) +{ + GLint gl_status; + + *program = glCreateProgram (); + glAttachShader (*program, vert); + glAttachShader (*program, frag); + glLinkProgram (*program); + glGetProgramiv (*program, GL_LINK_STATUS, &gl_status); + if (gl_status == GL_FALSE) { + GLint log_size; + glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); + if (0 < log_size) { + char *log = _cairo_malloc (log_size); + GLint chars; + + log[log_size - 1] = '\0'; + glGetProgramInfoLog (*program, log_size, &chars, log); + printf ("OpenGL shader link failed:\n%s\n", log); + + free (log); + } else { + printf ("OpenGL shader link failed.\n"); + } + + ASSERT_NOT_REACHED; + } +} + +static void +destroy_shader_core_2_0 (GLuint shader) +{ + glDeleteShader (shader); +} + +static void +destroy_program_core_2_0 (GLuint shader) +{ + glDeleteProgram (shader); +} + +static void +bind_float_core_2_0 (cairo_gl_shader_t *shader, + const char *name, + float value) +{ + GLint location = glGetUniformLocation (shader->program, name); + assert (location != -1); + glUniform1f (location, value); +} + +static void +bind_vec2_core_2_0 (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1) +{ + GLint location = glGetUniformLocation (shader->program, name); + assert (location != -1); + glUniform2f (location, value0, value1); +} + +static void +bind_vec3_core_2_0 (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1, + float value2) +{ + GLint location = glGetUniformLocation (shader->program, name); + assert (location != -1); + glUniform3f (location, value0, value1, value2); +} + +static void +bind_vec4_core_2_0 (cairo_gl_shader_t *shader, + const char *name, + float value0, + float value1, + float value2, + float value3) +{ + GLint location = glGetUniformLocation (shader->program, name); + assert (location != -1); + glUniform4f (location, value0, value1, value2, value3); +} + +static void +bind_matrix_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_matrix_t* m) +{ + GLint location = glGetUniformLocation (shader->program, name); + float gl_m[16] = { + m->xx, m->xy, m->x0, + m->yx, m->yy, m->y0, + 0, 0, 1 + }; + assert (location != -1); + glUniformMatrix3fv (location, 1, GL_TRUE, gl_m); +} + +static void +bind_texture_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_gl_tex_t tex_unit) +{ + GLint location = glGetUniformLocation (shader->program, name); + assert (location != -1); + glUniform1i (location, tex_unit); +} + +static void +use_program_core_2_0 (cairo_gl_shader_t *shader) +{ + if (shader) + glUseProgram (shader->program); + else + glUseProgram (0); +} + +static const cairo_gl_shader_impl_t shader_impl_core_2_0 = { + compile_shader_core_2_0, + link_shader_core_2_0, + destroy_shader_core_2_0, + destroy_program_core_2_0, + bind_float_core_2_0, + bind_vec2_core_2_0, + bind_vec3_core_2_0, + bind_vec4_core_2_0, + bind_matrix_core_2_0, + bind_texture_core_2_0, + use_program_core_2_0, +}; + +static const cairo_gl_shader_impl_t shader_impl_arb = { + compile_shader_arb, + link_shader_arb, + destroy_shader_arb, + destroy_program_arb, + bind_float_arb, + bind_vec2_arb, + bind_vec3_arb, + bind_vec4_arb, + bind_matrix_arb, + bind_texture_arb, + use_program_arb, +}; + +typedef struct _cairo_shader_cache_entry { + cairo_cache_entry_t base; + + cairo_gl_operand_type_t src; + cairo_gl_operand_type_t mask; + cairo_gl_operand_type_t dest; + cairo_gl_shader_in_t in; + + cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ + cairo_gl_shader_t shader; +} cairo_shader_cache_entry_t; + +static cairo_bool_t +_cairo_gl_shader_cache_equal (const void *key_a, const void *key_b) +{ + const cairo_shader_cache_entry_t *a = key_a; + const cairo_shader_cache_entry_t *b = key_b; + + return a->src == b->src && + a->mask == b->mask && + a->dest == b->dest && + a->in == b->in; +} + +static unsigned long +_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) +{ + return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in); +} + +static void +_cairo_gl_shader_cache_destroy (void *data) +{ + cairo_shader_cache_entry_t *entry = data; + + _cairo_gl_shader_fini (entry->ctx, &entry->shader); + if (entry->ctx->current_shader == &entry->shader) + entry->ctx->current_shader = NULL; + free (entry); +} + +static void +_cairo_gl_shader_init (cairo_gl_shader_t *shader) +{ + shader->fragment_shader = 0; + shader->program = 0; +} + +cairo_status_t +_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) +{ + static const char *fill_fs_source = + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + cairo_status_t status; + + /* XXX multiple device support? */ + if (GLEW_VERSION_2_0) { + ctx->shader_impl = &shader_impl_core_2_0; + } else if (GLEW_ARB_shader_objects && + GLEW_ARB_fragment_shader && + GLEW_ARB_vertex_program) { + ctx->shader_impl = &shader_impl_arb; + } else { + ctx->shader_impl = NULL; + } + + memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); + + status = _cairo_cache_init (&ctx->shaders, + _cairo_gl_shader_cache_equal, + NULL, + _cairo_gl_shader_cache_destroy, + CAIRO_GL_MAX_SHADERS_PER_CONTEXT); + if (unlikely (status)) + return status; + + if (ctx->shader_impl != NULL) { + _cairo_gl_shader_init (&ctx->fill_rectangles_shader); + status = _cairo_gl_shader_compile (ctx, + &ctx->fill_rectangles_shader, + CAIRO_GL_VAR_NONE, + CAIRO_GL_VAR_NONE, + fill_fs_source); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) +{ + int i; + + for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) { + if (ctx->vertex_shaders[i]) + ctx->shader_impl->destroy_shader (ctx->vertex_shaders[i]); + } + + _cairo_cache_fini (&ctx->shaders); +} + +void +_cairo_gl_shader_fini (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader) +{ + if (shader->fragment_shader) + ctx->shader_impl->destroy_shader (shader->fragment_shader); + + if (shader->program) + ctx->shader_impl->destroy_program (shader->program); +} + +static const char *operand_names[] = { "source", "mask", "dest" }; + +static cairo_gl_var_type_t +cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type) +{ + switch (type) { + default: + case CAIRO_GL_OPERAND_COUNT: + ASSERT_NOT_REACHED; + case CAIRO_GL_OPERAND_NONE: + case CAIRO_GL_OPERAND_CONSTANT: + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + case CAIRO_GL_OPERAND_RADIAL_GRADIENT: + return CAIRO_GL_VAR_NONE; + case CAIRO_GL_OPERAND_TEXTURE: + return CAIRO_GL_VAR_TEXCOORDS; + case CAIRO_GL_OPERAND_SPANS: + return CAIRO_GL_VAR_COVERAGE; + } +} + +static void +cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, + cairo_gl_var_type_t type, + cairo_gl_tex_t name) +{ + switch (type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_VAR_NONE: + break; + case CAIRO_GL_VAR_TEXCOORDS: + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n", + operand_names[name]); + break; + case CAIRO_GL_VAR_COVERAGE: + _cairo_output_stream_printf (stream, + "varying float %s_coverage;\n", + operand_names[name]); + break; + } +} + +static void +cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, + cairo_gl_var_type_t type, + cairo_gl_tex_t name) +{ + switch (type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_VAR_NONE: + break; + case CAIRO_GL_VAR_TEXCOORDS: + _cairo_output_stream_printf (stream, + " %s_texcoords = gl_MultiTexCoord%d.xy;\n", + operand_names[name], name); + break; + case CAIRO_GL_VAR_COVERAGE: + _cairo_output_stream_printf (stream, + " %s_coverage = gl_Color.a;\n", + operand_names[name]); + break; + } +} + +static cairo_status_t +cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + cairo_gl_var_type_t dest, + char **out) +{ + cairo_output_stream_t *stream = _cairo_memory_stream_create (); + unsigned char *source; + unsigned int length; + cairo_status_t status; + + cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); + + _cairo_output_stream_printf (stream, + "void main()\n" + "{\n" + " gl_Position = ftransform();\n"); + + cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); + + _cairo_output_stream_write (stream, + "}\n\0", 3); + + status = _cairo_memory_stream_destroy (stream, &source, &length); + if (unlikely (status)) + return status; + + *out = (char *) source; + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_gl_shader_emit_color (cairo_output_stream_t *stream, + GLuint tex_target, + cairo_gl_operand_type_t type, + cairo_gl_tex_t name) +{ + const char *namestr = operand_names[name]; + const char *rectstr = (tex_target == GL_TEXTURE_RECTANGLE_EXT ? "Rect" : ""); + + switch (type) { + case CAIRO_GL_OPERAND_COUNT: + default: + ASSERT_NOT_REACHED; + break; + case CAIRO_GL_OPERAND_NONE: + _cairo_output_stream_printf (stream, + "vec4 get_%s()\n" + "{\n" + " return vec4 (0, 0, 0, 1);\n" + "}\n", + namestr); + break; + case CAIRO_GL_OPERAND_CONSTANT: + _cairo_output_stream_printf (stream, + "uniform vec4 %s_constant;\n" + "vec4 get_%s()\n" + "{\n" + " return %s_constant;\n" + "}\n", + namestr, namestr, namestr); + break; + case CAIRO_GL_OPERAND_TEXTURE: + _cairo_output_stream_printf (stream, + "uniform sampler2D%s %s_sampler;\n" + "varying vec2 %s_texcoords;\n" + "vec4 get_%s()\n" + "{\n" + " return texture2D%s(%s_sampler, %s_texcoords);\n" + "}\n", + rectstr, namestr, namestr, namestr, rectstr, namestr, namestr); + break; + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + _cairo_output_stream_printf (stream, + "uniform sampler1D %s_sampler;\n" + "uniform mat3 %s_matrix;\n" + "uniform vec2 %s_segment;\n" + "\n" + "vec4 get_%s()\n" + "{\n" + " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" + " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n" + " return texture1D (%s_sampler, t);\n" + "}\n", + namestr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr); + break; + case CAIRO_GL_OPERAND_RADIAL_GRADIENT: + _cairo_output_stream_printf (stream, + "uniform sampler1D %s_sampler;\n" + "uniform mat3 %s_matrix;\n" + "uniform vec2 %s_circle_1;\n" + "uniform float %s_radius_0;\n" + "uniform float %s_radius_1;\n" + "\n" + "vec4 get_%s()\n" + "{\n" + " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" + " \n" + " float dr = %s_radius_1 - %s_radius_0;\n" + " float dot_circle_1 = dot (%s_circle_1, %s_circle_1);\n" + " float dot_pos_circle_1 = dot (pos, %s_circle_1);\n" + " \n" + " float A = dot_circle_1 - dr * dr;\n" + " float B = -2.0 * (dot_pos_circle_1 + %s_radius_0 * dr);\n" + " float C = dot (pos, pos) - %s_radius_0 * %s_radius_0;\n" + " float det = B * B - 4.0 * A * C;\n" + " det = max (det, 0.0);\n" + " \n" + " float sqrt_det = sqrt (det);\n" + " sqrt_det *= sign(A);\n" + " \n" + " float t = (-B + sqrt_det) / (2.0 * A);\n" + " return texture1D (%s_sampler, t);\n" + "}\n", + namestr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr, namestr, + namestr); + break; + case CAIRO_GL_OPERAND_SPANS: + _cairo_output_stream_printf (stream, + "varying float %s_coverage;\n" + "vec4 get_%s()\n" + "{\n" + " return vec4(0, 0, 0, %s_coverage);\n" + "}\n", + namestr, namestr, namestr); + break; + } +} + +static cairo_status_t +cairo_gl_shader_get_fragment_source (GLuint tex_target, + cairo_gl_shader_in_t in, + cairo_gl_operand_type_t src, + cairo_gl_operand_type_t mask, + cairo_gl_operand_type_t dest, + char **out) +{ + cairo_output_stream_t *stream = _cairo_memory_stream_create (); + unsigned char *source; + unsigned int length; + cairo_status_t status; + + cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK); + + _cairo_output_stream_printf (stream, + "void main()\n" + "{\n"); + switch (in) { + case CAIRO_GL_SHADER_IN_COUNT: + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_SHADER_IN_NORMAL: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source() * get_mask().a;\n"); + break; + case CAIRO_GL_SHADER_IN_CA_SOURCE: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source() * get_mask();\n"); + break; + case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source().a * get_mask();\n"); + break; + } + + _cairo_output_stream_write (stream, + "}\n\0", 3); + + status = _cairo_memory_stream_destroy (stream, &source, &length); + if (unlikely (status)) + return status; + + *out = (char *) source; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_shader_compile (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader, + cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + const char *fragment_text) +{ + unsigned int vertex_shader; + cairo_status_t status; + + if (ctx->shader_impl == NULL) + return CAIRO_STATUS_SUCCESS; + + assert (shader->program == 0); + + vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE); + if (ctx->vertex_shaders[vertex_shader] == 0) { + char *source; + + status = cairo_gl_shader_get_vertex_source (src, + mask, + CAIRO_GL_VAR_NONE, + &source); + if (unlikely (status)) + goto FAILURE; + + ctx->shader_impl->compile_shader (&ctx->vertex_shaders[vertex_shader], + GL_VERTEX_SHADER, + source); + free (source); + } + + ctx->shader_impl->compile_shader (&shader->fragment_shader, + GL_FRAGMENT_SHADER, + fragment_text); + + ctx->shader_impl->link_shader (&shader->program, + ctx->vertex_shaders[vertex_shader], + shader->fragment_shader); + + return CAIRO_STATUS_SUCCESS; + + FAILURE: + _cairo_gl_shader_fini (ctx, shader); + shader->fragment_shader = 0; + shader->program = 0; + + return status; +} + +void +_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, + const char *name, + float value) +{ + ctx->shader_impl->bind_float (ctx->current_shader, name, value); +} + +void +_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, + const char *name, + float value0, + float value1) +{ + ctx->shader_impl->bind_vec2 (ctx->current_shader, name, value0, value1); +} + +void +_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, + const char *name, + float value0, + float value1, + float value2) +{ + ctx->shader_impl->bind_vec3 (ctx->current_shader, name, value0, value1, value2); +} + +void +_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, + const char *name, + float value0, float value1, + float value2, float value3) +{ + ctx->shader_impl->bind_vec4 (ctx->current_shader, name, value0, value1, value2, value3); +} + +void +_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, + const char *name, cairo_matrix_t* m) +{ + ctx->shader_impl->bind_matrix (ctx->current_shader, name, m); +} + +void +_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx, + const char *name, GLuint tex_unit) +{ + ctx->shader_impl->bind_texture (ctx->current_shader, name, tex_unit); +} + +void +_cairo_gl_set_shader (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader) +{ + if (ctx->shader_impl == NULL) + return; + + if (ctx->current_shader == shader) + return; + + ctx->shader_impl->use (shader); + + ctx->current_shader = shader; +} + +cairo_status_t +_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, + cairo_gl_operand_type_t source, + cairo_gl_operand_type_t mask, + cairo_gl_shader_in_t in, + cairo_gl_shader_t **shader) +{ + cairo_shader_cache_entry_t lookup, *entry; + char *fs_source; + cairo_status_t status; + + if (ctx->shader_impl == NULL) { + *shader = NULL; + return CAIRO_STATUS_SUCCESS; + } + + lookup.src = source; + lookup.mask = mask; + lookup.dest = CAIRO_GL_OPERAND_NONE; + lookup.in = in; + lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); + lookup.base.size = 1; + + entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); + if (entry) { + assert (entry->shader.program); + *shader = &entry->shader; + return CAIRO_STATUS_SUCCESS; + } + + status = cairo_gl_shader_get_fragment_source (ctx->tex_target, + in, + source, + mask, + CAIRO_GL_OPERAND_NONE, + &fs_source); + if (unlikely (status)) + return status; + + entry = malloc (sizeof (cairo_shader_cache_entry_t)); + if (unlikely (entry == NULL)) { + free (fs_source); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); + + entry->ctx = ctx; + _cairo_gl_shader_init (&entry->shader); + status = _cairo_gl_shader_compile (ctx, + &entry->shader, + cairo_gl_operand_get_var_type (source), + cairo_gl_operand_get_var_type (mask), + fs_source); + free (fs_source); + + if (unlikely (status)) { + free (entry); + return status; + } + + status = _cairo_cache_insert (&ctx->shaders, &entry->base); + if (unlikely (status)) { + _cairo_gl_shader_fini (ctx, &entry->shader); + free (entry); + return status; + } + + *shader = &entry->shader; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-gl-surface.c b/libs/cairo/src/cairo-gl-surface.c new file mode 100644 index 000000000..458300050 --- /dev/null +++ b/libs/cairo/src/cairo-gl-surface.c @@ -0,0 +1,1601 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-gl-private.h" + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles (void *abstract_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects); + +static cairo_int_status_t +_cairo_gl_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + +static cairo_status_t +_cairo_gl_surface_flush (void *abstract_surface); + +static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) +{ + return surface->backend == &_cairo_gl_surface_backend; +} + +cairo_bool_t +_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha) +{ + *has_alpha = TRUE; + + switch (pixman_format) { + case PIXMAN_a8r8g8b8: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8r8g8b8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8b8g8r8: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8b8g8r8: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_b8g8r8a8: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + return TRUE; + case PIXMAN_b8g8r8x8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_r8g8b8: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_b8g8r8: + *internal_format = GL_RGB; + *format = GL_BGR; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_r5g6b5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + return TRUE; + case PIXMAN_b5g6r5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5_REV; + return TRUE; + case PIXMAN_a1r5g5b5: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1r5g5b5: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a1b5g5r5: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1b5g5r5: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8: + *internal_format = GL_ALPHA; + *format = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + return TRUE; + + case PIXMAN_a2b10g10r10: + case PIXMAN_x2b10g10r10: + case PIXMAN_a4r4g4b4: + case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: + case PIXMAN_x4b4g4r4: + case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: + case PIXMAN_a2r2g2b2: + case PIXMAN_a2b2g2r2: + case PIXMAN_c8: + case PIXMAN_x4a4: + /* case PIXMAN_x4c4: */ + case PIXMAN_x4g4: + case PIXMAN_a4: + case PIXMAN_r1g2b1: + case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: + case PIXMAN_a1b1g1r1: + case PIXMAN_c4: + case PIXMAN_g4: + case PIXMAN_a1: + case PIXMAN_g1: + case PIXMAN_yuy2: + case PIXMAN_yv12: + case PIXMAN_x2r10g10b10: + case PIXMAN_a2r10g10b10: + default: + return FALSE; + } +} + +cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op) +{ + return op < CAIRO_OPERATOR_SATURATE; +} + +void +_cairo_gl_surface_init (cairo_device_t *device, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height) +{ + _cairo_surface_init (&surface->base, + &_cairo_gl_surface_backend, + device, + content); + + surface->width = width; + surface->height = height; +} + +static cairo_surface_t * +_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, + cairo_content_t content, + GLuint tex, + int width, + int height) +{ + cairo_gl_surface_t *surface; + + assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size); + + surface = calloc (1, sizeof (cairo_gl_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (&ctx->base, surface, content, width, height); + surface->tex = tex; + + /* Create the texture used to store the surface's data. */ + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + glBindTexture (ctx->tex_target, surface->tex); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return &surface->base; +} + +static cairo_surface_t * +_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_surface_t *surface; + GLenum format; + GLuint tex; + + glGenTextures (1, &tex); + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch_for_texture (ctx, content, + tex, width, height); + if (unlikely (surface->base.status)) + return &surface->base; + + surface->owns_tex = TRUE; + + /* adjust the texture size after setting our real extents */ + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + switch (content) { + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + format = GL_RGBA; + break; + case CAIRO_CONTENT_ALPHA: + /* We want to be trying GL_ALPHA framebuffer objects here. */ + format = GL_RGBA; + break; + case CAIRO_CONTENT_COLOR: + /* GL_RGB is almost what we want here -- sampling 1 alpha when + * texturing, using 1 as destination alpha factor in blending, + * etc. However, when filtering with GL_CLAMP_TO_BORDER, the + * alpha channel of the border color will also be clamped to + * 1, when we actually want the border color we explicitly + * specified. So, we have to store RGBA, and fill the alpha + * channel with 1 when blending. + */ + format = GL_RGBA; + break; + } + + glTexImage2D (ctx->tex_target, 0, format, width, height, 0, + format, GL_UNSIGNED_BYTE, NULL); + + return &surface->base; +} + +static cairo_status_t +_cairo_gl_surface_clear (cairo_gl_surface_t *surface, + const cairo_color_t *color) +{ + cairo_gl_context_t *ctx; + cairo_status_t status; + double r, g, b, a; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + _cairo_gl_context_set_destination (ctx, surface); + if (surface->base.content & CAIRO_CONTENT_COLOR) { + r = color->red * color->alpha; + g = color->green * color->alpha; + b = color->blue * color->alpha; + } else { + r = g = b = 0; + } + if (surface->base.content & CAIRO_CONTENT_ALPHA) { + a = color->alpha; + } else { + a = 1.0; + } + + glDisable (GL_SCISSOR_TEST); + glClearColor (r, g, b, a); + glClear (GL_COLOR_BUFFER_BIT); + + return _cairo_gl_context_release (ctx, status); +} + +cairo_surface_t * +cairo_gl_surface_create (cairo_device_t *abstract_device, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_context_t *ctx; + cairo_gl_surface_t *surface; + cairo_status_t status; + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (abstract_device == NULL) { + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); + } + + if (abstract_device->status) + return _cairo_surface_create_in_error (abstract_device->status); + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + status = _cairo_gl_context_acquire (abstract_device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch (ctx, content, width, height); + if (unlikely (surface->base.status)) { + status = _cairo_gl_context_release (ctx, surface->base.status); + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + /* Cairo surfaces start out initialized to transparent (black) */ + status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + return &surface->base; +} +slim_hidden_def (cairo_gl_surface_create); + + +/** + * cairo_gl_surface_create_for_texture: + * @content: type of content in the surface + * @tex: name of texture to use for storage of surface pixels + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a GL surface for the specified texture with the specified + * content and dimensions. The texture must be kept around until the + * #cairo_surface_t is destroyed or cairo_surface_finish() is called + * on the surface. The initial contents of @tex will be used as the + * initial image contents; you must explicitly clear the buffer, + * using, for example, cairo_rectangle() and cairo_fill() if you want + * it cleared. The format of @tex should be compatible with @content, + * in the sense that it must have the color components required by + * @content. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + **/ +cairo_surface_t * +cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, + cairo_content_t content, + unsigned int tex, + int width, + int height) +{ + cairo_gl_context_t *ctx; + cairo_gl_surface_t *surface; + cairo_status_t status; + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (abstract_device == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + if (abstract_device->status) + return _cairo_surface_create_in_error (abstract_device->status); + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + status = _cairo_gl_context_acquire (abstract_device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch_for_texture (ctx, content, + tex, width, height); + status = _cairo_gl_context_release (ctx, status); + + return &surface->base; +} +slim_hidden_def (cairo_gl_surface_create_for_texture); + + +void +cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_gl (abstract_surface) || + ! _cairo_gl_surface_is_texture (surface)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + surface->width = width; + surface->height = height; +} + +int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->width; +} + +int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->height; +} + +void +cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_gl (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + if (! _cairo_gl_surface_is_texture (surface)) { + cairo_gl_context_t *ctx; + cairo_status_t status; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return; + + cairo_surface_flush (abstract_surface); + + ctx->swap_buffers (ctx, surface); + + status = _cairo_gl_context_release (ctx, status); + if (status) + status = _cairo_surface_set_error (abstract_surface, status); + } +} + +static cairo_surface_t * +_cairo_gl_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *surface = abstract_surface; + cairo_gl_context_t *ctx; + cairo_status_t status; + + if (width < 1 || height < 1) + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); + + status = _cairo_gl_context_acquire (surface->device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (width > ctx->max_framebuffer_size || + height > ctx->max_framebuffer_size) + { + surface = NULL; + goto RELEASE; + } + + surface = _cairo_gl_surface_create_scratch (ctx, content, width, height); + +RELEASE: + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + return surface; +} + +cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + GLenum internal_format, format, type; + cairo_bool_t has_alpha; + cairo_image_surface_t *clone = NULL; + cairo_gl_context_t *ctx; + int cpp; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (! _cairo_gl_get_image_format_and_type (src->pixman_format, + &internal_format, + &format, + &type, + &has_alpha)) + { + cairo_bool_t is_supported; + + clone = _cairo_image_surface_coerce (src); + if (unlikely (clone->base.status)) + return clone->base.status; + + is_supported = + _cairo_gl_get_image_format_and_type (clone->pixman_format, + &internal_format, + &format, + &type, + &has_alpha); + assert (is_supported); + src = clone; + } + + cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + status = _cairo_gl_surface_flush (&dst->base); + if (unlikely (status)) + goto FAIL; + + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); + if (_cairo_gl_surface_is_texture (dst)) { + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + glBindTexture (ctx->tex_target, dst->tex); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexSubImage2D (ctx->tex_target, 0, + dst_x, dst_y, width, height, + format, type, + src->data + src_y * src->stride + src_x * cpp); + + /* If we just treated some rgb-only data as rgba, then we have to + * go back and fix up the alpha channel where we filled in this + * texture data. + */ + if (!has_alpha) { + cairo_rectangle_int_t rect; + + rect.x = dst_x; + rect.y = dst_y; + rect.width = width; + rect.height = height; + + _cairo_gl_composite_flush (ctx); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + _cairo_gl_surface_fill_rectangles (dst, + CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_BLACK, + &rect, 1); + _cairo_gl_composite_flush (ctx); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } else { + cairo_surface_t *tmp; + + tmp = _cairo_gl_surface_create_scratch (ctx, + dst->base.content, + width, height); + if (unlikely (tmp->status)) { + cairo_surface_destroy (tmp); + goto FAIL; + } + status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, + src, + src_x, src_y, + width, height, + 0, 0); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_pattern_t tmp_pattern; + + _cairo_pattern_init_for_surface (&tmp_pattern, tmp); + _cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE, + &tmp_pattern.base, + NULL, + dst, + 0, 0, + 0, 0, + dst_x, dst_y, + width, height, + NULL); + _cairo_pattern_fini (&tmp_pattern.base); + } + + cairo_surface_destroy (tmp); + } + +FAIL: + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + + status = _cairo_gl_context_release (ctx, status); + + if (clone) + cairo_surface_destroy (&clone->base); + + return status; +} + +static cairo_status_t +_cairo_gl_surface_get_image (cairo_gl_surface_t *surface, + cairo_rectangle_int_t *interest, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *rect_out) +{ + cairo_image_surface_t *image; + cairo_gl_context_t *ctx; + GLenum format, type; + cairo_format_t cairo_format; + unsigned int cpp; + cairo_status_t status; + + /* Want to use a switch statement here but the compiler gets whiny. */ + if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { + format = GL_BGRA; + cairo_format = CAIRO_FORMAT_ARGB32; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_COLOR) { + format = GL_BGRA; + cairo_format = CAIRO_FORMAT_RGB24; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { + format = GL_ALPHA; + cairo_format = CAIRO_FORMAT_A8; + type = GL_UNSIGNED_BYTE; + cpp = 1; + } else { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + image = (cairo_image_surface_t*) + cairo_image_surface_create (cairo_format, + interest->width, interest->height); + if (unlikely (image->base.status)) + return image->base.status; + + /* This is inefficient, as we'd rather just read the thing without making + * it the destination. But then, this is the fallback path, so let's not + * fall back instead. + */ + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + _cairo_gl_composite_flush (ctx); + _cairo_gl_context_set_destination (ctx, surface); + + glPixelStorei (GL_PACK_ALIGNMENT, 1); + glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); + if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) + glPixelStorei (GL_PACK_INVERT_MESA, 1); + glReadPixels (interest->x, interest->y, + interest->width, interest->height, + format, type, image->data); + if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) + glPixelStorei (GL_PACK_INVERT_MESA, 0); + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return status; + } + + *image_out = image; + if (rect_out != NULL) + *rect_out = *interest; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_surface_finish (void *abstract_surface) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_gl_context_t *ctx; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) + _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); + if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) + _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); + if (ctx->current_target == surface) + ctx->current_target = NULL; + + if (surface->depth) + glDeleteFramebuffersEXT (1, &surface->depth); + if (surface->fb) + glDeleteFramebuffersEXT (1, &surface->fb); + if (surface->owns_tex) + glDeleteTextures (1, &surface->tex); + + return _cairo_gl_context_release (ctx, status); +} + +static cairo_status_t +_cairo_gl_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_rectangle_int_t extents; + + *image_extra = NULL; + + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL); +} + +static void +_cairo_gl_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_gl_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_gl_surface_t *surface = abstract_surface; + + *image_extra = NULL; + return _cairo_gl_surface_get_image (surface, interest_rect, image_out, + image_rect_out); +} + +static void +_cairo_gl_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_status_t status; + + status = _cairo_gl_surface_draw_image (abstract_surface, image, + 0, 0, + image->width, image->height, + image_rect->x, image_rect->y); + /* as we created the image, its format should be directly applicable */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_gl_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_gl_surface_t *surface = abstract_surface; + + /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ + if (src->device == surface->base.device && + _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + cairo_gl_surface_t *clone; + cairo_status_t status; + + clone = (cairo_gl_surface_t *) + _cairo_gl_surface_create_similar (&surface->base, + src->content, + width, height); + if (clone == NULL) + return UNSUPPORTED ("create_similar failed"); + if (clone->base.status) + return clone->base.status; + + status = _cairo_gl_surface_draw_image (clone, image_src, + src_x, src_y, + width, height, + 0, 0); + if (status) { + cairo_surface_destroy (&clone->base); + return status; + } + + *clone_out = &clone->base; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + + return CAIRO_STATUS_SUCCESS; + } + + return UNSUPPORTED ("unknown src surface type in clone_similar"); +} + +/** Creates a cairo-gl pattern surface for the given trapezoids */ +static cairo_status_t +_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + int width, int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_antialias_t antialias, + cairo_surface_pattern_t *pattern) +{ + pixman_format_code_t pixman_format; + pixman_image_t *image; + cairo_surface_t *surface; + int i; + + pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, + image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); + if (unlikely (image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < num_traps; i++) { + pixman_trapezoid_t trap; + + trap.top = _cairo_fixed_to_16_16 (traps[i].top); + trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); + + trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); + + trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + } + + surface = _cairo_image_surface_create_for_pixman_image (image, + pixman_format); + if (unlikely (surface->status)) { + pixman_image_unref (image); + return surface->status; + } + + _cairo_pattern_init_for_surface (pattern, surface); + cairo_surface_destroy (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_gl_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_gl_context_t *ctx; + cairo_status_t status; + cairo_gl_composite_t setup; + cairo_rectangle_int_t rect = { dst_x, dst_y, width, height }; + int dx, dy; + + if (op == CAIRO_OPERATOR_SOURCE && + mask == NULL && + src->type == CAIRO_PATTERN_TYPE_SURFACE && + _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) && + _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) { + cairo_image_surface_t *image = (cairo_image_surface_t *) + ((cairo_surface_pattern_t *) src)->surface; + dx += src_x; + dy += src_y; + if (dx >= 0 && + dy >= 0 && + dx + width <= (unsigned int) image->width && + dy + height <= (unsigned int) image->height) { + status = _cairo_gl_surface_draw_image (dst, image, + dx, dy, + width, height, + dst_x, dst_y); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + } + + status = _cairo_gl_composite_init (&setup, op, dst, + mask && mask->has_component_alpha, + &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_set_source (&setup, src, + src_x, src_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_set_mask (&setup, mask, + mask_x, mask_y, + dst_x, dst_y, + width, height); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_begin (&setup, &ctx); + if (unlikely (status)) + goto CLEANUP; + + if (clip_region != NULL) { + int i, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + _cairo_gl_composite_emit_rect (ctx, + rect.x, rect.y, + rect.x + rect.width, rect.y + rect.height, + 0); + } + } else { + _cairo_gl_composite_emit_rect (ctx, + dst_x, dst_y, + dst_x + width, dst_y + height, + 0); + } + + status = _cairo_gl_context_release (ctx, status); + + CLEANUP: + _cairo_gl_composite_fini (&setup); + + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, int src_y, + int dst_x, int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_surface_pattern_t traps_pattern; + cairo_int_status_t status; + + if (! _cairo_gl_operator_is_supported (op)) + return UNSUPPORTED ("unsupported operator"); + + status = _cairo_gl_get_traps_pattern (dst, + dst_x, dst_y, width, height, + traps, num_traps, antialias, + &traps_pattern); + if (unlikely (status)) + return status; + + status = _cairo_gl_surface_composite (op, + pattern, &traps_pattern.base, dst, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height, + clip_region); + + _cairo_pattern_fini (&traps_pattern.base); + + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_fill_rectangles (void *abstract_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_solid_pattern_t solid; + cairo_gl_context_t *ctx; + cairo_status_t status; + cairo_gl_composite_t setup; + int i; + + status = _cairo_gl_composite_init (&setup, op, dst, + FALSE, + /* XXX */ NULL); + if (unlikely (status)) + goto CLEANUP; + + _cairo_pattern_init_solid (&solid, color); + status = _cairo_gl_composite_set_source (&setup, &solid.base, + 0, 0, + 0, 0, + 0, 0); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_set_mask (&setup, NULL, + 0, 0, + 0, 0, + 0, 0); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_gl_composite_begin (&setup, &ctx); + if (unlikely (status)) + goto CLEANUP; + + for (i = 0; i < num_rects; i++) { + _cairo_gl_composite_emit_rect (ctx, + rects[i].x, + rects[i].y, + rects[i].x + rects[i].width, + rects[i].y + rects[i].height, + 0); + } + + status = _cairo_gl_context_release (ctx, status); + + CLEANUP: + _cairo_gl_composite_fini (&setup); + + return status; +} + +typedef struct _cairo_gl_surface_span_renderer { + cairo_span_renderer_t base; + + cairo_gl_composite_t setup; + + int xmin, xmax; + int ymin, ymax; + + cairo_gl_context_t *ctx; +} cairo_gl_surface_span_renderer_t; + +static cairo_status_t +_cairo_gl_render_bounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + _cairo_gl_composite_emit_rect (renderer->ctx, + spans[0].x, y, + spans[1].x, y + height, + spans[0].coverage); + } + + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_render_unbounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (y > renderer->ymin) { + _cairo_gl_composite_emit_rect (renderer->ctx, + renderer->xmin, renderer->ymin, + renderer->xmax, y, + 0); + } + + if (num_spans == 0) { + _cairo_gl_composite_emit_rect (renderer->ctx, + renderer->xmin, y, + renderer->xmax, y + height, + 0); + } else { + if (spans[0].x != renderer->xmin) { + _cairo_gl_composite_emit_rect (renderer->ctx, + renderer->xmin, y, + spans[0].x, y + height, + 0); + } + + do { + _cairo_gl_composite_emit_rect (renderer->ctx, + spans[0].x, y, + spans[1].x, y + height, + spans[0].coverage); + spans++; + } while (--num_spans > 1); + + if (spans[0].x != renderer->xmax) { + _cairo_gl_composite_emit_rect (renderer->ctx, + spans[0].x, y, + renderer->xmax, y + height, + 0); + } + } + + renderer->ymin = y + height; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gl_finish_unbounded_spans (void *abstract_renderer) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (renderer->ymax > renderer->ymin) { + _cairo_gl_composite_emit_rect (renderer->ctx, + renderer->xmin, renderer->ymin, + renderer->xmax, renderer->ymax, + 0); + } + + return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); +} + +static cairo_status_t +_cairo_gl_finish_bounded_spans (void *abstract_renderer) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); +} + +static void +_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) +{ + cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; + + if (!renderer) + return; + + _cairo_gl_composite_fini (&renderer->setup); + + free (renderer); +} + +static cairo_bool_t +_cairo_gl_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias) +{ + if (! _cairo_gl_operator_is_supported (op)) + return FALSE; + + return TRUE; + + (void) pattern; + (void) abstract_dst; + (void) antialias; +} + +static cairo_span_renderer_t * +_cairo_gl_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *src, + void *abstract_dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_gl_surface_span_renderer_t *renderer; + cairo_status_t status; + const cairo_rectangle_int_t *extents; + + renderer = calloc (1, sizeof (*renderer)); + if (unlikely (renderer == NULL)) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); + + renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; + if (rects->is_bounded) { + renderer->base.render_rows = _cairo_gl_render_bounded_spans; + renderer->base.finish = _cairo_gl_finish_bounded_spans; + extents = &rects->bounded; + } else { + renderer->base.render_rows = _cairo_gl_render_unbounded_spans; + renderer->base.finish = _cairo_gl_finish_unbounded_spans; + extents = &rects->unbounded; + } + renderer->xmin = extents->x; + renderer->xmax = extents->x + extents->width; + renderer->ymin = extents->y; + renderer->ymax = extents->y + extents->height; + + status = _cairo_gl_composite_init (&renderer->setup, + op, dst, + FALSE, extents); + if (unlikely (status)) + goto FAIL; + + status = _cairo_gl_composite_set_source (&renderer->setup, src, + extents->x, extents->y, + extents->x, extents->y, + extents->width, extents->height); + if (unlikely (status)) + goto FAIL; + + _cairo_gl_composite_set_mask_spans (&renderer->setup); + _cairo_gl_composite_set_clip_region (&renderer->setup, clip_region); + + status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx); + if (unlikely (status)) + goto FAIL; + + return &renderer->base; + +FAIL: + _cairo_gl_composite_fini (&renderer->setup); + free (renderer); + return _cairo_span_renderer_create_in_error (status); +} + +static cairo_bool_t +_cairo_gl_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_gl_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_gl_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); +} + +static cairo_status_t +_cairo_gl_surface_flush (void *abstract_surface) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_gl_context_t *ctx; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) || + (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) || + (ctx->current_target == surface)) + _cairo_gl_composite_flush (ctx); + + return _cairo_gl_context_release (ctx, status); +} + +static cairo_int_status_t +_cairo_gl_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + /* simplify the common case of clearing the surface */ + if (clip == NULL) { + if (op == CAIRO_OPERATOR_CLEAR) + return _cairo_gl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT); + else if (source->type == CAIRO_PATTERN_TYPE_SOLID && + (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) { + return _cairo_gl_surface_clear (abstract_surface, + &((cairo_solid_pattern_t *) source)->color); + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (_cairo_status_is_error (status))) + return status; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return UNSUPPORTED ("a clip surface would be required"); + } + + if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) + return UNSUPPORTED ("no span renderer"); + + if (op == CAIRO_OPERATOR_SOURCE) + return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + src = &_cairo_pattern_white.base; + } + + status = _cairo_surface_composite_polygon (&dst->base, + op, + src, + fill_rule, + antialias, + extents, + polygon, + clip_region); + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_polygon_t polygon; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + surface->width, + surface->height, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_gl_surface_polygon (surface, op, source, &polygon, + CAIRO_FILL_RULE_WINDING, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +_cairo_gl_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_polygon_t polygon; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + surface->width, + surface->height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + +#if 0 + if (extents.is_bounded && clip != NULL) { + cairo_clip_path_t *clip_path; + + if (((clip_path = _clip_get_single_path (clip)) != NULL) && + _cairo_path_fixed_equal (&clip_path->path, path)) + { + clip = NULL; + } + } +#endif + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_gl_surface_polygon (surface, op, source, &polygon, + fill_rule, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +const cairo_surface_backend_t _cairo_gl_surface_backend = { + CAIRO_SURFACE_TYPE_GL, + _cairo_gl_surface_create_similar, + _cairo_gl_surface_finish, + + _cairo_gl_surface_acquire_source_image, + _cairo_gl_surface_release_source_image, + _cairo_gl_surface_acquire_dest_image, + _cairo_gl_surface_release_dest_image, + + _cairo_gl_surface_clone_similar, + _cairo_gl_surface_composite, + _cairo_gl_surface_fill_rectangles, + _cairo_gl_surface_composite_trapezoids, + _cairo_gl_surface_create_span_renderer, + _cairo_gl_surface_check_span_renderer, + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_gl_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_gl_surface_get_font_options, + _cairo_gl_surface_flush, + NULL, /* mark_dirty_rectangle */ + _cairo_gl_surface_scaled_font_fini, + _cairo_gl_surface_scaled_glyph_fini, + _cairo_gl_surface_paint, + NULL, /* mask */ + _cairo_gl_surface_stroke, + _cairo_gl_surface_fill, + _cairo_gl_surface_show_glyphs, /* show_glyphs */ + NULL /* snapshot */ +}; diff --git a/libs/cairo/src/cairo-gl.h b/libs/cairo/src/cairo-gl.h new file mode 100644 index 000000000..17c5b03f1 --- /dev/null +++ b/libs/cairo/src/cairo-gl.h @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_GL_H +#define CAIRO_GL_H + +#include "cairo.h" + +#if CAIRO_HAS_GL_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_gl_surface_create (cairo_device_t *device, + cairo_content_t content, + int width, int height); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, + cairo_content_t content, + unsigned int tex, + int width, int height); +cairo_public void +cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); + +cairo_public int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public void +cairo_gl_surface_swapbuffers (cairo_surface_t *surface); + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_glx_device_create (Display *dpy, GLXContext gl_ctx); + +cairo_public Display * +cairo_glx_device_get_display (cairo_device_t *device); + +cairo_public GLXContext +cairo_glx_device_get_context (cairo_device_t *device); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_device_t *device, + Window win, + int width, int height); +#endif + +#if CAIRO_HAS_WGL_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_wgl_device_create (HGLRC rc); + +cairo_public HGLRC +cairo_wgl_device_get_context (cairo_device_t *device); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_dc (cairo_device_t *device, + HDC dc, + int width, + int height); +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_egl_device_create (EGLDisplay dpy, EGLContext egl); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_egl (cairo_device_t *device, + EGLSurface egl, + int width, + int height); + +#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/libs/cairo/src/cairo-glitz-private.h b/libs/cairo/src/cairo-glitz-private.h new file mode 100644 index 000000000..144eff4a5 --- /dev/null +++ b/libs/cairo/src/cairo-glitz-private.h @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_GLITZ_PRIVATE_H +#define CAIRO_GLITZ_PRIVATE_H + +#include "cairoint.h" +#include "cairo-glitz.h" + +slim_hidden_proto (cairo_glitz_surface_create); + +#endif /* CAIRO_GLITZ_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-glitz-surface.c b/libs/cairo/src/cairo-glitz-surface.c new file mode 100644 index 000000000..bd176d056 --- /dev/null +++ b/libs/cairo/src/cairo-glitz-surface.c @@ -0,0 +1,2424 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-glitz.h" +#include "cairo-glitz-private.h" +#include "cairo-region-private.h" + +typedef struct _cairo_glitz_surface { + cairo_surface_t base; + + glitz_surface_t *surface; + glitz_format_t *format; + + cairo_region_t *clip_region; + cairo_bool_t has_clip; + glitz_box_t *clip_boxes; + int num_clip_boxes; +} cairo_glitz_surface_t; + +static const cairo_surface_backend_t * +_cairo_glitz_surface_get_backend (void); + +static cairo_status_t +_cairo_glitz_surface_finish (void *abstract_surface) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + if (surface->clip_boxes) + free (surface->clip_boxes); + + cairo_region_destroy (surface->clip_region); + glitz_surface_destroy (surface->surface); + + return CAIRO_STATUS_SUCCESS; +} + +static glitz_format_name_t +_glitz_format_from_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return GLITZ_STANDARD_RGB24; + case CAIRO_CONTENT_ALPHA: + return GLITZ_STANDARD_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + return GLITZ_STANDARD_ARGB32; + } + + ASSERT_NOT_REACHED; + return GLITZ_STANDARD_ARGB32; +} + +static cairo_surface_t * +_cairo_glitz_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_glitz_surface_t *src = abstract_src; + cairo_surface_t *crsurface; + glitz_drawable_t *drawable; + glitz_surface_t *surface; + glitz_format_t *gformat; + + drawable = glitz_surface_get_drawable (src->surface); + + gformat = + glitz_find_standard_format (drawable, + _glitz_format_from_content (content)); + if (!gformat) + return NULL; + + surface = glitz_surface_create (drawable, gformat, + width <= 0 ? 1 : width, + height <= 0 ? 1 : height, + 0, NULL); + + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + crsurface = cairo_glitz_surface_create (surface); + + glitz_surface_destroy (surface); + + return crsurface; +} + +static cairo_status_t +_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, + cairo_rectangle_int_t *interest, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *rect_out) +{ + cairo_image_surface_t *image; + cairo_rectangle_int_t extents; + cairo_format_t format; + cairo_format_masks_t masks; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + + extents.x = 0; + extents.y = 0; + extents.width = glitz_surface_get_width (surface->surface); + extents.height = glitz_surface_get_height (surface->surface); + + if (interest != NULL) { + if (! _cairo_rectangle_intersect (&extents, interest)) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + if (rect_out != NULL) + *rect_out = extents; + + if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { + if (surface->format->color.red_size > 0) { + if (surface->format->color.alpha_size > 0) + format = CAIRO_FORMAT_ARGB32; + else + format = CAIRO_FORMAT_RGB24; + } else { + format = CAIRO_FORMAT_A8; + } + } else + format = CAIRO_FORMAT_ARGB32; + + image = (cairo_image_surface_t*) + cairo_image_surface_create (format, extents.width, extents.height); + if (image->base.status) + return image->base.status; + + _pixman_format_to_masks (image->pixman_format, &masks); + pf.fourcc = GLITZ_FOURCC_RGB; + pf.masks.bpp = masks.bpp; + pf.masks.alpha_mask = masks.alpha_mask; + pf.masks.red_mask = masks.red_mask; + pf.masks.green_mask = masks.green_mask; + pf.masks.blue_mask = masks.blue_mask; + pf.xoffset = 0; + pf.skip_lines = 0; + + /* XXX: we should eventually return images with negative stride, + need to verify that libpixman have no problem with this first. */ + pf.bytes_per_line = image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; + + buffer = glitz_buffer_create_for_data (image->data); + if (buffer == NULL) { + cairo_surface_destroy (&image->base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* clear out the glitz clip; the clip affects glitz_get_pixels */ + if (surface->has_clip) + glitz_surface_set_clip_region (surface->surface, + 0, 0, NULL, 0); + + glitz_get_pixels (surface->surface, + extents.x, extents.y, + extents.width, extents.height, + &pf, + buffer); + + glitz_buffer_destroy (buffer); + + /* restore the clip, if any */ + if (surface->has_clip) { + glitz_surface_set_clip_region (surface->surface, + 0, 0, + surface->clip_boxes, + surface->num_clip_boxes); + } + + *image_out = image; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_glitz_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image, + int src_x, + int src_y, + int width, + int height, + int x_dst, + int y_dst) +{ + cairo_glitz_surface_t *surface = abstract_surface; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + cairo_format_masks_t masks; + char *data; + + _pixman_format_to_masks (image->pixman_format, &masks); + + pf.fourcc = GLITZ_FOURCC_RGB; + pf.masks.bpp = masks.bpp; + pf.masks.alpha_mask = masks.alpha_mask; + pf.masks.red_mask = masks.red_mask; + pf.masks.green_mask = masks.green_mask; + pf.masks.blue_mask = masks.blue_mask; + pf.xoffset = src_x; + pf.skip_lines = src_y; + + /* check for negative stride */ + if (image->stride < 0) { + pf.bytes_per_line = -image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + data = (char *) image->data + image->stride * (image->height - 1); + } else { + pf.bytes_per_line = image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; + data = (char *) image->data; + } + + buffer = glitz_buffer_create_for_data (data); + if (buffer == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + glitz_set_pixels (surface->surface, + x_dst, y_dst, + width, height, + &pf, + buffer); + + glitz_buffer_destroy (buffer); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_glitz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + *image_extra = NULL; + + return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); +} + +static cairo_surface_t * +_cairo_glitz_surface_snapshot (void *abstract_surface) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_image_surface_t *image; + + status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + +static void +_cairo_glitz_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_glitz_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, + image_rect_out); + if (status) + return status; + + *image_out = image; + *image_extra = NULL; + + return status; +} + +static void +_cairo_glitz_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _cairo_glitz_surface_set_image (surface, image, 0, 0, + image->width, image->height, + image_rect->x, image_rect->y); + if (status) + status = _cairo_surface_set_error (&surface->base, status); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_glitz_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_glitz_surface_t *clone; + cairo_status_t status; + + if (surface->base.status) + return surface->base.status; + + if (src->backend == surface->base.backend) + { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + else if (_cairo_surface_is_image (src)) + { + cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; + + clone = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (surface, src->content, + width, height); + if (clone == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (clone->base.status) + return clone->base.status; + + status = _cairo_glitz_surface_set_image (clone, image_src, + src_x, src_y, + width, height, + 0, 0); + if (status) { + cairo_surface_destroy (&clone->base); + return status; + } + + *clone_out = &clone->base; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, + cairo_matrix_t *matrix) +{ + glitz_transform_t transform; + + transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); + transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); + transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); + + transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); + transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); + transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); + + transform.matrix[2][0] = 0; + transform.matrix[2][1] = 0; + transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1); + + glitz_surface_set_transform (surface->surface, &transform); +} + +static cairo_bool_t +_is_supported_operator (cairo_operator_t op) +{ + /* This is really just a if (op < SATURATE), but we use a switch + * so the compiler will warn if we ever add more operators. + */ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + ASSERT_NOT_REACHED; + case CAIRO_OPERATOR_SATURATE: + /* nobody likes saturate, expect that it's required to do + * seamless polygons! + */ + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return FALSE; + } +} + +static glitz_operator_t +_glitz_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_CLEAR: + return GLITZ_OPERATOR_CLEAR; + + case CAIRO_OPERATOR_SOURCE: + return GLITZ_OPERATOR_SRC; + case CAIRO_OPERATOR_OVER: + return GLITZ_OPERATOR_OVER; + case CAIRO_OPERATOR_IN: + return GLITZ_OPERATOR_IN; + case CAIRO_OPERATOR_OUT: + return GLITZ_OPERATOR_OUT; + case CAIRO_OPERATOR_ATOP: + return GLITZ_OPERATOR_ATOP; + + case CAIRO_OPERATOR_DEST: + return GLITZ_OPERATOR_DST; + case CAIRO_OPERATOR_DEST_OVER: + return GLITZ_OPERATOR_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return GLITZ_OPERATOR_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return GLITZ_OPERATOR_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: + return GLITZ_OPERATOR_ATOP_REVERSE; + + case CAIRO_OPERATOR_XOR: + return GLITZ_OPERATOR_XOR; + case CAIRO_OPERATOR_ADD: + return GLITZ_OPERATOR_ADD; + + default: + ASSERT_NOT_REACHED; + + /* Something's very broken if this line of code can be reached, so + * we want to return something that would give a noticeably + * incorrect result. The XOR operator seems so rearely desired + * that it should fit the bill here. + */ + return CAIRO_OPERATOR_XOR; + } +} + +#define CAIRO_GLITZ_FEATURE_OK(surface, name) \ + (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \ + (GLITZ_FEATURE_ ## name ## _MASK)) + +static glitz_status_t +_glitz_ensure_target (glitz_surface_t *surface) +{ + if (!glitz_surface_get_attached_drawable (surface)) + { + glitz_drawable_format_t *target_format, templ; + glitz_format_t *format; + glitz_drawable_t *drawable, *target; + unsigned int width, height; + unsigned long mask; + + drawable = glitz_surface_get_drawable (surface); + format = glitz_surface_get_format (surface); + width = glitz_surface_get_width (surface); + height = glitz_surface_get_height (surface); + + if (format->color.fourcc != GLITZ_FOURCC_RGB) + return CAIRO_INT_STATUS_UNSUPPORTED; + + templ.color = format->color; + templ.depth_size = 0; + templ.stencil_size = 0; + templ.doublebuffer = 0; + templ.samples = 1; + + mask = + GLITZ_FORMAT_RED_SIZE_MASK | + GLITZ_FORMAT_GREEN_SIZE_MASK | + GLITZ_FORMAT_BLUE_SIZE_MASK | + GLITZ_FORMAT_ALPHA_SIZE_MASK | + GLITZ_FORMAT_DEPTH_SIZE_MASK | + GLITZ_FORMAT_STENCIL_SIZE_MASK | + GLITZ_FORMAT_DOUBLEBUFFER_MASK | + GLITZ_FORMAT_SAMPLES_MASK; + + target_format = glitz_find_drawable_format (drawable, mask, &templ, 0); + if (!target_format) + return CAIRO_INT_STATUS_UNSUPPORTED; + + target = glitz_create_drawable (drawable, target_format, + width, height); + if (!target) + return CAIRO_INT_STATUS_UNSUPPORTED; + + glitz_surface_attach (surface, target, + GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); + + glitz_drawable_destroy (target); + } + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_glitz_surface_attributes { + cairo_surface_attributes_t base; + + glitz_fill_t fill; + glitz_filter_t filter; + glitz_fixed16_16_t *params; + int n_params; + cairo_bool_t acquired; +} cairo_glitz_surface_attributes_t; + +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, + cairo_glitz_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **surface_out, + cairo_glitz_surface_attributes_t *attr) +{ + cairo_glitz_surface_t *src = NULL; + + attr->acquired = FALSE; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + char *data; + glitz_fixed16_16_t *params; + unsigned int n_params; + unsigned int *pixels; + unsigned int i, n_base_params; + glitz_buffer_t *buffer; + static const glitz_pixel_format_t format = { + GLITZ_FOURCC_RGB, + { + 32, + 0xff000000, + 0x00ff0000, + 0x0000ff00, + 0x000000ff + }, + 0, 0, 0, + GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP + }; + + /* XXX: the current color gradient acceleration provided by glitz is + * experimental, it's been proven inappropriate in a number of ways, + * most importantly, it's currently implemented as filters and + * gradients are not filters. eventually, it will be replaced with + * something more appropriate. + */ + + if (gradient->n_stops < 2) + break; + + if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) + break; + + if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + n_base_params = 6; + else + n_base_params = 4; + + n_params = gradient->n_stops * 3 + n_base_params; + + /* check for int overflow */ + { + int size1, size2; + if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) || + gradient->n_stops >= INT32_MAX / sizeof (unsigned int)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + size1 = n_params * sizeof (glitz_fixed16_16_t); + size2 = gradient->n_stops * sizeof (unsigned int); + + if (size1 >= INT32_MAX - size2) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + data = malloc (size1 + size2); + } + + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + params = (glitz_fixed16_16_t *) data; + pixels = (unsigned int *) + (data + sizeof (glitz_fixed16_16_t) * n_params); + + buffer = glitz_buffer_create_for_data (pixels); + if (!buffer) { + free (data); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + src = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_CONTENT_COLOR_ALPHA, + gradient->n_stops, 1); + if (src->base.status) { + glitz_buffer_destroy (buffer); + free (data); + return src->base.status; + } + + for (i = 0; i < gradient->n_stops; i++) + { + pixels[i] = + (((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) | + (((int) (gradient->stops[i].color.red_short >> 8)) << 16) | + (((int) (gradient->stops[i].color.green_short >> 8)) << 8) | + (((int) (gradient->stops[i].color.blue_short >> 8))); + + params[n_base_params + 3 * i + 0] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + params[n_base_params + 3 * i + 1] = i << 16; + params[n_base_params + 3 * i + 2] = 0; + } + + glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1, + (glitz_pixel_format_t *)&format, buffer); + + glitz_buffer_destroy (buffer); + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; + + params[0] = _cairo_fixed_to_16_16 (grad->p1.x); + params[1] = _cairo_fixed_to_16_16 (grad->p1.y); + params[2] = _cairo_fixed_to_16_16 (grad->p2.x); + params[3] = _cairo_fixed_to_16_16 (grad->p2.y); + attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; + } + else + { + cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; + + params[0] = _cairo_fixed_to_16_16 (grad->c1.x); + params[1] = _cairo_fixed_to_16_16 (grad->c1.y); + params[2] = _cairo_fixed_to_16_16 (grad->r1); + params[3] = _cairo_fixed_to_16_16 (grad->c2.x); + params[4] = _cairo_fixed_to_16_16 (grad->c2.y); + params[5] = _cairo_fixed_to_16_16 (grad->r2); + attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; + } + + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_TRANSPARENT; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + case CAIRO_EXTEND_PAD: + attr->fill = GLITZ_FILL_NEAREST; + break; + } + + attr->params = params; + attr->n_params = n_params; + attr->base.matrix = pattern->matrix; + attr->base.x_offset = 0; + attr->base.y_offset = 0; + } break; + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_SURFACE: + default: + break; + } + + if (!src) + { + cairo_int_status_t status; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + x, y, width, height, + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) &src, + &attr->base); + if (status) + return status; + + if (src) + { + switch (attr->base.extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_TRANSPARENT; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + case CAIRO_EXTEND_PAD: + default: + attr->fill = GLITZ_FILL_NEAREST; + break; + } + + switch (attr->base.filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + attr->filter = GLITZ_FILTER_NEAREST; + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + default: + attr->filter = GLITZ_FILTER_BILINEAR; + break; + } + + attr->params = NULL; + attr->n_params = 0; + attr->acquired = TRUE; + } + } + + *surface_out = src; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern, + cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *attr) +{ + if (attr->acquired) + _cairo_pattern_release_surface (pattern, &surface->base, &attr->base); + else + cairo_surface_destroy (&surface->base); +} + +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_glitz_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **src_out, + cairo_glitz_surface_t **mask_out, + cairo_glitz_surface_attributes_t *sattr, + cairo_glitz_surface_attributes_t *mattr) +{ + cairo_int_status_t status; + cairo_solid_pattern_t tmp; + + /* If src and mask are both solid, then the mask alpha can be + * combined into src and mask can be ignored. */ + + /* XXX: This optimization assumes that there is no color + * information in mask, so this will need to change when we + * support RENDER-style 4-channel masks. */ + + if (src->type == CAIRO_PATTERN_TYPE_SOLID && + mask->type == CAIRO_PATTERN_TYPE_SOLID) + { + cairo_color_t combined; + cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; + cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; + + combined = src_solid->color; + _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); + + _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA); + + mask = NULL; + src = &tmp.base; + } + + status = _cairo_glitz_pattern_acquire_surface (src, dst, + src_x, src_y, + width, height, + src_out, sattr); + + if (src == &tmp.base) + _cairo_pattern_fini (&tmp.base); + + if (status) + return status; + + if (mask) + { + status = _cairo_glitz_pattern_acquire_surface (mask, dst, + mask_x, mask_y, + width, height, + mask_out, mattr); + + if (status) { + /* XXX src == &tmp.base -> invalid (currently inconsequential) */ + _cairo_glitz_pattern_release_surface (src, *src_out, sattr); + } + + return status; + } + else + { + *mask_out = NULL; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *a) +{ + _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); + glitz_surface_set_fill (surface->surface, a->fill); + glitz_surface_set_filter (surface->surface, a->filter, + a->params, a->n_params); +} + +static cairo_status_t +_cairo_glitz_get_boxes_from_region (cairo_region_t *region, + glitz_box_t **boxes, + int *nboxes) +{ + pixman_box32_t *pboxes; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + int n, i; + + n = 0; + pboxes = pixman_region32_rectangles (®ion->rgn, &n); + if (n == 0) { + *nboxes = 0; + return CAIRO_STATUS_SUCCESS; + } + + if (n > *nboxes) { + *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); + if (*boxes == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto done; + } + } + + for (i = 0; i < n; i++) { + (*boxes)[i].x1 = pboxes[i].x1; + (*boxes)[i].y1 = pboxes[i].y1; + (*boxes)[i].x2 = pboxes[i].x2; + (*boxes)[i].y2 = pboxes[i].y2; + } + + *nboxes = n; +done: + return status; +} + +static cairo_status_t +_cairo_glitz_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + if (region == surface->clip_region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + if (region != NULL) { + cairo_status_t status; + + status = _cairo_glitz_get_boxes_from_region (region, + &surface->clip_boxes, + &surface->num_clip_boxes); + if (status) + return status; + + glitz_surface_set_clip_region (surface->surface, + 0, 0, + surface->clip_boxes, + surface->num_clip_boxes); + surface->has_clip = TRUE; + } else { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); + surface->has_clip = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_glitz_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_glitz_surface_attributes_t src_attr, mask_attr; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask; + cairo_int_status_t status; + + if (! _is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_surface_set_clip_region (dst, clip_region); + if (status) + return status; + + status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, + dst, + src_x, src_y, + mask_x, mask_y, + width, height, + &src, &mask, + &src_attr, &mask_attr); + if (status) + return status; + + _cairo_glitz_surface_set_attributes (src, &src_attr); + if (mask) + { + _cairo_glitz_surface_set_attributes (mask, &mask_attr); + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + mask_x + mask_attr.base.x_offset, + mask_y + mask_attr.base.y_offset, + dst_x, dst_y, + width, height); + } + else + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (status == CAIRO_STATUS_SUCCESS && + ! _cairo_operator_bounded_by_source (op)) + { + int src_width, src_height; + int mask_width, mask_height; + + src_width = glitz_surface_get_width (src->surface); + src_height = glitz_surface_get_height (src->surface); + if (mask) + { + mask_width = glitz_surface_get_width (mask->surface); + mask_height = glitz_surface_get_height (mask->surface); + } + else + { + mask_width = 0; + mask_height = 0; + } + status = _cairo_surface_composite_fixup_unbounded (&dst->base, + &src_attr.base, + src_width, src_height, + mask ? &mask_attr.base : NULL, + mask_width, mask_height, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, width, height, + clip_region); + } + + if (mask) + { + if (mask_attr.n_params) + free (mask_attr.params); + + _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); + } + + if (src_attr.n_params) + free (src_attr.params); + + _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_glitz_surface_fill_rectangles (void *abstract_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int n_rects) +{ + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; + glitz_rectangle_t *glitz_rects = stack_rects; + glitz_rectangle_t *current_rect; + cairo_status_t status; + int i; + + if (! _is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_surface_set_clip_region (dst, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + + if (n_rects > ARRAY_LENGTH (stack_rects)) { + glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); + if (glitz_rects == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_rects; i++) { + glitz_rects[i].x = rects[i].x; + glitz_rects[i].y = rects[i].y; + glitz_rects[i].width = rects[i].width; + glitz_rects[i].height = rects[i].height; + } + + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: { + glitz_color_t glitz_color; + glitz_format_t *format; + + glitz_color.red = color->red_short; + glitz_color.green = color->green_short; + glitz_color.blue = color->blue_short; + glitz_color.alpha = color->alpha_short; + + /* + * XXX even if the dst surface don't have an alpha channel, the + * above alpha still effect the dst surface because the + * underlying glitz drawable may have an alpha channel. So + * replacing the color with an opaque one is needed. + */ + format = glitz_surface_get_format (dst->surface); + if (format->color.alpha_size == 0) + glitz_color.alpha = 0xffff; + + glitz_set_rectangles (dst->surface, &glitz_color, + glitz_rects, n_rects); + } break; + case CAIRO_OPERATOR_SATURATE: + return CAIRO_INT_STATUS_UNSUPPORTED; + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + default: + if (_glitz_ensure_target (dst->surface)) + { + if (glitz_rects != stack_rects) + free (glitz_rects); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + src = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_solid (&dst->base, + CAIRO_CONTENT_COLOR_ALPHA, + 1, 1, + (cairo_color_t *) color, + FALSE); + if (src == NULL || src->base.status) { + if (glitz_rects != stack_rects) + free (glitz_rects); + return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED; + } + + glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); + + current_rect = glitz_rects; + while (n_rects--) + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + 0, 0, + 0, 0, + current_rect->x, current_rect->y, + current_rect->width, current_rect->height); + current_rect++; + } + + cairo_surface_destroy (&src->base); + break; + } + + if (glitz_rects != stack_rects) + free (glitz_rects); + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int n_traps, + cairo_region_t *clip_region) +{ + cairo_glitz_surface_attributes_t attributes; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask = NULL; + glitz_buffer_t *buffer = NULL; + void *data = NULL; + cairo_int_status_t status; + unsigned short alpha; + pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; + pixman_trapezoid_t *pixman_traps = stack_traps; + int i; + + if (antialias != CAIRO_ANTIALIAS_DEFAULT && + antialias != CAIRO_ANTIALIAS_GRAY) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! _is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + + /* Convert traps to pixman traps */ + if (n_traps > ARRAY_LENGTH (stack_traps)) { + pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); + if (pixman_traps == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_traps; i++) { + pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); + pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); + pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); + pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); + pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); + pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); + pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); + pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); + pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); + pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); + } + + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); + if (status) + goto FAIL; + + alpha = 0xffff; + + if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) { + static const glitz_color_t clear_black = { 0, 0, 0, 0 }; + glitz_color_t color; + glitz_geometry_format_t format; + int n_trap_added; + int offset = 0; + int data_size = 0; + int size = 30 * n_traps; /* just a guess */ + + format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; + format.vertex.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); + format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; + format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; + format.vertex.mask.offset = 2 * sizeof (glitz_float_t); + + mask = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_CONTENT_ALPHA, + 2, 1); + if (mask == NULL) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + if (mask->base.status) { + status = mask->base.status; + goto FAIL; + } + + color.red = color.green = color.blue = color.alpha = 0xffff; + + glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); + glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); + + glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); + glitz_surface_set_filter (mask->surface, + GLITZ_FILTER_BILINEAR, + NULL, 0); + + size *= format.vertex.bytes_per_vertex; + + while (n_traps) { + if (data_size < size) { + void *p; + + data_size = size; + p = realloc (data, data_size); + if (p == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + data = p; + + if (buffer) + glitz_buffer_destroy (buffer); + + buffer = glitz_buffer_create_for_data (data); + if (buffer == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + free (data); + goto FAIL; + } + } + + offset += + glitz_add_trapezoids (buffer, + offset, size - offset, + format.vertex.type, mask->surface, + (glitz_trapezoid_t *) pixman_traps, n_traps, + &n_trap_added); + + n_traps -= n_trap_added; + traps += n_trap_added; + size *= 2; + } + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_VERTEX, + &format, buffer); + glitz_set_array (dst->surface, 0, 3, + offset / format.vertex.bytes_per_vertex, + 0, 0); + } else { + cairo_image_surface_t *image; + unsigned char *ptr; + int stride; + + stride = (width + 3) & -4; + data = calloc (stride, height); + if (data == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + /* using negative stride */ + ptr = (unsigned char *) data + stride * (height - 1); + + image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (ptr, + CAIRO_FORMAT_A8, + width, height, + -stride); + status = image->base.status; + if (status) { + free (data); + goto FAIL; + } + + pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, + n_traps, (pixman_trapezoid_t *) pixman_traps); + + mask = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_CONTENT_ALPHA, + width, height); + status = mask->base.status; + if (status) { + free (data); + cairo_surface_destroy (&image->base); + goto FAIL; + } + + status = _cairo_glitz_surface_set_image (mask, image, + 0, 0, width, height, 0, 0); + + cairo_surface_destroy (&image->base); + + if (status) + goto FAIL; + } + + _cairo_glitz_surface_set_attributes (src, &attributes); + + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + attributes.base.x_offset, + src_y + attributes.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + + if (attributes.n_params) + free (attributes.params); + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_NONE, + NULL, NULL); + + if (buffer) + glitz_buffer_destroy (buffer); + + free (data); + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + if (! _cairo_operator_bounded_by_mask (op)) { + status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, + &attributes.base, + glitz_surface_get_width (src->surface), + glitz_surface_get_height (src->surface), + width, height, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height, + clip_region); + } + +FAIL: + _cairo_glitz_pattern_release_surface (pattern, src, &attributes); + + if (mask != NULL) + cairo_surface_destroy (&mask->base); + + if (pixman_traps != stack_traps) + free (pixman_traps); + + return status; +} + +static cairo_bool_t +_cairo_glitz_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = glitz_surface_get_width (surface->surface); + rectangle->height = glitz_surface_get_height (surface->surface); + + return TRUE; +} + +#define CAIRO_GLITZ_AREA_AVAILABLE 0 +#define CAIRO_GLITZ_AREA_DIVIDED 1 +#define CAIRO_GLITZ_AREA_OCCUPIED 2 + +typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t; + +typedef struct _cairo_glitz_area { + int state; + int level; + int x, y; + int width, height; + struct _cairo_glitz_area *area[4]; + cairo_glitz_root_area_t *root; + void *closure; +} cairo_glitz_area_t; + +static cairo_glitz_area_t _empty_area = { + 0, 0, 0, 0, 0, 0, + { NULL, NULL, NULL, NULL }, + NULL, + NULL +}; + +typedef struct _cairo_glitz_area_funcs { + cairo_status_t (*move_in) (cairo_glitz_area_t *area, + void *closure); + + void (*move_out) (cairo_glitz_area_t *area, + void *closure); + + int (*compare_score) (cairo_glitz_area_t *area, + void *closure1, + void *closure2); +} cairo_glitz_area_funcs_t; + +struct _cairo_glitz_root_area { + int max_level; + int width, height; + cairo_glitz_area_t *area; + const cairo_glitz_area_funcs_t *funcs; +}; + +static cairo_status_t +_cairo_glitz_area_move_in (cairo_glitz_area_t *area, + void *closure) +{ + area->closure = closure; + area->state = CAIRO_GLITZ_AREA_OCCUPIED; + + return (*area->root->funcs->move_in) (area, area->closure); +} + +static void +_cairo_glitz_area_move_out (cairo_glitz_area_t *area) +{ + if (area->root) + { + (*area->root->funcs->move_out) (area, area->closure); + + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; + } +} + +static cairo_glitz_area_t * +_cairo_glitz_area_create (cairo_glitz_root_area_t *root, + int level, + int x, + int y, + int width, + int height) +{ + cairo_glitz_area_t *area; + int n = 4; + + area = malloc (sizeof (cairo_glitz_area_t)); + if (!area) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + area->level = level; + area->x = x; + area->y = y; + area->width = width; + area->height = height; + area->root = root; + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; + + while (n--) + area->area[n] = NULL; + + return area; +} + +static void +_cairo_glitz_area_destroy (cairo_glitz_area_t *area) +{ + if (area == NULL) + return; + + if (area->state == CAIRO_GLITZ_AREA_OCCUPIED) + { + _cairo_glitz_area_move_out (area); + } + else + { + int n = 4; + + while (n--) + _cairo_glitz_area_destroy (area->area[n]); + } + + free (area); +} + +static cairo_glitz_area_t * +_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area) +{ + if (!area) + return NULL; + + switch (area->state) { + case CAIRO_GLITZ_AREA_OCCUPIED: + return area; + case CAIRO_GLITZ_AREA_AVAILABLE: + break; + case CAIRO_GLITZ_AREA_DIVIDED: { + cairo_glitz_area_t *tmp, *top = NULL; + int i; + + for (i = 0; i < 4; i++) + { + tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]); + if (tmp && top) + { + if ((*area->root->funcs->compare_score) (tmp, + tmp->closure, + top->closure) > 0) + top = tmp; + } + else if (tmp) + { + top = tmp; + } + } + return top; + } + } + + return NULL; +} + +static cairo_int_status_t +_cairo_glitz_area_find (cairo_glitz_area_t *area, + int width, + int height, + cairo_bool_t kick_out, + void *closure) +{ + cairo_status_t status; + + if (area->width < width || area->height < height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + switch (area->state) { + case CAIRO_GLITZ_AREA_OCCUPIED: + if (kick_out) + { + if ((*area->root->funcs->compare_score) (area, + area->closure, + closure) >= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_glitz_area_move_out (area); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* fall-through */ + case CAIRO_GLITZ_AREA_AVAILABLE: { + if (area->level == area->root->max_level || + (area->width == width && area->height == height)) + { + return _cairo_glitz_area_move_in (area, closure); + } + else + { + int dx[4], dy[4], w[4], h[4], i; + + dx[0] = dx[2] = dy[0] = dy[1] = 0; + + w[0] = w[2] = dx[1] = dx[3] = width; + h[0] = h[1] = dy[2] = dy[3] = height; + + w[1] = w[3] = area->width - width; + h[2] = h[3] = area->height - height; + + for (i = 0; i < 2; i++) + { + if (w[i]) + area->area[i] = + _cairo_glitz_area_create (area->root, + area->level + 1, + area->x + dx[i], + area->y + dy[i], + w[i], h[i]); + } + + for (; i < 4; i++) + { + if (w[i] && h[i]) + area->area[i] = + _cairo_glitz_area_create (area->root, + area->level + 1, + area->x + dx[i], + area->y + dy[i], + w[i], h[i]); + } + + area->state = CAIRO_GLITZ_AREA_DIVIDED; + + status = _cairo_glitz_area_find (area->area[0], + width, height, + kick_out, closure); + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + } + } break; + case CAIRO_GLITZ_AREA_DIVIDED: { + cairo_glitz_area_t *to_area; + int i, rejected = FALSE; + + for (i = 0; i < 4; i++) + { + if (area->area[i]) + { + if (area->area[i]->width >= width && + area->area[i]->height >= height) + { + status = _cairo_glitz_area_find (area->area[i], + width, height, + kick_out, closure); + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + rejected = TRUE; + } + } + } + + if (rejected) + return CAIRO_INT_STATUS_UNSUPPORTED; + + to_area = _cairo_glitz_area_get_top_scored_sub_area (area); + if (to_area) + { + if (kick_out) + { + if ((*area->root->funcs->compare_score) (to_area, + to_area->closure, + closure) >= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + for (i = 0; i < 4; i++) + { + _cairo_glitz_area_destroy (area->area[i]); + area->area[i] = NULL; + } + + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; + + status = _cairo_glitz_area_find (area, width, height, + TRUE, closure); + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + } break; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root, + int max_level, + int width, + int height, + const cairo_glitz_area_funcs_t *funcs) +{ + root->max_level = max_level; + root->funcs = funcs; + + root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height); + if (!root->area) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root) +{ + _cairo_glitz_area_destroy (root->area); +} + +typedef struct _cairo_glitz_surface_font_private { + cairo_glitz_root_area_t root; + glitz_surface_t *surface; +} cairo_glitz_surface_font_private_t; + +typedef struct _cairo_glitz_surface_glyph_private { + cairo_glitz_area_t *area; + cairo_bool_t locked; + cairo_point_double_t p1, p2; +} cairo_glitz_surface_glyph_private_t; + +static cairo_status_t +_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area, + void *closure) +{ + cairo_glitz_surface_glyph_private_t *glyph_private = closure; + + glyph_private->area = area; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area, + void *closure) +{ + cairo_glitz_surface_glyph_private_t *glyph_private = closure; + + glyph_private->area = NULL; +} + +static int +_cairo_glitz_glyph_compare (cairo_glitz_area_t *area, + void *closure1, + void *closure2) +{ + cairo_glitz_surface_glyph_private_t *glyph_private = closure1; + + if (glyph_private->locked) + return 1; + + return -1; +} + +static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = { + _cairo_glitz_glyph_move_in, + _cairo_glitz_glyph_move_out, + _cairo_glitz_glyph_compare +}; + +#define GLYPH_CACHE_TEXTURE_SIZE 512 +#define GLYPH_CACHE_MAX_LEVEL 64 +#define GLYPH_CACHE_MAX_HEIGHT 96 +#define GLYPH_CACHE_MAX_WIDTH 96 + +#define WRITE_VEC2(ptr, _x, _y) \ + *(ptr)++ = (_x); \ + *(ptr)++ = (_y) + +#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \ + WRITE_VEC2 (ptr, _vx1, _vy1); \ + WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \ + WRITE_VEC2 (ptr, _vx2, _vy1); \ + WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \ + WRITE_VEC2 (ptr, _vx2, _vy2); \ + WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \ + WRITE_VEC2 (ptr, _vx1, _vy2); \ + WRITE_VEC2 (ptr, (p1)->x, (p1)->y) + +static cairo_status_t +_cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_format_t format) +{ + cairo_glitz_surface_font_private_t *font_private; + glitz_drawable_t *drawable; + glitz_format_t *surface_format = NULL; + cairo_int_status_t status; + + drawable = glitz_surface_get_drawable (surface->surface); + + switch (format) { + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_A8: + surface_format = + glitz_find_standard_format (drawable, GLITZ_STANDARD_A8); + break; + case CAIRO_FORMAT_RGB24: + ASSERT_NOT_REACHED; + break; + case CAIRO_FORMAT_ARGB32: + surface_format = + glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32); + default: + break; + } + + if (!surface_format) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font_private = malloc (sizeof (cairo_glitz_surface_font_private_t)); + if (!font_private) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->surface = glitz_surface_create (drawable, surface_format, + GLYPH_CACHE_TEXTURE_SIZE, + GLYPH_CACHE_TEXTURE_SIZE, + 0, NULL); + if (font_private->surface == NULL) + { + free (font_private); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (format == CAIRO_FORMAT_ARGB32) + glitz_surface_set_component_alpha (font_private->surface, 1); + + status = _cairo_glitz_root_area_init (&font_private->root, + GLYPH_CACHE_MAX_LEVEL, + GLYPH_CACHE_TEXTURE_SIZE, + GLYPH_CACHE_TEXTURE_SIZE, + &_cairo_glitz_area_funcs); + if (status != CAIRO_STATUS_SUCCESS) + { + glitz_surface_destroy (font_private->surface); + free (font_private); + + return status; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = _cairo_glitz_surface_get_backend (); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_glitz_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if (font_private) + { + _cairo_glitz_root_area_fini (&font_private->root); + glitz_surface_destroy (font_private->surface); + free (font_private); + } +} + +static void +_cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_glitz_surface_glyph_private_t *glyph_private; + + glyph_private = scaled_glyph->surface_private; + if (glyph_private) + { + if (glyph_private->area) + _cairo_glitz_area_move_out (glyph_private->area); + + free (glyph_private); + } +} + +#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536) + +static cairo_status_t +_cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_glitz_surface_font_private_t *font_private; + cairo_glitz_surface_glyph_private_t *glyph_private; + glitz_point_fixed_t p1, p2; + glitz_pixel_format_t pf; + glitz_buffer_t *buffer; + cairo_format_masks_t masks; + cairo_int_status_t status; + + glyph_private = scaled_glyph->surface_private; + if (glyph_private == NULL) + { + glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t)); + if (!glyph_private) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + glyph_private->area = NULL; + glyph_private->locked = FALSE; + + scaled_glyph->surface_private = (void *) glyph_private; + } + + if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH || + glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (scaled_font->surface_private == NULL) + { + status = _cairo_glitz_surface_font_init (surface, scaled_font, + glyph_surface->format); + if (status) + return status; + } + + font_private = scaled_font->surface_private; + + if (glyph_surface->width == 0 || glyph_surface->height == 0) + { + glyph_private->area = &_empty_area; + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_glitz_area_find (font_private->root.area, + glyph_surface->width, + glyph_surface->height, + FALSE, glyph_private)) + { + if (_cairo_glitz_area_find (font_private->root.area, + glyph_surface->width, + glyph_surface->height, + TRUE, glyph_private)) + return CAIRO_STATUS_SUCCESS; + } + + buffer = glitz_buffer_create_for_data (glyph_surface->data); + if (!buffer) + { + _cairo_glitz_area_move_out (glyph_private->area); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _pixman_format_to_masks (glyph_surface->pixman_format, &masks); + + pf.fourcc = GLITZ_FOURCC_RGB; + pf.masks.bpp = masks.bpp; + pf.masks.alpha_mask = masks.alpha_mask; + pf.masks.red_mask = masks.red_mask; + pf.masks.green_mask = masks.green_mask; + pf.masks.blue_mask = masks.blue_mask; + + pf.bytes_per_line = glyph_surface->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + pf.xoffset = 0; + pf.skip_lines = 0; + + glitz_set_pixels (font_private->surface, + glyph_private->area->x, + glyph_private->area->y, + glyph_surface->width, + glyph_surface->height, + &pf, buffer); + + glitz_buffer_destroy (buffer); + + p1.x = glyph_private->area->x << 16; + p1.y = glyph_private->area->y << 16; + p2.x = (glyph_private->area->x + glyph_surface->width) << 16; + p2.y = (glyph_private->area->y + glyph_surface->height) << 16; + + glitz_surface_translate_point (font_private->surface, &p1, &p1); + glitz_surface_translate_point (font_private->surface, &p2, &p2); + + glyph_private->p1.x = FIXED_TO_FLOAT (p1.x); + glyph_private->p1.y = FIXED_TO_FLOAT (p1.y); + glyph_private->p2.x = FIXED_TO_FLOAT (p2.x); + glyph_private->p2.y = FIXED_TO_FLOAT (p2.y); + + return CAIRO_STATUS_SUCCESS; +} + +#define N_STACK_BUF 256 + +static cairo_int_status_t +_cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_surface, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region) +{ + cairo_glitz_surface_attributes_t attributes; + cairo_glitz_surface_glyph_private_t *glyph_private; + cairo_glitz_surface_t *dst = abstract_surface; + cairo_glitz_surface_t *src; + cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF]; + cairo_scaled_glyph_t **scaled_glyphs; + glitz_float_t stack_vertices[N_STACK_BUF * 16]; + glitz_float_t *vertices; + glitz_buffer_t *buffer; + cairo_int_status_t status; + int x_offset, y_offset; + int i, cached_glyphs = 0; + int remaining_glyps = num_glyphs; + glitz_float_t x1, y1, x2, y2; + static const glitz_vertex_format_t format = { + GLITZ_PRIMITIVE_QUADS, + GLITZ_DATA_TYPE_FLOAT, + sizeof (glitz_float_t) * 4, + GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK, + { 0 }, + { + GLITZ_DATA_TYPE_FLOAT, + GLITZ_COORDINATE_SIZE_XY, + sizeof (glitz_float_t) * 2, + } + }; + + if (scaled_font->surface_backend != NULL && + scaled_font->surface_backend != _cairo_glitz_surface_get_backend ()) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX Unbounded operators are not handled correctly */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_surface_set_clip_region (dst, NULL); + if (unlikely (status)) + return status; + + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); + if (status) + return status; + + _cairo_glitz_surface_set_attributes (src, &attributes); + + if (num_glyphs > N_STACK_BUF) + { + char *data; + size_t size1, size2; + + if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) || + (size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) || + ((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16) + goto FAIL1; + + size1 = num_glyphs * sizeof(void *); + size2 = num_glyphs * sizeof(glitz_float_t) * 16; + if (size1 >= INT32_MAX - size2) + goto FAIL1; + + data = malloc (size1 + size2); + if (!data) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + scaled_glyphs = (cairo_scaled_glyph_t **) data; + vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *)); + } + else + { + scaled_glyphs = stack_scaled_glyphs; + vertices = stack_vertices; + } + + buffer = glitz_buffer_create_for_data (vertices); + if (!buffer) + goto FAIL2; + + _cairo_scaled_font_freeze_cache (scaled_font); + + for (i = 0; i < num_glyphs; i++) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyphs[i]); + if (status != CAIRO_STATUS_SUCCESS) + { + num_glyphs = i; + goto UNLOCK; + } + + glyph_private = scaled_glyphs[i]->surface_private; + if (!glyph_private || !glyph_private->area) + { + status = _cairo_glitz_surface_add_glyph (dst, + scaled_font, + scaled_glyphs[i]); + if (status != CAIRO_STATUS_SUCCESS) { + num_glyphs = i; + goto UNLOCK; + } + } + glyph_private = scaled_glyphs[i]->surface_private; + if (glyph_private && glyph_private->area) + { + remaining_glyps--; + + if (glyph_private->area->width) + { + x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; + y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; + + x1 = _cairo_lround (glyphs[i].x - x_offset); + y1 = _cairo_lround (glyphs[i].y - y_offset); + x2 = x1 + glyph_private->area->width; + y2 = y1 + glyph_private->area->height; + + WRITE_BOX (vertices, x1, y1, x2, y2, + &glyph_private->p1, &glyph_private->p2); + + glyph_private->locked = TRUE; + + cached_glyphs++; + } + } + } + + if (remaining_glyps) + { + cairo_surface_t *image; + cairo_glitz_surface_t *clone; + + for (i = 0; i < num_glyphs; i++) + { + glyph_private = scaled_glyphs[i]->surface_private; + if (!glyph_private || !glyph_private->area) + { + int glyph_width, glyph_height; + int clone_offset_x, clone_offset_y; + + image = &scaled_glyphs[i]->surface->base; + glyph_width = scaled_glyphs[i]->surface->width; + glyph_height = scaled_glyphs[i]->surface->height; + status = + _cairo_glitz_surface_clone_similar (abstract_surface, + image, + 0, + 0, + glyph_width, + glyph_height, + &clone_offset_x, + &clone_offset_y, + (cairo_surface_t **) + &clone); + if (status) + goto UNLOCK; + + assert (clone_offset_x == 0); + assert (clone_offset_y == 0); + + x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; + y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; + x1 = _cairo_lround (glyphs[i].x - x_offset); + y1 = _cairo_lround (glyphs[i].y - y_offset); + + glitz_composite (_glitz_operator (op), + src->surface, + clone->surface, + dst->surface, + src_x + attributes.base.x_offset + x1, + src_y + attributes.base.y_offset + y1, + 0, 0, + x1, y1, + glyph_width, + glyph_height); + + cairo_surface_destroy (&clone->base); + + if (glitz_surface_get_status (dst->surface) == + GLITZ_STATUS_NOT_SUPPORTED) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto UNLOCK; + } + } + } + } + + if (cached_glyphs) + { + cairo_glitz_surface_font_private_t *font_private; + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_VERTEX, + (glitz_geometry_format_t *) &format, + buffer); + + glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0); + + font_private = scaled_font->surface_private; + + glitz_composite (_glitz_operator (op), + src->surface, + font_private->surface, + dst->surface, + src_x + attributes.base.x_offset, + src_y + attributes.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_NONE, + NULL, NULL); + } + +UNLOCK: + if (cached_glyphs) + { + for (i = 0; i < num_glyphs; i++) + { + glyph_private = scaled_glyphs[i]->surface_private; + if (glyph_private) + glyph_private->locked = FALSE; + } + } + + _cairo_scaled_font_thaw_cache (scaled_font); + + glitz_buffer_destroy (buffer); + + FAIL2: + if (num_glyphs > N_STACK_BUF) + free (scaled_glyphs); + + FAIL1: + if (attributes.n_params) + free (attributes.params); + + _cairo_glitz_pattern_release_surface (pattern, src, &attributes); + + if (status) + return status; + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_glitz_surface_flush (void *abstract_surface) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + glitz_surface_flush (surface->surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_glitz_surface_is_similar (void *surface_a, + void *surface_b, + cairo_content_t content) +{ + cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a; + cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b; + + glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface); + glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface); + + /* XXX Disable caching of glitz surfaces by the solid pattern cache. + * Until glitz has a mechanism for releasing resources on connection + * closure, we will attempt to access invalid pointers when evicting + * old surfaces from the solid pattern cache. + */ + return FALSE; + + return drawable_a == drawable_b; +} + +static const cairo_surface_backend_t cairo_glitz_surface_backend = { + CAIRO_SURFACE_TYPE_GLITZ, + _cairo_glitz_surface_create_similar, + _cairo_glitz_surface_finish, + _cairo_glitz_surface_acquire_source_image, + _cairo_glitz_surface_release_source_image, + + _cairo_glitz_surface_acquire_dest_image, + _cairo_glitz_surface_release_dest_image, + _cairo_glitz_surface_clone_similar, + _cairo_glitz_surface_composite, + _cairo_glitz_surface_fill_rectangles, + _cairo_glitz_surface_composite_trapezoids, + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_glitz_surface_get_extents, + _cairo_glitz_surface_old_show_glyphs, + NULL, /* get_font_options */ + _cairo_glitz_surface_flush, + NULL, /* mark_dirty_rectangle */ + _cairo_glitz_surface_scaled_font_fini, + _cairo_glitz_surface_scaled_glyph_fini, + + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* show_glyphs */ + + _cairo_glitz_surface_snapshot, + _cairo_glitz_surface_is_similar, +}; + +static const cairo_surface_backend_t * +_cairo_glitz_surface_get_backend (void) +{ + return &cairo_glitz_surface_backend; +} + +static cairo_content_t +_glitz_format_to_content (glitz_format_t * format) +{ + assert (format->color.fourcc == GLITZ_FOURCC_RGB); + + if (format->color.alpha_size != 0) { + if (format->color.red_size != 0 && + format->color.green_size != 0 && + format->color.blue_size != 0) + return CAIRO_CONTENT_COLOR_ALPHA; + else + return CAIRO_CONTENT_ALPHA; + } + return CAIRO_CONTENT_COLOR; +} + +cairo_surface_t * +cairo_glitz_surface_create (glitz_surface_t *surface) +{ + cairo_glitz_surface_t *crsurface; + glitz_format_t *format; + + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + crsurface = malloc (sizeof (cairo_glitz_surface_t)); + if (crsurface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + format = glitz_surface_get_format (surface); + _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend, + _glitz_format_to_content (format)); + + glitz_surface_reference (surface); + + crsurface->surface = surface; + crsurface->format = format; + + crsurface->has_clip = FALSE; + crsurface->clip_boxes = NULL; + crsurface->num_clip_boxes = 0; + crsurface->clip_region = NULL; + + return &crsurface->base; +} +slim_hidden_def (cairo_glitz_surface_create); diff --git a/libs/cairo/src/cairo-glitz.h b/libs/cairo/src/cairo-glitz.h new file mode 100644 index 000000000..b74e887bc --- /dev/null +++ b/libs/cairo/src/cairo-glitz.h @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_GLITZ_H +#define CAIRO_GLITZ_H + +#include "cairo.h" + +#if CAIRO_HAS_GLITZ_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_glitz_surface_create (glitz_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_GLITZ_SURFACE */ +# error Cairo was not compiled with support for the glitz backend +#endif /* CAIRO_HAS_GLITZ_SURFACE */ + +#endif /* CAIRO_GLITZ_H */ diff --git a/libs/cairo/src/cairo-glx-context.c b/libs/cairo/src/cairo-glx-context.c new file mode 100644 index 000000000..1ceab6cc3 --- /dev/null +++ b/libs/cairo/src/cairo-glx-context.c @@ -0,0 +1,226 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include "cairo-error-private.h" + +#include + +/* XXX needs hooking into XCloseDisplay() */ + +typedef struct _cairo_glx_context { + cairo_gl_context_t base; + + Display *display; + Window dummy_window; + GLXContext context; +} cairo_glx_context_t; + +typedef struct _cairo_glx_surface { + cairo_gl_surface_t base; + + Window win; +} cairo_glx_surface_t; + +static void +_glx_acquire (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + GLXDrawable current_drawable; + + if (ctx->base.current_target == NULL || + _cairo_gl_surface_is_texture (ctx->base.current_target)) { + current_drawable = ctx->dummy_window; + } else { + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) ctx->base.current_target; + current_drawable = surface->win; + } + + glXMakeCurrent (ctx->display, current_drawable, ctx->context); +} + +static void +_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + /* Set the window as the target of our context. */ + glXMakeCurrent (ctx->display, surface->win, ctx->context); +} + +static void +_glx_release (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + + glXMakeCurrent (ctx->display, None, None); +} + +static void +_glx_swap_buffers (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + glXSwapBuffers (ctx->display, surface->win); +} + +static void +_glx_destroy (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + + if (ctx->dummy_window != None) + XDestroyWindow (ctx->display, ctx->dummy_window); + + glXMakeCurrent (ctx->display, 0, 0); +} + +static cairo_status_t +_glx_dummy_ctx (Display *dpy, GLXContext gl_ctx, Window *dummy) +{ + int attr[3] = { GLX_FBCONFIG_ID, 0, None }; + GLXFBConfig *config; + XVisualInfo *vi; + Colormap cmap; + XSetWindowAttributes swa; + Window win = None; + int cnt; + + /* Create a dummy window created for the target GLX context that we can + * use to query the available GL/GLX extensions. + */ + glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); + + cnt = 0; + config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); + if (unlikely (cnt == 0)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + vi = glXGetVisualFromFBConfig (dpy, config[0]); + XFree (config); + + if (unlikely (vi == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + cmap = XCreateColormap (dpy, + RootWindow (dpy, vi->screen), + vi->visual, + AllocNone); + swa.colormap = cmap; + swa.border_pixel = 0; + win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), + -1, -1, 1, 1, 0, + vi->depth, + InputOutput, + vi->visual, + CWBorderPixel | CWColormap, &swa); + XFreeColormap (dpy, cmap); + XFree (vi); + + XFlush (dpy); + if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { + XDestroyWindow (dpy, win); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *dummy = win; + return CAIRO_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) +{ + cairo_glx_context_t *ctx; + cairo_status_t status; + Window dummy = None; + + status = _glx_dummy_ctx (dpy, gl_ctx, &dummy); + if (unlikely (status)) + return _cairo_gl_context_create_in_error (status); + + ctx = calloc (1, sizeof (cairo_glx_context_t)); + if (unlikely (ctx == NULL)) + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + + ctx->display = dpy; + ctx->dummy_window = dummy; + ctx->context = gl_ctx; + + ctx->base.acquire = _glx_acquire; + ctx->base.release = _glx_release; + ctx->base.make_current = _glx_make_current; + ctx->base.swap_buffers = _glx_swap_buffers; + ctx->base.destroy = _glx_destroy; + + status = _cairo_gl_context_init (&ctx->base); + if (unlikely (status)) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + ctx->base.release (ctx); + + return &ctx->base.base; +} + +Display * +cairo_glx_device_get_display (cairo_device_t *device) +{ + cairo_glx_context_t *ctx; + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { + _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return NULL; + } + + ctx = (cairo_glx_context_t *) device; + + return ctx->display; +} + +GLXContext +cairo_glx_device_get_context (cairo_device_t *device) +{ + cairo_glx_context_t *ctx; + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { + _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return NULL; + } + + ctx = (cairo_glx_context_t *) device; + + return ctx->context; +} + +cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_device_t *device, + Window win, + int width, + int height) +{ + cairo_glx_surface_t *surface; + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + surface = calloc (1, sizeof (cairo_glx_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (device, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, width, height); + surface->win = win; + + return &surface->base.base; +} diff --git a/libs/cairo/src/cairo-gstate-private.h b/libs/cairo/src/cairo-gstate-private.h new file mode 100644 index 000000000..e8127d770 --- /dev/null +++ b/libs/cairo/src/cairo-gstate-private.h @@ -0,0 +1,354 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_GSTATE_PRIVATE_H +#define CAIRO_GSTATE_PRIVATE_H + +#include "cairo-clip-private.h" + +struct _cairo_gstate { + cairo_operator_t op; + + double tolerance; + cairo_antialias_t antialias; + + cairo_stroke_style_t stroke_style; + + cairo_fill_rule_t fill_rule; + + cairo_font_face_t *font_face; + cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ + cairo_scaled_font_t *previous_scaled_font; /* holdover */ + cairo_matrix_t font_matrix; + cairo_font_options_t font_options; + + cairo_clip_t clip; + + cairo_surface_t *target; /* The target to which all rendering is directed */ + cairo_surface_t *parent_target; /* The previous target which was receiving rendering */ + cairo_surface_t *original_target; /* The original target the initial gstate was created with */ + + /* the user is allowed to update the device after we have cached the matrices... */ + cairo_observer_t device_transform_observer; + + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */ + cairo_bool_t is_identity; + + cairo_pattern_t *source; + + struct _cairo_gstate *next; +}; + +/* cairo-gstate.c */ +cairo_private cairo_status_t +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target); + +cairo_private void +_cairo_gstate_fini (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist); + +cairo_private cairo_status_t +_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist); + +cairo_private cairo_bool_t +_cairo_gstate_is_redirected (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child); + +cairo_private cairo_surface_t * +_cairo_gstate_get_target (cairo_gstate_t *gstate); + +cairo_private cairo_surface_t * +_cairo_gstate_get_parent_target (cairo_gstate_t *gstate); + +cairo_private cairo_surface_t * +_cairo_gstate_get_original_target (cairo_gstate_t *gstate); + +cairo_private cairo_clip_t * +_cairo_gstate_get_clip (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source); + +cairo_private cairo_pattern_t * +_cairo_gstate_get_source (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op); + +cairo_private cairo_operator_t +_cairo_gstate_get_operator (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance); + +cairo_private double +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule); + +cairo_private cairo_fill_rule_t +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width); + +cairo_private double +_cairo_gstate_get_line_width (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); + +cairo_private cairo_line_cap_t +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join); + +cairo_private cairo_line_join_t +_cairo_gstate_get_line_join (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset); + +cairo_private void +_cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset); + +cairo_private cairo_status_t +_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit); + +cairo_private double +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate); + +cairo_private void +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty); + +cairo_private cairo_status_t +_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy); + +cairo_private cairo_status_t +_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle); + +cairo_private cairo_status_t +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private void +_cairo_gstate_identity_matrix (cairo_gstate_t *gstate); + +cairo_private void +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y); + +cairo_private void +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy); + +cairo_private void +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y); + +cairo_private void +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy); + +cairo_private void +_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); + +static inline void +_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _do_cairo_gstate_user_to_backend (gstate, x, y); +} + +cairo_private void +_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); + +static inline void +_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _do_cairo_gstate_backend_to_user (gstate, x, y); +} + +cairo_private void +_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight); + +cairo_private void +_cairo_gstate_path_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_paint (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask); + +cairo_private cairo_status_t +_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_copy_page (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_show_page (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret); + +cairo_private cairo_bool_t +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y); + +cairo_private cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y); + +cairo_private cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_reset_clip (cairo_gstate_t *gstate); + +cairo_private cairo_bool_t +_cairo_gstate_clip_extents (cairo_gstate_t *gstate, + double *x1, + double *y1, + double *x2, + double *y2); + +cairo_private cairo_rectangle_list_t* +_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_show_surface (cairo_gstate_t *gstate, + cairo_surface_t *surface, + double x, + double y, + double width, + double height); + +cairo_private cairo_status_t +_cairo_gstate_select_font_face (cairo_gstate_t *gstate, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +cairo_private cairo_status_t +_cairo_gstate_set_font_size (cairo_gstate_t *gstate, + double size); + +cairo_private void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private void +_cairo_gstate_get_font_options (cairo_gstate_t *gstate, + cairo_font_options_t *options); + +cairo_private void +_cairo_gstate_set_font_options (cairo_gstate_t *gstate, + const cairo_font_options_t *options); + +cairo_private cairo_status_t +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face); + +cairo_private cairo_status_t +_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, + cairo_scaled_font_t **scaled_font); + +cairo_private cairo_status_t +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents); + +cairo_private cairo_status_t +_cairo_gstate_set_font_face (cairo_gstate_t *gstate, + cairo_font_face_t *font_face); + +cairo_private cairo_status_t +_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + +cairo_private cairo_status_t +_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_private cairo_status_t +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags); + +cairo_private cairo_status_t +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias); + +cairo_private cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate); + +#endif /* CAIRO_GSTATE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-gstate.c b/libs/cairo/src/cairo-gstate.c new file mode 100644 index 000000000..6ba6f0b1d --- /dev/null +++ b/libs/cairo/src/cairo-gstate.c @@ -0,0 +1,2298 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-gstate-private.h" + +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + +static cairo_status_t +_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other); + +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); + +static cairo_status_t +_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); + +static void +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); + +static cairo_status_t +_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_glyph_t *transformed_glyphs, + int *num_transformed_glyphs, + cairo_text_cluster_t *transformed_clusters); + +static void +_cairo_gstate_update_device_transform (cairo_observer_t *observer, + void *arg) +{ + cairo_gstate_t *gstate = cairo_container_of (observer, + cairo_gstate_t, + device_transform_observer); + + gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) && + _cairo_matrix_is_identity (&gstate->target->device_transform)); +} + +cairo_status_t +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target) +{ + cairo_status_t status; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); + + gstate->next = NULL; + + gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT; + + gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + gstate->antialias = CAIRO_ANTIALIAS_DEFAULT; + + _cairo_stroke_style_init (&gstate->stroke_style); + + gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + + gstate->font_face = NULL; + gstate->scaled_font = NULL; + gstate->previous_scaled_font = NULL; + + cairo_matrix_init_scale (&gstate->font_matrix, + CAIRO_GSTATE_DEFAULT_FONT_SIZE, + CAIRO_GSTATE_DEFAULT_FONT_SIZE); + + _cairo_font_options_init_default (&gstate->font_options); + + _cairo_clip_init (&gstate->clip); + + gstate->target = cairo_surface_reference (target); + gstate->parent_target = NULL; + gstate->original_target = cairo_surface_reference (target); + + gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; + cairo_list_add (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); + cairo_matrix_init_identity (&gstate->ctm); + gstate->ctm_inverse = gstate->ctm; + gstate->source_ctm_inverse = gstate->ctm; + + gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base; + + /* Now that the gstate is fully initialized and ready for the eventual + * _cairo_gstate_fini(), we can check for errors (and not worry about + * the resource deallocation). */ + status = target->status; + if (unlikely (status)) + return status; + + status = gstate->source->status; + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_gstate_init_copy: + * + * Initialize @gstate by performing a deep copy of state fields from + * @other. Note that gstate->next is not copied but is set to %NULL by + * this function. + **/ +static cairo_status_t +_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) +{ + cairo_status_t status; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); + + gstate->op = other->op; + + gstate->tolerance = other->tolerance; + gstate->antialias = other->antialias; + + status = _cairo_stroke_style_init_copy (&gstate->stroke_style, + &other->stroke_style); + if (unlikely (status)) + return status; + + gstate->fill_rule = other->fill_rule; + + gstate->font_face = cairo_font_face_reference (other->font_face); + gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font); + gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font); + + gstate->font_matrix = other->font_matrix; + + _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); + + _cairo_clip_init_copy (&gstate->clip, &other->clip); + + gstate->target = cairo_surface_reference (other->target); + /* parent_target is always set to NULL; it's only ever set by redirect_target */ + gstate->parent_target = NULL; + gstate->original_target = cairo_surface_reference (other->original_target); + + gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; + cairo_list_add (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + gstate->is_identity = other->is_identity; + gstate->ctm = other->ctm; + gstate->ctm_inverse = other->ctm_inverse; + gstate->source_ctm_inverse = other->source_ctm_inverse; + + gstate->source = cairo_pattern_reference (other->source); + + gstate->next = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_fini (cairo_gstate_t *gstate) +{ + _cairo_stroke_style_fini (&gstate->stroke_style); + + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = NULL; + + cairo_scaled_font_destroy (gstate->previous_scaled_font); + gstate->previous_scaled_font = NULL; + + cairo_scaled_font_destroy (gstate->scaled_font); + gstate->scaled_font = NULL; + + _cairo_clip_reset (&gstate->clip); + + cairo_list_del (&gstate->device_transform_observer.link); + + cairo_surface_destroy (gstate->target); + gstate->target = NULL; + + cairo_surface_destroy (gstate->parent_target); + gstate->parent_target = NULL; + + cairo_surface_destroy (gstate->original_target); + gstate->original_target = NULL; + + cairo_pattern_destroy (gstate->source); + gstate->source = NULL; + + VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t))); +} + +/** + * _cairo_gstate_save: + * @gstate: input/output gstate pointer + * + * Makes a copy of the current state of @gstate and saves it + * to @gstate->next, then put the address of the newly allcated + * copy into @gstate. _cairo_gstate_restore() reverses this. + **/ +cairo_status_t +_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) +{ + cairo_gstate_t *top; + cairo_status_t status; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + top = *freelist; + if (top == NULL) { + top = malloc (sizeof (cairo_gstate_t)); + if (unlikely (top == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + *freelist = top->next; + + status = _cairo_gstate_init_copy (top, *gstate); + if (unlikely (status)) { + top->next = *freelist; + *freelist = top; + return status; + } + + top->next = *gstate; + *gstate = top; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_gstate_restore: + * @gstate: input/output gstate pointer + * + * Reverses the effects of one _cairo_gstate_save() call. + **/ +cairo_status_t +_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) +{ + cairo_gstate_t *top; + + top = *gstate; + if (top->next == NULL) + return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); + + *gstate = top->next; + + _cairo_gstate_fini (top); + VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *))); + top->next = *freelist; + *freelist = top; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_gstate_redirect_target: + * @gstate: a #cairo_gstate_t + * @child: the new child target + * + * Redirect @gstate rendering to a "child" target. The original + * "parent" target with which the gstate was created will not be + * affected. See _cairo_gstate_get_target(). + * + * Unless the redirected target has the same device offsets as the + * original #cairo_t target, the clip will be INVALID after this call, + * and the caller should either recreate or reset the clip. + **/ +cairo_status_t +_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) +{ + cairo_matrix_t matrix; + + /* If this gstate is already redirected, this is an error; we need a + * new gstate to be able to redirect */ + assert (gstate->parent_target == NULL); + + /* Set up our new parent_target based on our current target; + * gstate->parent_target will take the ref that is held by gstate->target + */ + cairo_surface_destroy (gstate->parent_target); + gstate->parent_target = gstate->target; + + /* Now set up our new target; we overwrite gstate->target directly, + * since its ref is now owned by gstate->parent_target */ + gstate->target = cairo_surface_reference (child); + gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform); + cairo_list_move (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + /* The clip is in surface backend coordinates for the previous target; + * translate it into the child's backend coordinates. */ + cairo_matrix_init_translate (&matrix, + child->device_transform.x0 - gstate->parent_target->device_transform.x0, + child->device_transform.y0 - gstate->parent_target->device_transform.y0); + _cairo_clip_reset (&gstate->clip); + return _cairo_clip_init_copy_transformed (&gstate->clip, + &gstate->next->clip, + &matrix); +} + +/** + * _cairo_gstate_is_redirected + * @gstate: a #cairo_gstate_t + * + * This space left intentionally blank. + * + * Return value: %TRUE if the gstate is redirected to a target + * different than the original, %FALSE otherwise. + **/ +cairo_bool_t +_cairo_gstate_is_redirected (cairo_gstate_t *gstate) +{ + return (gstate->target != gstate->original_target); +} + +/** + * _cairo_gstate_get_target: + * @gstate: a #cairo_gstate_t + * + * Return the current drawing target; if drawing is not redirected, + * this will be the same as _cairo_gstate_get_original_target(). + * + * Return value: the current target surface + **/ +cairo_surface_t * +_cairo_gstate_get_target (cairo_gstate_t *gstate) +{ + return gstate->target; +} + +/** + * _cairo_gstate_get_parent_target: + * @gstate: a #cairo_gstate_t + * + * Return the parent surface of the current drawing target surface; + * if this particular gstate isn't a redirect gstate, this will return %NULL. + **/ +cairo_surface_t * +_cairo_gstate_get_parent_target (cairo_gstate_t *gstate) +{ + return gstate->parent_target; +} + +/** + * _cairo_gstate_get_original_target: + * @gstate: a #cairo_gstate_t + * + * Return the original target with which @gstate was created. This + * function always returns the original target independent of any + * child target that may have been set with + * _cairo_gstate_redirect_target. + * + * Return value: the original target surface + **/ +cairo_surface_t * +_cairo_gstate_get_original_target (cairo_gstate_t *gstate) +{ + return gstate->original_target; +} + +/** + * _cairo_gstate_get_clip: + * @gstate: a #cairo_gstate_t + * + * This space left intentionally blank. + * + * Return value: a pointer to the gstate's #cairo_clip_t structure. + */ +cairo_clip_t * +_cairo_gstate_get_clip (cairo_gstate_t *gstate) +{ + return &gstate->clip; +} + +cairo_status_t +_cairo_gstate_set_source (cairo_gstate_t *gstate, + cairo_pattern_t *source) +{ + if (source->status) + return source->status; + + source = cairo_pattern_reference (source); + cairo_pattern_destroy (gstate->source); + gstate->source = source; + gstate->source_ctm_inverse = gstate->ctm_inverse; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_pattern_t * +_cairo_gstate_get_source (cairo_gstate_t *gstate) +{ + if (gstate->source == &_cairo_pattern_black.base) { + /* do not expose the static object to the user */ + gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + } + + return gstate->source; +} + +cairo_status_t +_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op) +{ + gstate->op = op; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_operator_t +_cairo_gstate_get_operator (cairo_gstate_t *gstate) +{ + return gstate->op; +} + +cairo_status_t +_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) +{ + gstate->tolerance = tolerance; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate) +{ + return gstate->tolerance; +} + +cairo_status_t +_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) +{ + gstate->fill_rule = fill_rule; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_fill_rule_t +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) +{ + return gstate->fill_rule; +} + +cairo_status_t +_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) +{ + gstate->stroke_style.line_width = width; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_line_width (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_width; +} + +cairo_status_t +_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) +{ + gstate->stroke_style.line_cap = line_cap; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_line_cap_t +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_cap; +} + +cairo_status_t +_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) +{ + gstate->stroke_style.line_join = line_join; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_line_join_t +_cairo_gstate_get_line_join (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_join; +} + +cairo_status_t +_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset) +{ + unsigned int i; + double dash_total; + + if (gstate->stroke_style.dash) + free (gstate->stroke_style.dash); + + gstate->stroke_style.num_dashes = num_dashes; + + if (gstate->stroke_style.num_dashes == 0) { + gstate->stroke_style.dash = NULL; + gstate->stroke_style.dash_offset = 0.0; + return CAIRO_STATUS_SUCCESS; + } + + gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double)); + if (unlikely (gstate->stroke_style.dash == NULL)) { + gstate->stroke_style.num_dashes = 0; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double)); + + dash_total = 0.0; + for (i = 0; i < gstate->stroke_style.num_dashes; i++) { + if (gstate->stroke_style.dash[i] < 0) + return _cairo_error (CAIRO_STATUS_INVALID_DASH); + + dash_total += gstate->stroke_style.dash[i]; + } + + if (dash_total == 0.0) + return _cairo_error (CAIRO_STATUS_INVALID_DASH); + + /* An odd dash value indicate symmetric repeating, so the total + * is twice as long. */ + if (gstate->stroke_style.num_dashes & 1) + dash_total *= 2; + + /* The dashing code doesn't like a negative offset or a big positive + * offset, so we compute an equivalent offset which is guaranteed to be + * positive and less than twice the pattern length. */ + offset = fmod (offset, dash_total); + if (offset < 0.0) + offset += dash_total; + if (offset <= 0.0) /* Take care of -0 */ + offset = 0.0; + gstate->stroke_style.dash_offset = offset; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_get_dash (cairo_gstate_t *gstate, + double *dashes, + int *num_dashes, + double *offset) +{ + if (dashes) { + memcpy (dashes, + gstate->stroke_style.dash, + sizeof (double) * gstate->stroke_style.num_dashes); + } + + if (num_dashes) + *num_dashes = gstate->stroke_style.num_dashes; + + if (offset) + *offset = gstate->stroke_style.dash_offset; +} + +cairo_status_t +_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) +{ + gstate->stroke_style.miter_limit = limit; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.miter_limit; +} + +void +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) +{ + *matrix = gstate->ctm; +} + +cairo_status_t +_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) +{ + cairo_matrix_t tmp; + + if (! ISFINITE (tx) || ! ISFINITE (ty)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_translate (&tmp, tx, ty); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_translate (&tmp, -tx, -ty); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) +{ + cairo_matrix_t tmp; + + if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + if (! ISFINITE (sx) || ! ISFINITE (sy)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_scale (&tmp, sx, sy); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) +{ + cairo_matrix_t tmp; + + if (angle == 0.) + return CAIRO_STATUS_SUCCESS; + + if (! ISFINITE (angle)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_rotate (&tmp, angle); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_rotate (&tmp, -angle); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + cairo_matrix_t tmp; + cairo_status_t status; + + if (! _cairo_matrix_is_invertible (matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (_cairo_matrix_is_identity (matrix)) + return CAIRO_STATUS_SUCCESS; + + tmp = *matrix; + status = cairo_matrix_invert (&tmp); + if (unlikely (status)) + return status; + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_matrix_is_invertible (matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (_cairo_matrix_is_identity (matrix)) { + _cairo_gstate_identity_matrix (gstate); + return CAIRO_STATUS_SUCCESS; + } + + _cairo_gstate_unset_scaled_font (gstate); + + gstate->ctm = *matrix; + gstate->ctm_inverse = *matrix; + status = cairo_matrix_invert (&gstate->ctm_inverse); + assert (status == CAIRO_STATUS_SUCCESS); + gstate->is_identity = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_identity_matrix (cairo_gstate_t *gstate) +{ + if (_cairo_matrix_is_identity (&gstate->ctm)) + return; + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_identity (&gstate->ctm); + cairo_matrix_init_identity (&gstate->ctm_inverse); + gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); +} + +void +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm, x, y); +} + +void +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, + double *dx, double *dy) +{ + cairo_matrix_transform_distance (&gstate->ctm, dx, dy); +} + +void +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); +} + +void +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, + double *dx, double *dy) +{ + cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); +} + +void +_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm, x, y); + cairo_matrix_transform_point (&gstate->target->device_transform, x, y); +} + +void +_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y); + cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); +} + +void +_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight) +{ + cairo_matrix_t matrix_inverse; + + cairo_matrix_multiply (&matrix_inverse, + &gstate->target->device_transform_inverse, + &gstate->ctm_inverse); + _cairo_matrix_transform_bounding_box (&matrix_inverse, + x1, y1, x2, y2, is_tight); +} + +/* XXX: NYI +cairo_status_t +_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) +{ + cairo_status_t status; + + _cairo_pen_init (&gstate); + return CAIRO_STATUS_SUCCESS; +} +*/ + +void +_cairo_gstate_path_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_box_t box; + double px1, py1, px2, py2; + + if (_cairo_path_fixed_extents (path, &box)) { + px1 = _cairo_fixed_to_double (box.p1.x); + py1 = _cairo_fixed_to_double (box.p1.y); + px2 = _cairo_fixed_to_double (box.p2.x); + py2 = _cairo_fixed_to_double (box.p2.y); + + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + } else { + px1 = 0.0; + py1 = 0.0; + px2 = 0.0; + py2 = 0.0; + } + + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; +} + +static void +_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original) +{ + /* First check if the we can replace the original with a much simpler + * pattern. For example, gradients that are uniform or just have a single + * stop can sometimes be replaced with a solid. + */ + + if (_cairo_pattern_is_clear (original)) { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + CAIRO_COLOR_TRANSPARENT); + return; + } + + if (original->type == CAIRO_PATTERN_TYPE_LINEAR || + original->type == CAIRO_PATTERN_TYPE_RADIAL) + { + cairo_color_t color; + if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original, + NULL, + &color)) + { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + &color); + return; + } + } + + _cairo_pattern_init_static_copy (pattern, original); +} + +static void +_cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_gstate_copy_pattern (pattern, original); + + /* apply device_transform first so that it is transformed by ctm_inverse */ + if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *surface; + + surface_pattern = (cairo_surface_pattern_t *) original; + surface = surface_pattern->surface; + + if (_cairo_surface_has_device_transform (surface)) + _cairo_pattern_transform (pattern, &surface->device_transform); + } + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); + + if (_cairo_surface_has_device_transform (gstate->target)) { + _cairo_pattern_transform (pattern, + &gstate->target->device_transform_inverse); + } +} + +static void +_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, + cairo_pattern_t *pattern) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + gstate->source, + &gstate->source_ctm_inverse); +} + +static void +_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + cairo_pattern_t *mask) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + mask, + &gstate->ctm_inverse); +} + +/* We need to take a copy of the clip so that the lower layers may modify it + * by, perhaps, intersecting it with the operation extents and other paths. + */ +#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip) + +static cairo_bool_t +_clipped (cairo_gstate_t *gstate) +{ + cairo_rectangle_int_t extents; + + if (gstate->clip.all_clipped) + return TRUE; + + /* XXX consider applying a surface clip? */ + + if (gstate->clip.path == NULL) + return FALSE; + + if (_cairo_surface_get_extents (gstate->target, &extents)) { + if (extents.width == 0 || extents.height == 0) + return TRUE; + + if (! _cairo_rectangle_intersect (&extents, + &gstate->clip.path->extents)) + { + return TRUE; + } + } + + /* perform a simple query to exclude trivial all-clipped cases */ + return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_operator_t +_reduce_op (cairo_gstate_t *gstate) +{ + cairo_operator_t op; + const cairo_pattern_t *pattern; + + op = gstate->op; + if (op != CAIRO_OPERATOR_SOURCE) + return op; + + pattern = gstate->source; + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + if (solid->color.alpha_short <= 0x00ff) { + op = CAIRO_OPERATOR_CLEAR; + } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) { + if ((solid->color.red_short | + solid->color.green_short | + solid->color.blue_short) <= 0x00ff) + { + op = CAIRO_OPERATOR_CLEAR; + } + } + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + if (surface->surface->is_clear && + surface->surface->content & CAIRO_CONTENT_ALPHA) + { + op = CAIRO_OPERATOR_CLEAR; + } + } else { + const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + if (gradient->n_stops == 0) + op = CAIRO_OPERATOR_CLEAR; + } + + return op; +} + +cairo_status_t +_cairo_gstate_paint (cairo_gstate_t *gstate) +{ + cairo_pattern_union_t source_pattern; + const cairo_pattern_t *pattern; + cairo_clip_t clip; + cairo_status_t status; + cairo_operator_t op; + + if (unlikely (gstate->source->status)) + return gstate->source->status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + + status = _cairo_surface_paint (gstate->target, + op, pattern, + _gstate_get_clip (gstate, &clip)); + _cairo_clip_fini (&clip); + + return status; +} + +cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask) +{ + cairo_pattern_union_t source_pattern, mask_pattern; + const cairo_pattern_t *source; + cairo_operator_t op; + cairo_clip_t clip; + cairo_status_t status; + + if (unlikely (mask->status)) + return mask->status; + + if (unlikely (gstate->source->status)) + return gstate->source->status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_pattern_is_opaque (mask, NULL)) + return _cairo_gstate_paint (gstate); + + if (_cairo_pattern_is_clear (mask) && + _cairo_operator_bounded_by_mask (gstate->op)) + { + return CAIRO_STATUS_SUCCESS; + } + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + source = &source_pattern.base; + } + _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); + + if (source->type == CAIRO_PATTERN_TYPE_SOLID && + mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID && + _cairo_operator_bounded_by_source (op)) + { + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + cairo_color_t combined; + + if (mask_pattern.base.has_component_alpha) { +#define M(R, A, B, c) R.c = A.c * B.c + M(combined, solid->color, mask_pattern.solid.color, red); + M(combined, solid->color, mask_pattern.solid.color, green); + M(combined, solid->color, mask_pattern.solid.color, blue); + M(combined, solid->color, mask_pattern.solid.color, alpha); +#undef M + } else { + combined = solid->color; + _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); + } + + _cairo_pattern_init_solid (&source_pattern.solid, &combined); + + status = _cairo_surface_paint (gstate->target, op, + &source_pattern.base, + _gstate_get_clip (gstate, &clip)); + } + else + { + status = _cairo_surface_mask (gstate->target, op, + source, + &mask_pattern.base, + _gstate_get_clip (gstate, &clip)); + } + _cairo_clip_fini (&clip); + + return status; +} + +cairo_status_t +_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) +{ + cairo_pattern_union_t source_pattern; + cairo_stroke_style_t style; + double dash[2]; + cairo_clip_t clip; + cairo_status_t status; + + if (unlikely (gstate->source->status)) + return gstate->source->status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (gstate->stroke_style.line_width <= 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); + if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) { + style.dash = dash; + _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, + &style.dash_offset, + style.dash, + &style.num_dashes); + } + + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + + status = _cairo_surface_stroke (gstate->target, + gstate->op, + &source_pattern.base, + path, + &style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + gstate->antialias, + _gstate_get_clip (gstate, &clip)); + _cairo_clip_fini (&clip); + + return status; +} + +cairo_status_t +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret) +{ + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_box_t limit; + cairo_traps_t traps; + + if (gstate->stroke_style.line_width <= 0.0) { + *inside_ret = FALSE; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_gstate_user_to_backend (gstate, &x, &y); + + /* Before we perform the expensive stroke analysis, + * check whether the point is within the extents of the path. + */ + _cairo_path_fixed_approximate_stroke_extents (path, + &gstate->stroke_style, + &gstate->ctm, + &extents); + if (x < extents.x || x > extents.x + extents.width || + y < extents.y || y > extents.y + extents.height) + { + *inside_ret = FALSE; + return CAIRO_STATUS_SUCCESS; + } + + limit.p1.x = _cairo_fixed_from_double (x) - 5; + limit.p1.y = _cairo_fixed_from_double (y) - 5; + limit.p2.x = limit.p1.x + 10; + limit.p2.y = limit.p1.y + 10; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, &limit, 1); + + status = _cairo_path_fixed_stroke_to_traps (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &traps); + if (unlikely (status)) + goto BAIL; + + *inside_ret = _cairo_traps_contain (&traps, x, y); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) +{ + cairo_clip_t clip; + cairo_status_t status; + + if (unlikely (gstate->source->status)) + return gstate->source->status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_path_fixed_fill_is_empty (path)) { + if (_cairo_operator_bounded_by_mask (gstate->op)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_paint (gstate->target, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + _gstate_get_clip (gstate, &clip)); + } else { + cairo_pattern_union_t source_pattern; + const cairo_pattern_t *pattern; + cairo_operator_t op; + cairo_rectangle_int_t extents; + cairo_box_t box; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + + /* Toolkits often paint the entire background with a fill */ + if (_cairo_surface_get_extents (gstate->target, &extents) && + _cairo_path_fixed_is_box (path, &box) && + box.p1.x <= _cairo_fixed_from_int (extents.x) && + box.p1.y <= _cairo_fixed_from_int (extents.y) && + box.p2.x >= _cairo_fixed_from_int (extents.x + extents.width) && + box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height)) + { + status = _cairo_surface_paint (gstate->target, op, pattern, + _gstate_get_clip (gstate, &clip)); + } + else + { + status = _cairo_surface_fill (gstate->target, op, pattern, + path, + gstate->fill_rule, + gstate->tolerance, + gstate->antialias, + _gstate_get_clip (gstate, &clip)); + } + } + + _cairo_clip_fini (&clip); + + return status; +} + +cairo_bool_t +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y) +{ + _cairo_gstate_user_to_backend (gstate, &x, &y); + + return _cairo_path_fixed_in_fill (path, + gstate->fill_rule, + gstate->tolerance, + x, y); +} + +cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y) +{ + cairo_clip_path_t *clip_path; + + if (gstate->clip.all_clipped) + return FALSE; + + clip_path = gstate->clip.path; + if (clip_path == NULL) + return TRUE; + + _cairo_gstate_user_to_backend (gstate, &x, &y); + + if (x < clip_path->extents.x || + x >= clip_path->extents.x + clip_path->extents.width || + y < clip_path->extents.y || + y >= clip_path->extents.y + clip_path->extents.height) + { + return FALSE; + } + + do { + if (! _cairo_path_fixed_in_fill (&clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + x, y)) + return FALSE; + } while ((clip_path = clip_path->prev) != NULL); + + return TRUE; +} + +cairo_status_t +_cairo_gstate_copy_page (cairo_gstate_t *gstate) +{ + cairo_surface_copy_page (gstate->target); + return cairo_surface_status (gstate->target); +} + +cairo_status_t +_cairo_gstate_show_page (cairo_gstate_t *gstate) +{ + cairo_surface_show_page (gstate->target); + return cairo_surface_status (gstate->target); +} + +static void +_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate, + cairo_traps_t *traps, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_box_t extents; + + if (traps->num_traps == 0) { + /* no traps, so we actually won't draw anything */ + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + } else { + double px1, py1, px2, py2; + + _cairo_traps_extents (traps, &extents); + + px1 = _cairo_fixed_to_double (extents.p1.x); + py1 = _cairo_fixed_to_double (extents.p1.y); + px2 = _cairo_fixed_to_double (extents.p2.x); + py2 = _cairo_fixed_to_double (extents.p2.y); + + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; + } +} + +cairo_status_t +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_status_t status; + cairo_traps_t traps; + + if (gstate->stroke_style.line_width <= 0.0) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_stroke_to_traps (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, + x1, y1, x2, y2); + } + + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_status_t status; + cairo_traps_t traps; + + if (path->is_empty_fill) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, + x1, y1, x2, y2); + } + + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_reset_clip (cairo_gstate_t *gstate) +{ + _cairo_clip_reset (&gstate->clip); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) +{ + return _cairo_clip_clip (&gstate->clip, + path, gstate->fill_rule, + gstate->tolerance, gstate->antialias); +} + +static cairo_bool_t +_cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, + cairo_rectangle_int_t *extents) +{ + const cairo_rectangle_int_t *clip_extents; + cairo_bool_t is_bounded; + + is_bounded = _cairo_surface_get_extents (gstate->target, extents); + + clip_extents = _cairo_clip_get_extents (&gstate->clip); + if (clip_extents != NULL) { + cairo_bool_t is_empty; + + is_empty = _cairo_rectangle_intersect (extents, clip_extents); + is_bounded = TRUE; + } + + return is_bounded; +} + +cairo_bool_t +_cairo_gstate_clip_extents (cairo_gstate_t *gstate, + double *x1, + double *y1, + double *x2, + double *y2) +{ + cairo_rectangle_int_t extents; + double px1, py1, px2, py2; + + if (! _cairo_gstate_int_clip_extents (gstate, &extents)) + return FALSE; + + px1 = extents.x; + py1 = extents.y; + px2 = extents.x + (int) extents.width; + py2 = extents.y + (int) extents.height; + + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; + + return TRUE; +} + +cairo_rectangle_list_t* +_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) +{ + cairo_clip_t clip; + cairo_rectangle_int_t extents; + cairo_rectangle_list_t *list; + + _cairo_clip_init_copy (&clip, &gstate->clip); + + if (_cairo_surface_get_extents (gstate->target, &extents)) + _cairo_clip_rectangle (&clip, &extents); + + list = _cairo_clip_copy_rectangle_list (&clip, gstate); + _cairo_clip_fini (&clip); + + return list; +} + +static void +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) +{ + if (gstate->scaled_font == NULL) + return; + + if (gstate->previous_scaled_font != NULL) + cairo_scaled_font_destroy (gstate->previous_scaled_font); + + gstate->previous_scaled_font = gstate->scaled_font; + gstate->scaled_font = NULL; +} + +cairo_status_t +_cairo_gstate_select_font_face (cairo_gstate_t *gstate, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_font_face_t *font_face; + cairo_status_t status; + + font_face = cairo_toy_font_face_create (family, slant, weight); + if (font_face->status) + return font_face->status; + + status = _cairo_gstate_set_font_face (gstate, font_face); + cairo_font_face_destroy (font_face); + + return status; +} + +cairo_status_t +_cairo_gstate_set_font_size (cairo_gstate_t *gstate, + double size) +{ + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_scale (&gstate->font_matrix, size, size); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_matrix_is_invertible (matrix)) { + /* rank 0 matrices are ok even though they are not invertible */ + if (!(matrix->xx == 0. && matrix->xy == 0. && + matrix->yx == 0. && matrix->yy == 0.)) { + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + } + } + + _cairo_gstate_unset_scaled_font (gstate); + + gstate->font_matrix = *matrix; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) +{ + *matrix = gstate->font_matrix; +} + +void +_cairo_gstate_set_font_options (cairo_gstate_t *gstate, + const cairo_font_options_t *options) +{ + if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0) + return; + + _cairo_gstate_unset_scaled_font (gstate); + + _cairo_font_options_init_copy (&gstate->font_options, options); +} + +void +_cairo_gstate_get_font_options (cairo_gstate_t *gstate, + cairo_font_options_t *options) +{ + *options = gstate->font_options; +} + +cairo_status_t +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_font_face (gstate); + if (unlikely (status)) + return status; + + *font_face = gstate->font_face; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, + cairo_scaled_font_t **scaled_font) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + *scaled_font = gstate->scaled_font; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Like everything else in this file, fonts involve Too Many Coordinate Spaces; + * it is easy to get confused about what's going on. + * + * The user's view + * --------------- + * + * Users ask for things in user space. When cairo starts, a user space unit + * is about 1/96 inch, which is similar to (but importantly different from) + * the normal "point" units most users think in terms of. When a user + * selects a font, its scale is set to "one user unit". The user can then + * independently scale the user coordinate system *or* the font matrix, in + * order to adjust the rendered size of the font. + * + * Metrics are returned in user space, whether they are obtained from + * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t + * which is a font specialized to a particular scale matrix, CTM, and target + * surface. + * + * The font's view + * --------------- + * + * Fonts are designed and stored (in say .ttf files) in "font space", which + * describes an "EM Square" (a design tile) and has some abstract number + * such as 1000, 1024, or 2048 units per "EM". This is basically an + * uninteresting space for us, but we need to remember that it exists. + * + * Font resources (from libraries or operating systems) render themselves + * to a particular device. Since they do not want to make most programmers + * worry about the font design space, the scaling API is simplified to + * involve just telling the font the required pixel size of the EM square + * (that is, in device space). + * + * + * Cairo's gstate view + * ------------------- + * + * In addition to the CTM and CTM inverse, we keep a matrix in the gstate + * called the "font matrix" which describes the user's most recent + * font-scaling or font-transforming request. This is kept in terms of an + * abstract scale factor, composed with the CTM and used to set the font's + * pixel size. So if the user asks to "scale the font by 12", the matrix + * is: + * + * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] + * + * It is an affine matrix, like all cairo matrices, where its tx and ty + * components are used to "nudging" fonts around and are handled in gstate + * and then ignored by the "scaled-font" layer. + * + * In order to perform any action on a font, we must build an object + * called a #cairo_font_scale_t; this contains the central 2x2 matrix + * resulting from "font matrix * CTM" (sans the font matrix translation + * components as stated in the previous paragraph). + * + * We pass this to the font when making requests of it, which causes it to + * reply for a particular [user request, device] combination, under the CTM + * (to accommodate the "zoom in" == "bigger fonts" issue above). + * + * The other terms in our communication with the font are therefore in + * device space. When we ask it to perform text->glyph conversion, it will + * produce a glyph string in device space. Glyph vectors we pass to it for + * measuring or rendering should be in device space. The metrics which we + * get back from the font will be in device space. The contents of the + * global glyph image cache will be in device space. + * + * + * Cairo's public view + * ------------------- + * + * Since the values entering and leaving via public API calls are in user + * space, the gstate functions typically need to multiply arguments by the + * CTM (for user-input glyph vectors), and return values by the CTM inverse + * (for font responses such as metrics or glyph vectors). + * + */ + +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) +{ + cairo_font_face_t *font_face; + + if (gstate->font_face != NULL) + return gstate->font_face->status; + + + font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); + if (font_face->status) + return font_face->status; + + gstate->font_face = font_face; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) +{ + cairo_status_t status; + cairo_font_options_t options; + cairo_scaled_font_t *scaled_font; + + if (gstate->scaled_font != NULL) + return gstate->scaled_font->status; + + status = _cairo_gstate_ensure_font_face (gstate); + if (unlikely (status)) + return status; + + cairo_surface_get_font_options (gstate->target, &options); + cairo_font_options_merge (&options, &gstate->font_options); + + scaled_font = cairo_scaled_font_create (gstate->font_face, + &gstate->font_matrix, + &gstate->ctm, + &options); + + status = cairo_scaled_font_status (scaled_font); + if (unlikely (status)) + return status; + + gstate->scaled_font = scaled_font; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) +{ + cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + cairo_scaled_font_extents (gstate->scaled_font, extents); + + return cairo_scaled_font_status (gstate->scaled_font); +} + +cairo_status_t +_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags); +} + +cairo_status_t +_cairo_gstate_set_font_face (cairo_gstate_t *gstate, + cairo_font_face_t *font_face) +{ + if (font_face && font_face->status) + return _cairo_error (font_face->status); + + if (font_face == gstate->font_face) + return CAIRO_STATUS_SUCCESS; + + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = cairo_font_face_reference (font_face); + + _cairo_gstate_unset_scaled_font (gstate); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + cairo_scaled_font_glyph_extents (gstate->scaled_font, + glyphs, num_glyphs, + extents); + + return cairo_scaled_font_status (gstate->scaled_font); +} + +cairo_status_t +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags) +{ + cairo_pattern_union_t source_pattern; + const cairo_pattern_t *pattern; + cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_glyph_t *transformed_glyphs; + cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; + cairo_text_cluster_t *transformed_clusters; + cairo_operator_t op; + cairo_status_t status; + cairo_clip_t clip; + + if (unlikely (gstate->source->status)) + return gstate->source->status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_clipped (gstate)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + transformed_glyphs = stack_transformed_glyphs; + transformed_clusters = stack_transformed_clusters; + + if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { + transformed_glyphs = cairo_glyph_allocate (num_glyphs); + if (unlikely (transformed_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_GLYPHS; + } + } + + /* Just in case */ + if (!clusters) + num_clusters = 0; + + if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { + transformed_clusters = cairo_text_cluster_allocate (num_clusters); + if (unlikely (transformed_clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_GLYPHS; + } + } + + status = _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + clusters, + num_clusters, + cluster_flags, + transformed_glyphs, + &num_glyphs, + transformed_clusters); + + if (status || num_glyphs == 0) + goto CLEANUP_GLYPHS; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + _cairo_clip_init(&clip); + + /* For really huge font sizes, we can just do path;fill instead of + * show_glyphs, as show_glyphs would put excess pressure on the cache, + * not all components below us correctly handle huge font sizes, and + * path filling can be cheaper since parts of glyphs are likely to be + * clipped out. 256 seems like a good limit. But alas, seems like cairo's + * rasterizer is something like ten times slower than freetype's for huge + * sizes. So, no win just yet when we're using cairo's rasterizer. + * For now, if we're using cairo's rasterizer, use path filling only + * for insanely-huge sizes, just to make sure we don't make anyone + * unhappy. When we get a really fast rasterizer in cairo, we may + * want to readjust this. The threshold calculation is + * encapsulated in _cairo_surface_get_text_path_fill_threshold. + * + * Needless to say, do this only if show_text_glyphs is not available. */ + if (cairo_surface_has_show_text_glyphs (gstate->target) || + _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= + _cairo_surface_get_text_path_fill_threshold (gstate->target)) + { + status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, + utf8, utf8_len, + transformed_glyphs, num_glyphs, + transformed_clusters, num_clusters, + cluster_flags, + gstate->scaled_font, + _gstate_get_clip (gstate, &clip)); + } + else + { + cairo_path_fixed_t path; + + _cairo_path_fixed_init (&path); + + status = _cairo_scaled_font_glyph_path (gstate->scaled_font, + transformed_glyphs, num_glyphs, + &path); + + if (status == CAIRO_STATUS_SUCCESS && !_cairo_path_fixed_fill_is_empty (&path)) { + status = _cairo_surface_fill (gstate->target, op, pattern, + &path, + CAIRO_FILL_RULE_WINDING, + gstate->tolerance, + gstate->scaled_font->options.antialias, + _gstate_get_clip (gstate, &clip)); + } else { + /* if _cairo_scaled_font_glyph_path() failed, maybe the font doesn't support + * returning paths, so try the _cairo_surface_show_text_glyphs() option + */ + status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, + utf8, utf8_len, + transformed_glyphs, num_glyphs, + transformed_clusters, num_clusters, + cluster_flags, + gstate->scaled_font, + _gstate_get_clip (gstate, &clip)); + } + + _cairo_path_fixed_fini (&path); + } + + _cairo_clip_fini (&clip); + +CLEANUP_GLYPHS: + if (transformed_glyphs != stack_transformed_glyphs) + cairo_glyph_free (transformed_glyphs); + if (transformed_clusters != stack_transformed_clusters) + cairo_text_cluster_free (transformed_clusters); + + return status; +} + +cairo_status_t +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) +{ + cairo_status_t status; + cairo_glyph_t *transformed_glyphs; + cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { + transformed_glyphs = stack_transformed_glyphs; + } else { + transformed_glyphs = cairo_glyph_allocate (num_glyphs); + if (unlikely (transformed_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + NULL, 0, 0, + transformed_glyphs, + NULL, NULL); + if (unlikely (status)) + goto CLEANUP_GLYPHS; + + status = _cairo_scaled_font_glyph_path (gstate->scaled_font, + transformed_glyphs, num_glyphs, + path); + + CLEANUP_GLYPHS: + if (transformed_glyphs != stack_transformed_glyphs) + cairo_glyph_free (transformed_glyphs); + + return status; +} + +cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias) +{ + gstate->antialias = antialias; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate) +{ + return gstate->antialias; +} + +/** + * _cairo_gstate_transform_glyphs_to_backend: + * @gstate: a #cairo_gstate_t + * @glyphs: the array of #cairo_glyph_t objects to be transformed + * @num_glyphs: the number of elements in @glyphs + * @transformed_glyphs: a pre-allocated array of at least @num_glyphs + * #cairo_glyph_t objects + * @num_transformed_glyphs: the number of elements in @transformed_glyphs + * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be + * dropped + * + * Transform an array of glyphs to backend space by first adding the offset + * of the font matrix, then transforming from user space to backend space. + * The result of the transformation is placed in @transformed_glyphs. + * + * This also uses information from the scaled font and the surface to + * cull/drop glyphs that will not be visible. + **/ +static cairo_status_t +_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_glyph_t *transformed_glyphs, + int *num_transformed_glyphs, + cairo_text_cluster_t *transformed_clusters) +{ + int i, j, k; + cairo_matrix_t *ctm = &gstate->ctm; + cairo_matrix_t *font_matrix = &gstate->font_matrix; + cairo_matrix_t *device_transform = &gstate->target->device_transform; + cairo_bool_t drop = FALSE; + double x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_transformed_glyphs != NULL) { + cairo_rectangle_int_t surface_extents; + + drop = TRUE; + if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { + drop = FALSE; /* unbounded surface */ + } else { + double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); + if (surface_extents.width == 0 || surface_extents.height == 0) { + /* No visible area. Don't draw anything */ + *num_transformed_glyphs = 0; + return CAIRO_STATUS_SUCCESS; + } + /* XXX We currently drop any glyphs that has its position outside + * of the surface boundaries by a safety margin depending on the + * font scale. This however can fail in extreme cases where the + * font has really long swashes for example... We can correctly + * handle that by looking the glyph up and using its device bbox + * to device if it's going to be visible, but I'm not inclined to + * do that now. + */ + x1 = surface_extents.x - scale10; + y1 = surface_extents.y - scale10; + x2 = surface_extents.x + (int) surface_extents.width + scale10; + y2 = surface_extents.y + (int) surface_extents.height + scale10; + } + + if (!drop) + *num_transformed_glyphs = num_glyphs; + } else + num_transformed_glyphs = &j; + +#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) + + j = 0; + if (_cairo_matrix_is_identity (ctm) && + _cairo_matrix_is_identity (device_transform) && + font_matrix->x0 == 0 && font_matrix->y0 == 0) + { + if (! drop) { + memcpy (transformed_glyphs, glyphs, + num_glyphs * sizeof (cairo_glyph_t)); + j = num_glyphs; + } else if (num_clusters == 0) { + for (i = 0; i < num_glyphs; i++) { + transformed_glyphs[j].index = glyphs[i].index; + transformed_glyphs[j].x = glyphs[i].x; + transformed_glyphs[j].y = glyphs[i].y; + if (KEEP_GLYPH (transformed_glyphs[j])) + j++; + } + } else { + const cairo_glyph_t *cur_glyph; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k].index = cur_glyph->index; + transformed_glyphs[j+k].x = cur_glyph->x; + transformed_glyphs[j+k].y = cur_glyph->y; + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + else if (_cairo_matrix_is_translation (ctm) && + _cairo_matrix_is_translation (device_transform)) + { + double tx = font_matrix->x0 + ctm->x0 + device_transform->x0; + double ty = font_matrix->y0 + ctm->y0 + device_transform->y0; + + if (! drop || num_clusters == 0) { + for (i = 0; i < num_glyphs; i++) { + transformed_glyphs[j].index = glyphs[i].index; + transformed_glyphs[j].x = glyphs[i].x + tx; + transformed_glyphs[j].y = glyphs[i].y + ty; + if (!drop || KEEP_GLYPH (transformed_glyphs[j])) + j++; + } + } else { + const cairo_glyph_t *cur_glyph; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k].index = cur_glyph->index; + transformed_glyphs[j+k].x = cur_glyph->x + tx; + transformed_glyphs[j+k].y = cur_glyph->y + ty; + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + else + { + cairo_matrix_t aggregate_transform; + + cairo_matrix_init_translate (&aggregate_transform, + gstate->font_matrix.x0, + gstate->font_matrix.y0); + cairo_matrix_multiply (&aggregate_transform, + &aggregate_transform, ctm); + cairo_matrix_multiply (&aggregate_transform, + &aggregate_transform, device_transform); + + if (! drop || num_clusters == 0) { + for (i = 0; i < num_glyphs; i++) { + transformed_glyphs[j] = glyphs[i]; + cairo_matrix_transform_point (&aggregate_transform, + &transformed_glyphs[j].x, + &transformed_glyphs[j].y); + if (! drop || KEEP_GLYPH (transformed_glyphs[j])) + j++; + } + } else { + const cairo_glyph_t *cur_glyph; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k] = *cur_glyph; + cairo_matrix_transform_point (&aggregate_transform, + &transformed_glyphs[j+k].x, + &transformed_glyphs[j+k].y); + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + *num_transformed_glyphs = j; + + if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) { + for (i = 0; i < --j; i++) { + cairo_glyph_t tmp; + + tmp = transformed_glyphs[i]; + transformed_glyphs[i] = transformed_glyphs[j]; + transformed_glyphs[j] = tmp; + } + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-hash-private.h b/libs/cairo/src/cairo-hash-private.h new file mode 100644 index 000000000..0c2759dbf --- /dev/null +++ b/libs/cairo/src/cairo-hash-private.h @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-hash.c b/libs/cairo/src/cairo-hash.c new file mode 100644 index 000000000..7e24d930d --- /dev/null +++ b/libs/cairo/src/cairo-hash.c @@ -0,0 +1,508 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/* + * An entry can be in one of three states: + * + * FREE: Entry has never been used, terminates all searches. + * Appears in the table as a %NULL pointer. + * + * DEAD: Entry had been live in the past. A dead entry can be reused + * but does not terminate a search for an exact entry. + * Appears in the table as a pointer to DEAD_ENTRY. + * + * LIVE: Entry is currently being used. + * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer. + */ + +#define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1) + +#define ENTRY_IS_FREE(entry) ((entry) == NULL) +#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) +#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) + +/* We expect keys will not be destroyed frequently, so our table does not + * contain any explicit shrinking code nor any chain-coalescing code for + * entries randomly deleted by memory pressure (except during rehashing, of + * course). These assumptions are potentially bad, but they make the + * implementation straightforward. + * + * Revisit later if evidence appears that we're using excessive memory from + * a mostly-dead table. + * + * This table is open-addressed with double hashing. Each table size is a + * prime chosen to be a little more than double the high water mark for a + * given arrangement, so the tables should remain < 50% full. The table + * size makes for the "first" hash modulus; a second prime (2 less than the + * first prime) serves as the "second" hash modulus, which is co-prime and + * thus guarantees a complete permutation of table indices. + * + * This structure, and accompanying table, is borrowed/modified from the + * file xserver/render/glyph.c in the freedesktop.org x server, with + * permission (and suggested modification of doubling sizes) by Keith + * Packard. + */ + +typedef struct _cairo_hash_table_arrangement { + unsigned long high_water_mark; + unsigned long size; + unsigned long rehash; +} cairo_hash_table_arrangement_t; + +static const cairo_hash_table_arrangement_t hash_table_arrangements [] = { + { 16, 43, 41 }, + { 32, 73, 71 }, + { 64, 151, 149 }, + { 128, 283, 281 }, + { 256, 571, 569 }, + { 512, 1153, 1151 }, + { 1024, 2269, 2267 }, + { 2048, 4519, 4517 }, + { 4096, 9013, 9011 }, + { 8192, 18043, 18041 }, + { 16384, 36109, 36107 }, + { 32768, 72091, 72089 }, + { 65536, 144409, 144407 }, + { 131072, 288361, 288359 }, + { 262144, 576883, 576881 }, + { 524288, 1153459, 1153457 }, + { 1048576, 2307163, 2307161 }, + { 2097152, 4613893, 4613891 }, + { 4194304, 9227641, 9227639 }, + { 8388608, 18455029, 18455027 }, + { 16777216, 36911011, 36911009 }, + { 33554432, 73819861, 73819859 }, + { 67108864, 147639589, 147639587 }, + { 134217728, 295279081, 295279079 }, + { 268435456, 590559793, 590559791 } +}; + +#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements) + +struct _cairo_hash_table { + cairo_hash_keys_equal_func_t keys_equal; + + const cairo_hash_table_arrangement_t *arrangement; + cairo_hash_entry_t **entries; + + unsigned long live_entries; + unsigned long iterating; /* Iterating, no insert, no resize */ +}; + +/** + * _cairo_hash_table_create: + * @keys_equal: a function to return %TRUE if two keys are equal + * + * Creates a new hash table which will use the keys_equal() function + * to compare hash keys. Data is provided to the hash table in the + * form of user-derived versions of #cairo_hash_entry_t. A hash entry + * must be able to hold both a key (including a hash code) and a + * value. Sometimes only the key will be necessary, (as in + * _cairo_hash_table_remove), and other times both a key and a value + * will be necessary, (as in _cairo_hash_table_insert). + * + * See #cairo_hash_entry_t for more details. + * + * Return value: the new hash table or %NULL if out of memory. + **/ +cairo_hash_table_t * +_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) +{ + cairo_hash_table_t *hash_table; + + hash_table = malloc (sizeof (cairo_hash_table_t)); + if (unlikely (hash_table == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + hash_table->keys_equal = keys_equal; + + hash_table->arrangement = &hash_table_arrangements[0]; + + hash_table->entries = calloc (hash_table->arrangement->size, + sizeof(cairo_hash_entry_t *)); + if (unlikely (hash_table->entries == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + free (hash_table); + return NULL; + } + + hash_table->live_entries = 0; + hash_table->iterating = 0; + + return hash_table; +} + +/** + * _cairo_hash_table_destroy: + * @hash_table: an empty hash table to destroy + * + * Immediately destroys the given hash table, freeing all resources + * associated with it. + * + * WARNING: The hash_table must have no live entries in it before + * _cairo_hash_table_destroy is called. It is a fatal error otherwise, + * and this function will halt. The rationale for this behavior is to + * avoid memory leaks and to avoid needless complication of the API + * with destroy notifiy callbacks. + * + * WARNING: The hash_table must have no running iterators in it when + * _cairo_hash_table_destroy is called. It is a fatal error otherwise, + * and this function will halt. + **/ +void +_cairo_hash_table_destroy (cairo_hash_table_t *hash_table) +{ + /* The hash table must be empty. Otherwise, halt. */ + assert (hash_table->live_entries == 0); + /* No iterators can be running. Otherwise, halt. */ + assert (hash_table->iterating == 0); + + free (hash_table->entries); + hash_table->entries = NULL; + + free (hash_table); +} + +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; + + table_size = hash_table->arrangement->size; + idx = key->hash % table_size; + + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + } while (++i < table_size); + + ASSERT_NOT_REACHED; + return NULL; +} + +/** + * _cairo_hash_table_resize: + * @hash_table: a hash table + * + * Resize the hash table if the number of entries has gotten much + * bigger or smaller than the ideal number of entries for the current + * size. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if out of memory. + **/ +static cairo_status_t +_cairo_hash_table_resize (cairo_hash_table_t *hash_table) +{ + cairo_hash_table_t tmp; + unsigned long new_size, i; + + /* This keeps the hash table between 25% and 50% full. */ + unsigned long high = hash_table->arrangement->high_water_mark; + unsigned long low = high >> 2; + + if (hash_table->live_entries >= low && hash_table->live_entries <= high) + return CAIRO_STATUS_SUCCESS; + + tmp = *hash_table; + + if (hash_table->live_entries > high) + { + tmp.arrangement = hash_table->arrangement + 1; + /* This code is being abused if we can't make a table big enough. */ + assert (tmp.arrangement - hash_table_arrangements < + NUM_HASH_TABLE_ARRANGEMENTS); + } + else /* hash_table->live_entries < low */ + { + /* Can't shrink if we're at the smallest size */ + if (hash_table->arrangement == &hash_table_arrangements[0]) + return CAIRO_STATUS_SUCCESS; + tmp.arrangement = hash_table->arrangement - 1; + } + + new_size = tmp.arrangement->size; + tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); + if (unlikely (tmp.entries == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < hash_table->arrangement->size; ++i) { + if (ENTRY_IS_LIVE (hash_table->entries[i])) { + *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i]) + = hash_table->entries[i]; + } + } + + free (hash_table->entries); + hash_table->entries = tmp.entries; + hash_table->arrangement = tmp.arrangement; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_hash_table_lookup: + * @hash_table: a hash table + * @key: the key of interest + * + * Performs a lookup in @hash_table looking for an entry which has a + * key that matches @key, (as determined by the keys_equal() function + * passed to _cairo_hash_table_create). + * + * Return value: the matching entry, of %NULL if no match was found. + **/ +void * +_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + cairo_hash_entry_t *entry; + unsigned long table_size, i, idx, step; + + table_size = hash_table->arrangement->size; + idx = key->hash % table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry)) { + if (hash_table->keys_equal (key, entry)) + return entry; + } else if (ENTRY_IS_FREE (entry)) + return NULL; + + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry)) { + if (hash_table->keys_equal (key, entry)) + return entry; + } else if (ENTRY_IS_FREE (entry)) + return NULL; + } while (++i < table_size); + + return NULL; +} + +/** + * _cairo_hash_table_random_entry: + * @hash_table: a hash table + * @predicate: a predicate function. + * + * Find a random entry in the hash table satisfying the given + * @predicate. + * + * We use the same algorithm as the lookup algorithm to walk over the + * entries in the hash table in a pseudo-random order. Walking + * linearly would favor entries following gaps in the hash table. We + * could also call rand() repeatedly, which works well for almost-full + * tables, but degrades when the table is almost empty, or predicate + * returns %TRUE for most entries. + * + * Return value: a random live entry or %NULL if there are no entries + * that match the given predicate. In particular, if predicate is + * %NULL, a %NULL return value indicates that the table is empty. + **/ +void * +_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, + cairo_hash_predicate_func_t predicate) +{ + cairo_hash_entry_t *entry; + unsigned long hash; + unsigned long table_size, i, idx, step; + + assert (predicate != NULL); + + table_size = hash_table->arrangement->size; + hash = rand (); + idx = hash % table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + + i = 1; + step = hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + } while (++i < table_size); + + return NULL; +} + +/** + * _cairo_hash_table_insert: + * @hash_table: a hash table + * @key_and_value: an entry to be inserted + * + * Insert the entry #key_and_value into the hash table. + * + * WARNING: There must not be an existing entry in the hash table + * with a matching key. + * + * WARNING: It is a fatal error to insert an element while + * an iterator is running + * + * Instead of using insert to replace an entry, consider just editing + * the entry obtained with _cairo_hash_table_lookup. Or if absolutely + * necessary, use _cairo_hash_table_remove first. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. + **/ +cairo_status_t +_cairo_hash_table_insert (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key_and_value) +{ + cairo_status_t status; + + /* Insert is illegal while an iterator is running. */ + assert (hash_table->iterating == 0); + + hash_table->live_entries++; + status = _cairo_hash_table_resize (hash_table); + if (unlikely (status)) { + /* abort the insert... */ + hash_table->live_entries--; + return status; + } + + *_cairo_hash_table_lookup_unique_key (hash_table, + key_and_value) = key_and_value; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; + + table_size = hash_table->arrangement->size; + idx = key->hash % table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + } while (++i < table_size); + + ASSERT_NOT_REACHED; + return NULL; +} +/** + * _cairo_hash_table_remove: + * @hash_table: a hash table + * @key: key of entry to be removed + * + * Remove an entry from the hash table which points to @key. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if out of memory. + **/ +void +_cairo_hash_table_remove (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY; + hash_table->live_entries--; + + /* Check for table resize. Don't do this when iterating as this will + * reorder elements of the table and cause the iteration to potentially + * skip some elements. */ + if (hash_table->iterating == 0) { + /* This call _can_ fail, but only in failing to allocate new + * memory to shrink the hash table. It does leave the table in a + * consistent state, and we've already succeeded in removing the + * entry, so we don't examine the failure status of this call. */ + _cairo_hash_table_resize (hash_table); + } +} + +/** + * _cairo_hash_table_foreach: + * @hash_table: a hash table + * @hash_callback: function to be called for each live entry + * @closure: additional argument to be passed to @hash_callback + * + * Call @hash_callback for each live entry in the hash table, in a + * non-specified order. + * + * Entries in @hash_table may be removed by code executed from @hash_callback. + * + * Entries may not be inserted to @hash_table, nor may @hash_table + * be destroyed by code executed from @hash_callback. The relevant + * functions will halt in these cases. + **/ +void +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, + cairo_hash_callback_func_t hash_callback, + void *closure) +{ + unsigned long i; + cairo_hash_entry_t *entry; + + /* Mark the table for iteration */ + ++hash_table->iterating; + for (i = 0; i < hash_table->arrangement->size; i++) { + entry = hash_table->entries[i]; + if (ENTRY_IS_LIVE(entry)) + hash_callback (entry, closure); + } + /* If some elements were deleted during the iteration, + * the table may need resizing. Just do this every time + * as the check is inexpensive. + */ + if (--hash_table->iterating == 0) { + /* Should we fail to shrink the hash table, it is left unaltered, + * and we don't need to propagate the error status. */ + _cairo_hash_table_resize (hash_table); + } +} diff --git a/libs/cairo/src/cairo-hull.c b/libs/cairo/src/cairo-hull.c new file mode 100644 index 000000000..a9cb27982 --- /dev/null +++ b/libs/cairo/src/cairo-hull.c @@ -0,0 +1,203 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-image-info-private.h b/libs/cairo/src/cairo-image-info-private.h new file mode 100644 index 000000000..1107fa4f9 --- /dev/null +++ b/libs/cairo/src/cairo-image-info-private.h @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-image-info.c b/libs/cairo/src/cairo-image-info.c new file mode 100644 index 000000000..5269ce25f --- /dev/null +++ b/libs/cairo/src/cairo-image-info.c @@ -0,0 +1,259 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-image-info-private.h" + +static uint32_t +_get_be32 (const unsigned char *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +/* JPEG (image/jpeg) + * + * http://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ + +/* Markers with no parameters. All other markers are followed by a two + * byte length of the parameters. */ +#define TEM 0x01 +#define RST_begin 0xd0 +#define RST_end 0xd7 +#define SOI 0xd8 +#define EOI 0xd9 + +/* Start of frame markers. */ +#define SOF0 0xc0 +#define SOF1 0xc1 +#define SOF2 0xc2 +#define SOF3 0xc3 +#define SOF5 0xc5 +#define SOF6 0xc6 +#define SOF7 0xc7 +#define SOF9 0xc9 +#define SOF10 0xca +#define SOF11 0xcb +#define SOF13 0xcd +#define SOF14 0xce +#define SOF15 0xcf + +static const unsigned char * +_jpeg_skip_segment (const unsigned char *p) +{ + int len; + + p++; + len = (p[0] << 8) | p[1]; + + return p + len; +} + +static void +_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p) +{ + info->width = (p[6] << 8) + p[7]; + info->height = (p[4] << 8) + p[5]; + info->num_components = p[8]; + info->bits_per_component = p[3]; +} + +cairo_int_status_t +_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, + const unsigned char *data, + long length) +{ + const unsigned char *p = data; + + while (p + 1 < data + length) { + if (*p != 0xff) + return CAIRO_INT_STATUS_UNSUPPORTED; + p++; + + switch (*p) { + /* skip fill bytes */ + case 0xff: + p++; + break; + + case TEM: + case SOI: + case EOI: + p++; + break; + + case SOF0: + case SOF1: + case SOF2: + case SOF3: + case SOF5: + case SOF6: + case SOF7: + case SOF9: + case SOF10: + case SOF11: + case SOF13: + case SOF14: + case SOF15: + /* Start of frame found. Extract the image parameters. */ + if (p + 8 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _jpeg_extract_info (info, p); + return CAIRO_STATUS_SUCCESS; + + default: + if (*p >= RST_begin && *p <= RST_end) { + p++; + break; + } + + if (p + 2 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpeg_skip_segment (p); + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/* JPEG 2000 (image/jp2) + * + * http://www.jpeg.org/public/15444-1annexi.pdf + */ + +#define JPX_FILETYPE 0x66747970 +#define JPX_JP2_HEADER 0x6A703268 +#define JPX_IMAGE_HEADER 0x69686472 + +static const unsigned char _jpx_signature[] = { + 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a +}; + +static const unsigned char * +_jpx_next_box (const unsigned char *p) +{ + return p + _get_be32 (p); +} + +static const unsigned char * +_jpx_get_box_contents (const unsigned char *p) +{ + return p + 8; +} + +static cairo_bool_t +_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + uint32_t length; + + if (p + 8 < end) { + length = _get_be32 (p); + if (_get_be32 (p + 4) == type && p + length < end) + return TRUE; + } + + return FALSE; +} + +static const unsigned char * +_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + while (p < end) { + if (_jpx_match_box (p, end, type)) + return p; + p = _jpx_next_box (p); + } + + return NULL; +} + +static void +_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) +{ + info->height = _get_be32 (p); + info->width = _get_be32 (p + 4); + info->num_components = (p[8] << 8) + p[9]; + info->bits_per_component = p[10]; +} + +cairo_int_status_t +_cairo_image_info_get_jpx_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + /* First 12 bytes must be the JPEG 2000 signature box. */ + if (length < ARRAY_LENGTH(_jpx_signature) || + memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += ARRAY_LENGTH(_jpx_signature); + + /* Next box must be a File Type Box */ + if (! _jpx_match_box (p, end, JPX_FILETYPE)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpx_next_box (p); + + /* Locate the JP2 header box. */ + p = _jpx_find_box (p, end, JPX_JP2_HEADER); + if (!p) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Step into the JP2 header box. First box must be the Image + * Header */ + p = _jpx_get_box_contents (p); + if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Get the image info */ + p = _jpx_get_box_contents (p); + _jpx_extract_info (p, info); + + return CAIRO_STATUS_SUCCESS; +} + +/* PNG (image/png) + * + * http://www.w3.org/TR/2003/REC-PNG-20031110/ + */ + +#define PNG_IHDR 0x49484452 + +static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + +cairo_int_status_t +_cairo_image_info_get_png_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + if (length < 8 || memcmp (data, _png_magic, 8) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 8; + + /* The first chunk must be IDHR. IDHR has 13 bytes of data plus + * the 12 bytes of overhead for the chunk. */ + if (p + 13 + 12 > end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + if (_get_be32 (p) != PNG_IHDR) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + info->width = _get_be32 (p); + p += 4; + info->height = _get_be32 (p); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-image-surface.c b/libs/cairo/src/cairo-image-surface.c new file mode 100644 index 000000000..cc496ece5 --- /dev/null +++ b/libs/cairo/src/cairo-image-surface.c @@ -0,0 +1,4743 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" + +/* Limit on the width / height of an image surface in pixels. This is + * mainly determined by coordinates of things sent to pixman at the + * moment being in 16.16 format. */ +#define MAX_IMAGE_SIZE 32767 +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +/** + * SECTION:cairo-image + * @Title: Image Surfaces + * @Short_Description: Rendering to memory buffers + * @See_Also: #cairo_surface_t + * + * Image surfaces provide the ability to render to memory buffers + * either allocated by cairo or by the calling code. The supported + * image formats are those defined in #cairo_format_t. + */ + +/** + * CAIRO_HAS_IMAGE_SURFACE: + * + * Defined if the image surface backend is available. + * The image surface backend is always built in. + * This macro was added for completeness in cairo 1.8. + * + * @Since: 1.8 + */ + +static cairo_int_status_t +_cairo_image_surface_fill (void *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +static pixman_image_t * +_pixman_image_for_solid (const cairo_solid_pattern_t *pattern); + +static cairo_bool_t +_cairo_image_surface_is_size_valid (int width, int height) +{ + return 0 <= width && width <= MAX_IMAGE_SIZE && + 0 <= height && height <= MAX_IMAGE_SIZE; +} + +cairo_format_t +_cairo_format_from_pixman_format (pixman_format_code_t pixman_format) +{ + switch (pixman_format) { + case PIXMAN_a8r8g8b8: + return CAIRO_FORMAT_ARGB32; + case PIXMAN_x8r8g8b8: + return CAIRO_FORMAT_RGB24; + case PIXMAN_a8: + return CAIRO_FORMAT_A8; + case PIXMAN_a1: + return CAIRO_FORMAT_A1; + case PIXMAN_r5g6b5: + return CAIRO_FORMAT_RGB16_565; + case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8: + case PIXMAN_b8g8r8: case PIXMAN_b5g6r5: + case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: + case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: + case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4: + case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: + case PIXMAN_g4: case PIXMAN_g1: + case PIXMAN_yuy2: case PIXMAN_yv12: + case PIXMAN_b8g8r8x8: + case PIXMAN_b8g8r8a8: + case PIXMAN_x2b10g10r10: + case PIXMAN_a2b10g10r10: + case PIXMAN_x2r10g10b10: + case PIXMAN_a2r10g10b10: + default: + return CAIRO_FORMAT_INVALID; + } + + return CAIRO_FORMAT_INVALID; +} + +cairo_content_t +_cairo_content_from_pixman_format (pixman_format_code_t pixman_format) +{ + cairo_content_t content; + + content = 0; + if (PIXMAN_FORMAT_RGB (pixman_format)) + content |= CAIRO_CONTENT_COLOR; + if (PIXMAN_FORMAT_A (pixman_format)) + content |= CAIRO_CONTENT_ALPHA; + + return content; +} + +cairo_surface_t * +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + pixman_format_code_t pixman_format) +{ + cairo_image_surface_t *surface; + int width = pixman_image_get_width (pixman_image); + int height = pixman_image_get_height (pixman_image); + + surface = malloc (sizeof (cairo_image_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_image_surface_backend, + NULL, /* device */ + _cairo_content_from_pixman_format (pixman_format)); + + surface->pixman_image = pixman_image; + + surface->pixman_format = pixman_format; + surface->format = _cairo_format_from_pixman_format (pixman_format); + surface->data = (uint8_t *) pixman_image_get_data (pixman_image); + surface->owns_data = FALSE; + surface->transparency = CAIRO_IMAGE_UNKNOWN; + + surface->width = width; + surface->height = height; + surface->stride = pixman_image_get_stride (pixman_image); + surface->depth = pixman_image_get_depth (pixman_image); + + return &surface->base; +} + +cairo_bool_t +_pixman_format_from_masks (cairo_format_masks_t *masks, + pixman_format_code_t *format_ret) +{ + pixman_format_code_t format; + int format_type; + int a, r, g, b; + cairo_format_masks_t format_masks; + + a = _cairo_popcount (masks->alpha_mask); + r = _cairo_popcount (masks->red_mask); + g = _cairo_popcount (masks->green_mask); + b = _cairo_popcount (masks->blue_mask); + + if (masks->red_mask) { + if (masks->red_mask > masks->blue_mask) + format_type = PIXMAN_TYPE_ARGB; + else + format_type = PIXMAN_TYPE_ABGR; + } else if (masks->alpha_mask) { + format_type = PIXMAN_TYPE_A; + } else { + return FALSE; + } + + format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b); + + if (! pixman_format_supported_destination (format)) + return FALSE; + + /* Sanity check that we got out of PIXMAN_FORMAT exactly what we + * expected. This avoid any problems from something bizarre like + * alpha in the least-significant bits, or insane channel order, + * or whatever. */ + if (!_pixman_format_to_masks (format, &format_masks) || + masks->bpp != format_masks.bpp || + masks->red_mask != format_masks.red_mask || + masks->green_mask != format_masks.green_mask || + masks->blue_mask != format_masks.blue_mask) + { + return FALSE; + } + + *format_ret = format; + return TRUE; +} + +/* A mask consisting of N bits set to 1. */ +#define MASK(N) ((1UL << (N))-1) + +cairo_bool_t +_pixman_format_to_masks (pixman_format_code_t format, + cairo_format_masks_t *masks) +{ + int a, r, g, b; + + masks->bpp = PIXMAN_FORMAT_BPP (format); + + /* Number of bits in each channel */ + a = PIXMAN_FORMAT_A (format); + r = PIXMAN_FORMAT_R (format); + g = PIXMAN_FORMAT_G (format); + b = PIXMAN_FORMAT_B (format); + + switch (PIXMAN_FORMAT_TYPE (format)) { + case PIXMAN_TYPE_ARGB: + masks->alpha_mask = MASK (a) << (r + g + b); + masks->red_mask = MASK (r) << (g + b); + masks->green_mask = MASK (g) << (b); + masks->blue_mask = MASK (b); + return TRUE; + case PIXMAN_TYPE_ABGR: + masks->alpha_mask = MASK (a) << (b + g + r); + masks->blue_mask = MASK (b) << (g + r); + masks->green_mask = MASK (g) << (r); + masks->red_mask = MASK (r); + return TRUE; +#ifdef PIXMAN_TYPE_BGRA + case PIXMAN_TYPE_BGRA: + masks->blue_mask = MASK (b) << (masks->bpp - b); + masks->green_mask = MASK (g) << (masks->bpp - b - g); + masks->red_mask = MASK (r) << (masks->bpp - b - g - r); + masks->alpha_mask = MASK (a); + return TRUE; +#endif + case PIXMAN_TYPE_A: + masks->alpha_mask = MASK (a); + masks->red_mask = 0; + masks->green_mask = 0; + masks->blue_mask = 0; + return TRUE; + case PIXMAN_TYPE_OTHER: + case PIXMAN_TYPE_COLOR: + case PIXMAN_TYPE_GRAY: + case PIXMAN_TYPE_YUY2: + case PIXMAN_TYPE_YV12: + default: + masks->alpha_mask = 0; + masks->red_mask = 0; + masks->green_mask = 0; + masks->blue_mask = 0; + return FALSE; + } +} + +pixman_format_code_t +_cairo_format_to_pixman_format_code (cairo_format_t format) +{ + pixman_format_code_t ret; + switch (format) { + case CAIRO_FORMAT_A1: + ret = PIXMAN_a1; + break; + case CAIRO_FORMAT_A8: + ret = PIXMAN_a8; + break; + case CAIRO_FORMAT_RGB24: + ret = PIXMAN_x8r8g8b8; + break; + case CAIRO_FORMAT_RGB16_565: + ret = PIXMAN_r5g6b5; + break; + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_INVALID: + default: + ret = PIXMAN_a8r8g8b8; + break; + } + return ret; +} + +cairo_surface_t * +_cairo_image_surface_create_with_pixman_format (unsigned char *data, + pixman_format_code_t pixman_format, + int width, + int height, + int stride) +{ + cairo_surface_t *surface; + pixman_image_t *pixman_image; + + if (! _cairo_image_surface_is_size_valid (width, height)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + pixman_image = pixman_image_create_bits (pixman_format, width, height, + (uint32_t *) data, stride ? stride : 4); + + if (unlikely (pixman_image == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + if (unlikely (surface->status)) { + pixman_image_unref (pixman_image); + return surface; + } + + /* we can not make any assumptions about the initial state of user data */ + surface->is_clear = data == NULL; + return surface; +} + +/** + * cairo_image_surface_create: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates an image surface of the specified format and + * dimensions. Initially the surface contents are all + * 0. (Specifically, within each pixel, each color or alpha channel + * belonging to format will be 0. The contents of bits within a pixel, + * but not belonging to the given format are undefined). + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + **/ +cairo_surface_t * +cairo_image_surface_create (cairo_format_t format, + int width, + int height) +{ + pixman_format_code_t pixman_format; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + pixman_format = _cairo_format_to_pixman_format_code (format); + + return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, + width, height, -1); +} +slim_hidden_def (cairo_image_surface_create); + +cairo_surface_t * +_cairo_image_surface_create_with_content (cairo_content_t content, + int width, + int height) +{ + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); +} + +/** + * cairo_format_stride_for_width: + * @format: A #cairo_format_t value + * @width: The desired width of an image surface to be created. + * + * This function provides a stride value that will respect all + * alignment requirements of the accelerated image-rendering code + * within cairo. Typical usage will be of the form: + * + * + * int stride; + * unsigned char *data; + * #cairo_surface_t *surface; + * + * stride = cairo_format_stride_for_width (format, width); + * data = malloc (stride * height); + * surface = cairo_image_surface_create_for_data (data, format, + * width, height, + * stride); + * + * + * Return value: the appropriate stride to use given the desired + * format and width, or -1 if either the format is invalid or the width + * too large. + * + * Since: 1.6 + **/ +int +cairo_format_stride_for_width (cairo_format_t format, + int width) +{ + int bpp; + + if (! CAIRO_FORMAT_VALID (format)) { + _cairo_error_throw (CAIRO_STATUS_INVALID_FORMAT); + return -1; + } + + bpp = _cairo_format_bits_per_pixel (format); + if ((unsigned) (width) >= (INT32_MAX - 7) / (unsigned) (bpp)) + return -1; + + return CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); +} +slim_hidden_def (cairo_format_stride_for_width); + +/** + * cairo_image_surface_create_for_data: + * @data: a pointer to a buffer supplied by the application in which + * to write contents. This pointer must be suitably aligned for any + * kind of variable, (for example, a pointer returned by malloc). + * @format: the format of pixels in the buffer + * @width: the width of the image to be stored in the buffer + * @height: the height of the image to be stored in the buffer + * @stride: the number of bytes between the start of rows in the + * buffer as allocated. This value should always be computed by + * cairo_format_stride_for_width() before allocating the data + * buffer. + * + * Creates an image surface for the provided pixel data. The output + * buffer must be kept around until the #cairo_surface_t is destroyed + * or cairo_surface_finish() is called on the surface. The initial + * contents of @data will be used as the initial image contents; you + * must explicitly clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Note that the stride may be larger than + * width*bytes_per_pixel to provide proper alignment for each pixel + * and row. This alignment is required to allow high-performance rendering + * within cairo. The correct way to obtain a legal stride value is to + * call cairo_format_stride_for_width() with the desired format and + * maximum image width value, and then use the resulting stride value + * to allocate the data and to create the image surface. See + * cairo_format_stride_for_width() for example code. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface in the case of an error such as out of + * memory or an invalid stride value. In case of invalid stride value + * the error status of the returned surface will be + * %CAIRO_STATUS_INVALID_STRIDE. You can use + * cairo_surface_status() to check for this. + * + * See cairo_surface_set_user_data() for a means of attaching a + * destroy-notification fallback to the surface if necessary. + **/ +cairo_surface_t * +cairo_image_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride) +{ + pixman_format_code_t pixman_format; + int minstride; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + if (! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + minstride = cairo_format_stride_for_width (format, width); + if (stride < 0) { + if (stride > -minstride) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + } else { + if (stride < minstride) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + } + + pixman_format = _cairo_format_to_pixman_format_code (format); + return _cairo_image_surface_create_with_pixman_format (data, + pixman_format, + width, height, + stride); +} +slim_hidden_def (cairo_image_surface_create_for_data); + +/** + * cairo_image_surface_get_data: + * @surface: a #cairo_image_surface_t + * + * Get a pointer to the data of the image surface, for direct + * inspection or modification. + * + * Return value: a pointer to the image data of this surface or %NULL + * if @surface is not an image surface, or if cairo_surface_finish() + * has been called. + * + * Since: 1.2 + **/ +unsigned char * +cairo_image_surface_get_data (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return image_surface->data; +} +slim_hidden_def (cairo_image_surface_get_data); + +/** + * cairo_image_surface_get_format: + * @surface: a #cairo_image_surface_t + * + * Get the format of the surface. + * + * Return value: the format of the surface + * + * Since: 1.2 + **/ +cairo_format_t +cairo_image_surface_get_format (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return CAIRO_FORMAT_INVALID; + } + + return image_surface->format; +} +slim_hidden_def (cairo_image_surface_get_format); + +/** + * cairo_image_surface_get_width: + * @surface: a #cairo_image_surface_t + * + * Get the width of the image surface in pixels. + * + * Return value: the width of the surface in pixels. + **/ +int +cairo_image_surface_get_width (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->width; +} +slim_hidden_def (cairo_image_surface_get_width); + +/** + * cairo_image_surface_get_height: + * @surface: a #cairo_image_surface_t + * + * Get the height of the image surface in pixels. + * + * Return value: the height of the surface in pixels. + **/ +int +cairo_image_surface_get_height (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->height; +} +slim_hidden_def (cairo_image_surface_get_height); + +/** + * cairo_image_surface_get_stride: + * @surface: a #cairo_image_surface_t + * + * Get the stride of the image surface in bytes + * + * Return value: the stride of the image surface in bytes (or 0 if + * @surface is not an image surface). The stride is the distance in + * bytes from the beginning of one row of the image data to the + * beginning of the next row. + * + * Since: 1.2 + **/ +int +cairo_image_surface_get_stride (cairo_surface_t *surface) +{ + + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->stride; +} +slim_hidden_def (cairo_image_surface_get_stride); + +cairo_format_t +_cairo_format_from_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return CAIRO_FORMAT_RGB24; + case CAIRO_CONTENT_ALPHA: + return CAIRO_FORMAT_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + return CAIRO_FORMAT_ARGB32; + } + + ASSERT_NOT_REACHED; + return CAIRO_FORMAT_INVALID; +} + +cairo_content_t +_cairo_content_from_format (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return CAIRO_CONTENT_COLOR_ALPHA; + case CAIRO_FORMAT_RGB24: + return CAIRO_CONTENT_COLOR; + case CAIRO_FORMAT_RGB16_565: + return CAIRO_CONTENT_COLOR; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + return CAIRO_CONTENT_ALPHA; + case CAIRO_FORMAT_INVALID: + break; + } + + ASSERT_NOT_REACHED; + return CAIRO_CONTENT_COLOR_ALPHA; +} + +int +_cairo_format_bits_per_pixel (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return 32; + case CAIRO_FORMAT_RGB24: + return 32; + case CAIRO_FORMAT_RGB16_565: + return 16; + case CAIRO_FORMAT_A8: + return 8; + case CAIRO_FORMAT_A1: + return 1; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static cairo_surface_t * +_cairo_image_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + cairo_image_surface_t *other = abstract_other; + + if (! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + if (content == other->base.content) { + return _cairo_image_surface_create_with_pixman_format (NULL, + other->pixman_format, + width, height, + 0); + } + + return _cairo_image_surface_create_with_content (content, + width, height); +} + +static cairo_status_t +_cairo_image_surface_finish (void *abstract_surface) +{ + cairo_image_surface_t *surface = abstract_surface; + + if (surface->pixman_image) { + pixman_image_unref (surface->pixman_image); + surface->pixman_image = NULL; + } + + if (surface->owns_data) { + free (surface->data); + surface->data = NULL; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) +{ + surface->owns_data = TRUE; +} + +static cairo_status_t +_cairo_image_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + *image_out = abstract_surface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ +} + +/* XXX: I think we should fix pixman to match the names/order of the + * cairo operators, but that will likely be better done at the same + * time the X server is ported to pixman, (which will change a lot of + * things in pixman I think). + */ +static pixman_op_t +_pixman_operator (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return PIXMAN_OP_CLEAR; + + case CAIRO_OPERATOR_SOURCE: + return PIXMAN_OP_SRC; + case CAIRO_OPERATOR_OVER: + return PIXMAN_OP_OVER; + case CAIRO_OPERATOR_IN: + return PIXMAN_OP_IN; + case CAIRO_OPERATOR_OUT: + return PIXMAN_OP_OUT; + case CAIRO_OPERATOR_ATOP: + return PIXMAN_OP_ATOP; + + case CAIRO_OPERATOR_DEST: + return PIXMAN_OP_DST; + case CAIRO_OPERATOR_DEST_OVER: + return PIXMAN_OP_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return PIXMAN_OP_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return PIXMAN_OP_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: + return PIXMAN_OP_ATOP_REVERSE; + + case CAIRO_OPERATOR_XOR: + return PIXMAN_OP_XOR; + case CAIRO_OPERATOR_ADD: + return PIXMAN_OP_ADD; + case CAIRO_OPERATOR_SATURATE: + return PIXMAN_OP_SATURATE; + + case CAIRO_OPERATOR_MULTIPLY: + return PIXMAN_OP_MULTIPLY; + case CAIRO_OPERATOR_SCREEN: + return PIXMAN_OP_SCREEN; + case CAIRO_OPERATOR_OVERLAY: + return PIXMAN_OP_OVERLAY; + case CAIRO_OPERATOR_DARKEN: + return PIXMAN_OP_DARKEN; + case CAIRO_OPERATOR_LIGHTEN: + return PIXMAN_OP_LIGHTEN; + case CAIRO_OPERATOR_COLOR_DODGE: + return PIXMAN_OP_COLOR_DODGE; + case CAIRO_OPERATOR_COLOR_BURN: + return PIXMAN_OP_COLOR_BURN; + case CAIRO_OPERATOR_HARD_LIGHT: + return PIXMAN_OP_HARD_LIGHT; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PIXMAN_OP_SOFT_LIGHT; + case CAIRO_OPERATOR_DIFFERENCE: + return PIXMAN_OP_DIFFERENCE; + case CAIRO_OPERATOR_EXCLUSION: + return PIXMAN_OP_EXCLUSION; + case CAIRO_OPERATOR_HSL_HUE: + return PIXMAN_OP_HSL_HUE; + case CAIRO_OPERATOR_HSL_SATURATION: + return PIXMAN_OP_HSL_SATURATION; + case CAIRO_OPERATOR_HSL_COLOR: + return PIXMAN_OP_HSL_COLOR; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PIXMAN_OP_HSL_LUMINOSITY; + + default: + ASSERT_NOT_REACHED; + return PIXMAN_OP_OVER; + } +} + +static cairo_status_t +_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, + cairo_region_t *region) +{ + if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface) +{ + pixman_image_set_clip_region32 (surface->pixman_image, NULL); +} + +static double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} + +static cairo_bool_t +_nearest_sample (cairo_filter_t filter, double *tx, double *ty) +{ + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { + *tx = _pixman_nearest_sample (*tx); + *ty = _pixman_nearest_sample (*ty); + } else { + if (*tx != floor (*tx) || *ty != floor (*ty)) + return FALSE; + } + return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; +} + +#if PIXMAN_HAS_ATOMIC_OPS +static pixman_image_t *__pixman_transparent_image; +static pixman_image_t *__pixman_black_image; +static pixman_image_t *__pixman_white_image; + +static pixman_image_t * +_pixman_transparent_image (void) +{ + pixman_image_t *image; + + image = __pixman_transparent_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0x00; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static pixman_image_t * +_pixman_black_image (void) +{ + pixman_image_t *image; + + image = __pixman_black_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0xffff; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static pixman_image_t * +_pixman_white_image (void) +{ + pixman_image_t *image; + + image = __pixman_white_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0xffff; + color.green = 0xffff; + color.blue = 0xffff; + color.alpha = 0xffff; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static struct { + cairo_color_t color; + pixman_image_t *image; +} cache[16]; +static int n_cached; + +#else /* !PIXMAN_HAS_ATOMIC_OPS */ +static pixman_image_t * +_pixman_transparent_image (void) +{ + return _pixman_image_for_solid (&_cairo_pattern_clear); +} + +static pixman_image_t * +_pixman_black_image (void) +{ + return _pixman_image_for_solid (&_cairo_pattern_black); +} + +static pixman_image_t * +_pixman_white_image (void) +{ + return _pixman_image_for_solid (&_cairo_pattern_white); +} +#endif /* !PIXMAN_HAS_ATOMIC_OPS */ + +void +_cairo_image_reset_static_data (void) +{ +#if PIXMAN_HAS_ATOMIC_OPS + while (n_cached) + pixman_image_unref (cache[--n_cached].image); + + if (__pixman_transparent_image) { + pixman_image_unref (__pixman_transparent_image); + __pixman_transparent_image = NULL; + } + + if (__pixman_black_image) { + pixman_image_unref (__pixman_black_image); + __pixman_black_image = NULL; + } + + if (__pixman_white_image) { + pixman_image_unref (__pixman_white_image); + __pixman_white_image = NULL; + } +#endif +} + +static pixman_image_t * +_pixman_image_for_solid (const cairo_solid_pattern_t *pattern) +{ + pixman_color_t color; + pixman_image_t *image; + +#if PIXMAN_HAS_ATOMIC_OPS + int i; + + if (pattern->color.alpha_short <= 0x00ff) + return _pixman_transparent_image (); + + if (pattern->color.alpha_short >= 0xff00) { + if (pattern->color.red_short <= 0x00ff && + pattern->color.green_short <= 0x00ff && + pattern->color.blue_short <= 0x00ff) + { + return _pixman_black_image (); + } + + if (pattern->color.red_short >= 0xff00 && + pattern->color.green_short >= 0xff00 && + pattern->color.blue_short >= 0xff00) + { + return _pixman_white_image (); + } + } + + CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); + for (i = 0; i < n_cached; i++) { + if (_cairo_color_equal (&cache[i].color, &pattern->color)) { + image = pixman_image_ref (cache[i].image); + goto UNLOCK; + } + } +#endif + + color.red = pattern->color.red_short; + color.green = pattern->color.green_short; + color.blue = pattern->color.blue_short; + color.alpha = pattern->color.alpha_short; + + image = pixman_image_create_solid_fill (&color); +#if PIXMAN_HAS_ATOMIC_OPS + if (image == NULL) + goto UNLOCK; + + if (n_cached < ARRAY_LENGTH (cache)) { + i = n_cached++; + } else { + i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); + pixman_image_unref (cache[i].image); + } + cache[i].image = pixman_image_ref (image); + cache[i].color = pattern->color; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); +#endif + return image; +} + +static double +clamp (double val, double min, double max) +{ + return val < min ? min : (val > max ? max : val); +} + +static pixman_image_t * +_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + pixman_gradient_stop_t pixman_stops_static[2]; + pixman_gradient_stop_t *pixman_stops = pixman_stops_static; + cairo_matrix_t matrix = pattern->base.matrix; + double tx, ty; + unsigned int i; + + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof(pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return NULL; + } + + for (i = 0; i < pattern->n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); + pixman_stops[i].color.red = pattern->stops[i].color.red_short; + pixman_stops[i].color.green = pattern->stops[i].color.green_short; + pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; + pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + } + + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + pixman_point_fixed_t p1, p2; + double x0, y0, x1, y1, maxabs; + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ + x0 = _cairo_fixed_to_double (linear->p1.x); + y0 = _cairo_fixed_to_double (linear->p1.y); + x1 = _cairo_fixed_to_double (linear->p2.x); + y1 = _cairo_fixed_to_double (linear->p2.y); + cairo_matrix_transform_point (&matrix, &x0, &y0); + cairo_matrix_transform_point (&matrix, &x1, &y1); + maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); + + if (maxabs > PIXMAN_MAX_INT) + { + double sf; + cairo_matrix_t scale; + + sf = PIXMAN_MAX_INT / maxabs; + + p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); + p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); + p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); + p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); + + /* cairo_matrix_scale does a pre-scale, we want a post-scale */ + cairo_matrix_init_scale (&scale, sf, sf); + cairo_matrix_multiply (&matrix, &matrix, &scale); + } + else + { + p1.x = _cairo_fixed_to_16_16 (linear->p1.x); + p1.y = _cairo_fixed_to_16_16 (linear->p1.y); + p2.x = _cairo_fixed_to_16_16 (linear->p2.x); + p2.y = _cairo_fixed_to_16_16 (linear->p2.y); + } + + pixman_image = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + pattern->n_stops); + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + pixman_point_fixed_t c1, c2; + pixman_fixed_t r1, r2; + + c1.x = _cairo_fixed_to_16_16 (radial->c1.x); + c1.y = _cairo_fixed_to_16_16 (radial->c1.y); + r1 = _cairo_fixed_to_16_16 (radial->r1); + + c2.x = _cairo_fixed_to_16_16 (radial->c2.x); + c2.y = _cairo_fixed_to_16_16 (radial->c2.y); + r2 = _cairo_fixed_to_16_16 (radial->r2); + + pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2, + pixman_stops, + pattern->n_stops); + } + + if (pixman_stops != pixman_stops_static) + free (pixman_stops); + + if (unlikely (pixman_image == NULL)) + return NULL; + + tx = matrix.x0; + ty = matrix.y0; + if (! _cairo_matrix_is_translation (&matrix) || + ! _nearest_sample (pattern->base.filter, &tx, &ty)) + { + pixman_transform_t pixman_transform; + + if (tx != 0. || ty != 0.) { + cairo_matrix_t m, inv; + cairo_status_t status; + double x, y, max_x, max_y; + + /* Pixman also limits the [xy]_offset to 16 bits. We try to evenly + * spread the bits between the two, but we need to ensure that + * fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT && + * fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT, + * otherwise the gradient won't render. + */ + inv = matrix; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + + x = _cairo_lround (inv.x0 / 2); + y = _cairo_lround (inv.y0 / 2); + + max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width); + x = clamp(x, -max_x, max_x); + max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height); + y = clamp(y, -max_y, max_y); + + tx = -x; + ty = -y; + cairo_matrix_init_translate (&inv, x, y); + cairo_matrix_multiply (&m, &inv, &matrix); + _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + } else { + tx = ty = 0; + _cairo_matrix_to_pixman_matrix (&pattern->base.matrix, + &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + } + + if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { + pixman_image_unref (pixman_image); + return NULL; + } + } + *ix = tx; + *iy = ty; + + { + pixman_repeat_t pixman_repeat; + + switch (pattern->base.extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + return pixman_image; +} + +struct acquire_source_cleanup { + cairo_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +_acquire_source_cleanup (pixman_image_t *pixman_image, + void *closure) +{ + struct acquire_source_cleanup *data = closure; + + _cairo_surface_release_source_image (data->surface, + data->image, + data->image_extra); + free (data); +} + +static cairo_filter_t +sampled_area (const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *sample) +{ + cairo_filter_t filter; + double x1, x2, y1, y2; + double pad; + + x1 = extents->x; + y1 = extents->y; + x2 = extents->x + (int) extents->width; + y2 = extents->y + (int) extents->height; + + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &x1, &y1, &x2, &y2, + NULL); + + filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); + sample->x = floor (x1 - pad); + sample->y = floor (y1 - pad); + sample->width = ceil (x2 + pad) - sample->x; + sample->height = ceil (y2 + pad) - sample->y; + + return filter; +} + +static uint16_t +expand_channel (uint16_t v, uint32_t bits) +{ + int offset = 16 - bits; + while (offset > 0) { + v |= v >> bits; + offset -= bits; + bits += bits; + } + return v; +} + +static pixman_image_t * +_pixel_to_solid (cairo_image_surface_t *image, int x, int y) +{ + uint32_t pixel; + pixman_color_t color; + + switch (image->format) { + default: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return NULL; + + case CAIRO_FORMAT_A1: + pixel = *(uint8_t *) (image->data + y * image->stride + x/8); + return pixel & (1 << (x&7)) ? _pixman_white_image () : _pixman_transparent_image (); + + case CAIRO_FORMAT_A8: + color.alpha = *(uint8_t *) (image->data + y * image->stride + x); + color.alpha |= color.alpha << 8; + if (color.alpha == 0) + return _pixman_transparent_image (); + + color.red = color.green = color.blue = 0; + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_RGB16_565: + pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); + if (pixel == 0) + return _pixman_black_image (); + if (pixel == 0xffff) + return _pixman_white_image (); + + color.alpha = 0xffff; + color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); + color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); + color.blue = expand_channel ((pixel & 0x1f) << 11, 5); + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); + color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; + if (color.alpha == 0) + return _pixman_transparent_image (); + if (pixel == 0xffffffff) + return _pixman_white_image (); + if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) + return _pixman_black_image (); + + color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); + color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); + color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); + return pixman_image_create_solid_fill (&color); + } +} + +static pixman_image_t * +_pixman_image_for_surface (const cairo_surface_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + cairo_rectangle_int_t sample; + cairo_extend_t extend; + cairo_filter_t filter; + double tx, ty; + + tx = pattern->base.matrix.x0; + ty = pattern->base.matrix.y0; + + extend = pattern->base.extend; + filter = sampled_area (pattern, extents, &sample); + + pixman_image = NULL; + if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && + (! is_mask || ! pattern->base.has_component_alpha || + (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) + { + cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; + cairo_surface_type_t type; + + if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target; + + type = source->base.backend->type; + if (type == CAIRO_SURFACE_TYPE_IMAGE) { + if (sample.width == 1 && sample.height == 1) { + if (sample.x < 0 || + sample.y < 0 || + sample.x >= source->width || + sample.y >= source->height) + { + if (extend == CAIRO_EXTEND_NONE) + return _pixman_transparent_image (); + } + else + { + return _pixel_to_solid (source, sample.x, sample.y); + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + /* avoid allocating a 'pattern' image if we can reuse the original */ + if (extend == CAIRO_EXTEND_NONE && + _cairo_matrix_is_translation (&pattern->base.matrix) && + _nearest_sample (filter, &tx, &ty)) + { + *ix = tx; + *iy = ty; + return pixman_image_ref (source->pixman_image); + } +#endif + + pixman_image = pixman_image_create_bits (source->pixman_format, + source->width, + source->height, + (uint32_t *) source->data, + source->stride); + if (unlikely (pixman_image == NULL)) + return NULL; + } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub; + cairo_bool_t is_contained = FALSE; + + sub = (cairo_surface_subsurface_t *) source; + source = (cairo_image_surface_t *) sub->target; + + if (sample.x >= 0 && + sample.y >= 0 && + sample.x + sample.width <= sub->extents.width && + sample.y + sample.height <= sub->extents.height) + { + is_contained = TRUE; + } + + if (sample.width == 1 && sample.height == 1) { + if (is_contained) { + return _pixel_to_solid (source, + sub->extents.x + sample.x, + sub->extents.y + sample.y); + } else { + if (extend == CAIRO_EXTEND_NONE) + return _pixman_transparent_image (); + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + if (is_contained && + _cairo_matrix_is_translation (&pattern->base.matrix) && + _nearest_sample (filter, &tx, &ty)) + { + *ix = tx + sub->extents.x; + *iy = ty + sub->extents.y; + return pixman_image_ref (source->pixman_image); + } +#endif + + /* Avoid sub-byte offsets, force a copy in that case. */ + if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { + void *data = source->data + + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + + sub->extents.y * source->stride; + pixman_image = pixman_image_create_bits (source->pixman_format, + sub->extents.width, + sub->extents.height, + data, + source->stride); + if (unlikely (pixman_image == NULL)) + return NULL; + } + } + } + + if (pixman_image == NULL) { + struct acquire_source_cleanup *cleanup; + cairo_image_surface_t *image; + void *extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); + if (unlikely (status)) + return NULL; + + if (sample.width == 1 && sample.height == 1) { + if (sample.x < 0 || + sample.y < 0 || + sample.x >= image->width || + sample.y >= image->height) + { + if (extend == CAIRO_EXTEND_NONE) { + pixman_image = _pixman_transparent_image (); + _cairo_surface_release_source_image (pattern->surface, image, extra); + return pixman_image; + } + } + else + { + pixman_image = _pixel_to_solid (image, sample.x, sample.y); + _cairo_surface_release_source_image (pattern->surface, image, extra); + return pixman_image; + } + } + + pixman_image = pixman_image_create_bits (image->pixman_format, + image->width, + image->height, + (uint32_t *) image->data, + image->stride); + if (unlikely (pixman_image == NULL)) { + _cairo_surface_release_source_image (pattern->surface, image, extra); + return NULL; + } + + cleanup = malloc (sizeof (*cleanup)); + if (unlikely (cleanup == NULL)) { + _cairo_surface_release_source_image (pattern->surface, image, extra); + pixman_image_unref (pixman_image); + return NULL; + } + + cleanup->surface = pattern->surface; + cleanup->image = image; + cleanup->image_extra = extra; + pixman_image_set_destroy_function (pixman_image, + _acquire_source_cleanup, cleanup); + } + + if (! _cairo_matrix_is_translation (&pattern->base.matrix) || + ! _nearest_sample (filter, &tx, &ty)) + { + pixman_transform_t pixman_transform; + cairo_matrix_t m; + + m = pattern->base.matrix; + if (m.x0 != 0. || m.y0 != 0.) { + cairo_matrix_t inv; + cairo_status_t status; + double x, y; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + inv = m; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + + x = floor (inv.x0 / 2); + y = floor (inv.y0 / 2); + tx = -x; + ty = -y; + cairo_matrix_init_translate (&inv, x, y); + cairo_matrix_multiply (&m, &inv, &m); + } else { + tx = ty = 0; + } + + _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, + extents->x + extents->width/2., + extents->y + extents->height/2.); + if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { + pixman_image_unref (pixman_image); + return NULL; + } + } + *ix = tx; + *iy = ty; + + if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) && + tx == pattern->base.matrix.x0 && + ty == pattern->base.matrix.y0) + { + pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); + } + else + { + pixman_filter_t pixman_filter; + + switch (filter) { + case CAIRO_FILTER_FAST: + pixman_filter = PIXMAN_FILTER_FAST; + break; + case CAIRO_FILTER_GOOD: + pixman_filter = PIXMAN_FILTER_GOOD; + break; + case CAIRO_FILTER_BEST: + pixman_filter = PIXMAN_FILTER_BEST; + break; + case CAIRO_FILTER_NEAREST: + pixman_filter = PIXMAN_FILTER_NEAREST; + break; + case CAIRO_FILTER_BILINEAR: + pixman_filter = PIXMAN_FILTER_BILINEAR; + break; + case CAIRO_FILTER_GAUSSIAN: + /* XXX: The GAUSSIAN value has no implementation in cairo + * whatsoever, so it was really a mistake to have it in the + * API. We could fix this by officially deprecating it, or + * else inventing semantics and providing an actual + * implementation for it. */ + default: + pixman_filter = PIXMAN_FILTER_BEST; + } + + pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); + } + + { + pixman_repeat_t pixman_repeat; + + switch (extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + if (pattern->base.has_component_alpha) + pixman_image_set_component_alpha (pixman_image, TRUE); + + return pixman_image; +} + +static pixman_image_t * +_pixman_image_for_pattern (const cairo_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + int *tx, int *ty) +{ + *tx = *ty = 0; + + if (pattern == NULL) + return _pixman_white_image (); + + switch (pattern->type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); + + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_LINEAR: + return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, + extents, tx, ty); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern, + is_mask, extents, tx, ty); + } +} + +static cairo_status_t +_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst, + const cairo_composite_rectangles_t *rects, + cairo_clip_t *clip) +{ + pixman_image_t *mask = NULL; + pixman_box32_t boxes[4]; + int i, mask_x = 0, mask_y = 0, n_boxes = 0; + + if (clip != NULL) { + cairo_surface_t *clip_surface; + int clip_x, clip_y; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; + mask_x = -clip_x; + mask_y = -clip_y; + } else { + if (rects->bounded.width == rects->unbounded.width && + rects->bounded.height == rects->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + /* wholly unbounded? */ + if (rects->bounded.width == 0 || rects->bounded.height == 0) { + int x = rects->unbounded.x; + int y = rects->unbounded.y; + int width = rects->unbounded.width; + int height = rects->unbounded.height; + + if (mask != NULL) { + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } else { + pixman_color_t color = { 0, }; + pixman_box32_t box = { x, y, x + width, y + height }; + + if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, + dst->pixman_image, + &color, + 1, &box)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; + } + + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + boxes[n_boxes].x1 = rects->unbounded.x; + boxes[n_boxes].y1 = rects->unbounded.y; + boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; + boxes[n_boxes].y2 = rects->bounded.y; + n_boxes++; + } + + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + boxes[n_boxes].x1 = rects->unbounded.x; + boxes[n_boxes].y1 = rects->bounded.y; + boxes[n_boxes].x2 = rects->bounded.x; + boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; + n_boxes++; + } + + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width; + boxes[n_boxes].y1 = rects->bounded.y; + boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; + boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; + n_boxes++; + } + + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + boxes[n_boxes].x1 = rects->unbounded.x; + boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height; + boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; + boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height; + n_boxes++; + } + + if (mask != NULL) { + for (i = 0; i < n_boxes; i++) { + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + boxes[i].x1 + mask_x, boxes[i].y1 + mask_y, + 0, 0, + boxes[i].x1, boxes[i].y1, + boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1); + } + } else { + pixman_color_t color = { 0, }; + + if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, + dst->pixman_image, + &color, + n_boxes, + boxes)) + { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_region_t *clip_region, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + // If we have no boxes then we need to clear the entire extents + // because we have nothing to draw. + if (boxes->num_boxes < 1 && clip_region == NULL) { + int x = extents->unbounded.x; + int y = extents->unbounded.y; + int width = extents->unbounded.width; + int height = extents->unbounded.height; + + pixman_color_t color = { 0 }; + pixman_box32_t box = { x, y, x + width, y + height }; + + if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, + dst->pixman_image, + &color, + 1, &box)) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + return CAIRO_STATUS_SUCCESS; + } + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); + int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); + int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); + int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); + + x1 = (x1 < 0 ? 0 : x1); + y1 = (y1 < 0 ? 0 : y1); + if (x2 <= x1 || y2 <= y1) + continue; + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + 0); + } + } + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return dst->base.is_clear && + dst->base.content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +/* low level compositor */ +typedef cairo_status_t +(*image_draw_func_t) (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *src, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); + +static pixman_image_t * +_create_composite_mask_pattern (cairo_clip_t *clip, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_region_t *clip_region = NULL; + pixman_image_t *mask; + cairo_status_t status; + cairo_bool_t need_clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (! _cairo_status_is_error (status)); + + /* The all-clipped state should never propagate this far. */ + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return NULL; + + /* Is it worth setting the clip region here? */ + if (clip_region != NULL) { + pixman_bool_t ret; + + pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y); + ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn); + pixman_region32_translate (&clip_region->rgn, extents->x, extents->y); + + if (! ret) { + pixman_image_unref (mask); + return NULL; + } + } + + status = draw_func (draw_closure, + mask, PIXMAN_a8, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) { + pixman_image_unref (mask); + return NULL; + } + + if (need_clip_surface) { + cairo_surface_t *tmp; + + tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8); + if (unlikely (tmp->status)) { + pixman_image_unref (mask); + return NULL; + } + + pixman_image_ref (mask); + + status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y); + cairo_surface_destroy (tmp); + if (unlikely (status)) { + pixman_image_unref (mask); + return NULL; + } + } + + if (clip_region != NULL) + pixman_image_set_clip_region (mask, NULL); + + return mask; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *mask; + + mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (pattern == NULL) { + if (dst->pixman_format == PIXMAN_a8) { + pixman_image_composite32 (_pixman_operator (op), + mask, NULL, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + pixman_image_t *src; + + src = _pixman_white_image (); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + pixman_image_unref (src); + } + } else { + pixman_image_t *src; + int src_x, src_y; + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + pixman_image_unref (src); + } + + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *tmp; + cairo_surface_t *clip_surface; + int clip_x, clip_y; + cairo_status_t status; + + tmp = pixman_image_create_bits (dst->pixman_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (tmp == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (src == NULL) { + status = (*draw_func) (draw_closure, + tmp, dst->pixman_format, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + } else { + /* Initialize the temporary surface from the destination surface */ + if (! dst->base.is_clear) { + pixman_image_composite32 (PIXMAN_OP_SRC, + dst->pixman_image, NULL, tmp, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height); + } + + status = (*draw_func) (draw_closure, + tmp, dst->pixman_format, + op, src, + extents->x, extents->y, + extents, NULL); + } + if (unlikely (status)) + goto CLEANUP_SURFACE; + + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + if (! dst->base.is_clear) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip_x, + extents->y - clip_y, + extents->x, extents->y, + extents->width, extents->height); +#else + /* Punch the clip out of the destination */ + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + NULL, dst->pixman_image, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now add the two results together */ + pixman_image_composite32 (PIXMAN_OP_ADD, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip_x, + extents->y - clip_y, + extents->x, extents->y, + extents->width, extents->height); +#endif + } else { + pixman_image_composite32 (PIXMAN_OP_SRC, + tmp, + ((cairo_image_surface_t *) clip_surface)->pixman_image, + dst->pixman_image, + 0, 0, + extents->x - clip_x, + extents->y - clip_y, + extents->x, extents->y, + extents->width, extents->height); + } + + CLEANUP_SURFACE: + pixman_image_unref (tmp); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *pattern, + image_draw_func_t draw_func, + void *draw_closure, + cairo_image_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *mask, *src; + int src_x, src_y; + + if (pattern == NULL) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = draw_func (draw_closure, + dst->pixman_image, dst->pixman_format, + CAIRO_OPERATOR_SOURCE, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) + return status; + + if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0); + + return status; + } + + /* Create a surface that is mask IN clip */ + mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + if (! dst->base.is_clear) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); +#else + /* Compute dest' = dest OUT (mask IN clip) */ + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + pixman_image_composite32 (PIXMAN_OP_ADD, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); +#endif + } else { + pixman_image_composite32 (PIXMAN_OP_SRC, + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + + pixman_image_unref (src); + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_clip_and_composite (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + image_draw_func_t draw_func, + void *draw_closure, + cairo_composite_rectangles_t*extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (_cairo_status_is_error (status))) + return status; + + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip_region != NULL) { + cairo_rectangle_int_t rect; + cairo_bool_t is_empty; + + cairo_region_get_extents (clip_region, &rect); + is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect); + if (unlikely (is_empty)) + return CAIRO_STATUS_SUCCESS; + + is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect); + if (unlikely (is_empty && extents->is_bounded)) + return CAIRO_STATUS_SUCCESS; + + if (cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + } + + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + if (reduce_alpha_op (dst, op, src)) { + op = CAIRO_OPERATOR_ADD; + src = NULL; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + if (op == CAIRO_OPERATOR_CLEAR) { + src = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + + if (need_clip_surface) { + if (extents->is_bounded) { + status = _clip_and_composite_with_mask (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } else { + status = _clip_and_composite_combine (clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } + } else { + status = draw_func (draw_closure, + dst->pixman_image, dst->pixman_format, + op, src, + 0, 0, + &extents->bounded, + clip_region); + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + status = _cairo_image_surface_fixup_unbounded (dst, extents, + need_clip_surface ? clip : NULL); + } + + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return status; +} + +#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) +#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x <= CAIRO_FIXED_16_16_MIN || + line->p1.x >= CAIRO_FIXED_16_16_MAX || + + line->p2.x <= CAIRO_FIXED_16_16_MIN || + line->p2.x >= CAIRO_FIXED_16_16_MAX || + + line->p1.y <= CAIRO_FIXED_16_16_MIN || + line->p1.y >= CAIRO_FIXED_16_16_MAX || + + line->p2.y <= CAIRO_FIXED_16_16_MIN || + line->p2.y >= CAIRO_FIXED_16_16_MAX; +} + +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + pixman_line_fixed_t *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + + +typedef struct { + cairo_trapezoid_t *traps; + int num_traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +static void +_pixman_image_add_traps (pixman_image_t *image, + int dst_x, int dst_y, + composite_traps_info_t *info) +{ + cairo_trapezoid_t *t = info->traps; + int num_traps = info->num_traps; + while (num_traps--) { + pixman_trapezoid_t trap; + + /* top/bottom will be clamped to surface bounds */ + trap.top = _cairo_fixed_to_16_16 (t->top); + trap.bottom = _cairo_fixed_to_16_16 (t->bottom); + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (_line_exceeds_16_16 (&t->left))) { + _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); + trap.left.p1.y = trap.top; + trap.left.p2.y = trap.bottom; + } else { + trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); + } + + if (unlikely (_line_exceeds_16_16 (&t->right))) { + _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); + trap.right.p1.y = trap.top; + trap.right.p2.y = trap.bottom; + } else { + trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); + } + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + + t++; + } +} + +static cairo_status_t +_composite_traps (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_traps_info_t *info = closure; + pixman_image_t *src, *mask; + pixman_format_code_t format; + int src_x = 0, src_y = 0; + cairo_status_t status; + + /* Special case adding trapezoids onto a mask surface; we want to avoid + * creating an intermediate temporary mask unnecessarily. + * + * We make the assumption here that the portion of the trapezoids + * contained within the surface is bounded by [dst_x,dst_y,width,height]; + * the Cairo core code passes bounds based on the trapezoid extents. + */ + format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; + if (dst_format == format && + (pattern == NULL || + (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern)))) + { + _pixman_image_add_traps (dst, dst_x, dst_y, info); + return CAIRO_STATUS_SUCCESS; + } + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + mask = pixman_image_create_bits (format, extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_SOURCE; + } + + _pixman_image_add_traps (mask, extents->x, extents->y, info); + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + + status = CAIRO_STATUS_SUCCESS; + CLEANUP_SOURCE: + pixman_image_unref (src); + + return status; +} + +static inline uint32_t +color_to_uint32 (const cairo_color_t *color) +{ + return + (color->alpha_short >> 8 << 24) | + (color->red_short >> 8 << 16) | + (color->green_short & 0xff00) | + (color->blue_short >> 8); +} + +static inline cairo_bool_t +color_to_pixel (const cairo_color_t *color, + pixman_format_code_t format, + uint32_t *pixel) +{ + uint32_t c; + + if (!(format == PIXMAN_a8r8g8b8 || + format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5 || + format == PIXMAN_a8)) + { + return FALSE; + } + + c = color_to_uint32 (color); + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { + c = ((c & 0xff000000) >> 0) | + ((c & 0x00ff0000) >> 16) | + ((c & 0x0000ff00) >> 0) | + ((c & 0x000000ff) << 16); + } + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { + c = ((c & 0xff000000) >> 24) | + ((c & 0x00ff0000) >> 8) | + ((c & 0x0000ff00) << 8) | + ((c & 0x000000ff) << 24); + } + + if (format == PIXMAN_a8) { + c = c >> 24; + } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { + c = ((((c) >> 3) & 0x001f) | + (((c) >> 5) & 0x07e0) | + (((c) >> 8) & 0xf800)); + } + + *pixel = c; + return TRUE; +} + +static inline cairo_bool_t +pattern_to_pixel (const cairo_solid_pattern_t *solid, + cairo_operator_t op, + pixman_format_code_t format, + uint32_t *pixel) +{ + if (op == CAIRO_OPERATOR_CLEAR) { + *pixel = 0; + return TRUE; + } + + if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + if (op == CAIRO_OPERATOR_OVER) { + if (solid->color.alpha_short >= 0xff00) + op = CAIRO_OPERATOR_SOURCE; + } + + if (op != CAIRO_OPERATOR_SOURCE) + return FALSE; + + return color_to_pixel (&solid->color, format, pixel); +} + +typedef struct _fill_span { + cairo_span_renderer_t base; + + uint8_t *mask_data; + pixman_image_t *src, *dst, *mask; +} fill_span_renderer_t; + +static cairo_status_t +_fill_span (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + fill_span_renderer_t *renderer = abstract_renderer; + uint8_t *row; + unsigned i; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + row = renderer->mask_data - spans[0].x; + for (i = 0; i < num_spans - 1; i++) { + /* We implement setting the most common single pixel wide + * span case to avoid the overhead of a memset call. + * Open coding setting longer spans didn't show a + * noticeable improvement over memset. + */ + if (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].x); + } + } + + do { + pixman_image_composite32 (PIXMAN_OP_OVER, + renderer->src, renderer->mask, renderer->dst, + 0, 0, 0, 0, + spans[0].x, y++, + spans[i].x - spans[0].x, 1); + } while (--height); + + return CAIRO_STATUS_SUCCESS; +} + +/* avoid using region code to re-validate boxes */ +static cairo_status_t +_fill_unaligned_boxes (cairo_image_surface_t *dst, + const cairo_pattern_t *pattern, + uint32_t pixel, + const cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + fill_span_renderer_t renderer; + cairo_rectangular_scan_converter_t converter; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int i; + + /* XXX + * using composite for fill: + * spiral-box-nonalign-evenodd-fill.512 2201957 2.202 + * spiral-box-nonalign-nonzero-fill.512 336726 0.337 + * spiral-box-pixalign-evenodd-fill.512 352256 0.352 + * spiral-box-pixalign-nonzero-fill.512 147056 0.147 + * using fill: + * spiral-box-nonalign-evenodd-fill.512 3174565 3.175 + * spiral-box-nonalign-nonzero-fill.512 182710 0.183 + * spiral-box-pixalign-evenodd-fill.512 353863 0.354 + * spiral-box-pixalign-nonzero-fill.512 147402 0.147 + * + * cairo-perf-trace seems to favour using fill. + */ + + renderer.base.render_rows = _fill_span; + renderer.dst = dst->pixman_image; + + if ((unsigned) extents->bounded.width <= sizeof (buf)) { + renderer.mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, 1, + (uint32_t *) buf, + sizeof (buf)); + } else { + renderer.mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, 1, + NULL, 0); + } + if (unlikely (renderer.mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask); + + renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); + if (unlikely (renderer.src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_MASK; + } + + _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); + + /* first blit any aligned part of the boxes */ + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_ceil (box[i].p1.x); + int y1 = _cairo_fixed_integer_ceil (box[i].p1.y); + int x2 = _cairo_fixed_integer_floor (box[i].p2.x); + int y2 = _cairo_fixed_integer_floor (box[i].p2.y); + + x1 = (x1 < 0 ? 0 : x1); + y1 = (y1 < 0 ? 0 : y1); + if (x2 > x1 && y2 > y1) { + cairo_box_t b; + + pixman_fill ((uint32_t *) dst->data, + dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + + /* top */ + if (! _cairo_fixed_is_integer (box[i].p1.y)) { + b.p1.x = box[i].p1.x; + b.p1.y = box[i].p1.y; + b.p2.x = box[i].p2.x; + b.p2.y = _cairo_fixed_from_int (y1); + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* left */ + if (! _cairo_fixed_is_integer (box[i].p1.x)) { + b.p1.x = box[i].p1.x; + b.p1.y = box[i].p1.y; + b.p2.x = _cairo_fixed_from_int (x1); + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* right */ + if (! _cairo_fixed_is_integer (box[i].p2.x)) { + b.p1.x = _cairo_fixed_from_int (x2); + b.p1.y = box[i].p1.y; + b.p2.x = box[i].p2.x; + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + + /* bottom */ + if (! _cairo_fixed_is_integer (box[i].p2.y)) { + b.p1.x = box[i].p1.x; + b.p1.y = _cairo_fixed_from_int (y2); + b.p2.x = box[i].p2.x; + b.p2.y = box[i].p2.y; + + status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + } else { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + } + } + } + + status = converter.base.generate (&converter.base, &renderer.base); + + CLEANUP_CONVERTER: + converter.base.destroy (&converter.base); + pixman_image_unref (renderer.src); + CLEANUP_MASK: + pixman_image_unref (renderer.mask); + + return status; +} + +typedef struct _cairo_image_surface_span_renderer { + cairo_span_renderer_t base; + + uint8_t *mask_data; + uint32_t mask_stride; +} cairo_image_surface_span_renderer_t; + +cairo_status_t +_cairo_image_surface_span (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + uint8_t *row; + unsigned i; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + /* XXX will it be quicker to repeat the sparse memset, + * or perform a simpler memcpy? + * The fairly dense spiral benchmarks suggests that the sparse + * memset is a win there as well. + */ + row = renderer->mask_data + y * renderer->mask_stride; + do { + for (i = 0; i < num_spans - 1; i++) { + if (! spans[i].coverage) + continue; + + /* We implement setting rendering the most common single + * pixel wide span case to avoid the overhead of a memset + * call. Open coding setting longer spans didn't show a + * noticeable improvement over memset. */ + if (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].x); + } + } + row += renderer->mask_stride; + } while (--height); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_composite_unaligned_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + cairo_image_surface_span_renderer_t renderer; + cairo_rectangular_scan_converter_t converter; + pixman_image_t *mask, *src; + cairo_status_t status; + const struct _cairo_boxes_chunk *chunk; + int i, src_x, src_y; + + i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height; + if ((unsigned) i <= sizeof (buf)) { + mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, + extents->bounded.height, + (uint32_t *) buf, + CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8)); + memset (buf, 0, i); + } else { + mask = pixman_image_create_bits (PIXMAN_a8, + extents->bounded.width, + extents->bounded.height, + NULL, 0); + } + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + renderer.base.render_rows = _cairo_image_surface_span; + renderer.mask_stride = pixman_image_get_stride (mask); + renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); + renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x; + + _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP; + } + } + + status = converter.base.generate (&converter.base, &renderer.base); + if (unlikely (status)) + goto CLEANUP; + + src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + pixman_image_unref (src); + + CLEANUP: + converter.base.destroy (&converter.base); + pixman_image_unref (mask); + + return status; +} + +static cairo_status_t +_composite_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_mask = FALSE; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + uint32_t pixel; + int i; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_mask && + (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) { + if (need_clip_mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, + dst->pixman_format, &pixel)) + { + return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents); + } + else + { + return _composite_unaligned_boxes (dst, op, pattern, boxes, extents); + } + } + } + + status = CAIRO_STATUS_SUCCESS; + if (! need_clip_mask && + pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format, + &pixel)) + { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); + int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); + int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); + int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); + + x1 = (x1 < 0 ? 0 : x1); + y1 = (y1 < 0 ? 0 : y1); + if (x2 <= x1 || y2 <= y1) + continue; + + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + } + } + } + else + { + pixman_image_t *src = NULL, *mask = NULL; + int src_x, src_y, mask_x = 0, mask_y = 0; + pixman_op_t pixman_op = _pixman_operator (op); + + if (need_clip_mask) { + cairo_surface_t *clip_surface; + int clip_x, clip_y; + + clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + mask_x = -clip_x; + mask_y = -clip_y; + + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = NULL; + pixman_op = PIXMAN_OP_OUT_REVERSE; + } + + mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; + } + + if (pattern != NULL) { + src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + src = mask; + src_x = mask_x; + src_y = mask_y; + mask = NULL; + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); + int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); + int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); + int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); + + if (x2 == x1 || y2 == y1) + continue; + + pixman_image_composite32 (pixman_op, + src, mask, dst->pixman_image, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1, y1, + x2 - x1, y2 - y1); + } + } + + if (pattern != NULL) + pixman_image_unref (src); + + if (! extents->is_bounded) { + status = + _cairo_image_surface_fixup_unbounded_boxes (dst, extents, + clip_region, boxes); + } + } + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_traps_t traps; + cairo_status_t status; + composite_traps_info_t info; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise render via a mask and composite in the usual fashion. */ + status = _cairo_traps_init_boxes (&traps, boxes); + if (unlikely (status)) + return status; + + info.num_traps = traps.num_traps; + info.traps = traps.traps; + info.antialias = antialias; + status = _clip_and_composite (dst, op, src, + _composite_traps, &info, + extents, clip); + + _cairo_traps_fini (&traps); + return status; +} + +static cairo_bool_t +_mono_edge_is_vertical (const cairo_line_t *line) +{ + return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); +} + +static cairo_bool_t +_traps_are_pixel_aligned (cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + if (! _mono_edge_is_vertical (&traps->traps[i].left) || + ! _mono_edge_is_vertical (&traps->traps[i].right)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } + + return TRUE; +} + +static void +_boxes_for_traps (cairo_boxes_t *boxes, + cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + _cairo_boxes_init (boxes); + + boxes->num_boxes = traps->num_traps; + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.count = traps->num_traps; + boxes->chunks.size = traps->num_traps; + + if (antialias != CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + boxes->chunks.base[i].p1.x = x1; + boxes->chunks.base[i].p1.y = y1; + boxes->chunks.base[i].p2.x = x2; + boxes->chunks.base[i].p2.y = y2; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } + } else { + boxes->is_pixel_aligned = TRUE; + + for (i = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + /* round down here to match Pixman's behavior when using traps. */ + boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); + boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); + boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); + boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); + } + } +} + +static cairo_status_t +_clip_and_composite_trapezoids (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t info; + cairo_bool_t need_clip_surface = FALSE; + cairo_status_t status; + + if (traps->num_traps == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + cairo_region_t *clip_region; + + status = _cairo_clip_get_region (clip, &clip_region); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (traps->has_intersections) { + if (traps->is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + else if (traps->is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + return status; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) && + (! need_clip_surface || + (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) + { + cairo_boxes_t boxes; + + _boxes_for_traps (&boxes, traps, antialias); + return _clip_and_composite_boxes (dst, op, src, + &boxes, antialias, + extents, clip); + } + + /* No fast path, exclude self-intersections and clip trapezoids. */ + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + info.traps = traps->traps; + info.num_traps = traps->num_traps; + info.antialias = antialias; + return _clip_and_composite (dst, op, src, + _composite_traps, &info, + extents, clip); +} + +static cairo_clip_path_t * +_clip_get_single_path (cairo_clip_t *clip) +{ + if (clip->path->prev == NULL) + return clip->path; + + return NULL; +} + +/* high level image interface */ + +static cairo_int_status_t +_cairo_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_clip_path_t *clip_path; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &rect, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && + extents.is_bounded && + (clip_path = _clip_get_single_path (clip)) != NULL) + { + status = _cairo_image_surface_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } + else + { + cairo_boxes_t boxes; + + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (surface, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_status_t +_composite_mask (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + const cairo_pattern_t *mask_pattern = closure; + pixman_image_t *src, *mask = NULL; + int src_x = 0, src_y = 0; + int mask_x = 0, mask_y = 0; + + if (src_pattern != NULL) { + src = _pixman_image_for_pattern (src_pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + mask = _pixman_image_for_pattern (mask_pattern, TRUE, extents, &mask_x, &mask_y); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + if (mask_pattern->has_component_alpha) + pixman_image_set_component_alpha (mask, TRUE); + } else { + src = _pixman_image_for_pattern (mask_pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (_pixman_operator (op), src, mask, dst, + extents->x + src_x, extents->y + src_y, + extents->x + mask_x, extents->y + mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + if (mask != NULL) + pixman_image_unref (mask); + pixman_image_unref (src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_image_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &rect, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + + have_clip = TRUE; + } + + status = _clip_and_composite (surface, op, source, + _composite_mask, (void *) mask, + &extents, clip); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_polygon_t *polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_spans_info_t; + +//#define USE_BOTOR_SCAN_CONVERTER +static cairo_status_t +_composite_spans (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE]; + composite_spans_info_t *info = closure; + cairo_image_surface_span_renderer_t renderer; +#if USE_BOTOR_SCAN_CONVERTER + cairo_box_t box; + cairo_botor_scan_converter_t converter; +#else + cairo_scan_converter_t *converter; +#endif + pixman_image_t *mask; + cairo_status_t status; + +#if USE_BOTOR_SCAN_CONVERTER + box.p1.x = _cairo_fixed_from_int (extents->x); + box.p1.y = _cairo_fixed_from_int (extents->y); + box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); + box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); + _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); + status = converter.base.add_polygon (&converter.base, info->polygon); +#else + converter = _cairo_tor_scan_converter_create (extents->x, extents->y, + extents->x + extents->width, + extents->y + extents->height, + info->fill_rule); + status = converter->add_polygon (converter, info->polygon); +#endif + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + /* TODO: support rendering to A1 surfaces (or: go add span + * compositing to pixman.) */ + + if (pattern == NULL && + dst_format == PIXMAN_a8 && + op == CAIRO_OPERATOR_SOURCE) + { + mask = dst; + dst = NULL; + } + else + { + int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8); + uint8_t *data = mask_buf; + + if (extents->height * stride <= (int) sizeof (mask_buf)) + memset (data, 0, extents->height * stride); + else + data = NULL, stride = 0; + + mask = pixman_image_create_bits (PIXMAN_a8, + extents->width, + extents->height, + (uint32_t *) data, + stride); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_CONVERTER; + } + } + + renderer.base.render_rows = _cairo_image_surface_span; + renderer.mask_stride = pixman_image_get_stride (mask); + renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); + if (dst != NULL) + renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; + else + renderer.mask_data -= dst_y * renderer.mask_stride + dst_x; + +#if USE_BOTOR_SCAN_CONVERTER + status = converter.base.generate (&converter.base, &renderer.base); +#else + status = converter->generate (converter, &renderer.base); +#endif + if (unlikely (status)) + goto CLEANUP_RENDERER; + + if (dst != NULL) { + pixman_image_t *src; + int src_x, src_y; + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_RENDERER; + } + + pixman_image_composite32 (_pixman_operator (op), src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, /* mask.x, mask.y */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + pixman_image_unref (src); + } + + CLEANUP_RENDERER: + if (dst != NULL) + pixman_image_unref (mask); + CLEANUP_CONVERTER: +#if USE_BOTOR_SCAN_CONVERTER + converter.base.destroy (&converter.base); +#else + converter->destroy (converter); +#endif + return status; +} + +static cairo_status_t +_clip_and_composite_polygon (cairo_image_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (polygon->num_edges == 0) { + cairo_traps_t traps; + + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + _cairo_traps_init (&traps); + status = _clip_and_composite_trapezoids (dst, op, src, + &traps, antialias, + extents, clip); + _cairo_traps_fini (&traps); + + return status; + } + + if (_cairo_operator_bounded_by_mask(op)) { + _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) + return CAIRO_STATUS_SUCCESS; + } + + if (antialias != CAIRO_ANTIALIAS_NONE) { + composite_spans_info_t info; + + info.polygon = polygon; + info.fill_rule = fill_rule; + info.antialias = antialias; + + status = _clip_and_composite (dst, op, src, + _composite_spans, &info, + extents, clip); + } else { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_trapezoids (dst, op, src, + &traps, antialias, + extents, clip); + } + + _cairo_traps_fini (&traps); + } + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &rect, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (path->is_rectilinear) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_polygon (surface, op, source, &polygon, + CAIRO_FILL_RULE_WINDING, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &rect, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (extents.is_bounded && clip != NULL) { + cairo_clip_path_t *clip_path; + + if (((clip_path = _clip_get_single_path (clip)) != NULL) && + _cairo_path_fixed_equal (&clip_path->path, path)) + { + clip = NULL; + } + } + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (_cairo_path_fixed_is_rectilinear_fill (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + } else { + cairo_polygon_t polygon; + + assert (! path->is_empty_fill); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_polygon (surface, op, source, &polygon, + fill_rule, antialias, + &extents, clip); + } + + _cairo_polygon_fini (&polygon); + } + + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_scaled_font_t *font; + cairo_glyph_t *glyphs; + int num_glyphs; +} composite_glyphs_info_t; + +static cairo_status_t +_composite_glyphs_via_mask (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_font_t *font = info->font; + cairo_glyph_t *glyphs = info->glyphs; + int num_glyphs = info->num_glyphs; + pixman_image_t *mask = NULL; + pixman_image_t *src; + pixman_image_t *white; + pixman_format_code_t mask_format = 0; /* silence gcc */ + cairo_status_t status; + int src_x, src_y; + int i; + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + white = _pixman_white_image (); + if (unlikely (white == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_scaled_font_freeze_cache (font); + + for (i = 0; i < num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (font, glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + goto CLEANUP; + + glyph_surface = scaled_glyph->surface; + + if (glyph_surface->width == 0 || glyph_surface->height == 0) + continue; + + /* To start, create the mask using the format from the first + * glyph. Later we'll deal with different formats. */ + if (mask == NULL) { + mask_format = glyph_surface->pixman_format; + mask = pixman_image_create_bits (mask_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + if (PIXMAN_FORMAT_RGB (mask_format)) + pixman_image_set_component_alpha (mask, TRUE); + } + + /* If we have glyphs of different formats, we "upgrade" the mask + * to the wider of the formats. */ + if (glyph_surface->pixman_format != mask_format && + PIXMAN_FORMAT_BPP (mask_format) < + PIXMAN_FORMAT_BPP (glyph_surface->pixman_format)) + { + pixman_image_t *new_mask; + + mask_format = glyph_surface->pixman_format; + new_mask = pixman_image_create_bits (mask_format, + extents->width, extents->height, + NULL, 0); + if (unlikely (new_mask == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + white, mask, new_mask, + 0, 0, 0, 0, 0, 0, + extents->width, extents->height); + + pixman_image_unref (mask); + mask = new_mask; + if (PIXMAN_FORMAT_RGB (mask_format)) + pixman_image_set_component_alpha (mask, TRUE); + } + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (glyphs[i].y - + glyph_surface->base.device_transform.y0); + if (glyph_surface->pixman_format == mask_format) { + pixman_image_composite32 (PIXMAN_OP_ADD, + glyph_surface->pixman_image, NULL, mask, + 0, 0, 0, 0, + x - extents->x, y - extents->y, + glyph_surface->width, + glyph_surface->height); + } else { + pixman_image_composite32 (PIXMAN_OP_ADD, + white, glyph_surface->pixman_image, mask, + 0, 0, 0, 0, + x - extents->x, y - extents->y, + glyph_surface->width, + glyph_surface->height); + } + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + +CLEANUP: + _cairo_scaled_font_thaw_cache (font); + if (mask != NULL) + pixman_image_unref (mask); + pixman_image_unref (src); + pixman_image_unref (white); + + return status; +} + +static cairo_status_t +_composite_glyphs (void *closure, + pixman_image_t *dst, + pixman_format_code_t dst_format, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_glyph_t *glyph_cache[64]; + pixman_op_t pixman_op = _pixman_operator (op); + pixman_image_t *src = NULL; + int src_x = 0, src_y = 0; + cairo_status_t status; + int i; + + memset (glyph_cache, 0, sizeof (glyph_cache)); + status = CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (info->font); + for (i = 0; i < info->num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width && glyph_surface->height) { + int x1, y1, x2, y2; + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[i].y - + glyph_surface->base.device_transform.y0); + + x1 = x; + if (x1 < extents->x) + x1 = extents->x; + x2 = x + glyph_surface->width; + if (x2 > extents->x + extents->width) + x2 = extents->x + extents->width; + + y1 = y; + if (y1 < extents->y) + y1 = extents->y; + y2 = y + glyph_surface->height; + if (y2 > extents->y + extents->height) + y2 = extents->y + extents->height; + + if (glyph_surface->format == CAIRO_FORMAT_A8 || + glyph_surface->format == CAIRO_FORMAT_A1 || + (glyph_surface->format == CAIRO_FORMAT_ARGB32 && + pixman_image_get_component_alpha (glyph_surface->pixman_image))) + { + if (unlikely (src == NULL)) { + if (pattern != NULL) { + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + src_x -= dst_x; + src_y -= dst_y; + } else { + src = _pixman_white_image (); + } + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + } + } + + pixman_image_composite32 (pixman_op, + src, glyph_surface->pixman_image, dst, + x1 + src_x, y1 + src_y, + x1 - x, y1 - y, + x1 - dst_x, y1 - dst_y, + x2 - x1, y2 - y1); + } else { + pixman_image_composite32 (pixman_op, + glyph_surface->pixman_image, NULL, dst, + x1 - x, y1 - y, + 0, 0, + x1 - dst_x, y1 - dst_y, + x2 - x1, y2 - y1); + } + } + } + _cairo_scaled_font_thaw_cache (info->font); + + if (src != NULL) + pixman_image_unref (src); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *num_remaining) +{ + cairo_image_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + composite_glyphs_info_t glyph_info; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_bool_t overlap; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &rect, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents.mask)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + + have_clip = TRUE; + } + + glyph_info.font = scaled_font; + glyph_info.glyphs = glyphs; + glyph_info.num_glyphs = num_glyphs; + + status = _clip_and_composite (surface, op, source, + overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs, + &glyph_info, + &extents, clip); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + *num_remaining = 0; + return status; +} + +static cairo_bool_t +_cairo_image_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_image_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_image_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); +} + +/* legacy interface kept for compatibility until surface-fallback is removed */ +static cairo_status_t +_cairo_image_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_image_surface_t *surface = abstract_surface; + + image_rect_out->x = 0; + image_rect_out->y = 0; + image_rect_out->width = surface->width; + image_rect_out->height = surface->height; + + *image_out = surface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ +} + +static cairo_status_t +_cairo_image_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_image_surface_t *surface = abstract_surface; + + if (src->backend == surface->base.backend) { + *clone_offset_x = *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_image_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_image_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + pixman_image_t *src; + int src_offset_x, src_offset_y; + cairo_status_t status; + + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + extents.source.x = src_x; + extents.source.y = src_y; + extents.source.width = width; + extents.source.height = height; + + extents.mask.x = mask_x; + extents.mask.y = mask_y; + extents.mask.width = width; + extents.mask.height = height; + + extents.bounded.x = dst_x; + extents.bounded.y = dst_y; + extents.bounded.width = width; + extents.bounded.height = height; + + extents.unbounded.x = 0; + extents.unbounded.y = 0; + extents.unbounded.width = dst->width; + extents.unbounded.height = dst->height; + + if (clip_region != NULL) { + cairo_rectangle_int_t rect; + + cairo_region_get_extents (clip_region, &rect); + if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) + return CAIRO_STATUS_SUCCESS; + } + + extents.is_bounded = _cairo_operator_bounded_by_either (op); + + src = _pixman_image_for_pattern (src_pattern, FALSE, &extents.source, &src_offset_x, &src_offset_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = CAIRO_STATUS_SUCCESS; + if (mask_pattern != NULL) { + pixman_image_t *mask; + int mask_offset_x, mask_offset_y; + + mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents.mask, &mask_offset_x, &mask_offset_y); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + src_x + src_offset_x, + src_y + src_offset_y, + mask_x + mask_offset_x, + mask_y + mask_offset_y, + dst_x, dst_y, width, height); + + pixman_image_unref (mask); + } else { + pixman_image_composite32 (_pixman_operator (op), + src, NULL, dst->pixman_image, + src_x + src_offset_x, + src_y + src_offset_y, + 0, 0, + dst_x, dst_y, width, height); + } + + pixman_image_unref (src); + + if (! extents.is_bounded) + status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); + + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_image_surface_t *surface = abstract_surface; + + pixman_color_t pixman_color; + pixman_box32_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; + pixman_box32_t *pixman_boxes = stack_boxes; + int i; + + cairo_int_status_t status; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pixman_color.red = color->red_short; + pixman_color.green = color->green_short; + pixman_color.blue = color->blue_short; + pixman_color.alpha = color->alpha_short; + + if (num_rects > ARRAY_LENGTH (stack_boxes)) { + pixman_boxes = _cairo_malloc_ab (num_rects, sizeof (pixman_box32_t)); + if (unlikely (pixman_boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < num_rects; i++) { + pixman_boxes[i].x1 = rects[i].x; + pixman_boxes[i].y1 = rects[i].y; + pixman_boxes[i].x2 = rects[i].x + rects[i].width; + pixman_boxes[i].y2 = rects[i].y + rects[i].height; + } + + status = CAIRO_STATUS_SUCCESS; + if (! pixman_image_fill_boxes (_pixman_operator (op), + surface->pixman_image, + &pixman_color, + num_rects, + pixman_boxes)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + if (pixman_boxes != stack_boxes) + free (pixman_boxes); + + return status; +} + +static cairo_int_status_t +_cairo_image_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_image_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + cairo_pattern_union_t source_pattern; + composite_traps_info_t info; + cairo_status_t status; + + if (height == 0 || width == 0) + return CAIRO_STATUS_SUCCESS; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + extents.source.x = src_x; + extents.source.y = src_y; + extents.source.width = width; + extents.source.height = height; + + extents.mask.x = dst_x; + extents.mask.y = dst_y; + extents.mask.width = width; + extents.mask.height = height; + + extents.bounded.x = dst_x; + extents.bounded.y = dst_y; + extents.bounded.width = width; + extents.bounded.height = height; + + extents.unbounded.x = 0; + extents.unbounded.y = 0; + extents.unbounded.width = dst->width; + extents.unbounded.height = dst->height; + + if (clip_region != NULL) { + cairo_rectangle_int_t rect; + + cairo_region_get_extents (clip_region, &rect); + if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) + return CAIRO_STATUS_SUCCESS; + } + + extents.is_bounded = _cairo_operator_bounded_by_either (op); + + if (clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + _cairo_pattern_init_static_copy (&source_pattern.base, pattern); + cairo_matrix_translate (&source_pattern.base.matrix, + src_x - extents.bounded.x, + src_y - extents.bounded.y); + + info.traps = traps; + info.num_traps = num_traps; + info.antialias = antialias; + status = _composite_traps (&info, + dst->pixman_image, + dst->pixman_format, + op, + &source_pattern.base, + 0, 0, + &extents.bounded, + clip_region); + + if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded) + status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); + + if (clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return status; +} + +typedef struct _legacy_image_surface_span_renderer { + cairo_span_renderer_t base; + + cairo_operator_t op; + const cairo_pattern_t *pattern; + cairo_antialias_t antialias; + cairo_region_t *clip_region; + + pixman_image_t *mask; + uint8_t *mask_data; + uint32_t mask_stride; + + cairo_image_surface_t *dst; + cairo_composite_rectangles_t composite_rectangles; +} legacy_image_surface_span_renderer_t; + +void +_cairo_image_surface_span_render_row ( + int y, + const cairo_half_open_span_t *spans, + unsigned num_spans, + uint8_t *data, + uint32_t stride) +{ + uint8_t *row; + unsigned i; + + if (num_spans == 0) + return; + + row = data + y * stride; + for (i = 0; i < num_spans - 1; i++) { + if (! spans[i].coverage) + continue; + + /* We implement setting the most common single pixel wide + * span case to avoid the overhead of a memset call. + * Open coding setting longer spans didn't show a + * noticeable improvement over memset. + */ + if (spans[i+1].x == spans[i].x + 1) { + row[spans[i].x] = spans[i].coverage; + } else { + memset (row + spans[i].x, + spans[i].coverage, + spans[i+1].x - spans[i].x); + } + } +} + +static cairo_status_t +_cairo_image_surface_span_renderer_render_rows ( + void *abstract_renderer, + int y, + int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; + while (height--) + _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_span_renderer_destroy (void *abstract_renderer) +{ + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; + if (renderer == NULL) + return; + + pixman_image_unref (renderer->mask); + + free (renderer); +} + +static cairo_status_t +_cairo_image_surface_span_renderer_finish (void *abstract_renderer) +{ + legacy_image_surface_span_renderer_t *renderer = abstract_renderer; + cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; + cairo_image_surface_t *dst = renderer->dst; + pixman_image_t *src; + int src_x, src_y; + cairo_status_t status; + + if (renderer->clip_region != NULL) { + status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region); + if (unlikely (status)) + return status; + } + + src = _pixman_image_for_pattern (renderer->pattern, FALSE, &rects->bounded, &src_x, &src_y); + if (src == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = CAIRO_STATUS_SUCCESS; + pixman_image_composite32 (_pixman_operator (renderer->op), + src, + renderer->mask, + dst->pixman_image, + rects->bounded.x + src_x, + rects->bounded.y + src_y, + 0, 0, + rects->bounded.x, rects->bounded.y, + rects->bounded.width, rects->bounded.height); + + if (! rects->is_bounded) + status = _cairo_image_surface_fixup_unbounded (dst, rects, NULL); + + if (renderer->clip_region != NULL) + _cairo_image_surface_unset_clip_region (dst); + + return status; +} + +static cairo_bool_t +_cairo_image_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias) +{ + return TRUE; + (void) op; + (void) pattern; + (void) abstract_dst; + (void) antialias; +} + +static cairo_span_renderer_t * +_cairo_image_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) +{ + cairo_image_surface_t *dst = abstract_dst; + legacy_image_surface_span_renderer_t *renderer; + + renderer = calloc(1, sizeof(*renderer)); + if (unlikely (renderer == NULL)) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); + + renderer->base.destroy = _cairo_image_surface_span_renderer_destroy; + renderer->base.finish = _cairo_image_surface_span_renderer_finish; + renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows; + renderer->op = op; + renderer->pattern = pattern; + renderer->antialias = antialias; + renderer->dst = dst; + renderer->clip_region = clip_region; + + renderer->composite_rectangles = *rects; + + /* TODO: support rendering to A1 surfaces (or: go add span + * compositing to pixman.) */ + renderer->mask = pixman_image_create_bits (PIXMAN_a8, + rects->bounded.width, + rects->bounded.height, + NULL, 0); + if (renderer->mask == NULL) { + free (renderer); + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + renderer->mask_stride = pixman_image_get_stride (renderer->mask); + renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride; + + return &renderer->base; +} + +/** + * _cairo_surface_is_image: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_image_surface_t + * + * Return value: %TRUE if the surface is an image surface + **/ +cairo_bool_t +_cairo_surface_is_image (const cairo_surface_t *surface) +{ + return surface->backend == &_cairo_image_surface_backend; +} + +const cairo_surface_backend_t _cairo_image_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_image_surface_create_similar, + _cairo_image_surface_finish, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_acquire_dest_image, + _cairo_image_surface_release_dest_image, + _cairo_image_surface_clone_similar, + _cairo_image_surface_composite, + _cairo_image_surface_fill_rectangles, + _cairo_image_surface_composite_trapezoids, + _cairo_image_surface_create_span_renderer, + _cairo_image_surface_check_span_renderer, + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_image_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_image_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark dirty */ + NULL, /* font_fini */ + NULL, /* glyph_fini */ + + _cairo_image_surface_paint, + _cairo_image_surface_mask, + _cairo_image_surface_stroke, + _cairo_image_surface_fill, + _cairo_image_surface_glyphs, + NULL, /* show_text_glyphs */ + NULL, /* snapshot */ + NULL, /* is_similar */ +}; + +/* A convenience function for when one needs to coerce an image + * surface to an alternate format. */ +cairo_image_surface_t * +_cairo_image_surface_coerce (cairo_image_surface_t *surface) +{ + return _cairo_image_surface_coerce_to_format (surface, + _cairo_format_from_content (surface->base.content)); + +} + +/* A convenience function for when one needs to coerce an image + * surface to an alternate format. */ +cairo_image_surface_t * +_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, + cairo_format_t format) +{ + cairo_image_surface_t *clone; + cairo_status_t status; + + status = surface->base.status; + if (unlikely (status)) + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); + + if (surface->format == format) + return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); + + clone = (cairo_image_surface_t *) + cairo_image_surface_create (format, surface->width, surface->height); + if (unlikely (clone->base.status)) + return clone; + + pixman_image_composite32 (PIXMAN_OP_SRC, + surface->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + surface->width, surface->height); + clone->base.is_clear = FALSE; + + clone->base.device_transform = + surface->base.device_transform; + clone->base.device_transform_inverse = + surface->base.device_transform_inverse; + + return clone; +} + +cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image) +{ + int x, y; + + if (image->transparency != CAIRO_IMAGE_UNKNOWN) + return image->transparency; + + if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) + return image->transparency = CAIRO_IMAGE_IS_OPAQUE; + + if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { + if (image->format == CAIRO_FORMAT_A1) + return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + else + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + } + + if (image->format == CAIRO_FORMAT_RGB16_565) { + image->transparency = CAIRO_IMAGE_IS_OPAQUE; + return CAIRO_IMAGE_IS_OPAQUE; + } + + if (image->format != CAIRO_FORMAT_ARGB32) + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + + image->transparency = CAIRO_IMAGE_IS_OPAQUE; + for (y = 0; y < image->height; y++) { + uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel++) { + int a = (*pixel & 0xff000000) >> 24; + if (a > 0 && a < 255) { + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + } else if (a == 0) { + image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + } + } + } + + return image->transparency; +} diff --git a/libs/cairo/src/cairo-list-private.h b/libs/cairo/src/cairo-list-private.h new file mode 100644 index 000000000..ca4e368fc --- /dev/null +++ b/libs/cairo/src/cairo-list-private.h @@ -0,0 +1,183 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_LIST_PRIVATE_H +#define CAIRO_LIST_PRIVATE_H + +#include "cairo-compiler-private.h" + +/* Basic circular, doubly linked list implementation */ + +typedef struct _cairo_list { + struct _cairo_list *next, *prev; +} cairo_list_t; + +#define cairo_list_entry(ptr, type, member) \ + cairo_container_of(ptr, type, member) + +#define cairo_list_first_entry(ptr, type, member) \ + cairo_list_entry((ptr)->next, type, member) + +#define cairo_list_last_entry(ptr, type, member) \ + cairo_list_entry((ptr)->prev, type, member) + +#define cairo_list_foreach(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define cairo_list_foreach_entry(pos, type, head, member) \ + for (pos = cairo_list_entry((head)->next, type, member);\ + &pos->member != (head); \ + pos = cairo_list_entry(pos->member.next, type, member)) + +#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \ + for (pos = cairo_list_entry ((head)->next, type, member),\ + n = cairo_list_entry (pos->member.next, type, member);\ + &pos->member != (head); \ + pos = n, n = cairo_list_entry (n->member.next, type, member)) + +#define cairo_list_foreach_entry_reverse(pos, type, head, member) \ + for (pos = cairo_list_entry((head)->prev, type, member);\ + &pos->member != (head); \ + pos = cairo_list_entry(pos->member.prev, type, member)) + +#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \ + for (pos = cairo_list_entry((head)->prev, type, member),\ + n = cairo_list_entry (pos->member.prev, type, member);\ + &pos->member != (head); \ + pos = n, n = cairo_list_entry (n->member.prev, type, member)) + +#ifdef CAIRO_LIST_DEBUG +static inline void +_cairo_list_validate (const cairo_list_t *link) +{ + assert (link->next->prev == link); + assert (link->prev->next == link); +} +static inline void +cairo_list_validate (const cairo_list_t *head) +{ + cairo_list_t *link; + + cairo_list_foreach (link, head) + _cairo_list_validate (link); +} +static inline cairo_bool_t +cairo_list_is_empty (const cairo_list_t *head); +static inline void +cairo_list_validate_is_empty (const cairo_list_t *head) +{ + assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev)); +} +#else +#define _cairo_list_validate(link) +#define cairo_list_validate(head) +#define cairo_list_validate_is_empty(head) +#endif + +static inline void +cairo_list_init (cairo_list_t *entry) +{ + entry->next = entry; + entry->prev = entry; +} + +static inline void +__cairo_list_add (cairo_list_t *entry, + cairo_list_t *prev, + cairo_list_t *next) +{ + next->prev = entry; + entry->next = next; + entry->prev = prev; + prev->next = entry; +} + +static inline void +cairo_list_add (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + cairo_list_validate_is_empty (entry); + __cairo_list_add (entry, head, head->next); + cairo_list_validate (head); +} + +static inline void +cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + cairo_list_validate_is_empty (entry); + __cairo_list_add (entry, head->prev, head); + cairo_list_validate (head); +} + +static inline void +__cairo_list_del (cairo_list_t *prev, cairo_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +cairo_list_del (cairo_list_t *entry) +{ + __cairo_list_del (entry->prev, entry->next); + cairo_list_init (entry); +} + +static inline void +cairo_list_move (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + __cairo_list_del (entry->prev, entry->next); + __cairo_list_add (entry, head, head->next); + cairo_list_validate (head); +} + +static inline void +cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + __cairo_list_del (entry->prev, entry->next); + __cairo_list_add (entry, head->prev, head); + cairo_list_validate (head); +} + +static inline void +cairo_list_swap (cairo_list_t *entry, cairo_list_t *other) +{ + __cairo_list_add (entry, other->prev, other->next); + cairo_list_init (other); +} + +static inline cairo_bool_t +cairo_list_is_first (const cairo_list_t *entry, + const cairo_list_t *head) +{ + cairo_list_validate (head); + return entry->prev == head; +} + +static inline cairo_bool_t +cairo_list_is_last (const cairo_list_t *entry, + const cairo_list_t *head) +{ + cairo_list_validate (head); + return entry->next == head; +} + +static inline cairo_bool_t +cairo_list_is_empty (const cairo_list_t *head) +{ + cairo_list_validate (head); + return head->next == head; +} + +static inline cairo_bool_t +cairo_list_is_singular (const cairo_list_t *head) +{ + cairo_list_validate (head); + return head->next == head || head->next == head->prev; +} + +#endif /* CAIRO_LIST_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-lzw.c b/libs/cairo/src/cairo-lzw.c new file mode 100644 index 000000000..e98e613bc --- /dev/null +++ b/libs/cairo/src/cairo-lzw.c @@ -0,0 +1,372 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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<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/libs/cairo/src/cairo-malloc-private.h b/libs/cairo/src/cairo-malloc-private.h new file mode 100644 index 000000000..765fb65b4 --- /dev/null +++ b/libs/cairo/src/cairo-malloc-private.h @@ -0,0 +1,116 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_MALLOC_PRIVATE_H +#define CAIRO_MALLOC_PRIVATE_H + +#include "cairo-wideint-private.h" + +#if HAVE_MEMFAULT +#include +#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT() +#else +#define CAIRO_INJECT_FAULT() 0 +#endif + +/** + * _cairo_malloc: + * @size: size in bytes + * + * Allocate @size memory using malloc(). + * The memory should be freed using free(). + * malloc is skipped, if 0 bytes are requested, and %NULL will be returned. + * + * Return value: A pointer to the newly allocated memory, or %NULL in + * case of malloc() failure or size is 0. + */ + +#define _cairo_malloc(size) \ + ((size) ? malloc((unsigned) (size)) : NULL) + +/** + * _cairo_malloc_ab: + * @n: number of elements to allocate + * @size: size of each element + * + * Allocates @n*@size memory using _cairo_malloc(), taking care to not + * overflow when doing the multiplication. Behaves much like + * calloc(), except that the returned memory is not set to zero. + * The memory should be freed using free(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * Return value: A pointer to the newly allocated memory, or %NULL in + * case of malloc() failure or overflow. + */ + +#define _cairo_malloc_ab(a, size) \ + ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ + _cairo_malloc((unsigned) (a) * (unsigned) (size))) + +/** + * _cairo_realloc_ab: + * @ptr: original pointer to block of memory to be resized + * @n: number of elements to allocate + * @size: size of each element + * + * Reallocates @ptr a block of @n*@size memory using realloc(), taking + * care to not overflow when doing the multiplication. The memory + * should be freed using free(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * Return value: A pointer to the newly allocated memory, or %NULL in + * case of realloc() failure or overflow (whereupon the original block + * of memory * is left untouched). + */ + +#define _cairo_realloc_ab(ptr, a, size) \ + ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ + realloc(ptr, (unsigned) (a) * (unsigned) (size))) + +/** + * _cairo_malloc_abc: + * @n: first factor of number of elements to allocate + * @b: second factor of number of elements to allocate + * @size: size of each element + * + * Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not + * overflow when doing the multiplication. Behaves like + * _cairo_malloc_ab(). The memory should be freed using free(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * Return value: A pointer to the newly allocated memory, or %NULL in + * case of malloc() failure or overflow. + */ + +#define _cairo_malloc_abc(a, b, size) \ + ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \ + (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \ + _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size))) + +/** + * _cairo_malloc_ab_plus_c: + * @n: number of elements to allocate + * @size: size of each element + * @k: additional size to allocate + * + * Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not + * overflow when doing the arithmetic. Behaves like + * _cairo_malloc_ab(). The memory should be freed using free(). + * + * Return value: A pointer to the newly allocated memory, or %NULL in + * case of malloc() failure or overflow. + */ + +#define _cairo_malloc_ab_plus_c(n, size, k) \ + ((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \ + (unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \ + _cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k))) + +#endif /* CAIRO_MALLOC_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-matrix.c b/libs/cairo/src/cairo-matrix.c new file mode 100644 index 000000000..b9691b8ee --- /dev/null +++ b/libs/cairo/src/cairo-matrix.c @@ -0,0 +1,974 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + +/** + * SECTION:cairo-matrix + * @Title: cairo_matrix_t + * @Short_Description: Generic matrix operations + * @See_Also: #cairo_t + * + * #cairo_matrix_t is used throughout cairo to convert between different + * coordinate spaces. A #cairo_matrix_t holds an affine transformation, + * such as a scale, rotation, shear, or a combination of these. + * The transformation of a point (x,y) + * is given by: + * + * + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; + * + * + * The current transformation matrix of a #cairo_t, represented as a + * #cairo_matrix_t, defines the transformation from user-space + * coordinates to device-space coordinates. See cairo_get_matrix() and + * cairo_set_matrix(). + */ + +static void +_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); + +static void +_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); + +/** + * cairo_matrix_init_identity: + * @matrix: a #cairo_matrix_t + * + * Modifies @matrix to be an identity transformation. + **/ +void +cairo_matrix_init_identity (cairo_matrix_t *matrix) +{ + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_identity); + +/** + * cairo_matrix_init: + * @matrix: a #cairo_matrix_t + * @xx: xx component of the affine transformation + * @yx: yx component of the affine transformation + * @xy: xy component of the affine transformation + * @yy: yy component of the affine transformation + * @x0: X translation component of the affine transformation + * @y0: Y translation component of the affine transformation + * + * Sets @matrix to be the affine transformation given by + * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given + * by: + * + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; + * + **/ +void +cairo_matrix_init (cairo_matrix_t *matrix, + double xx, double yx, + + double xy, double yy, + double x0, double y0) +{ + matrix->xx = xx; matrix->yx = yx; + matrix->xy = xy; matrix->yy = yy; + matrix->x0 = x0; matrix->y0 = y0; +} +slim_hidden_def(cairo_matrix_init); + +/** + * _cairo_matrix_get_affine: + * @matrix: a #cairo_matrix_t + * @xx: location to store xx component of matrix + * @yx: location to store yx component of matrix + * @xy: location to store xy component of matrix + * @yy: location to store yy component of matrix + * @x0: location to store x0 (X-translation component) of matrix, or %NULL + * @y0: location to store y0 (Y-translation component) of matrix, or %NULL + * + * Gets the matrix values for the affine transformation that @matrix represents. + * See cairo_matrix_init(). + * + * + * This function is a leftover from the old public API, but is still + * mildly useful as an internal means for getting at the matrix + * members in a positional way. For example, when reassigning to some + * external matrix type, or when renaming members to more meaningful + * names (such as a,b,c,d,e,f) for particular manipulations. + **/ +void +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0) +{ + *xx = matrix->xx; + *yx = matrix->yx; + + *xy = matrix->xy; + *yy = matrix->yy; + + if (x0) + *x0 = matrix->x0; + if (y0) + *y0 = matrix->y0; +} + +/** + * cairo_matrix_init_translate: + * @matrix: a #cairo_matrix_t + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Initializes @matrix to a transformation that translates by @tx and + * @ty in the X and Y dimensions, respectively. + **/ +void +cairo_matrix_init_translate (cairo_matrix_t *matrix, + double tx, double ty) +{ + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + tx, ty); +} +slim_hidden_def(cairo_matrix_init_translate); + +/** + * cairo_matrix_translate: + * @matrix: a #cairo_matrix_t + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Applies a translation by @tx, @ty to the transformation in + * @matrix. The effect of the new transformation is to first translate + * the coordinates by @tx and @ty, then apply the original transformation + * to the coordinates. + **/ +void +cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_translate (&tmp, tx, ty); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} +slim_hidden_def (cairo_matrix_translate); + +/** + * cairo_matrix_init_scale: + * @matrix: a #cairo_matrix_t + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction + * + * Initializes @matrix to a transformation that scales by @sx and @sy + * in the X and Y dimensions, respectively. + **/ +void +cairo_matrix_init_scale (cairo_matrix_t *matrix, + double sx, double sy) +{ + cairo_matrix_init (matrix, + sx, 0, + 0, sy, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_scale); + +/** + * cairo_matrix_scale: + * @matrix: a #cairo_matrix_t + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction + * + * Applies scaling by @sx, @sy to the transformation in @matrix. The + * effect of the new transformation is to first scale the coordinates + * by @sx and @sy, then apply the original transformation to the coordinates. + **/ +void +cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_scale (&tmp, sx, sy); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} +slim_hidden_def(cairo_matrix_scale); + +/** + * cairo_matrix_init_rotate: + * @matrix: a #cairo_matrix_t + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * + * Initialized @matrix to a transformation that rotates by @radians. + **/ +void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians) +{ + double s; + double c; + + s = sin (radians); + c = cos (radians); + + cairo_matrix_init (matrix, + c, s, + -s, c, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_rotate); + +/** + * cairo_matrix_rotate: + * @matrix: a #cairo_matrix_t + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * + * Applies rotation by @radians to the transformation in + * @matrix. The effect of the new transformation is to first rotate the + * coordinates by @radians, then apply the original transformation + * to the coordinates. + **/ +void +cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_rotate (&tmp, radians); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} + +/** + * cairo_matrix_multiply: + * @result: a #cairo_matrix_t in which to store the result + * @a: a #cairo_matrix_t + * @b: a #cairo_matrix_t + * + * Multiplies the affine transformations in @a and @b together + * and stores the result in @result. The effect of the resulting + * transformation is to first apply the transformation in @a to the + * coordinates and then apply the transformation in @b to the + * coordinates. + * + * It is allowable for @result to be identical to either @a or @b. + **/ +/* + * XXX: The ordering of the arguments to this function corresponds + * to [row_vector]*A*B. If we want to use column vectors instead, + * then we need to switch the two arguments and fix up all + * uses. + */ +void +cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) +{ + cairo_matrix_t r; + + r.xx = a->xx * b->xx + a->yx * b->xy; + r.yx = a->xx * b->yx + a->yx * b->yy; + + r.xy = a->xy * b->xx + a->yy * b->xy; + r.yy = a->xy * b->yx + a->yy * b->yy; + + r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; + r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; + + *result = r; +} +slim_hidden_def(cairo_matrix_multiply); + +/** + * cairo_matrix_transform_distance: + * @matrix: a #cairo_matrix_t + * @dx: X component of a distance vector. An in/out parameter + * @dy: Y component of a distance vector. An in/out parameter + * + * Transforms the distance vector (@dx,@dy) by @matrix. This is + * similar to cairo_matrix_transform_point() except that the translation + * components of the transformation are ignored. The calculation of + * the returned vector is as follows: + * + * + * dx2 = dx1 * a + dy1 * c; + * dy2 = dx1 * b + dy1 * d; + * + * + * Affine transformations are position invariant, so the same vector + * always transforms to the same vector. If (@x1,@y1) transforms + * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to + * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. + **/ +void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) +{ + double new_x, new_y; + + new_x = (matrix->xx * *dx + matrix->xy * *dy); + new_y = (matrix->yx * *dx + matrix->yy * *dy); + + *dx = new_x; + *dy = new_y; +} +slim_hidden_def(cairo_matrix_transform_distance); + +/** + * cairo_matrix_transform_point: + * @matrix: a #cairo_matrix_t + * @x: X position. An in/out parameter + * @y: Y position. An in/out parameter + * + * Transforms the point (@x, @y) by @matrix. + **/ +void +cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y) +{ + cairo_matrix_transform_distance (matrix, x, y); + + *x += matrix->x0; + *y += matrix->y0; +} +slim_hidden_def(cairo_matrix_transform_point); + +void +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight) +{ + int i; + double quad_x[4], quad_y[4]; + double min_x, max_x; + double min_y, max_y; + + if (matrix->xy == 0. && matrix->yx == 0.) { + /* non-rotation/skew matrix, just map the two extreme points */ + + if (matrix->xx != 1.) { + quad_x[0] = *x1 * matrix->xx; + quad_x[1] = *x2 * matrix->xx; + if (quad_x[0] < quad_x[1]) { + *x1 = quad_x[0]; + *x2 = quad_x[1]; + } else { + *x1 = quad_x[1]; + *x2 = quad_x[0]; + } + } + if (matrix->x0 != 0.) { + *x1 += matrix->x0; + *x2 += matrix->x0; + } + + if (matrix->yy != 1.) { + quad_y[0] = *y1 * matrix->yy; + quad_y[1] = *y2 * matrix->yy; + if (quad_y[0] < quad_y[1]) { + *y1 = quad_y[0]; + *y2 = quad_y[1]; + } else { + *y1 = quad_y[1]; + *y2 = quad_y[0]; + } + } + if (matrix->y0 != 0.) { + *y1 += matrix->y0; + *y2 += matrix->y0; + } + + if (is_tight) + *is_tight = TRUE; + + return; + } + + /* general matrix */ + quad_x[0] = *x1; + quad_y[0] = *y1; + cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); + + quad_x[1] = *x2; + quad_y[1] = *y1; + cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]); + + quad_x[2] = *x1; + quad_y[2] = *y2; + cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]); + + quad_x[3] = *x2; + quad_y[3] = *y2; + cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]); + + min_x = max_x = quad_x[0]; + min_y = max_y = quad_y[0]; + + for (i=1; i < 4; i++) { + if (quad_x[i] < min_x) + min_x = quad_x[i]; + if (quad_x[i] > max_x) + max_x = quad_x[i]; + + if (quad_y[i] < min_y) + min_y = quad_y[i]; + if (quad_y[i] > max_y) + max_y = quad_y[i]; + } + + *x1 = min_x; + *y1 = min_y; + *x2 = max_x; + *y2 = max_y; + + if (is_tight) { + /* it's tight if and only if the four corner points form an axis-aligned + rectangle. + And that's true if and only if we can derive corners 0 and 3 from + corners 1 and 2 in one of two straightforward ways... + We could use a tolerance here but for now we'll fall back to FALSE in the case + of floating point error. + */ + *is_tight = + (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] && + quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) || + (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] && + quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]); + } +} + +cairo_private void +_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, + cairo_box_t *bbox, + cairo_bool_t *is_tight) +{ + double x1, y1, x2, y2; + + _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2); + _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight); + _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2); +} + +static void +_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) +{ + matrix->xx *= scalar; + matrix->yx *= scalar; + + matrix->xy *= scalar; + matrix->yy *= scalar; + + matrix->x0 *= scalar; + matrix->y0 *= scalar; +} + +/* This function isn't a correct adjoint in that the implicit 1 in the + homogeneous result should actually be ad-bc instead. But, since this + adjoint is only used in the computation of the inverse, which + divides by det (A)=ad-bc anyway, everything works out in the end. */ +static void +_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) +{ + /* adj (A) = transpose (C:cofactor (A,i,j)) */ + double a, b, c, d, tx, ty; + + _cairo_matrix_get_affine (matrix, + &a, &b, + &c, &d, + &tx, &ty); + + cairo_matrix_init (matrix, + d, -b, + -c, a, + c*ty - d*tx, b*tx - a*ty); +} + +/** + * cairo_matrix_invert: + * @matrix: a #cairo_matrix_t + * + * Changes @matrix to be the inverse of its original value. Not + * all transformation matrices have inverses; if the matrix + * collapses points together (it is degenerate), + * then it has no inverse and this function will fail. + * + * Returns: If @matrix has an inverse, modifies @matrix to + * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, + * returns %CAIRO_STATUS_INVALID_MATRIX. + **/ +cairo_status_t +cairo_matrix_invert (cairo_matrix_t *matrix) +{ + double det; + + /* Simple scaling|translation matrices are quite common... */ + if (matrix->xy == 0. && matrix->yx == 0.) { + matrix->x0 = -matrix->x0; + matrix->y0 = -matrix->y0; + + if (matrix->xx != 1.) { + if (matrix->xx == 0.) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + matrix->xx = 1. / matrix->xx; + matrix->x0 *= matrix->xx; + } + + if (matrix->yy != 1.) { + if (matrix->yy == 0.) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + matrix->yy = 1. / matrix->yy; + matrix->y0 *= matrix->yy; + } + + return CAIRO_STATUS_SUCCESS; + } + + /* inv (A) = 1/det (A) * adj (A) */ + det = _cairo_matrix_compute_determinant (matrix); + + if (! ISFINITE (det)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (det == 0) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_matrix_compute_adjoint (matrix); + _cairo_matrix_scalar_multiply (matrix, 1 / det); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def(cairo_matrix_invert); + +cairo_bool_t +_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) +{ + double det; + + det = _cairo_matrix_compute_determinant (matrix); + + return ISFINITE (det) && det != 0.; +} + +cairo_bool_t +_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) +{ + return matrix->xx == 0. && + matrix->xy == 0. && + matrix->yx == 0. && + matrix->yy == 0.; +} + +double +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) +{ + double a, b, c, d; + + a = matrix->xx; b = matrix->yx; + c = matrix->xy; d = matrix->yy; + + return a*d - b*c; +} + +/** + * _cairo_matrix_compute_basis_scale_factors: + * @matrix: a matrix + * @basis_scale: the scale factor in the direction of basis + * @normal_scale: the scale factor in the direction normal to the basis + * @x_basis: basis to use. X basis if true, Y basis otherwise. + * + * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1] + * otherwise, and M is @matrix. + * + * Return value: the scale factor of @matrix on the height of the font, + * or 1.0 if @matrix is %NULL. + **/ +cairo_status_t +_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, + double *basis_scale, double *normal_scale, + cairo_bool_t x_basis) +{ + double det; + + det = _cairo_matrix_compute_determinant (matrix); + + if (! ISFINITE (det)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (det == 0) + { + *basis_scale = *normal_scale = 0; + } + else + { + double x = x_basis != 0; + double y = x == 0; + double major, minor; + + cairo_matrix_transform_distance (matrix, &x, &y); + major = hypot (x, y); + /* + * ignore mirroring + */ + if (det < 0) + det = -det; + if (major) + minor = det / major; + else + minor = 0.0; + if (x_basis) + { + *basis_scale = major; + *normal_scale = minor; + } + else + { + *basis_scale = minor; + *normal_scale = major; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_matrix_is_identity (const cairo_matrix_t *matrix) +{ + return (matrix->xx == 1.0 && matrix->yx == 0.0 && + matrix->xy == 0.0 && matrix->yy == 1.0 && + matrix->x0 == 0.0 && matrix->y0 == 0.0); +} + +cairo_bool_t +_cairo_matrix_is_translation (const cairo_matrix_t *matrix) +{ + return (matrix->xx == 1.0 && matrix->yx == 0.0 && + matrix->xy == 0.0 && matrix->yy == 1.0); +} + +cairo_bool_t +_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, + int *itx, int *ity) +{ + if (_cairo_matrix_is_translation (matrix)) + { + cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0); + cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0); + + if (_cairo_fixed_is_integer (x0_fixed) && + _cairo_fixed_is_integer (y0_fixed)) + { + if (itx) + *itx = _cairo_fixed_integer_part (x0_fixed); + if (ity) + *ity = _cairo_fixed_integer_part (y0_fixed); + + return TRUE; + } + } + + return FALSE; +} + +cairo_bool_t +_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix) +{ + if (matrix->xy == 0.0 && matrix->yx == 0.0) { + if (! (matrix->xx == 1.0 || matrix->xx == -1.0)) + return FALSE; + if (! (matrix->yy == 1.0 || matrix->yy == -1.0)) + return FALSE; + } else if (matrix->xx == 0.0 && matrix->yy == 0.0) { + if (! (matrix->xy == 1.0 || matrix->xy == -1.0)) + return FALSE; + if (! (matrix->yx == 1.0 || matrix->yx == -1.0)) + return FALSE; + } else + return FALSE; + + return TRUE; +} + +/* By pixel exact here, we mean a matrix that is composed only of + * 90 degree rotations, flips, and integer translations and produces a 1:1 + * mapping between source and destination pixels. If we transform an image + * with a pixel-exact matrix, filtering is not useful. + */ +cairo_bool_t +_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) +{ + cairo_fixed_t x0_fixed, y0_fixed; + + if (! _cairo_matrix_has_unity_scale (matrix)) + return FALSE; + + x0_fixed = _cairo_fixed_from_double (matrix->x0); + y0_fixed = _cairo_fixed_from_double (matrix->y0); + + return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed); +} + +/* + A circle in user space is transformed into an ellipse in device space. + + The following is a derivation of a formula to calculate the length of the + major axis for this ellipse; this is useful for error bounds calculations. + + Thanks to Walter Brisken for this derivation: + + 1. First some notation: + + All capital letters represent vectors in two dimensions. A prime ' + represents a transformed coordinate. Matrices are written in underlined + form, ie _R_. Lowercase letters represent scalar real values. + + 2. The question has been posed: What is the maximum expansion factor + achieved by the linear transformation + + X' = X _R_ + + where _R_ is a real-valued 2x2 matrix with entries: + + _R_ = [a b] + [c d] . + + In other words, what is the maximum radius, MAX[ |X'| ], reached for any + X on the unit circle ( |X| = 1 ) ? + + 3. Some useful formulae + + (A) through (C) below are standard double-angle formulae. (D) is a lesser + known result and is derived below: + + (A) sin²(θ) = (1 - cos(2*θ))/2 + (B) cos²(θ) = (1 + cos(2*θ))/2 + (C) sin(θ)*cos(θ) = sin(2*θ)/2 + (D) MAX[a*cos(θ) + b*sin(θ)] = sqrt(a² + b²) + + Proof of (D): + + find the maximum of the function by setting the derivative to zero: + + -a*sin(θ)+b*cos(θ) = 0 + + From this it follows that + + tan(θ) = b/a + + and hence + + sin(θ) = b/sqrt(a² + b²) + + and + + cos(θ) = a/sqrt(a² + b²) + + Thus the maximum value is + + MAX[a*cos(θ) + b*sin(θ)] = (a² + b²)/sqrt(a² + b²) + = sqrt(a² + b²) + + 4. Derivation of maximum expansion + + To find MAX[ |X'| ] we search brute force method using calculus. The unit + circle on which X is constrained is to be parameterized by t: + + X(θ) = (cos(θ), sin(θ)) + + Thus + + X'(θ) = X(θ) * _R_ = (cos(θ), sin(θ)) * [a b] + [c d] + = (a*cos(θ) + c*sin(θ), b*cos(θ) + d*sin(θ)). + + Define + + r(θ) = |X'(θ)| + + Thus + + r²(θ) = (a*cos(θ) + c*sin(θ))² + (b*cos(θ) + d*sin(θ))² + = (a² + b²)*cos²(θ) + (c² + d²)*sin²(θ) + + 2*(a*c + b*d)*cos(θ)*sin(θ) + + Now apply the double angle formulae (A) to (C) from above: + + r²(θ) = (a² + b² + c² + d²)/2 + + (a² + b² - c² - d²)*cos(2*θ)/2 + + (a*c + b*d)*sin(2*θ) + = f + g*cos(φ) + h*sin(φ) + + Where + + f = (a² + b² + c² + d²)/2 + g = (a² + b² - c² - d²)/2 + h = (a*c + d*d) + φ = 2*θ + + It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]). Here we determine MAX[ r² ] + using (D) from above: + + MAX[ r² ] = f + sqrt(g² + h²) + + And finally + + MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) ) + + Which is the solution to this problem. + + Walter Brisken + 2004/10/08 + + (Note that the minor axis length is at the minimum of the above solution, + which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)). + + + For another derivation of the same result, using Singular Value Decomposition, + see doc/tutorial/src/singular.c. +*/ + +/* determine the length of the major and minor axes of a circle of the given + radius after applying the transformation matrix. */ +void +_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, + double radius, + double *major, + double *minor) +{ + double a, b, c, d, f, g, h, i, j, k; + + _cairo_matrix_get_affine (matrix, + &a, &b, + &c, &d, + NULL, NULL); + + i = a*a + b*b; + j = c*c + d*d; + k = a*c + b*d; + + f = 0.5 * (i + j); + g = 0.5 * (i - j); + h = hypot (g, k); + + if (major) + *major = radius * sqrt (f + h); + if (minor) + *minor = radius * sqrt (f - h); +} + +/* determine the length of the major axis of a circle of the given radius + after applying the transformation matrix. */ +double +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) +{ + double major; + + _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL); + + return major; +} + +void +_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, + pixman_transform_t *pixman_transform, + double xc, + double yc) +{ + static const pixman_transform_t pixman_identity_transform = {{ + {1 << 16, 0, 0}, + { 0, 1 << 16, 0}, + { 0, 0, 1 << 16} + }}; + + if (_cairo_matrix_is_identity (matrix)) { + *pixman_transform = pixman_identity_transform; + } else { + cairo_matrix_t inv; + unsigned max_iterations; + + pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); + pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); + pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); + + pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); + pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); + pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); + + pixman_transform->matrix[2][0] = 0; + pixman_transform->matrix[2][1] = 0; + pixman_transform->matrix[2][2] = 1 << 16; + + /* The conversion above breaks cairo's translation invariance: + * a translation of (a, b) in device space translates to + * a translation of (xx * a + xy * b, yx * a + yy * b) + * for cairo, while pixman uses rounded versions of xx ... yy. + * This error increases as a and b get larger. + * + * To compensate for this, we fix the point (xc, yc) in pattern + * space and adjust pixman's transform to agree with cairo's at + * that point. + */ + + if (_cairo_matrix_has_unity_scale (matrix)) + return; + + /* Note: If we can't invert the transformation, skip the adjustment. */ + inv = *matrix; + if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) + return; + + /* find the pattern space coordinate that maps to (xc, yc) */ + xc += .5; yc += .5; /* offset for the pixel centre */ + max_iterations = 5; + do { + double x,y; + pixman_vector_t vector; + cairo_fixed_16_16_t dx, dy; + + vector.vector[0] = _cairo_fixed_16_16_from_double (xc); + vector.vector[1] = _cairo_fixed_16_16_from_double (yc); + vector.vector[2] = 1 << 16; + + if (! pixman_transform_point_3d (pixman_transform, &vector)) + return; + + x = pixman_fixed_to_double (vector.vector[0]); + y = pixman_fixed_to_double (vector.vector[1]); + cairo_matrix_transform_point (&inv, &x, &y); + + /* Ideally, the vector should now be (xc, yc). + * We can now compensate for the resulting error. + */ + x -= xc; + y -= yc; + cairo_matrix_transform_distance (matrix, &x, &y); + dx = _cairo_fixed_16_16_from_double (x); + dy = _cairo_fixed_16_16_from_double (y); + pixman_transform->matrix[0][2] -= dx; + pixman_transform->matrix[1][2] -= dy; + + if (dx == 0 && dy == 0) + break; + } while (--max_iterations); + } +} diff --git a/libs/cairo/src/cairo-meta-surface-private.h b/libs/cairo/src/cairo-meta-surface-private.h new file mode 100644 index 000000000..c1f6c40f2 --- /dev/null +++ b/libs/cairo/src/cairo-meta-surface-private.h @@ -0,0 +1,155 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_META_SURFACE_H +#define CAIRO_META_SURFACE_H + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef enum { + /* The 5 basic drawing operations. */ + CAIRO_COMMAND_PAINT, + CAIRO_COMMAND_MASK, + CAIRO_COMMAND_STROKE, + CAIRO_COMMAND_FILL, + CAIRO_COMMAND_SHOW_TEXT_GLYPHS, + + /* Other junk. For most of these, we should be able to assert that + * they never get called except as part of fallbacks for the 5 + * basic drawing operations (which we implement already so the + * fallbacks should never get triggered). So the plan is to + * eliminate as many of these as possible. */ + + CAIRO_COMMAND_INTERSECT_CLIP_PATH + +} cairo_command_type_t; + +typedef enum { + CAIRO_META_REGION_ALL, + CAIRO_META_REGION_NATIVE, + CAIRO_META_REGION_IMAGE_FALLBACK +} cairo_meta_region_type_t; + +typedef struct _cairo_command_header { + cairo_command_type_t type; + cairo_meta_region_type_t region; + cairo_rectangle_int_t extents; +} cairo_command_header_t; + +typedef struct _cairo_command_paint { + cairo_command_header_t header; + cairo_operator_t op; + cairo_pattern_union_t source; +} cairo_command_paint_t; + +typedef struct _cairo_command_mask { + cairo_command_header_t header; + cairo_operator_t op; + cairo_pattern_union_t source; + cairo_pattern_union_t mask; +} cairo_command_mask_t; + +typedef struct _cairo_command_stroke { + cairo_command_header_t header; + cairo_operator_t op; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_stroke_t; + +typedef struct _cairo_command_fill { + cairo_command_header_t header; + cairo_operator_t op; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_fill_t; + +typedef struct _cairo_command_show_text_glyphs { + cairo_command_header_t header; + cairo_operator_t op; + cairo_pattern_union_t source; + char *utf8; + int utf8_len; + cairo_glyph_t *glyphs; + unsigned int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + cairo_scaled_font_t *scaled_font; +} cairo_command_show_text_glyphs_t; + +typedef struct _cairo_command_intersect_clip_path { + cairo_command_header_t header; + cairo_path_fixed_t *path_pointer; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_intersect_clip_path_t; + +typedef union _cairo_command { + cairo_command_header_t header; + + /* The 5 basic drawing operations. */ + cairo_command_paint_t paint; + cairo_command_mask_t mask; + cairo_command_stroke_t stroke; + cairo_command_fill_t fill; + cairo_command_show_text_glyphs_t show_text_glyphs; + + /* The other junk. */ + cairo_command_intersect_clip_path_t intersect_clip_path; +} cairo_command_t; + +typedef struct _cairo_meta_surface { + cairo_surface_t base; + + cairo_content_t content; + + /* A meta-surface is logically unbounded, but when used as a + * source we need to render it to an image, so we need a size at + * which to create that image. */ + double width_pixels; + double height_pixels; + cairo_rectangle_int_t extents; + + cairo_array_t commands; + cairo_surface_t *commands_owner; + + cairo_bool_t is_clipped; + int replay_start_idx; +} cairo_meta_surface_t; + +slim_hidden_proto (cairo_meta_surface_create); +slim_hidden_proto (cairo_meta_surface_replay); + +cairo_private cairo_int_status_t +_cairo_meta_surface_get_path (cairo_surface_t *surface, + cairo_path_fixed_t *path); + + +cairo_private cairo_status_t +_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_private cairo_status_t +_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target); +cairo_private cairo_status_t +_cairo_meta_surface_replay_region (cairo_surface_t *surface, + cairo_surface_t *target, + cairo_meta_region_type_t region); + +cairo_private cairo_bool_t +_cairo_surface_is_meta (const cairo_surface_t *surface); + +#endif /* CAIRO_META_SURFACE_H */ diff --git a/libs/cairo/src/cairo-misc.c b/libs/cairo/src/cairo-misc.c new file mode 100644 index 000000000..6aa793f61 --- /dev/null +++ b/libs/cairo/src/cairo-misc.c @@ -0,0 +1,895 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED); +COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); + +/** + * SECTION:cairo-status + * @Title: Error handling + * @Short_Description: Decoding cairo's status + * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(), + * cairo_font_face_status(), cairo_scaled_font_status(), + * cairo_region_status() + * + * Cairo uses a single status type to represent all kinds of errors. A status + * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value + * of zero. All other status values represent an error. + * + * Cairo's error handling is designed to be easy to use and safe. All major + * cairo objects retain an error status internally which + * can be queried anytime by the users using cairo*_status() calls. In + * the mean time, it is safe to call all cairo functions normally even if the + * underlying object is in an error status. This means that no error handling + * code is required before or after each individual cairo function call. + */ + +/* Public stuff */ + +/** + * cairo_status_to_string: + * @status: a cairo status + * + * Provides a human-readable description of a #cairo_status_t. + * + * Returns: a string representation of the status + */ +const char * +cairo_status_to_string (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_SUCCESS: + return "no error has occurred"; + case CAIRO_STATUS_NO_MEMORY: + return "out of memory"; + case CAIRO_STATUS_INVALID_RESTORE: + return "cairo_restore() without matching cairo_save()"; + case CAIRO_STATUS_INVALID_POP_GROUP: + return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()"; + case CAIRO_STATUS_NO_CURRENT_POINT: + return "no current point defined"; + case CAIRO_STATUS_INVALID_MATRIX: + return "invalid matrix (not invertible)"; + case CAIRO_STATUS_INVALID_STATUS: + return "invalid value for an input cairo_status_t"; + case CAIRO_STATUS_NULL_POINTER: + return "NULL pointer"; + case CAIRO_STATUS_INVALID_STRING: + return "input string not valid UTF-8"; + case CAIRO_STATUS_INVALID_PATH_DATA: + return "input path data not valid"; + case CAIRO_STATUS_READ_ERROR: + return "error while reading from input stream"; + case CAIRO_STATUS_WRITE_ERROR: + return "error while writing to output stream"; + case CAIRO_STATUS_SURFACE_FINISHED: + return "the target surface has been finished"; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + return "the surface type is not appropriate for the operation"; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + return "the pattern type is not appropriate for the operation"; + case CAIRO_STATUS_INVALID_CONTENT: + return "invalid value for an input cairo_content_t"; + case CAIRO_STATUS_INVALID_FORMAT: + return "invalid value for an input cairo_format_t"; + case CAIRO_STATUS_INVALID_VISUAL: + return "invalid value for an input Visual*"; + case CAIRO_STATUS_FILE_NOT_FOUND: + return "file not found"; + case CAIRO_STATUS_INVALID_DASH: + return "invalid value for a dash setting"; + case CAIRO_STATUS_INVALID_DSC_COMMENT: + return "invalid value for a DSC comment"; + case CAIRO_STATUS_INVALID_INDEX: + return "invalid index passed to getter"; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + return "clip region not representable in desired format"; + case CAIRO_STATUS_TEMP_FILE_ERROR: + return "error creating or writing to a temporary file"; + case CAIRO_STATUS_INVALID_STRIDE: + return "invalid value for stride"; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + return "the font type is not appropriate for the operation"; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + return "the user-font is immutable"; + case CAIRO_STATUS_USER_FONT_ERROR: + return "error occurred in a user-font callback function"; + case CAIRO_STATUS_NEGATIVE_COUNT: + return "negative number used where it is not allowed"; + case CAIRO_STATUS_INVALID_CLUSTERS: + return "input clusters do not represent the accompanying text and glyph arrays"; + case CAIRO_STATUS_INVALID_SLANT: + return "invalid value for an input cairo_font_slant_t"; + case CAIRO_STATUS_INVALID_WEIGHT: + return "invalid value for an input cairo_font_weight_t"; + case CAIRO_STATUS_INVALID_SIZE: + return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)"; + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + return "user-font method not implemented"; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + return "the device type is not appropriate for the operation"; + case CAIRO_STATUS_DEVICE_ERROR: + return "an operation to the device caused an unspecified error"; + default: + case CAIRO_STATUS_LAST_STATUS: + return ""; + } +} + + +/** + * cairo_glyph_allocate: + * @num_glyphs: number of glyphs to allocate + * + * Allocates an array of #cairo_glyph_t's. + * This function is only useful in implementations of + * #cairo_user_scaled_font_text_to_glyphs_func_t where the user + * needs to allocate an array of glyphs that cairo will free. + * For all other uses, user can use their own allocation method + * for glyphs. + * + * This function returns %NULL if @num_glyphs is not positive, + * or if out of memory. That means, the %NULL return value + * signals out-of-memory only if @num_glyphs was positive. + * + * Returns: the newly allocated array of glyphs that should be + * freed using cairo_glyph_free() + * + * Since: 1.8 + */ +cairo_glyph_t * +cairo_glyph_allocate (int num_glyphs) +{ + if (num_glyphs <= 0) + return NULL; + + return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); +} +slim_hidden_def (cairo_glyph_allocate); + +/** + * cairo_glyph_free: + * @glyphs: array of glyphs to free, or %NULL + * + * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate(). + * This function is only useful to free glyph array returned + * by cairo_scaled_font_text_to_glyphs() where cairo returns + * an array of glyphs that the user will free. + * For all other uses, user can use their own allocation method + * for glyphs. + * + * Since: 1.8 + */ +void +cairo_glyph_free (cairo_glyph_t *glyphs) +{ + if (glyphs) + free (glyphs); +} +slim_hidden_def (cairo_glyph_free); + +/** + * cairo_text_cluster_allocate: + * @num_clusters: number of text_clusters to allocate + * + * Allocates an array of #cairo_text_cluster_t's. + * This function is only useful in implementations of + * #cairo_user_scaled_font_text_to_glyphs_func_t where the user + * needs to allocate an array of text clusters that cairo will free. + * For all other uses, user can use their own allocation method + * for text clusters. + * + * This function returns %NULL if @num_clusters is not positive, + * or if out of memory. That means, the %NULL return value + * signals out-of-memory only if @num_clusters was positive. + * + * Returns: the newly allocated array of text clusters that should be + * freed using cairo_text_cluster_free() + * + * Since: 1.8 + */ +cairo_text_cluster_t * +cairo_text_cluster_allocate (int num_clusters) +{ + if (num_clusters <= 0) + return NULL; + + return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); +} +slim_hidden_def (cairo_text_cluster_allocate); + +/** + * cairo_text_cluster_free: + * @clusters: array of text clusters to free, or %NULL + * + * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate(). + * This function is only useful to free text cluster array returned + * by cairo_scaled_font_text_to_glyphs() where cairo returns + * an array of text clusters that the user will free. + * For all other uses, user can use their own allocation method + * for text clusters. + * + * Since: 1.8 + */ +void +cairo_text_cluster_free (cairo_text_cluster_t *clusters) +{ + if (clusters) + free (clusters); +} +slim_hidden_def (cairo_text_cluster_free); + + +/* Private stuff */ + +/** + * _cairo_validate_text_clusters: + * @utf8: UTF-8 text + * @utf8_len: length of @utf8 in bytes + * @glyphs: array of glyphs + * @num_glyphs: number of glyphs + * @clusters: array of cluster mapping information + * @num_clusters: number of clusters in the mapping + * @cluster_flags: cluster flags + * + * Check that clusters cover the entire glyphs and utf8 arrays, + * and that cluster boundaries are UTF-8 boundaries. + * + * Return value: %CAIRO_STATUS_SUCCESS upon success, or + * %CAIRO_STATUS_INVALID_CLUSTERS on error. + * The error is either invalid UTF-8 input, + * or bad cluster mapping. + */ +cairo_status_t +_cairo_validate_text_clusters (const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags) +{ + cairo_status_t status; + unsigned int n_bytes = 0; + unsigned int n_glyphs = 0; + int i; + + for (i = 0; i < num_clusters; i++) { + int cluster_bytes = clusters[i].num_bytes; + int cluster_glyphs = clusters[i].num_glyphs; + + if (cluster_bytes < 0 || cluster_glyphs < 0) + goto BAD; + + /* A cluster should cover at least one character or glyph. + * I can't see any use for a 0,0 cluster. + * I can't see an immediate use for a zero-text cluster + * right now either, but they don't harm. + * Zero-glyph clusters on the other hand are useful for + * things like U+200C ZERO WIDTH NON-JOINER */ + if (cluster_bytes == 0 && cluster_glyphs == 0) + goto BAD; + + /* Since n_bytes and n_glyphs are unsigned, but the rest of + * values involved are signed, we can detect overflow easily */ + if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs) + goto BAD; + + /* Make sure we've got valid UTF-8 for the cluster */ + status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL); + if (unlikely (status)) + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); + + n_bytes += cluster_bytes ; + n_glyphs += cluster_glyphs; + } + + if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) { + BAD: + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_operator_bounded_by_mask: + * @op: a #cairo_operator_t + * + * A bounded operator is one where mask pixel + * of zero results in no effect on the destination image. + * + * Unbounded operators often require special handling; if you, for + * example, draw trapezoids with an unbounded operator, the effect + * extends past the bounding box of the trapezoids. + * + * Return value: %TRUE if the operator is bounded by the mask operand + **/ +cairo_bool_t +_cairo_operator_bounded_by_mask (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return TRUE; + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +/** + * _cairo_operator_bounded_by_source: + * @op: a #cairo_operator_t + * + * A bounded operator is one where source pixels of zero + * (in all four components, r, g, b and a) effect no change + * in the resulting destination image. + * + * Unbounded operators often require special handling; if you, for + * example, copy a surface with the SOURCE operator, the effect + * extends past the bounding box of the source surface. + * + * Return value: %TRUE if the operator is bounded by the source operand + **/ +cairo_bool_t +_cairo_operator_bounded_by_source (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return TRUE; + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +uint32_t +_cairo_operator_bounded_by_either (cairo_operator_t op) +{ + switch (op) { + default: + ASSERT_NOT_REACHED; + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE; + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + return CAIRO_OPERATOR_BOUND_BY_MASK; + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return 0; + } + +} + +#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L +/* This function is identical to the C99 function lround(), except that it + * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and + * has a valid input range of (INT_MIN, INT_MAX] instead of + * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems + * than other commonly used methods for rounding (lround, round, rint, lrint + * or float (d + 0.5)). + * + * The reason why this function is much faster on x86 than other + * methods is due to the fact that it avoids the fldcw instruction. + * This instruction incurs a large performance penalty on modern Intel + * processors due to how it prevents efficient instruction pipelining. + * + * The reason why this function is much faster on FPU-less systems is for + * an entirely different reason. All common rounding methods involve multiple + * floating-point operations. Each one of these operations has to be + * emulated in software, which adds up to be a large performance penalty. + * This function doesn't perform any floating-point calculations, and thus + * avoids this penalty. + */ +int +_cairo_lround (double d) +{ + uint32_t top, shift_amount, output; + union { + double d; + uint64_t ui64; + uint32_t ui32[2]; + } u; + + u.d = d; + + /* If the integer word order doesn't match the float word order, we swap + * the words of the input double. This is needed because we will be + * treating the whole double as a 64-bit unsigned integer. Notice that we + * use WORDS_BIGENDIAN to detect the integer word order, which isn't + * exactly correct because WORDS_BIGENDIAN refers to byte order, not word + * order. Thus, we are making the assumption that the byte order is the + * same as the integer word order which, on the modern machines that we + * care about, is OK. + */ +#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \ + (!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN)) + { + uint32_t temp = u.ui32[0]; + u.ui32[0] = u.ui32[1]; + u.ui32[1] = temp; + } +#endif + +#ifdef WORDS_BIGENDIAN + #define MSW (0) /* Most Significant Word */ + #define LSW (1) /* Least Significant Word */ +#else + #define MSW (1) + #define LSW (0) +#endif + + /* By shifting the most significant word of the input double to the + * right 20 places, we get the very "top" of the double where the exponent + * and sign bit lie. + */ + top = u.ui32[MSW] >> 20; + + /* Here, we calculate how much we have to shift the mantissa to normalize + * it to an integer value. We extract the exponent "top" by masking out the + * sign bit, then we calculate the shift amount by subtracting the exponent + * from the bias. Notice that the correct bias for 64-bit doubles is + * actually 1075, but we use 1053 instead for two reasons: + * + * 1) To perform rounding later on, we will first need the target + * value in a 31.1 fixed-point format. Thus, the bias needs to be one + * less: (1075 - 1: 1074). + * + * 2) To avoid shifting the mantissa as a full 64-bit integer (which is + * costly on certain architectures), we break the shift into two parts. + * First, the upper and lower parts of the mantissa are shifted + * individually by a constant amount that all valid inputs will require + * at the very least. This amount is chosen to be 21, because this will + * allow the two parts of the mantissa to later be combined into a + * single 32-bit representation, on which the remainder of the shift + * will be performed. Thus, we decrease the bias by an additional 21: + * (1074 - 21: 1053). + */ + shift_amount = 1053 - (top & 0x7FF); + + /* We are done with the exponent portion in "top", so here we shift it off + * the end. + */ + top >>= 11; + + /* Before we perform any operations on the mantissa, we need to OR in + * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask + * off the sign bit nor the exponent bits because these higher bits won't + * make a bit of difference in the rest of our calculations. + */ + u.ui32[MSW] |= 0x100000; + + /* If the input double is negative, we have to decrease the mantissa + * by a hair. This is an important part of performing arithmetic rounding, + * as negative numbers must round towards positive infinity in the + * halfwase case of -x.5. Since "top" contains only the sign bit at this + * point, we can just decrease the mantissa by the value of "top". + */ + u.ui64 -= top; + + /* By decrementing "top", we create a bitmask with a value of either + * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive + * and thus the unsigned subtraction underflowed) that we'll use later. + */ + top--; + + /* Here, we shift the mantissa by the constant value as described above. + * We can emulate a 64-bit shift right by 21 through shifting the top 32 + * bits left 11 places and ORing in the bottom 32 bits shifted 21 places + * to the right. Both parts of the mantissa are now packed into a single + * 32-bit integer. Although we severely truncate the lower part in the + * process, we still have enough significant bits to perform the conversion + * without error (for all valid inputs). + */ + output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21); + + /* Next, we perform the shift that converts the X.Y fixed-point number + * currently found in "output" to the desired 31.1 fixed-point format + * needed for the following rounding step. It is important to consider + * all possible values for "shift_amount" at this point: + * + * - {shift_amount < 0} Since shift_amount is an unsigned integer, it + * really can't have a value less than zero. But, if the shift_amount + * calculation above caused underflow (which would happen with + * input > INT_MAX or input <= INT_MIN) then shift_amount will now be + * a very large number, and so this shift will result in complete + * garbage. But that's OK, as the input was out of our range, so our + * output is undefined. + * + * - {shift_amount > 31} If the magnitude of the input was very small + * (i.e. |input| << 1.0), shift_amount will have a value greater than + * 31. Thus, this shift will also result in garbage. After performing + * the shift, we will zero-out "output" if this is the case. + * + * - {0 <= shift_amount < 32} In this case, the shift will properly convert + * the mantissa into a 31.1 fixed-point number. + */ + output >>= shift_amount; + + /* This is where we perform rounding with the 31.1 fixed-point number. + * Since what we're after is arithmetic rounding, we simply add the single + * fractional bit into the integer part of "output", and just keep the + * integer part. + */ + output = (output >> 1) + (output & 1); + + /* Here, we zero-out the result if the magnitude if the input was very small + * (as explained in the section above). Notice that all input out of the + * valid range is also caught by this condition, which means we produce 0 + * for all invalid input, which is a nice side effect. + * + * The most straightforward way to do this would be: + * + * if (shift_amount > 31) + * output = 0; + * + * But we can use a little trick to avoid the potential branch. The + * expression (shift_amount > 31) will be either 1 or 0, which when + * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow), + * which can be used to conditionally mask away all the bits in "output" + * (in the 0x0 case), effectively zeroing it out. Certain, compilers would + * have done this for us automatically. + */ + output &= ((shift_amount > 31) - 1); + + /* If the input double was a negative number, then we have to negate our + * output. The most straightforward way to do this would be: + * + * if (!top) + * output = -output; + * + * as "top" at this point is either 0x0 (if the input was negative) or + * 0xFFFFFFFF (if the input was positive). But, we can use a trick to + * avoid the branch. Observe that the following snippet of code has the + * same effect as the reference snippet above: + * + * if (!top) + * output = 0 - output; + * else + * output = output - 0; + * + * Armed with the bitmask found in "top", we can condense the two statements + * into the following: + * + * output = (output & top) - (output & ~top); + * + * where, in the case that the input double was negative, "top" will be 0, + * and the statement will be equivalent to: + * + * output = (0) - (output); + * + * and if the input double was positive, "top" will be 0xFFFFFFFF, and the + * statement will be equivalent to: + * + * output = (output) - (0); + * + * Which, as pointed out earlier, is equivalent to the original reference + * snippet. + */ + output = (output & top) - (output & ~top); + + return output; +#undef MSW +#undef LSW +} +#endif + +/* Convert a 32-bit IEEE single precision floating point number to a + * 'half' representation (s10.5) + */ +uint16_t +_cairo_half_from_float (float f) +{ + union { + uint32_t ui; + float f; + } u; + int s, e, m; + + u.f = f; + s = (u.ui >> 16) & 0x00008000; + e = ((u.ui >> 23) & 0x000000ff) - (127 - 15); + m = u.ui & 0x007fffff; + if (e <= 0) { + if (e < -10) { + /* underflow */ + return 0; + } + + m = (m | 0x00800000) >> (1 - e); + + /* round to nearest, round 0.5 up. */ + if (m & 0x00001000) + m += 0x00002000; + return s | (m >> 13); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + /* infinity */ + return s | 0x7c00; + } else { + /* nan */ + m >>= 13; + return s | 0x7c00 | m | (m == 0); + } + } else { + /* round to nearest, round 0.5 up. */ + if (m & 0x00001000) { + m += 0x00002000; + + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + /* overflow -> infinity */ + return s | 0x7c00; + } + + return s | (e << 10) | (m >> 13); + } +} + + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +#include +#include + +#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/libs/cairo/src/cairo-mutex-impl-private.h b/libs/cairo/src/cairo-mutex-impl-private.h new file mode 100644 index 000000000..72086036c --- /dev/null +++ b/libs/cairo/src/cairo-mutex-impl-private.h @@ -0,0 +1,244 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_MUTEX_IMPL_PRIVATE_H +#define CAIRO_MUTEX_IMPL_PRIVATE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_LOCKDEP +#include +#endif + +/* A fully qualified no-operation statement */ +#define CAIRO_MUTEX_IMPL_NOOP do {/*no-op*/} while (0) +/* And one that evaluates its argument once */ +#define CAIRO_MUTEX_IMPL_NOOP1(expr) do { (void)(expr); } while (0) +/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the + * result of __attribute__((warn_used_result)) functions. */ + +/* Cairo mutex implementation: + * + * Any new mutex implementation needs to do the following: + * + * - Condition on the right header or feature. Headers are + * preferred as eg. you still can use win32 mutex implementation + * on a win32 system even if you do not compile the win32 + * surface/backend. + * + * - typedef #cairo_mutex_impl_t to the proper mutex type on your target + * system. Note that you may or may not need to use a pointer, + * depending on what kinds of initialization your mutex + * implementation supports. No trailing semicolon needed. + * You should be able to compile the following snippet (don't try + * running it): + * + * + * cairo_mutex_impl_t _cairo_some_mutex; + * + * + * - #define %CAIRO_MUTEX_IMPL_ 1 with suitable name for your platform. You + * can later use this symbol in cairo-system.c. + * + * - #define CAIRO_MUTEX_IMPL_LOCK(mutex) and CAIRO_MUTEX_IMPL_UNLOCK(mutex) to + * proper statement to lock/unlock the mutex object passed in. + * You can (and should) assume that the mutex is already + * initialized, and is-not-already-locked/is-locked, + * respectively. Use the "do { ... } while (0)" idiom if necessary. + * No trailing semicolons are needed (in any macro you define here). + * You should be able to compile the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex; + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - #define %CAIRO_MUTEX_IMPL_NIL_INITIALIZER to something that can + * initialize the #cairo_mutex_impl_t type you defined. Most of the + * time one of 0, %NULL, or {} works. At this point + * you should be able to compile the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - If the above code is not enough to initialize a mutex on + * your platform, #define CAIRO_MUTEX_IMPL_INIT(mutex) to statement + * to initialize the mutex (allocate resources, etc). Such that + * you should be able to compile AND RUN the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; + * + * CAIRO_MUTEX_IMPL_INIT (_cairo_some_mutex); + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - If you define CAIRO_MUTEX_IMPL_INIT(mutex), cairo will use it to + * initialize all static mutex'es. If for any reason that should + * not happen (eg. %CAIRO_MUTEX_IMPL_INIT is just a faster way than + * what cairo does using %CAIRO_MUTEX_IMPL_NIL_INITIALIZER), then + * + * #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP + * + * + * - If your system supports freeing a mutex object (deallocating + * resources, etc), then #define CAIRO_MUTEX_IMPL_FINI(mutex) to do + * that. + * + * - If you define CAIRO_MUTEX_IMPL_FINI(mutex), cairo will use it to + * define a finalizer function to finalize all static mutex'es. + * However, it's up to you to call CAIRO_MUTEX_IMPL_FINALIZE() at + * proper places, eg. when the system is unloading the cairo library. + * So, if for any reason finalizing static mutex'es is not needed + * (eg. you never call CAIRO_MUTEX_IMPL_FINALIZE()), then + * + * #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP + * + * + * - That is all. If for any reason you think the above API is + * not enough to implement #cairo_mutex_impl_t on your system, please + * stop and write to the cairo mailing list about it. DO NOT + * poke around cairo-mutex-private.h for possible solutions. + */ + +#if CAIRO_NO_MUTEX + +/* No mutexes */ + + typedef int cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_NO 1 +# define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP +# define CAIRO_MUTEX_IMPL_LOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 + + typedef int cairo_recursive_mutex_impl_t; + +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER 0 + +#elif defined(_WIN32) /******************************************************/ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +/* We require Windows 7 features */ +#if !defined(WINVER) || (WINVER < 0x0601) +# define WINVER 0x0601 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) +# define _WIN32_WINNT 0x0601 +#endif + +# include + + typedef SRWLOCK cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_WIN32 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) AcquireSRWLockExclusive (&(mutex)) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) ReleaseSRWLockExclusive (&(mutex)) +# define CAIRO_MUTEX_IMPL_INIT(mutex) InitializeSRWLock (&(mutex)) +# define CAIRO_MUTEX_IMPL_FINI(mutex) CAIRO_MUTEX_IMPL_NOOP +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER SRWLOCK_INIT + +#elif defined __OS2__ /******************************************************/ + +# define INCL_BASE +# define INCL_PM +# include + + typedef HMTX cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_OS2 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) DosReleaseMutexSem(mutex) +# define CAIRO_MUTEX_IMPL_INIT(mutex) DosCreateMutexSem (NULL, &(mutex), 0L, FALSE) +# define CAIRO_MUTEX_IMPL_FINI(mutex) DosCloseMutexSem (mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 + +#elif CAIRO_HAS_BEOS_SURFACE /***********************************************/ + + typedef BLocker* cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_BEOS 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) (mutex)->Lock() +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) (mutex)->Unlock() +# define CAIRO_MUTEX_IMPL_INIT(mutex) (mutex) = new BLocker() +# define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL + +#elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/ + +# include + + typedef pthread_mutex_t cairo_mutex_impl_t; + typedef pthread_mutex_t cairo_recursive_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_PTHREAD 1 +#if HAVE_LOCKDEP +/* expose all mutexes to the validator */ +# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) +#endif +# define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) +#if HAVE_LOCKDEP +# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) +# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) +#endif +# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) +#if ! HAVE_LOCKDEP +# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP +#endif +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init (&attr); \ + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init (&(mutex), &attr); \ + pthread_mutexattr_destroy (&attr); \ +} while (0) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + +#else /**********************************************************************/ + +# error "XXX: No mutex implementation found. Cairo will not work with multiple threads. Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support." + +#endif + +/* By default mutex implementations are assumed to be recursive */ +#if ! CAIRO_MUTEX_HAS_RECURSIVE_IMPL + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 + + typedef cairo_mutex_impl_t cairo_recursive_mutex_impl_t; + +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) CAIRO_MUTEX_IMPL_INIT(mutex) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER + +#endif + +#endif diff --git a/libs/cairo/src/cairo-mutex-list-private.h b/libs/cairo/src/cairo-mutex-list-private.h new file mode 100644 index 000000000..3f2e44119 --- /dev/null +++ b/libs/cairo/src/cairo-mutex-list-private.h @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_FEATURES_H +/* This block is to just make this header file standalone */ +#define CAIRO_MUTEX_DECLARE(mutex) +#endif + +CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) + +CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex) + +CAIRO_MUTEX_DECLARE (_cairo_error_mutex) +CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) +CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) +CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) +CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex) +CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex) + +#if CAIRO_HAS_FT_FONT +CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) +#endif + +#if CAIRO_HAS_WIN32_FONT +CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex) +#endif + +#if CAIRO_HAS_XLIB_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) +#endif + +#if CAIRO_HAS_XCB_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) +#endif + +#if CAIRO_HAS_GL_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) +#endif + +#if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) +CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) +#endif + +#if CAIRO_HAS_DRM_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex) +#endif +/* Undefine, to err on unintended inclusion */ +#undef CAIRO_MUTEX_DECLARE diff --git a/libs/cairo/src/cairo-mutex-private.h b/libs/cairo/src/cairo-mutex-private.h new file mode 100644 index 000000000..e9359ef55 --- /dev/null +++ b/libs/cairo/src/cairo-mutex-private.h @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_MUTEX_PRIVATE_H +#define CAIRO_MUTEX_PRIVATE_H + +#include "cairo-mutex-type-private.h" + +CAIRO_BEGIN_DECLS + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +cairo_private void _cairo_mutex_initialize (void); +#endif +#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER +cairo_private void _cairo_mutex_finalize (void); +#endif +/* only if using static initializer and/or finalizer define the boolean */ +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER + cairo_private extern cairo_bool_t _cairo_mutex_initialized; +#endif + +/* Finally, extern the static mutexes and undef */ + +#define CAIRO_MUTEX_DECLARE(mutex) cairo_private extern cairo_mutex_t mutex; +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE + +CAIRO_END_DECLS + +#endif diff --git a/libs/cairo/src/cairo-mutex-type-private.h b/libs/cairo/src/cairo-mutex-type-private.h new file mode 100644 index 000000000..eac1d48e6 --- /dev/null +++ b/libs/cairo/src/cairo-mutex-type-private.h @@ -0,0 +1,158 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-mutex.c b/libs/cairo/src/cairo-mutex.c new file mode 100644 index 000000000..d859e28d9 --- /dev/null +++ b/libs/cairo/src/cairo-mutex.c @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-mutex-private.h" + +#define CAIRO_MUTEX_DECLARE(mutex) cairo_mutex_t mutex = CAIRO_MUTEX_NIL_INITIALIZER; +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER + +# if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE FALSE +# else +# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE TRUE +# endif + +cairo_bool_t _cairo_mutex_initialized = _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE; + +# undef _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE + +#endif + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +void _cairo_mutex_initialize (void) +{ + if (_cairo_mutex_initialized) + return; + + _cairo_mutex_initialized = TRUE; + +#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_INIT (mutex); +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE +} +#endif + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER +void _cairo_mutex_finalize (void) +{ + if (!_cairo_mutex_initialized) + return; + + _cairo_mutex_initialized = FALSE; + +#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex); +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE +} +#endif diff --git a/libs/cairo/src/cairo-no-features.h b/libs/cairo/src/cairo-no-features.h new file mode 100644 index 000000000..9b3d86be2 --- /dev/null +++ b/libs/cairo/src/cairo-no-features.h @@ -0,0 +1,12 @@ +/* Generated by configure. Do not edit */ +#ifndef CAIRO_NO_FEATURES_H +#define CAIRO_NO_FEATURES_H + +#include + +/* This is a dummy header, to trick gtk-doc only */ + +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_WIN32_SURFACE 1 + +#endif diff --git a/libs/cairo/src/cairo-observer.c b/libs/cairo/src/cairo-observer.c new file mode 100644 index 000000000..c8ce5eea2 --- /dev/null +++ b/libs/cairo/src/cairo-observer.c @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +void +_cairo_observers_notify (cairo_list_t *observers, void *arg) +{ + cairo_observer_t *obs, *next; + + cairo_list_foreach_entry_safe (obs, next, + cairo_observer_t, + observers, link) + { + obs->callback (obs, arg); + } +} diff --git a/libs/cairo/src/cairo-os2-private.h b/libs/cairo/src/cairo-os2-private.h new file mode 100644 index 000000000..e47efd316 --- /dev/null +++ b/libs/cairo/src/cairo-os2-private.h @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-os2-surface.c b/libs/cairo/src/cairo-os2-surface.c new file mode 100644 index 000000000..c0464f62b --- /dev/null +++ b/libs/cairo/src/cairo-os2-surface.c @@ -0,0 +1,1440 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-os2-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FC_FONT +#include +#endif + +#include +#ifdef BUILD_CAIRO_DLL +# include "cairo-os2.h" +# ifndef __WATCOMC__ +# include +# endif +#endif + +/* + * Here comes the extra API for the OS/2 platform. Currently it consists + * of two extra functions, the cairo_os2_init() and the + * cairo_os2_fini(). Both of them are called automatically if + * Cairo is compiled to be a DLL file, but you have to call them before + * using the Cairo API if you link to Cairo statically! + * + * You'll also find the code in here which deals with DLL initialization + * and termination, if the code is built to be a DLL. + * (if BUILD_CAIRO_DLL is defined) + */ + +/* Initialization counter: */ +static int cairo_os2_initialization_count = 0; + +static inline void +DisableFPUException (void) +{ + unsigned short usCW; + + /* Some OS/2 PM API calls modify the FPU Control Word, + * but forget to restore it. + * + * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions, + * so to be sure, we disable Invalid Opcode FPU exception + * before using FPU stuffs. + */ + usCW = _control87 (0, 0); + usCW = usCW | EM_INVALID | 0x80; + _control87 (usCW, MCW_EM | 0x80); +} + +/** + * cairo_os2_init: + * + * Initializes the Cairo library. This function is automatically called if + * Cairo was compiled to be a DLL (however it's not a problem if it's called + * multiple times). But if you link to Cairo statically, you have to call it + * once to set up Cairo's internal structures and mutexes. + * + * Since: 1.4 + **/ +cairo_public void +cairo_os2_init (void) +{ + /* This may initialize some stuffs, like create mutex semaphores etc.. */ + + cairo_os2_initialization_count++; + if (cairo_os2_initialization_count > 1) return; + + DisableFPUException (); + +#if CAIRO_HAS_FC_FONT + /* Initialize FontConfig */ + FcInit (); +#endif + + CAIRO_MUTEX_INITIALIZE (); +} + +/** + * cairo_os2_fini: + * + * Uninitializes the Cairo library. This function is automatically called if + * Cairo was compiled to be a DLL (however it's not a problem if it's called + * multiple times). But if you link to Cairo statically, you have to call it + * once to shut down Cairo, to let it free all the resources it has allocated. + * + * Since: 1.4 + **/ +cairo_public void +cairo_os2_fini (void) +{ + /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */ + + if (cairo_os2_initialization_count <= 0) return; + cairo_os2_initialization_count--; + if (cairo_os2_initialization_count > 0) return; + + DisableFPUException (); + + cairo_debug_reset_static_data (); + +#if CAIRO_HAS_FC_FONT +# if HAVE_FCFINI + /* Uninitialize FontConfig */ + FcFini (); +# endif +#endif + +#ifdef __WATCOMC__ + /* It can happen that the libraries we use have memory leaks, + * so there are still memory chunks allocated at this point. + * In these cases, Watcom might still have a bigger memory chunk, + * called "the heap" allocated from the OS. + * As we want to minimize the memory we lose from the point of + * view of the OS, we call this function to shrink that heap + * as much as possible. + */ + _heapshrink (); +#else + /* GCC has a heapmin function that approximately corresponds to + * what the Watcom function does + */ + _heapmin (); +#endif +} + +/* + * This function calls the allocation function depending on which + * method was compiled into the library: it can be native allocation + * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free). + * Actually, for pixel buffers that we use this function for, cairo + * uses _cairo_malloc_abc, so we use that here, too. And use the + * change to check the size argument + */ +void *_buffer_alloc (size_t a, size_t b, const unsigned int size) +{ + size_t nbytes; + void *buffer = NULL; + + if (!a || !b || !size || + a >= INT32_MAX / b || a*b >= INT32_MAX / size) { + return NULL; + } + nbytes = a * b * size; + +#ifdef OS2_USE_PLATFORM_ALLOC + /* Using OBJ_ANY on a machine that isn't configured for hi-mem + * will cause ERROR_INVALID_PARAMETER. If this occurs, or this + * build doesn't have hi-mem enabled, fall back to using lo-mem. + */ +#ifdef OS2_HIGH_MEMORY + if (!DosAllocMem (&buffer, nbytes, + OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) + return buffer; +#endif + if (DosAllocMem (&buffer, nbytes, + PAG_READ | PAG_WRITE | PAG_COMMIT)) + return NULL; +#else + /* Clear the malloc'd buffer the way DosAllocMem() does. */ + buffer = malloc (nbytes); + if (buffer) { + memset (buffer, 0, nbytes); + } +#endif + return buffer; +} + +/* + * This function selects the free function depending on which + * allocation method was compiled into the library + */ +void _buffer_free (void *buffer) +{ +#ifdef OS2_USE_PLATFORM_ALLOC + DosFreeMem (buffer); +#else + free (buffer); +#endif +} + +/* XXX + * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and + * the LibMain code moved to cairo-system.c. It should also call + * cairo_debug_reset_static_data() instead of duplicating its logic... + */ + +#ifdef BUILD_CAIRO_DLL +/* The main DLL entry for DLL initialization and uninitialization */ +/* Only include this code if we're about to build a DLL. */ + +#ifdef __WATCOMC__ +unsigned _System +LibMain (unsigned hmod, + unsigned termination) +#else +unsigned long _System +_DLL_InitTerm (unsigned long hModule, + unsigned long termination) +#endif +{ + if (termination) { + /* Unloading the DLL */ + cairo_os2_fini (); + +#ifndef __WATCOMC__ + /* Uninitialize RTL of GCC */ + __ctordtorTerm (); + _CRT_term (); +#endif + return 1; + } else { + /* Loading the DLL */ +#ifndef __WATCOMC__ + /* Initialize RTL of GCC */ + if (_CRT_init () != 0) + return 0; + __ctordtorInit (); +#endif + + cairo_os2_init (); + return 1; + } +} + +#endif /* BUILD_CAIRO_DLL */ + +/* + * The following part of the source file contains the code which might + * be called the "core" of the OS/2 backend support. This contains the + * OS/2 surface support functions and structures. + */ + +/* Forward declaration */ +static const cairo_surface_backend_t cairo_os2_surface_backend; + +/* Unpublished API: + * GpiEnableYInversion = PMGPI.723 + * GpiQueryYInversion = PMGPI.726 + * BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); + * LONG APIENTRY GpiQueryYInversion (HPS hps); + */ +BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); +LONG APIENTRY GpiQueryYInversion (HPS hps); + +#ifdef __WATCOMC__ +/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */ +LONG APIENTRY GpiDrawBits (HPS hps, + PVOID pBits, + PBITMAPINFO2 pbmiInfoTable, + LONG lCount, + PPOINTL aptlPoints, + LONG lRop, + ULONG flOptions); +#endif + +static void +_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + POINTL aptlPoints[4]; + LONG lOldYInversion; + LONG rc = GPI_OK; + + /* Check the limits (may not be necessary) */ + if (prcl_begin_paint_rect->xLeft < 0) + prcl_begin_paint_rect->xLeft = 0; + if (prcl_begin_paint_rect->yBottom < 0) + prcl_begin_paint_rect->yBottom = 0; + if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) + prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; + if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) + prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; + + /* Exit if the rectangle is empty */ + if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || + prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) + return; + + /* Set the Target & Source coordinates */ + *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; + *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; + + /* Make the Target coordinates non-inclusive */ + aptlPoints[1].x -= 1; + aptlPoints[1].y -= 1; + + /* Enable Y Inversion for the HPS, so GpiDrawBits will + * work with upside-top image, not with upside-down image! + */ + lOldYInversion = GpiQueryYInversion (hps_begin_paint); + GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); + + /* Debug code to draw rectangle limits */ +#if 0 + { + int x, y; + unsigned char *pixels; + + pixels = surface->pixels; + for (x = 0; x < surface->bitmap_info.cx; x++) { + for (y = 0; y < surface->bitmap_info.cy; y++) { + if ((x == 0) || + (y == 0) || + (x == y) || + (x >= surface->bitmap_info.cx-1) || + (y >= surface->bitmap_info.cy-1)) + { + pixels[y*surface->bitmap_info.cx*4+x*4] = 255; + } + } + } + } +#endif + if (!surface->use_24bpp) { + rc = GpiDrawBits (hps_begin_paint, + surface->pixels, + &(surface->bitmap_info), + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = TRUE; + } + + if (surface->use_24bpp) { + /* If GpiDrawBits () failed then this is most likely because the + * display driver could not handle a 32bit bitmap. So we need to + * - create a buffer that only contains 3 bytes per pixel + * - change the bitmap info header to contain 24bit + * - pass the new buffer to GpiDrawBits () again + * - clean up the new buffer + */ + BITMAPINFO2 bmpinfo; + unsigned char *pchPixBuf; + unsigned char *pchTarget; + ULONG *pulSource; + ULONG ulX; + ULONG ulY; + ULONG ulPad; + + /* Set up the bitmap header, but this time for 24bit depth. */ + bmpinfo = surface->bitmap_info; + bmpinfo.cBitCount = 24; + + /* The start of each row has to be DWORD aligned. Calculate the + * of number aligned bytes per row, the total size of the bitmap, + * and the number of padding bytes at the end of each row. + */ + ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; + bmpinfo.cbImage = ulX * bmpinfo.cy; + ulPad = ulX - bmpinfo.cx * 3; + + /* Allocate temporary pixel buffer. If the rows don't need + * padding, it has to be 1 byte larger than the size of the + * bitmap or else the high-order byte from the last source + * row will end up in unallocated memory. + */ + pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, + bmpinfo.cbImage + (ulPad ? 0 : 1)); + + if (pchPixBuf) { + /* Copy 4 bytes from the source but advance the target ptr only + * 3 bytes, so the high-order alpha byte will be overwritten by + * the next copy. At the end of each row, skip over the padding. + */ + pchTarget = pchPixBuf; + pulSource = (ULONG*)surface->pixels; + for (ulY = bmpinfo.cy; ulY; ulY--) { + for (ulX = bmpinfo.cx; ulX; ulX--) { + *((ULONG*)pchTarget) = *pulSource++; + pchTarget += 3; + } + pchTarget += ulPad; + } + + rc = GpiDrawBits (hps_begin_paint, + pchPixBuf, + &bmpinfo, + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = FALSE; + + _buffer_free (pchPixBuf); + } + } + + /* Restore Y inversion */ + GpiEnableYInversion (hps_begin_paint, lOldYInversion); +} + +static void +_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + HPS hps; + HDC hdc; + SIZEL sizlTemp; + HBITMAP hbmpTemp; + BITMAPINFO2 bmi2Temp; + POINTL aptlPoints[4]; + int y; + unsigned char *pchTemp; + + /* To copy pixels from screen to our buffer, we do the following steps: + * + * - Blit pixels from screen to a HBITMAP: + * -- Create Memory Device Context + * -- Create a PS into it + * -- Create a HBITMAP + * -- Select HBITMAP into memory PS + * -- Blit dirty pixels from screen to HBITMAP + * - Copy HBITMAP lines (pixels) into our buffer + * - Free resources + */ + + /* Create a memory device context */ + hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE); + if (!hdc) { + return; + } + + /* Create a memory PS */ + sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft; + sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom; + hps = GpiCreatePS (0, + hdc, + &sizlTemp, + PU_PELS | GPIT_NORMAL | GPIA_ASSOC); + if (!hps) { + DevCloseDC (hdc); + return; + } + + /* Create an uninitialized bitmap. */ + /* Prepare BITMAPINFO2 structure for our buffer */ + memset (&bmi2Temp, 0, sizeof (bmi2Temp)); + bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2); + bmi2Temp.cx = sizlTemp.cx; + bmi2Temp.cy = sizlTemp.cy; + bmi2Temp.cPlanes = 1; + bmi2Temp.cBitCount = 32; + + hbmpTemp = GpiCreateBitmap (hps, + (PBITMAPINFOHEADER2) &bmi2Temp, + 0, + NULL, + NULL); + + if (!hbmpTemp) { + GpiDestroyPS (hps); + DevCloseDC (hdc); + return; + } + + /* Select the bitmap into the memory device context. */ + GpiSetBitmap (hps, hbmpTemp); + + /* Target coordinates (Noninclusive) */ + aptlPoints[0].x = 0; + aptlPoints[0].y = 0; + + aptlPoints[1].x = sizlTemp.cx; + aptlPoints[1].y = sizlTemp.cy; + + /* Source coordinates (Inclusive) */ + aptlPoints[2].x = prcl_begin_paint_rect->xLeft; + aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; + + aptlPoints[3].x = prcl_begin_paint_rect->xRight; + aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop; + + /* Blit pixels from screen to bitmap */ + GpiBitBlt (hps, + hps_begin_paint, + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + + /* Now we have to extract the pixels from the bitmap. */ + pchTemp = + surface->pixels + + (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 + + prcl_begin_paint_rect->xLeft*4; + for (y = 0; y < sizlTemp.cy; y++) { + /* Get one line of pixels */ + GpiQueryBitmapBits (hps, + sizlTemp.cy - y - 1, /* lScanStart */ + 1, /* lScans */ + (PBYTE)pchTemp, + &bmi2Temp); + + /* Go for next line */ + pchTemp += surface->bitmap_info.cx*4; + } + + /* Clean up resources */ + GpiSetBitmap (hps, (HBITMAP) NULL); + GpiDeleteBitmap (hbmpTemp); + GpiDestroyPS (hps); + DevCloseDC (hdc); +} + +static cairo_status_t +_cairo_os2_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + /* Increase lend counter */ + local_os2_surface->pixel_array_lend_count++; + + *image_out = local_os2_surface->image_surface; + *image_extra = NULL; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_os2_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + /* Decrease Lend counter! */ + DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + if (local_os2_surface->pixel_array_lend_count > 0) + local_os2_surface->pixel_array_lend_count--; + DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + return; +} + +static cairo_status_t +_cairo_os2_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + /* Increase lend counter */ + local_os2_surface->pixel_array_lend_count++; + + *image_out = local_os2_surface->image_surface; + *image_extra = NULL; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = local_os2_surface->bitmap_info.cx; + image_rect->height = local_os2_surface->bitmap_info.cy; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_os2_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_os2_surface_t *local_os2_surface; + RECTL rclToBlit; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + /* So, we got back the image, and if all goes well, then + * something has been changed inside the interest_rect. + * So, we blit it to the screen! + */ + + if (local_os2_surface->blit_as_changes) { + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { + /* Could not get mutex! */ + return; + } + + if (local_os2_surface->hwnd_client_window) { + /* We know the HWND, so let's invalidate the window region, + * so the application will redraw itself, using the + * cairo_os2_surface_refresh_window () API from its own PM thread. + * + * This is the safe method, which should be preferred every time. + */ + rclToBlit.xLeft = interest_rect->x; + rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ + rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (interest_rect->y); + rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (interest_rect->y+interest_rect->height); /* Noninclusive */ + + WinInvalidateRect (local_os2_surface->hwnd_client_window, + &rclToBlit, + FALSE); + } else { + /* We don't know the HWND, so try to blit the pixels from here! + * Please note that it can be problematic if this is not the PM thread! + * + * It can cause internal PM stuffs to be scewed up, for some reason. + * Please always tell the HWND to the surface using the + * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () + * from your WM_PAINT, if it's possible! + */ + rclToBlit.xLeft = interest_rect->x; + rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ + rclToBlit.yBottom = interest_rect->y; + rclToBlit.yTop = interest_rect->y+interest_rect->height; /* Noninclusive */ + /* Now blit there the stuffs! */ + _cairo_os2_surface_blit_pixels (local_os2_surface, + local_os2_surface->hps_client_window, + &rclToBlit); + } + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + } + /* Also decrease Lend counter! */ + DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + if (local_os2_surface->pixel_array_lend_count > 0) + local_os2_surface->pixel_array_lend_count--; + DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); +} + +static cairo_bool_t +_cairo_os2_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = local_os2_surface->bitmap_info.cx; + rectangle->height = local_os2_surface->bitmap_info.cy; + + return TRUE; +} + +/** + * cairo_os2_surface_create: + * @hps_client_window: the presentation handle to bind the surface to + * @width: the width of the surface + * @height: the height of the surface + * + * Create a Cairo surface which is bound to a given presentation space (HPS). + * The caller retains ownership of the HPS and must dispose of it after the + * the surface has been destroyed. The surface will be created to have the + * given size. By default every change to the surface will be made visible + * immediately by blitting it into the window. This can be changed with + * cairo_os2_surface_set_manual_window_refresh(). + * Note that the surface will contain garbage when created, so the pixels + * have to be initialized by hand first. You can use the Cairo functions to + * fill it with black, or use cairo_surface_mark_dirty() to fill the surface + * with pixels from the window/HPS. + * + * Return value: the newly created surface + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_os2_surface_create (HPS hps_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface = 0; + cairo_status_t status; + int rc; + + /* Check the size of the window */ + if ((width <= 0) || (height <= 0)) { + status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); + goto error_exit; + } + + /* Allocate an OS/2 surface structure. */ + local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); + if (!local_os2_surface) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; + } + + memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); + + /* Allocate resources: mutex & event semaphores and the pixel buffer */ + if (DosCreateMutexSem (NULL, + &(local_os2_surface->hmtx_use_private_fields), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + goto error_exit; + } + + if (DosCreateEventSem (NULL, + &(local_os2_surface->hev_pixel_array_came_back), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + goto error_exit; + } + + local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); + if (!local_os2_surface->pixels) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; + } + + /* Create image surface from pixel array */ + local_os2_surface->image_surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (local_os2_surface->pixels, + CAIRO_FORMAT_ARGB32, + width, /* Width */ + height, /* Height */ + width * 4); /* Rowstride */ + status = local_os2_surface->image_surface->base.status; + if (status) + goto error_exit; + + /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. + * Note: hps_client_window may be null if this was called by + * cairo_os2_surface_create_for_window(). + */ + local_os2_surface->hps_client_window = hps_client_window; + local_os2_surface->blit_as_changes = TRUE; + + /* Prepare BITMAPINFO2 structure for our buffer */ + local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); + local_os2_surface->bitmap_info.cx = width; + local_os2_surface->bitmap_info.cy = height; + local_os2_surface->bitmap_info.cPlanes = 1; + local_os2_surface->bitmap_info.cBitCount = 32; + + /* Initialize base surface */ + _cairo_surface_init (&local_os2_surface->base, + &cairo_os2_surface_backend, + NULL, /* device */ + _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); + + /* Successful exit */ + return (cairo_surface_t *)local_os2_surface; + + error_exit: + + /* This point will only be reached if an error occured */ + + if (local_os2_surface) { + if (local_os2_surface->pixels) + _buffer_free (local_os2_surface->pixels); + if (local_os2_surface->hev_pixel_array_came_back) + DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); + if (local_os2_surface->hmtx_use_private_fields) + DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); + free (local_os2_surface); + } + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_os2_surface_create_for_window: + * @hwnd_client_window: the window handle to bind the surface to + * @width: the width of the surface + * @height: the height of the surface + * + * Create a Cairo surface which is bound to a given window; the caller retains + * ownership of the window. This is a convenience function for use with + * windows that will only be updated when cairo_os2_surface_refresh_window() + * is called (usually in response to a WM_PAINT message). It avoids the need + * to create a persistent HPS for every window and assumes that one will be + * supplied by the caller when a cairo function needs one. If it isn't, an + * HPS will be created on-the-fly and released before the function which needs + * it returns. + * + * Return value: the newly created surface + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + + /* A window handle must be provided. */ + if (!hwnd_client_window) { + return _cairo_surface_create_in_error ( + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + /* Create the surface. */ + local_os2_surface = (cairo_os2_surface_t *) + cairo_os2_surface_create (0, width, height); + + /* If successful, save the hwnd & turn off automatic repainting. */ + if (!local_os2_surface->image_surface->base.status) { + local_os2_surface->hwnd_client_window = hwnd_client_window; + local_os2_surface->blit_as_changes = FALSE; + } + + return (cairo_surface_t *)local_os2_surface; +} + +/** + * cairo_os2_surface_set_size: + * @surface: the cairo surface to resize + * @new_width: the new width of the surface + * @new_height: the new height of the surface + * @timeout: timeout value in milliseconds + * + * When the client window is resized, call this API to set the new size in the + * underlying surface accordingly. This function will reallocate everything, + * so you'll have to redraw everything in the surface after this call. + * The surface will contain garbage after the resizing. So the notes of + * cairo_os2_surface_create() apply here, too. + * + * The timeout value specifies how long the function should wait on other parts + * of the program to release the buffers. It is necessary, because it can happen + * that Cairo is just drawing something into the surface while we want to + * destroy and recreate it. + * + * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * %CAIRO_STATUS_INVALID_SIZE for invalid sizes + * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the + * timeout happened before all the buffers were released + * + * Since: 1.4 + **/ +int +cairo_os2_surface_set_size (cairo_surface_t *surface, + int new_width, + int new_height, + int timeout) +{ + cairo_os2_surface_t *local_os2_surface; + unsigned char *pchNewPixels; + int rc; + cairo_image_surface_t *pNewImageSurface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + if ((new_width <= 0) || + (new_height <= 0)) + { + /* Invalid size! */ + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + } + + /* Allocate memory for new stuffs */ + pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4); + if (!pchNewPixels) { + /* Not enough memory for the pixels! + * Everything remains the same! + */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Create image surface from new pixel array */ + pNewImageSurface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (pchNewPixels, + CAIRO_FORMAT_ARGB32, + new_width, /* Width */ + new_height, /* Height */ + new_width * 4); /* Rowstride */ + + if (pNewImageSurface->base.status) { + /* Could not create image surface! + * Everything remains the same! + */ + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Okay, new memory allocated, so it's time to swap old buffers + * to new ones! + */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { + /* Could not get mutex! + * Everything remains the same! + */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* We have to make sure that we won't destroy a surface which + * is lent to some other code (Cairo is drawing into it)! + */ + while (local_os2_surface->pixel_array_lend_count > 0) { + ULONG ulPostCount; + DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount); + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + /* Wait for somebody to return the pixels! */ + rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout); + if (rc != NO_ERROR) { + /* Either timeout or something wrong... Exit. */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + /* Okay, grab mutex and check counter again! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! + * Everything remains the same! + */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + /* Destroy old image surface */ + cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); + /* Destroy old pixel buffer */ + _buffer_free (local_os2_surface->pixels); + /* Set new image surface */ + local_os2_surface->image_surface = pNewImageSurface; + /* Set new pixel buffer */ + local_os2_surface->pixels = pchNewPixels; + /* Change bitmap2 structure */ + local_os2_surface->bitmap_info.cx = new_width; + local_os2_surface->bitmap_info.cy = new_height; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_refresh_window: + * @surface: the cairo surface to refresh + * @hps_begin_paint: the presentation handle of the window to refresh + * @prcl_begin_paint_rect: the rectangle to redraw + * + * This function can be used to force a repaint of a given area of the client + * window. It should usually be called from the WM_PAINT processing of the + * window procedure. However, it can be called any time a given part of the + * window has to be updated. + * + * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call + * of the window procedure, but you can also get the HPS using WinGetPS, and you + * can assemble your own update rectangle by hand. + * If hps_begin_paint is %NULL, the function will use the HPS passed into + * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function + * will query the current window size and repaint the whole window. + * + * Cairo assumes that if you set the HWND to the surface using + * cairo_os2_surface_set_hwnd(), this function will be called by the application + * every time it gets a WM_PAINT for that HWND. If the HWND is set in the + * surface, Cairo uses this function to handle dirty areas too. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_refresh_window (cairo_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + cairo_os2_surface_t *local_os2_surface; + RECTL rclTemp; + HPS hpsTemp = 0; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + /* If an HPS wasn't provided, see if we can get one. */ + if (!hps_begin_paint) { + hps_begin_paint = local_os2_surface->hps_client_window; + if (!hps_begin_paint) { + if (local_os2_surface->hwnd_client_window) { + hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window); + hps_begin_paint = hpsTemp; + } + /* No HPS & no way to get one, so exit */ + if (!hps_begin_paint) + return; + } + } + + if (prcl_begin_paint_rect == NULL) { + /* Update the whole window! */ + rclTemp.xLeft = 0; + rclTemp.xRight = local_os2_surface->bitmap_info.cx; + rclTemp.yTop = local_os2_surface->bitmap_info.cy; + rclTemp.yBottom = 0; + } else { + /* Use the rectangle we got passed as parameter! */ + rclTemp.xLeft = prcl_begin_paint_rect->xLeft; + rclTemp.xRight = prcl_begin_paint_rect->xRight; + rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; + rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ; + } + + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + if (hpsTemp) + WinReleasePS(hpsTemp); + return; + } + + if ((local_os2_surface->dirty_area_present) && + (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) && + (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) && + (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) && + (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom)) + { + /* Aha, this call was because of a dirty area, so in this case we + * have to blit the pixels from the screen to the surface! + */ + local_os2_surface->dirty_area_present = FALSE; + _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, + hps_begin_paint, + &rclTemp); + } else { + /* Okay, we have the surface, have the HPS + * (might be from WinBeginPaint () or from WinGetPS () ) + * Now blit there the stuffs! + */ + _cairo_os2_surface_blit_pixels (local_os2_surface, + hps_begin_paint, + &rclTemp); + } + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + if (hpsTemp) + WinReleasePS(hpsTemp); +} + +static cairo_status_t +_cairo_os2_surface_finish (void *abstract_surface) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + /* Destroy old image surface */ + cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); + /* Destroy old pixel buffer */ + _buffer_free (local_os2_surface->pixels); + DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); + DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); + + /* The memory itself will be free'd by the cairo_surface_destroy () + * who called us. + */ + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_set_hwnd: + * @surface: the cairo surface to associate with the window handle + * @hwnd_client_window: the window handle of the client window + * + * Sets window handle for surface; the caller retains ownership of the window. + * If Cairo wants to blit into the window because it is set to blit as the + * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then + * there are two ways it can choose: + * If it knows the HWND of the surface, then it invalidates that area, so the + * application will get a WM_PAINT message and it can call + * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself + * will use the HPS it got at surface creation time, and blit the pixels itself. + * It's also a solution, but experience shows that if this happens from a non-PM + * thread, then it can screw up PM internals. + * + * So, best solution is to set the HWND for the surface after the surface + * creation, so every blit will be done from application's message processing + * loop, which is the safest way to do. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_set_hwnd (cairo_surface_t *surface, + HWND hwnd_client_window) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + return; + } + + local_os2_surface->hwnd_client_window = hwnd_client_window; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); +} + +/** + * cairo_os2_surface_set_manual_window_refresh: + * @surface: the cairo surface to set the refresh mode for + * @manual_refresh: the switch for manual surface refresh + * + * This API can tell Cairo if it should show every change to this surface + * immediately in the window or if it should be cached and will only be visible + * once the user calls cairo_os2_surface_refresh_window() explicitly. If the + * HWND was not set in the cairo surface, then the HPS will be used to blit the + * graphics. Otherwise it will invalidate the given window region so the user + * will get the WM_PAINT message to redraw that area of the window. + * + * So, if you're only interested in displaying the final result after several + * drawing operations, you might get better performance if you put the surface + * into manual refresh mode by passing a true value to this function. Then call + * cairo_os2_surface_refresh() whenever desired. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, + cairo_bool_t manual_refresh) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + local_os2_surface->blit_as_changes = !manual_refresh; +} + +/** + * cairo_os2_surface_get_manual_window_refresh: + * @surface: the cairo surface to query the refresh mode from + * + * This space left intentionally blank. + * + * Return value: current refresh mode of the surface (true by default) + * + * Since: 1.4 + **/ +cairo_bool_t +cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return FALSE; + } + + return !(local_os2_surface->blit_as_changes); +} + +/** + * cairo_os2_surface_get_hps: + * @surface: the cairo surface to be querued + * @hps: HPS currently associated with the surface (if any) + * + * This API retrieves the HPS associated with the surface. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * %CAIRO_STATUS_NULL_POINTER if the hps argument is null + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + if (!hps) + { + return _cairo_error (CAIRO_STATUS_NULL_POINTER); + } + *hps = local_os2_surface->hps_client_window; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_set_hps: + * @surface: the cairo surface to associate with the HPS + * @hps: new HPS to be associated with the surface (the HPS may be null) + * + * This API replaces the HPS associated with the surface with a new one. + * The caller retains ownership of the HPS and must dispose of it after + * the surface has been destroyed or it has been replaced by another + * call to this function. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + local_os2_surface->hps_client_window = hps; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_os2_surface_mark_dirty_rectangle (void *surface, + int x, + int y, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + RECTL rclToBlit; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + return CAIRO_STATUS_NO_MEMORY; + } + + /* Check for defaults */ + if (width < 0) + width = local_os2_surface->bitmap_info.cx; + if (height < 0) + height = local_os2_surface->bitmap_info.cy; + + if (local_os2_surface->hwnd_client_window) { + /* We know the HWND, so let's invalidate the window region, + * so the application will redraw itself, using the + * cairo_os2_surface_refresh_window () API from its own PM thread. + * From that function we'll note that it's not a redraw but a + * dirty-rectangle deal stuff, so we'll handle the things from + * there. + * + * This is the safe method, which should be preferred every time. + */ + rclToBlit.xLeft = x; + rclToBlit.xRight = x + width; /* Noninclusive */ + rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y); + rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */ + +#if 0 + if (local_os2_surface->dirty_area_present) { + /* Yikes, there is already a dirty area which should be + * cleaned up, but we'll overwrite it. Sorry. + * TODO: Something clever should be done here. + */ + } +#endif + + /* Set up dirty area reminder stuff */ + memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL)); + local_os2_surface->dirty_area_present = TRUE; + + /* Invalidate window area */ + WinInvalidateRect (local_os2_surface->hwnd_client_window, + &rclToBlit, + FALSE); + } else { + /* We don't know the HWND, so try to blit the pixels from here! + * Please note that it can be problematic if this is not the PM thread! + * + * It can cause internal PM stuffs to be scewed up, for some reason. + * Please always tell the HWND to the surface using the + * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () + * from your WM_PAINT, if it's possible! + */ + + rclToBlit.xLeft = x; + rclToBlit.xRight = x + width; /* Noninclusive */ + rclToBlit.yBottom = y; + rclToBlit.yTop = y + height; /* Noninclusive */ + /* Now get the pixels from the screen! */ + _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, + local_os2_surface->hps_client_window, + &rclToBlit); + } + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_os2_surface_backend = { + CAIRO_SURFACE_TYPE_OS2, + NULL, /* create_similar */ + _cairo_os2_surface_finish, + _cairo_os2_surface_acquire_source_image, + _cairo_os2_surface_release_source_image, + _cairo_os2_surface_acquire_dest_image, + _cairo_os2_surface_release_dest_image, + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_os2_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + _cairo_os2_surface_mark_dirty_rectangle, + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* show_glyphs */ + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* has_show_text_glyphs */ + NULL /* show_text_glyphs */ +}; diff --git a/libs/cairo/src/cairo-os2.h b/libs/cairo/src/cairo-os2.h new file mode 100644 index 000000000..16a4fc564 --- /dev/null +++ b/libs/cairo/src/cairo-os2.h @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CAIRO_OS2_H_ +#define _CAIRO_OS2_H_ + +#define INCL_DOS +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#define INCL_WIN +#define INCL_GPI + +#include "cairo.h" + +#include + +CAIRO_BEGIN_DECLS + +/* The OS/2 Specific Cairo API */ + +cairo_public void +cairo_os2_init (void); + +cairo_public void +cairo_os2_fini (void); + +#if CAIRO_HAS_OS2_SURFACE + +cairo_public cairo_surface_t * +cairo_os2_surface_create (HPS hps_client_window, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height); + +cairo_public void +cairo_os2_surface_set_hwnd (cairo_surface_t *surface, + HWND hwnd_client_window); + +cairo_public int +cairo_os2_surface_set_size (cairo_surface_t *surface, + int new_width, + int new_height, + int timeout); + +cairo_public void +cairo_os2_surface_refresh_window (cairo_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect); + +cairo_public void +cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, + cairo_bool_t manual_refresh); + +cairo_public cairo_bool_t +cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps); + +cairo_public cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps); + +#else /* CAIRO_HAS_OS2_SURFACE */ +# error Cairo was not compiled with support for the OS/2 backend +#endif /* CAIRO_HAS_OS2_SURFACE */ + +CAIRO_END_DECLS + +#endif /* _CAIRO_OS2_H_ */ diff --git a/libs/cairo/src/cairo-output-stream-private.h b/libs/cairo/src/cairo-output-stream-private.h new file mode 100644 index 000000000..8f3cb3c6b --- /dev/null +++ b/libs/cairo/src/cairo-output-stream-private.h @@ -0,0 +1,165 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H +#define CAIRO_OUTPUT_STREAM_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +#include +#include +#include + +typedef cairo_status_t +(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, + const unsigned char *data, + unsigned int length); + +typedef cairo_status_t +(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); + +typedef cairo_status_t +(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); + +struct _cairo_output_stream { + cairo_output_stream_write_func_t write_func; + cairo_output_stream_flush_func_t flush_func; + cairo_output_stream_close_func_t close_func; + unsigned long position; + cairo_status_t status; + cairo_bool_t closed; +}; + +extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; + +cairo_private void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, + cairo_output_stream_close_func_t close_func); + +cairo_private cairo_status_t +_cairo_output_stream_fini (cairo_output_stream_t *stream); + + +/* We already have the following declared in cairo.h: + +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); +*/ +typedef cairo_status_t (*cairo_close_func_t) (void *closure); + + +/* This function never returns %NULL. If an error occurs (NO_MEMORY) + * while trying to create the output stream this function returns a + * valid pointer to a nil output stream. + * + * Note that even with a nil surface, the close_func callback will be + * called by a call to _cairo_output_stream_close or + * _cairo_output_stream_destroy. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, + void *closure); + +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_in_error (cairo_status_t status); + +/* Tries to flush any buffer maintained by the stream or its delegates. */ +cairo_private cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream); + +/* Returns the final status value associated with this object, just + * before its last gasp. This final status value will capture any + * status failure returned by the stream's close_func as well. */ +cairo_private cairo_status_t +_cairo_output_stream_close (cairo_output_stream_t *stream); + +/* Returns the final status value associated with this object, just + * before its last gasp. This final status value will capture any + * status failure returned by the stream's close_func as well. */ +cairo_private cairo_status_t +_cairo_output_stream_destroy (cairo_output_stream_t *stream); + +cairo_private void +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length); + +cairo_private void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const unsigned char *data, + size_t length); + +cairo_private void +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, + va_list ap) CAIRO_PRINTF_FORMAT ( 2, 0); + +cairo_private void +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, + ...) CAIRO_PRINTF_FORMAT (2, 3); + +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/libs/cairo/src/cairo-output-stream.c b/libs/cairo/src/cairo-output-stream.c new file mode 100644 index 000000000..5f8d774cd --- /dev/null +++ b/libs/cairo/src/cairo-output-stream.c @@ -0,0 +1,738 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for snprintf() */ +#include "cairoint.h" + +#include "cairo-output-stream-private.h" +#include "cairo-error-private.h" +#include "cairo-compiler-private.h" + +#include +#include +#include + +/* Numbers printed with %f are printed with this number of significant + * digits after the decimal. + */ +#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6 + +/* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS + * bits of precision available after the decimal point. + * + * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal + * digits after the decimal point required to preserve the available + * precision. + * + * The conversion is: + * + * + * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) ) + * + * + * We can replace ceil(x) with (int)(x+1) since x will never be an + * integer for any likely value of %CAIRO_FIXED_FRAC_BITS. + */ +#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1)) + +void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, + cairo_output_stream_close_func_t close_func) +{ + stream->write_func = write_func; + stream->flush_func = flush_func; + stream->close_func = close_func; + stream->position = 0; + stream->status = CAIRO_STATUS_SUCCESS; + stream->closed = FALSE; +} + +cairo_status_t +_cairo_output_stream_fini (cairo_output_stream_t *stream) +{ + return _cairo_output_stream_close (stream); +} + +const cairo_output_stream_t _cairo_output_stream_nil = { + NULL, /* write_func */ + NULL, /* flush_func */ + NULL, /* close_func */ + 0, /* position */ + CAIRO_STATUS_NO_MEMORY, + FALSE /* closed */ +}; + +static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { + NULL, /* write_func */ + NULL, /* flush_func */ + NULL, /* close_func */ + 0, /* position */ + CAIRO_STATUS_WRITE_ERROR, + FALSE /* closed */ +}; + +typedef struct _cairo_output_stream_with_closure { + cairo_output_stream_t base; + cairo_write_func_t write_func; + cairo_close_func_t close_func; + void *closure; +} cairo_output_stream_with_closure_t; + + +static cairo_status_t +closure_write (cairo_output_stream_t *stream, + const unsigned char *data, unsigned int length) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + if (stream_with_closure->write_func == NULL) + return CAIRO_STATUS_SUCCESS; + + return stream_with_closure->write_func (stream_with_closure->closure, + data, length); +} + +static cairo_status_t +closure_close (cairo_output_stream_t *stream) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + if (stream_with_closure->close_func != NULL) + return stream_with_closure->close_func (stream_with_closure->closure); + else + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, + void *closure) +{ + cairo_output_stream_with_closure_t *stream; + + stream = 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 . + * 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 + */ +static void +_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) +{ + struct lconv *locale_data; + const char *decimal_point; + int decimal_point_len; + char *p; + int decimal_len; + int num_zeros, decimal_digits; + + /* Omit the minus sign from negative zero. */ + if (d == 0.0) + d = 0.0; + +#ifdef HAVE_LOCALECONV + locale_data = localeconv (); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen (decimal_point); +#else + decimal_point = "."; + decimal_point_len = 1; +#endif + + assert (decimal_point_len != 0); + + if (limited_precision) { + snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d); + } else { + /* Using "%f" to print numbers less than 0.1 will result in + * reduced precision due to the default 6 digits after the + * decimal point. + * + * For numbers is < 0.1, we print with maximum precision and count + * the number of zeros between the decimal point and the first + * significant digit. We then print the number again with the + * number of decimal places that gives us the required number of + * significant digits. This ensures the number is correctly + * rounded. + */ + if (fabs (d) >= 0.1) { + snprintf (buffer, size, "%f", d); + } else { + snprintf (buffer, size, "%.18f", d); + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (_cairo_isdigit (*p)) + p++; + + if (strncmp (p, decimal_point, decimal_point_len) == 0) + p += decimal_point_len; + + num_zeros = 0; + while (*p++ == '0') + num_zeros++; + + decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL; + + if (decimal_digits < 18) + snprintf (buffer, size, "%.*f", decimal_digits, d); + } + } + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (_cairo_isdigit (*p)) + p++; + + if (strncmp (p, decimal_point, decimal_point_len) == 0) { + *p = '.'; + decimal_len = strlen (p + decimal_point_len); + memmove (p + 1, p + decimal_point_len, decimal_len); + p[1 + decimal_len] = 0; + + /* Remove trailing zeros and decimal point if possible. */ + for (p = p + decimal_len; *p == '0'; p--) + *p = 0; + + if (*p == '.') { + *p = 0; + p--; + } + } +} + +enum { + LENGTH_MODIFIER_LONG = 0x100 +}; + +/* Here's a limited reimplementation of printf. The reason for doing + * this is primarily to special case handling of doubles. We want + * locale independent formatting of doubles and we want to trim + * trailing zeros. This is handled by dtostr() above, and the code + * below handles everything else by calling snprintf() to do the + * formatting. This functionality is only for internal use and we + * only implement the formats we actually use. + */ +void +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, va_list ap) +{ +#define SINGLE_FMT_BUFFER_SIZE 32 + char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE]; + int single_fmt_length; + char *p; + const char *f, *start; + int length_modifier, width; + cairo_bool_t var_width; + + if (stream->status) + return; + + f = fmt; + p = buffer; + while (*f != '\0') { + if (p == buffer + sizeof (buffer)) { + _cairo_output_stream_write (stream, buffer, sizeof (buffer)); + p = buffer; + } + + if (*f != '%') { + *p++ = *f++; + continue; + } + + start = f; + f++; + + if (*f == '0') + f++; + + var_width = FALSE; + if (*f == '*') { + var_width = TRUE; + f++; + } + + while (_cairo_isdigit (*f)) + f++; + + length_modifier = 0; + if (*f == 'l') { + length_modifier = LENGTH_MODIFIER_LONG; + f++; + } + + /* The only format strings exist in the cairo implementation + * itself. So there's an internal consistency problem if any + * of them is larger than our format buffer size. */ + single_fmt_length = f - start + 1; + assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE); + + /* Reuse the format string for this conversion. */ + memcpy (single_fmt, start, single_fmt_length); + single_fmt[single_fmt_length] = '\0'; + + /* Flush contents of buffer before snprintf()'ing into it. */ + _cairo_output_stream_write (stream, buffer, p - buffer); + + /* We group signed and unsigned together in this switch, the + * only thing that matters here is the size of the arguments, + * since we're just passing the data through to sprintf(). */ + switch (*f | length_modifier) { + case '%': + buffer[0] = *f; + buffer[1] = 0; + break; + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (var_width) { + width = va_arg (ap, int); + snprintf (buffer, sizeof buffer, + single_fmt, width, va_arg (ap, int)); + } else { + snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int)); + } + break; + case 'd' | LENGTH_MODIFIER_LONG: + case 'u' | LENGTH_MODIFIER_LONG: + case 'o' | LENGTH_MODIFIER_LONG: + case 'x' | LENGTH_MODIFIER_LONG: + case 'X' | LENGTH_MODIFIER_LONG: + if (var_width) { + width = va_arg (ap, int); + snprintf (buffer, sizeof buffer, + single_fmt, width, va_arg (ap, long int)); + } else { + snprintf (buffer, sizeof buffer, + single_fmt, va_arg (ap, long int)); + } + break; + case 's': + snprintf (buffer, sizeof buffer, + single_fmt, va_arg (ap, const char *)); + break; + case 'f': + _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE); + break; + case 'g': + _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE); + break; + case 'c': + buffer[0] = va_arg (ap, int); + buffer[1] = 0; + break; + default: + ASSERT_NOT_REACHED; + } + p = buffer + strlen (buffer); + f++; + } + + _cairo_output_stream_write (stream, buffer, p - buffer); +} + +void +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + + _cairo_output_stream_vprintf (stream, fmt, ap); + + va_end (ap); +} + +long +_cairo_output_stream_get_position (cairo_output_stream_t *stream) +{ + return stream->position; +} + +cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream) +{ + return stream->status; +} + +/* Maybe this should be a configure time option, so embedded targets + * don't have to pull in stdio. */ + + +typedef struct _stdio_stream { + cairo_output_stream_t base; + FILE *file; +} stdio_stream_t; + +static cairo_status_t +stdio_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + stdio_stream_t *stream = (stdio_stream_t *) base; + + if (fwrite (data, 1, length, stream->file) != length) + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +stdio_flush (cairo_output_stream_t *base) +{ + stdio_stream_t *stream = (stdio_stream_t *) base; + + fflush (stream->file); + + if (ferror (stream->file)) + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + else + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +stdio_close (cairo_output_stream_t *base) +{ + cairo_status_t status; + stdio_stream_t *stream = (stdio_stream_t *) base; + + status = stdio_flush (base); + + fclose (stream->file); + + return status; +} + +cairo_output_stream_t * +_cairo_output_stream_create_for_file (FILE *file) +{ + stdio_stream_t *stream; + + if (file == NULL) { + _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + } + + stream = malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_flush); + stream->file = file; + + return &stream->base; +} + +cairo_output_stream_t * +_cairo_output_stream_create_for_filename (const char *filename) +{ + stdio_stream_t *stream; + FILE *file; + + if (filename == NULL) + return _cairo_null_stream_create (); + + file = fopen (filename, "wb"); + if (file == NULL) { + switch (errno) { + case ENOMEM: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + default: + _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + } + } + + stream = malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + fclose (file); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_close); + stream->file = file; + + return &stream->base; +} + + +typedef struct _memory_stream { + cairo_output_stream_t base; + cairo_array_t array; +} memory_stream_t; + +static cairo_status_t +memory_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_append_multiple (&stream->array, data, length); +} + +static cairo_status_t +memory_close (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + _cairo_array_fini (&stream->array); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_memory_stream_create (void) +{ + memory_stream_t *stream; + + stream = malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); + _cairo_array_init (&stream->array, 1); + + return &stream->base; +} + +cairo_status_t +_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, + unsigned char **data_out, + unsigned long *length_out) +{ + memory_stream_t *stream; + cairo_status_t status; + + status = abstract_stream->status; + if (unlikely (status)) + return _cairo_output_stream_destroy (abstract_stream); + + stream = (memory_stream_t *) abstract_stream; + + *length_out = _cairo_array_num_elements (&stream->array); + *data_out = malloc (*length_out); + if (unlikely (*data_out == NULL)) { + status = _cairo_output_stream_destroy (abstract_stream); + assert (status == CAIRO_STATUS_SUCCESS); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out); + + return _cairo_output_stream_destroy (abstract_stream); +} + +void +_cairo_memory_stream_copy (cairo_output_stream_t *base, + cairo_output_stream_t *dest) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + if (dest->status) + return; + + if (base->status) { + dest->status = base->status; + return; + } + + _cairo_output_stream_write (dest, + _cairo_array_index (&stream->array, 0), + _cairo_array_num_elements (&stream->array)); +} + +int +_cairo_memory_stream_length (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_num_elements (&stream->array); +} + +static cairo_status_t +null_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_null_stream_create (void) +{ + cairo_output_stream_t *stream; + + stream = malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (stream, null_write, NULL, NULL); + + return stream; +} diff --git a/libs/cairo/src/cairo-paginated-private.h b/libs/cairo/src/cairo-paginated-private.h new file mode 100644 index 000000000..5687cf85c --- /dev/null +++ b/libs/cairo/src/cairo-paginated-private.h @@ -0,0 +1,134 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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__surface_create(). The paginated backend will be careful + * to not let the user see that they really got a "wrapped" + * surface. See test-paginated-surface.c for a fairly minimal example + * of a paginated-using surface. That should be a reasonable example + * to follow. + * + * What the paginated surface does is first save all drawing + * operations for a page into a recording-surface. Then when the user calls + * cairo_show_page(), the paginated surface performs the following + * sequence of operations (using the backend functions passed to + * cairo_paginated_surface_create()): + * + * 1. Calls start_page() (if not %NULL). At this point, it is appropriate + * for the target to emit any page-specific header information into + * its output. + * + * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE + * + * 3. Replays the recording-surface to the target surface, (with an + * analysis surface inserted between which watches the return value + * from each operation). This analysis stage is used to decide which + * operations will require fallbacks. + * + * 4. Calls set_bounding_box() to provide the target surface with the + * tight bounding box of the page. + * + * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER + * + * 6. Replays a subset of the recording-surface operations to the target surface + * + * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK + * + * 8. Replays the remaining operations to an image surface, sets an + * appropriate clip on the target, then paints the resulting image + * surface to the target. + * + * So, the target will see drawing operations during three separate + * stages, (ANALYZE, RENDER and FALLBACK). During the ANALYZE phase + * the target should not actually perform any rendering, (for example, + * if performing output to a file, no output should be generated + * during this stage). Instead the drawing functions simply need to + * return %CAIRO_STATUS_SUCCESS or %CAIRO_INT_STATUS_UNSUPPORTED to + * indicate whether rendering would be supported. And it should do + * this as quickly as possible. The FALLBACK phase allows the surface + * to distinguish fallback images from native rendering in case they + * need to be handled as a special case. + * + * Note: The paginated surface layer assumes that the target surface + * is "blank" by default at the beginning of each page, without any + * need for an explicit erase operation, (as opposed to an image + * surface, for example, which might have uninitialized content + * originally). As such, it optimizes away CLEAR operations that + * happen at the beginning of each page---the target surface will not + * even see these operations. + */ +cairo_private cairo_surface_t * +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + const cairo_paginated_surface_backend_t *backend); + +cairo_private cairo_surface_t * +_cairo_paginated_surface_get_target (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_surface_is_paginated (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_paginated_surface_set_size (cairo_surface_t *surface, + int width, + int height); + +#endif /* CAIRO_PAGINATED_H */ diff --git a/libs/cairo/src/cairo-paginated-surface-private.h b/libs/cairo/src/cairo-paginated-surface-private.h new file mode 100644 index 000000000..0b4e81f23 --- /dev/null +++ b/libs/cairo/src/cairo-paginated-surface-private.h @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-paginated-surface.c b/libs/cairo/src/cairo-paginated-surface.c new file mode 100644 index 000000000..febcd05fa --- /dev/null +++ b/libs/cairo/src/cairo-paginated-surface.c @@ -0,0 +1,617 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* The paginated surface layer exists to provide as much code sharing + * as possible for the various paginated surface backends in cairo + * (PostScript, PDF, etc.). See cairo-paginated-private.h for + * more details on how it works and how to use it. + */ + +#include "cairoint.h" + +#include "cairo-paginated-private.h" +#include "cairo-paginated-surface-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" + +static const cairo_surface_backend_t cairo_paginated_surface_backend; + +static cairo_int_status_t +_cairo_paginated_surface_show_page (void *abstract_surface); + +static cairo_surface_t * +_cairo_paginated_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t rect; + rect.x = rect.y = 0.; + rect.width = width; + rect.height = height; + return cairo_recording_surface_create (content, &rect); +} + +static cairo_surface_t * +_create_recording_surface_for_target (cairo_surface_t *target, + cairo_content_t content) +{ + cairo_rectangle_int_t rect; + + if (_cairo_surface_get_extents (target, &rect)) { + cairo_rectangle_t recording_extents; + + recording_extents.x = rect.x; + recording_extents.y = rect.y; + recording_extents.width = rect.width; + recording_extents.height = rect.height; + + return cairo_recording_surface_create (content, &recording_extents); + } else { + return cairo_recording_surface_create (content, NULL); + } +} + +cairo_surface_t * +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + const cairo_paginated_surface_backend_t *backend) +{ + cairo_paginated_surface_t *surface; + cairo_status_t status; + + surface = malloc (sizeof (cairo_paginated_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_surface_init (&surface->base, + &cairo_paginated_surface_backend, + NULL, /* device */ + content); + + /* Override surface->base.type with target's type so we don't leak + * evidence of the paginated wrapper out to the user. */ + surface->base.type = target->type; + + surface->target = cairo_surface_reference (target); + + surface->content = content; + surface->backend = backend; + + surface->recording_surface = _create_recording_surface_for_target (target, content); + status = surface->recording_surface->status; + if (unlikely (status)) + goto FAIL_CLEANUP_SURFACE; + + surface->page_num = 1; + surface->base.is_clear = TRUE; + + return &surface->base; + + FAIL_CLEANUP_SURFACE: + cairo_surface_destroy (target); + free (surface); + FAIL: + return _cairo_surface_create_in_error (status); +} + +cairo_bool_t +_cairo_surface_is_paginated (cairo_surface_t *surface) +{ + return surface->backend == &cairo_paginated_surface_backend; +} + +cairo_surface_t * +_cairo_paginated_surface_get_target (cairo_surface_t *surface) +{ + cairo_paginated_surface_t *paginated_surface; + + assert (_cairo_surface_is_paginated (surface)); + + paginated_surface = (cairo_paginated_surface_t *) surface; + + return paginated_surface->target; +} + +static cairo_status_t +_cairo_paginated_surface_finish (void *abstract_surface) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (! surface->base.is_clear || surface->page_num == 1) { + /* Bypass some of the sanity checking in cairo-surface.c, as we + * know that the surface is finished... + */ + status = _cairo_paginated_surface_show_page (surface); + } + + /* XXX We want to propagate any errors from destroy(), but those are not + * returned via the api. So we need to explicitly finish the target, + * and check the status afterwards. However, we can only call finish() + * on the target, if we own it. + */ + if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1) + cairo_surface_finish (surface->target); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->target); + cairo_surface_destroy (surface->target); + + cairo_surface_finish (surface->recording_surface); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->recording_surface); + cairo_surface_destroy (surface->recording_surface); + + return status; +} + +static cairo_surface_t * +_cairo_paginated_surface_create_image_surface (void *abstract_surface, + int width, + int height) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_surface_t *image; + cairo_font_options_t options; + + image = _cairo_image_surface_create_with_content (surface->content, + width, + height); + + cairo_surface_get_font_options (&surface->base, &options); + _cairo_surface_set_font_options (image, &options); + + return image; +} + +static cairo_status_t +_cairo_paginated_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_bool_t is_bounded; + cairo_surface_t *image; + cairo_status_t status; + cairo_rectangle_int_t extents; + + is_bounded = _cairo_surface_get_extents (surface->target, &extents); + if (! is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = _cairo_paginated_surface_create_image_surface (surface, + extents.width, + extents.height); + + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t*) image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_paginated_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_int_status_t +_paint_fallback_image (cairo_paginated_surface_t *surface, + cairo_rectangle_int_t *rect) +{ + double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; + double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; + int x, y, width, height; + cairo_status_t status; + cairo_surface_t *image; + cairo_surface_pattern_t pattern; + cairo_clip_t clip; + + x = rect->x; + y = rect->y; + width = rect->width; + height = rect->height; + image = _cairo_paginated_surface_create_image_surface (surface, + ceil (width * x_scale), + ceil (height * y_scale)); + _cairo_surface_set_device_scale (image, x_scale, y_scale); + /* set_device_offset just sets the x0/y0 components of the matrix; + * so we have to do the scaling manually. */ + cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); + + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) + goto CLEANUP_IMAGE; + + _cairo_pattern_init_for_surface (&pattern, image); + cairo_matrix_init (&pattern.base.matrix, + x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); + /* the fallback should be rendered at native resolution, so disable + * filtering (if possible) to avoid introducing potential artifacts. */ + pattern.base.filter = CAIRO_FILTER_NEAREST; + + _cairo_clip_init (&clip); + status = _cairo_clip_rectangle (&clip, rect); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_surface_paint (surface->target, + CAIRO_OPERATOR_SOURCE, + &pattern.base, &clip); + } + + _cairo_clip_fini (&clip); + _cairo_pattern_fini (&pattern.base); + +CLEANUP_IMAGE: + cairo_surface_destroy (image); + + return status; +} + +static cairo_int_status_t +_paint_page (cairo_paginated_surface_t *surface) +{ + cairo_surface_t *analysis; + cairo_status_t status; + cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; + + if (unlikely (surface->target->status)) + return surface->target->status; + + analysis = _cairo_analysis_surface_create (surface->target); + if (unlikely (analysis->status)) + return _cairo_surface_set_error (surface->target, analysis->status); + + surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_ANALYZE); + status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, + analysis); + if (status || analysis->status) { + if (status == CAIRO_STATUS_SUCCESS) + status = analysis->status; + goto FAIL; + } + + if (surface->backend->set_bounding_box) { + cairo_box_t bbox; + + _cairo_analysis_surface_get_bounding_box (analysis, &bbox); + status = surface->backend->set_bounding_box (surface->target, &bbox); + if (unlikely (status)) + goto FAIL; + } + + if (surface->backend->set_fallback_images_required) { + cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis); + + status = surface->backend->set_fallback_images_required (surface->target, + has_fallbacks); + if (unlikely (status)) + goto FAIL; + } + + /* Finer grained fallbacks are currently only supported for some + * surface types */ + if (surface->backend->supports_fine_grained_fallbacks != NULL && + surface->backend->supports_fine_grained_fallbacks (surface->target)) + { + has_supported = _cairo_analysis_surface_has_supported (analysis); + has_page_fallback = FALSE; + has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis); + } + else + { + if (_cairo_analysis_surface_has_unsupported (analysis)) { + has_supported = FALSE; + has_page_fallback = TRUE; + } else { + has_supported = TRUE; + has_page_fallback = FALSE; + } + has_finegrained_fallback = FALSE; + } + + if (has_supported) { + surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_RENDER); + + status = _cairo_recording_surface_replay_region (surface->recording_surface, + NULL, + surface->target, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + goto FAIL; + } + + if (has_page_fallback) { + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + + surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_FALLBACK); + + is_bounded = _cairo_surface_get_extents (surface->target, &extents); + if (! is_bounded) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + status = _paint_fallback_image (surface, &extents); + if (unlikely (status)) + goto FAIL; + } + + if (has_finegrained_fallback) { + cairo_region_t *region; + int num_rects, i; + + surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_FALLBACK); + + region = _cairo_analysis_surface_get_unsupported (analysis); + + num_rects = cairo_region_num_rectangles (region); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + status = _paint_fallback_image (surface, &rect); + if (unlikely (status)) + goto FAIL; + } + } + + FAIL: + cairo_surface_destroy (analysis); + + return _cairo_surface_set_error (surface->target, status); +} + +static cairo_status_t +_start_page (cairo_paginated_surface_t *surface) +{ + if (surface->target->status) + return surface->target->status; + + if (! surface->backend->start_page) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_set_error (surface->target, + surface->backend->start_page (surface->target)); +} + +static cairo_int_status_t +_cairo_paginated_surface_copy_page (void *abstract_surface) +{ + cairo_status_t status; + cairo_paginated_surface_t *surface = abstract_surface; + + status = _start_page (surface); + if (unlikely (status)) + return status; + + status = _paint_page (surface); + if (unlikely (status)) + return status; + + surface->page_num++; + + /* XXX: It might make sense to add some support here for calling + * cairo_surface_copy_page on the target surface. It would be an + * optimization for the output, but the interaction with image + * fallbacks gets tricky. For now, we just let the target see a + * show_page and we implement the copying by simply not destroying + * the recording-surface. */ + + cairo_surface_show_page (surface->target); + return cairo_surface_status (surface->target); +} + +static cairo_int_status_t +_cairo_paginated_surface_show_page (void *abstract_surface) +{ + cairo_status_t status; + cairo_paginated_surface_t *surface = abstract_surface; + + status = _start_page (surface); + if (unlikely (status)) + return status; + + status = _paint_page (surface); + if (unlikely (status)) + return status; + + cairo_surface_show_page (surface->target); + status = surface->target->status; + if (unlikely (status)) + return status; + + status = surface->recording_surface->status; + if (unlikely (status)) + return status; + + if (! surface->base.finished) { + cairo_surface_destroy (surface->recording_surface); + + surface->recording_surface = _create_recording_surface_for_target (surface->target, + surface->content); + status = surface->recording_surface->status; + if (unlikely (status)) + return status; + + surface->page_num++; + surface->base.is_clear = TRUE; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_paginated_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static void +_cairo_paginated_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + cairo_surface_get_font_options (surface->target, options); +} + +static cairo_int_status_t +_cairo_paginated_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_paint (surface->recording_surface, op, source, clip); +} + +static cairo_int_status_t +_cairo_paginated_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_paginated_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_stroke (surface->recording_surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_paginated_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_fill (surface->recording_surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_bool_t +_cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_cairo_paginated_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); +} + +static cairo_surface_t * +_cairo_paginated_surface_snapshot (void *abstract_other) +{ + cairo_paginated_surface_t *other = abstract_other; + + return _cairo_surface_snapshot (other->recording_surface); +} + +static const cairo_surface_backend_t cairo_paginated_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, + _cairo_paginated_surface_create_similar, + _cairo_paginated_surface_finish, + _cairo_paginated_surface_acquire_source_image, + _cairo_paginated_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + _cairo_paginated_surface_copy_page, + _cairo_paginated_surface_show_page, + _cairo_paginated_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_paginated_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _cairo_paginated_surface_paint, + _cairo_paginated_surface_mask, + _cairo_paginated_surface_stroke, + _cairo_paginated_surface_fill, + NULL, /* show_glyphs */ + _cairo_paginated_surface_snapshot, + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + _cairo_paginated_surface_has_show_text_glyphs, + _cairo_paginated_surface_show_text_glyphs +}; diff --git a/libs/cairo/src/cairo-path-bounds.c b/libs/cairo/src/cairo-path-bounds.c new file mode 100644 index 000000000..c752fbca1 --- /dev/null +++ b/libs/cairo/src/cairo-path-bounds.c @@ -0,0 +1,318 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef struct cairo_path_bounder { + cairo_point_t current_point; + cairo_bool_t has_initial_point; + cairo_bool_t has_point; + + cairo_box_t extents; +} cairo_path_bounder_t; + +static void +_cairo_path_bounder_init (cairo_path_bounder_t *bounder) +{ + bounder->has_initial_point = FALSE; + bounder->has_point = FALSE; +} + +static void +_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, + const cairo_point_t *point) +{ + if (bounder->has_point) { + if (point->x < bounder->extents.p1.x) + bounder->extents.p1.x = point->x; + + if (point->y < bounder->extents.p1.y) + bounder->extents.p1.y = point->y; + + if (point->x > bounder->extents.p2.x) + bounder->extents.p2.x = point->x; + + if (point->y > bounder->extents.p2.y) + bounder->extents.p2.y = point->y; + } else { + bounder->extents.p1.x = point->x; + bounder->extents.p1.y = point->y; + bounder->extents.p2.x = point->x; + bounder->extents.p2.y = point->y; + bounder->has_point = TRUE; + } +} + +static cairo_status_t +_cairo_path_bounder_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_path_bounder_t *bounder = closure; + + bounder->current_point = *point; + bounder->has_initial_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_bounder_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_path_bounder_t *bounder = closure; + + if (bounder->has_initial_point) { + _cairo_path_bounder_add_point (bounder, &bounder->current_point); + bounder->has_initial_point = FALSE; + } + + _cairo_path_bounder_add_point (bounder, point); + bounder->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_bounder_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_path_bounder_t *bounder = closure; + + /* If the bbox of the control points is entirely inside, then we + * do not need to further evaluate the spline. + */ + if (! bounder->has_point || + b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x || + b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y || + c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x || + c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y || + d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x || + d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y) + { + return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder, + &bounder->current_point, b, c, d); + } + else + { + /* All control points are within the current extents. */ + bounder->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } +} + +static cairo_status_t +_cairo_path_bounder_close_path (void *closure) +{ + return CAIRO_STATUS_SUCCESS; +} + +/* This computes the extents of all the points in the path, not those of + * the damage area (i.e it does not consider winding and it only inspects + * the control points of the curves, not the flattened path). + */ +void +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + if (path->extents.p1.x < path->extents.p2.x) { + _cairo_box_round_to_rectangle (&path->extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +/* A slightly better approximation than above - we actually decompose the + * Bezier, but we continue to ignore winding. + */ +void +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + if (! path->has_curve_to) { + bounder.extents = path->extents; + bounder.has_point = path->extents.p1.x < path->extents.p2.x; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + } + + if (bounder.has_point) { + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +void +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + if (! path->has_curve_to) { + bounder.extents = path->extents; + bounder.has_point = path->extents.p1.x < path->extents.p2.x; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_close_path, + &bounder, tolerance); + assert (status == CAIRO_STATUS_SUCCESS); + } + + if (bounder.has_point) { + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +/* Adjusts the fill extents (above) by the device-space pen. */ +void +_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + if (! path->has_curve_to) { + bounder.extents = path->extents; + + /* include trailing move-to for degenerate segments */ + if (path->has_last_move_point) { + const cairo_point_t *point = &path->last_move_point; + + if (point->x < bounder.extents.p1.x) + bounder.extents.p1.x = point->x; + if (point->y < bounder.extents.p1.y) + bounder.extents.p1.y = point->y; + + if (point->x > bounder.extents.p2.x) + bounder.extents.p2.x = point->x; + if (point->y > bounder.extents.p2.y) + bounder.extents.p2.y = point->y; + } + + bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x; + bounder.has_initial_point = FALSE; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + } + + if (bounder.has_point) { + double dx, dy; + + _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); + + bounder.extents.p1.x -= _cairo_fixed_from_double (dx); + bounder.extents.p2.x += _cairo_fixed_from_double (dx); + bounder.extents.p1.y -= _cairo_fixed_from_double (dy); + bounder.extents.p2.y += _cairo_fixed_from_double (dy); + + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else if (bounder.has_initial_point) { + double dx, dy; + + /* accommodate capping of degenerate paths */ + + _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); + + bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx); + bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx); + bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy); + bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy); + + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +cairo_status_t +_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_rectangle_int_t *extents) +{ + cairo_traps_t traps; + cairo_box_t bbox; + cairo_status_t status; + + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_stroke_to_traps (path, + stroke_style, + ctm, + ctm_inverse, + tolerance, + &traps); + + _cairo_traps_extents (&traps, &bbox); + _cairo_traps_fini (&traps); + + _cairo_box_round_to_rectangle (&bbox, extents); + + return status; +} + +cairo_bool_t +_cairo_path_fixed_extents (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + if (! path->has_curve_to) { + *box = path->extents; + return path->extents.p1.x <= path->extents.p2.x; + } + + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + + *box = bounder.extents; + return bounder.has_point; +} diff --git a/libs/cairo/src/cairo-path-fill.c b/libs/cairo/src/cairo-path-fill.c new file mode 100644 index 000000000..40d41157a --- /dev/null +++ b/libs/cairo/src/cairo-path-fill.c @@ -0,0 +1,433 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-region-private.h" + +typedef struct cairo_filler { + double tolerance; + cairo_polygon_t *polygon; +} cairo_filler_t; + +static void +_cairo_filler_init (cairo_filler_t *filler, + double tolerance, + cairo_polygon_t *polygon) +{ + filler->tolerance = tolerance; + filler->polygon = polygon; +} + +static void +_cairo_filler_fini (cairo_filler_t *filler) +{ +} + +static cairo_status_t +_cairo_filler_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_filler_t *filler = closure; + cairo_polygon_t *polygon = filler->polygon; + + return _cairo_polygon_close (polygon) || + _cairo_polygon_move_to (polygon, point); +} + +static cairo_status_t +_cairo_filler_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_filler_t *filler = closure; + return _cairo_polygon_line_to (filler->polygon, point); +} + +static cairo_status_t +_cairo_filler_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_filler_t *filler = closure; + cairo_spline_t spline; + + if (! _cairo_spline_init (&spline, + _cairo_filler_line_to, filler, + &filler->polygon->current_point, b, c, d)) + { + return _cairo_filler_line_to (closure, d); + } + + return _cairo_spline_decompose (&spline, filler->tolerance); +} + +static cairo_status_t +_cairo_filler_close_path (void *closure) +{ + cairo_filler_t *filler = closure; + return _cairo_polygon_close (filler->polygon); +} + +cairo_status_t +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon) +{ + cairo_filler_t filler; + cairo_status_t status; + + _cairo_filler_init (&filler, tolerance, polygon); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close_path, + &filler); + if (unlikely (status)) + return status; + + status = _cairo_polygon_close (polygon); + _cairo_filler_fini (&filler); + + return status; +} + +cairo_status_t +_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_traps_t *traps) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + if (path->is_empty_fill) + return CAIRO_STATUS_SUCCESS; + + _cairo_polygon_init (&polygon); + if (traps->num_limits) + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + status = _cairo_path_fixed_fill_to_polygon (path, + tolerance, + &polygon); + if (unlikely (status || polygon.num_edges == 0)) + goto CLEANUP; + + if (path->is_rectilinear) { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps, + &polygon, + fill_rule); + } else { + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, + fill_rule); + } + + CLEANUP: + _cairo_polygon_fini (&polygon); + return status; +} + +static cairo_region_t * +_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents) +{ + cairo_box_t box; + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_status_t status; + cairo_region_t *region; + + /* first try to bypass fill-to-polygon */ + _cairo_traps_init (&traps); + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (_cairo_status_is_error (status)) + goto CLEANUP_TRAPS; + + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_traps_extract_region (&traps, ®ion); + goto CLEANUP_TRAPS; + } + + /* path is not rectangular, try extracting clipped rectilinear edges */ + _cairo_polygon_init (&polygon); + if (extents != NULL) { + _cairo_box_from_rectangle (&box, extents); + _cairo_polygon_limit (&polygon, &box, 1); + } + + /* tolerance will be ignored as the path is rectilinear */ + status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (polygon.num_edges == 0) { + region = cairo_region_create (); + } else { + status = + _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_traps_extract_region (&traps, ®ion); + } + + CLEANUP_POLYGON: + _cairo_polygon_fini (&polygon); + + CLEANUP_TRAPS: + _cairo_traps_fini (&traps); + + if (unlikely (status)) + region = _cairo_region_create_in_error (status); + + return region; +} + +/* This special-case filler supports only a path that describes a + * device-axis aligned rectangle. It exists to avoid the overhead of + * the general tessellator when drawing very common rectangles. + * + * If the path described anything but a device-axis aligned rectangle, + * this function will abort. + */ +cairo_region_t * +_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents) +{ + cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_box_t box; + cairo_region_t *region = NULL; + + assert (path->maybe_fill_region); + assert (! path->is_empty_fill); + + if (_cairo_path_fixed_is_box (path, &box)) { + rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x); + rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y); + rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) - + rectangle_stack[0].x; + rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) - + rectangle_stack[0].y; + if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents)) + region = cairo_region_create (); + else + region = cairo_region_create_rectangle (&rectangle_stack[0]); + } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { + cairo_rectangle_int_t *rects = rectangle_stack; + cairo_path_fixed_iter_t iter; + int last_cw = -1; + int size = ARRAY_LENGTH (rectangle_stack); + int count = 0; + + /* Support a series of rectangles as can be expected to describe a + * GdkRegion clip region during exposes. + */ + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + int cw = 0; + + if (box.p1.x > box.p2.x) { + cairo_fixed_t t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; + + cw = ! cw; + } + + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + + cw = ! cw; + } + + if (last_cw < 0) + last_cw = cw; + else if (last_cw != cw) + goto TESSELLATE; + + if (count == size) { + cairo_rectangle_int_t *new_rects; + + size *= 4; + if (rects == rectangle_stack) { + new_rects = _cairo_malloc_ab (size, + sizeof (cairo_rectangle_int_t)); + if (unlikely (new_rects == NULL)) { + /* XXX _cairo_region_nil */ + break; + } + memcpy (new_rects, rects, sizeof (rectangle_stack)); + } else { + new_rects = _cairo_realloc_ab (rects, size, + sizeof (cairo_rectangle_int_t)); + if (unlikely (new_rects == NULL)) { + /* XXX _cairo_region_nil */ + break; + } + } + rects = new_rects; + } + + rects[count].x = _cairo_fixed_integer_part (box.p1.x); + rects[count].y = _cairo_fixed_integer_part (box.p1.y); + rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x; + rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y; + if (_cairo_rectangle_intersect (&rects[count], extents)) + count++; + } + + if (_cairo_path_fixed_iter_at_end (&iter)) + region = cairo_region_create_rectangles (rects, count); + +TESSELLATE: + if (rects != rectangle_stack) + free (rects); + } + + if (region == NULL) { + /* Hmm, complex polygon */ + region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path, + fill_rule, + extents); + + + } + + return region; +} + +cairo_int_status_t +_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps) +{ + cairo_box_t box; + cairo_status_t status; + + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + if (_cairo_path_fixed_is_box (path, &box)) { + return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); + } else { + cairo_path_fixed_iter_t iter; + + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; + } + + status = _cairo_traps_tessellate_rectangle (traps, + &box.p1, &box.p2); + if (unlikely (status)) { + _cairo_traps_clear (traps); + return status; + } + } + + if (_cairo_path_fixed_iter_at_end (&iter)) + return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule); + + _cairo_traps_clear (traps); + return CAIRO_INT_STATUS_UNSUPPORTED; + } +} + +static cairo_status_t +_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + _cairo_polygon_init (&polygon); + if (boxes->num_limits) { + _cairo_polygon_limit (&polygon, boxes->limits, boxes->num_limits); + boxes->num_limits = 0; + } + + /* tolerance will be ignored as the path is rectilinear */ + status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = + _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon, + fill_rule, + boxes); + } + + _cairo_polygon_fini (&polygon); + + return status; +} + +cairo_status_t +_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes) +{ + cairo_path_fixed_iter_t iter; + cairo_status_t status; + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) + return _cairo_boxes_add (boxes, &box); + + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) + continue; + + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; + } + + status = _cairo_boxes_add (boxes, &box); + if (unlikely (status)) + return status; + } + + if (_cairo_path_fixed_iter_at_end (&iter)) + return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes); + + /* path is not rectangular, try extracting clipped rectilinear edges */ + _cairo_boxes_clear (boxes); + return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path, + fill_rule, + boxes); +} diff --git a/libs/cairo/src/cairo-path-fixed-private.h b/libs/cairo/src/cairo-path-fixed-private.h new file mode 100644 index 000000000..69972505a --- /dev/null +++ b/libs/cairo/src/cairo-path-fixed-private.h @@ -0,0 +1,134 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PATH_FIXED_PRIVATE_H +#define CAIRO_PATH_FIXED_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-list-private.h" + +#define WATCH_PATH 0 +#if WATCH_PATH +#include +#endif + +enum cairo_path_op { + CAIRO_PATH_OP_MOVE_TO = 0, + CAIRO_PATH_OP_LINE_TO = 1, + CAIRO_PATH_OP_CURVE_TO = 2, + CAIRO_PATH_OP_CLOSE_PATH = 3 +}; + +/* we want to make sure a single byte is used for the enum */ +typedef char cairo_path_op_t; + +/* make _cairo_path_fixed fit into ~512 bytes -- about 50 items */ +#define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \ + / (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t))) + +typedef struct _cairo_path_buf { + cairo_list_t link; + unsigned int num_ops; + unsigned int size_ops; + unsigned int num_points; + unsigned int size_points; + + cairo_path_op_t *op; + cairo_point_t *points; +} cairo_path_buf_t; + +typedef struct _cairo_path_buf_fixed { + cairo_path_buf_t base; + + cairo_path_op_t op[CAIRO_PATH_BUF_SIZE]; + cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE]; +} cairo_path_buf_fixed_t; + +struct _cairo_path_fixed { + cairo_point_t last_move_point; + cairo_point_t current_point; + unsigned int has_current_point : 1; + unsigned int has_last_move_point : 1; + unsigned int has_curve_to : 1; + unsigned int is_rectilinear : 1; + unsigned int maybe_fill_region : 1; + unsigned int is_empty_fill : 1; + + cairo_box_t extents; + + cairo_path_buf_fixed_t buf; +}; + +cairo_private void +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy); + +cairo_private cairo_status_t +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_direction_t dir, + cairo_fixed_t tx, + cairo_fixed_t ty); + +cairo_private unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path); + +cairo_private unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b); + +typedef struct _cairo_path_fixed_iter { + const cairo_path_buf_t *first; + const cairo_path_buf_t *buf; + unsigned int n_op; + unsigned int n_point; +} cairo_path_fixed_iter_t; + +cairo_private void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); + +static inline cairo_bool_t +_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) +{ + return path->is_empty_fill; +} + +static inline cairo_bool_t +_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path) +{ + if (! path->is_rectilinear) + return 0; + + if (! path->has_current_point) + return 1; + + /* check whether the implicit close preserves the rectilinear property */ + return path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; +} + +static inline cairo_bool_t +_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path) +{ +#if WATCH_PATH + fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n", + path->maybe_fill_region ? "true" : "false"); +#endif + return path->maybe_fill_region; +} + +#endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-path-fixed.c b/libs/cairo/src/cairo-path-fixed.c new file mode 100644 index 000000000..3ba5f75e0 --- /dev/null +++ b/libs/cairo/src/cairo-path-fixed.c @@ -0,0 +1,1390 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" + +static cairo_status_t +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + const cairo_point_t *points, + int num_points); + +static void +_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, + cairo_path_buf_t *buf); + +static cairo_path_buf_t * +_cairo_path_buf_create (int size_ops, int size_points); + +static void +_cairo_path_buf_destroy (cairo_path_buf_t *buf); + +static void +_cairo_path_buf_add_op (cairo_path_buf_t *buf, + cairo_path_op_t op); + +static void +_cairo_path_buf_add_points (cairo_path_buf_t *buf, + const cairo_point_t *points, + int num_points); + +#define cairo_path_head(path__) (&(path__)->buf.base) +#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__)) + +#define cairo_path_buf_next(pos__) \ + cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link) +#define cairo_path_buf_prev(pos__) \ + cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link) + +#define cairo_path_foreach_buf_start(pos__, path__) \ + pos__ = cairo_path_head (path__); do +#define cairo_path_foreach_buf_end(pos__, path__) \ + while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__)) + +void +_cairo_path_fixed_init (cairo_path_fixed_t *path) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + + cairo_list_init (&path->buf.base.link); + + path->buf.base.num_ops = 0; + path->buf.base.num_points = 0; + path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); + path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); + path->buf.base.op = path->buf.op; + path->buf.base.points = path->buf.points; + + path->current_point.x = 0; + path->current_point.y = 0; + path->last_move_point = path->current_point; + path->has_last_move_point = FALSE; + path->has_current_point = FALSE; + path->has_curve_to = FALSE; + path->is_rectilinear = TRUE; + path->maybe_fill_region = TRUE; + path->is_empty_fill = TRUE; + + path->extents.p1.x = path->extents.p1.y = INT_MAX; + path->extents.p2.x = path->extents.p2.y = INT_MIN; +} + +cairo_status_t +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other) +{ + cairo_path_buf_t *buf, *other_buf; + unsigned int num_points, num_ops; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + + cairo_list_init (&path->buf.base.link); + + path->buf.base.op = path->buf.op; + path->buf.base.points = path->buf.points; + path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); + path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); + + path->current_point = other->current_point; + path->last_move_point = other->last_move_point; + path->has_last_move_point = other->has_last_move_point; + path->has_current_point = other->has_current_point; + path->has_curve_to = other->has_curve_to; + path->is_rectilinear = other->is_rectilinear; + path->maybe_fill_region = other->maybe_fill_region; + path->is_empty_fill = other->is_empty_fill; + + path->extents = other->extents; + + path->buf.base.num_ops = other->buf.base.num_ops; + path->buf.base.num_points = other->buf.base.num_points; + memcpy (path->buf.op, other->buf.base.op, + other->buf.base.num_ops * sizeof (other->buf.op[0])); + memcpy (path->buf.points, other->buf.points, + other->buf.base.num_points * sizeof (other->buf.points[0])); + + num_points = num_ops = 0; + for (other_buf = cairo_path_buf_next (cairo_path_head (other)); + other_buf != cairo_path_head (other); + other_buf = cairo_path_buf_next (other_buf)) + { + num_ops += other_buf->num_ops; + num_points += other_buf->num_points; + } + + if (num_ops) { + buf = _cairo_path_buf_create (num_ops, num_points); + if (unlikely (buf == NULL)) { + _cairo_path_fixed_fini (path); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (other_buf = cairo_path_buf_next (cairo_path_head (other)); + other_buf != cairo_path_head (other); + other_buf = cairo_path_buf_next (other_buf)) + { + memcpy (buf->op + buf->num_ops, other_buf->op, + other_buf->num_ops * sizeof (buf->op[0])); + buf->num_ops += other_buf->num_ops; + + memcpy (buf->points + buf->num_points, other_buf->points, + other_buf->num_points * sizeof (buf->points[0])); + buf->num_points += other_buf->num_points; + } + + _cairo_path_fixed_add_buf (path, buf); + } + + return CAIRO_STATUS_SUCCESS; +} + +unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + const cairo_path_buf_t *buf; + int num_points, num_ops; + + hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents)); + + num_ops = num_points = 0; + cairo_path_foreach_buf_start (buf, path) { + hash = _cairo_hash_bytes (hash, buf->op, + buf->num_ops * sizeof (buf->op[0])); + hash = _cairo_hash_bytes (hash, buf->points, + buf->num_points * sizeof (buf->points[0])); + + num_ops += buf->num_ops; + num_points += buf->num_points; + } cairo_path_foreach_buf_end (buf, path); + + hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops)); + hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points)); + + return hash; +} + +unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf; + int num_points, num_ops; + + num_ops = num_points = 0; + cairo_path_foreach_buf_start (buf, path) { + num_ops += buf->num_ops; + num_points += buf->num_points; + } cairo_path_foreach_buf_end (buf, path); + + return num_ops * sizeof (buf->op[0]) + + num_points * sizeof (buf->points[0]); +} + +cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b) +{ + const cairo_path_buf_t *buf_a, *buf_b; + const cairo_path_op_t *ops_a, *ops_b; + const cairo_point_t *points_a, *points_b; + int num_points_a, num_ops_a; + int num_points_b, num_ops_b; + + if (a == b) + return TRUE; + + /* use the flags to quickly differentiate based on contents */ + if (a->is_empty_fill != b->is_empty_fill || + a->has_curve_to != b->has_curve_to || + a->maybe_fill_region != b->maybe_fill_region || + a->is_rectilinear != b->is_rectilinear) + { + return FALSE; + } + + if (a->extents.p1.x != b->extents.p1.x || + a->extents.p1.y != b->extents.p1.y || + a->extents.p2.x != b->extents.p2.x || + a->extents.p2.y != b->extents.p2.y) + { + return FALSE; + } + + num_ops_a = num_points_a = 0; + cairo_path_foreach_buf_start (buf_a, a) { + num_ops_a += buf_a->num_ops; + num_points_a += buf_a->num_points; + } cairo_path_foreach_buf_end (buf_a, a); + + num_ops_b = num_points_b = 0; + cairo_path_foreach_buf_start (buf_b, b) { + num_ops_b += buf_b->num_ops; + num_points_b += buf_b->num_points; + } cairo_path_foreach_buf_end (buf_b, b); + + if (num_ops_a == 0 && num_ops_b == 0) + return TRUE; + + if (num_ops_a != num_ops_b || num_points_a != num_points_b) + return FALSE; + + buf_a = cairo_path_head (a); + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + + buf_b = cairo_path_head (b); + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + + while (TRUE) { + int num_ops = MIN (num_ops_a, num_ops_b); + int num_points = MIN (num_points_a, num_points_b); + + if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) + return FALSE; + if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) + return FALSE; + + num_ops_a -= num_ops; + ops_a += num_ops; + num_points_a -= num_points; + points_a += num_points; + if (num_ops_a == 0 || num_points_a == 0) { + if (num_ops_a || num_points_a) + return FALSE; + + buf_a = cairo_path_buf_next (buf_a); + if (buf_a == cairo_path_head (a)) + break; + + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + } + + num_ops_b -= num_ops; + ops_b += num_ops; + num_points_b -= num_points; + points_b += num_points; + if (num_ops_b == 0 || num_points_b == 0) { + if (num_ops_b || num_points_b) + return FALSE; + + buf_b = cairo_path_buf_next (buf_b); + if (buf_b == cairo_path_head (b)) + break; + + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + } + } + + return TRUE; +} + +cairo_path_fixed_t * +_cairo_path_fixed_create (void) +{ + cairo_path_fixed_t *path; + + path = malloc (sizeof (cairo_path_fixed_t)); + if (!path) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + _cairo_path_fixed_init (path); + return path; +} + +void +_cairo_path_fixed_fini (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + buf = cairo_path_buf_next (cairo_path_head (path)); + while (buf != cairo_path_head (path)) { + cairo_path_buf_t *this = buf; + buf = cairo_path_buf_next (buf); + _cairo_path_buf_destroy (this); + } + + VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t))); +} + +void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path) +{ + _cairo_path_fixed_fini (path); + free (path); +} + +static cairo_path_op_t +_cairo_path_last_op (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + buf = cairo_path_tail (path); + if (buf->num_ops == 0) + return -1; + + return buf->op[buf->num_ops - 1]; +} + +static inline void +_cairo_path_fixed_extents_add (cairo_path_fixed_t *path, + const cairo_point_t *point) +{ + if (point->x < path->extents.p1.x) + path->extents.p1.x = point->x; + if (point->y < path->extents.p1.y) + path->extents.p1.y = point->y; + + if (point->x > path->extents.p2.x) + path->extents.p2.x = point->x; + if (point->y > path->extents.p2.y) + path->extents.p2.y = point->y; +} + +cairo_status_t +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + cairo_status_t status; + cairo_point_t point; + + point.x = x; + point.y = y; + + /* If the previous op was also a MOVE_TO, then just change its + * point rather than adding a new op. */ + if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) { + cairo_path_buf_t *buf; + + buf = cairo_path_tail (path); + buf->points[buf->num_points - 1] = point; + } else { + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); + if (unlikely (status)) + return status; + + if (path->has_current_point && path->is_rectilinear) { + /* a move-to is first an implicit close */ + path->is_rectilinear = path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; + path->maybe_fill_region &= path->is_rectilinear; + } + if (path->maybe_fill_region) { + path->maybe_fill_region = + _cairo_fixed_is_integer (path->last_move_point.x) && + _cairo_fixed_is_integer (path->last_move_point.y); + } + } + + path->current_point = point; + path->last_move_point = point; + path->has_last_move_point = TRUE; + path->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path) +{ + path->has_current_point = FALSE; +} + +cairo_status_t +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_move_to (path, + path->current_point.x + dx, + path->current_point.y + dy); + +} + +cairo_status_t +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + cairo_status_t status; + cairo_point_t point; + + point.x = x; + point.y = y; + + /* When there is not yet a current point, the line_to operation + * becomes a move_to instead. Note: We have to do this by + * explicitly calling into _cairo_path_fixed_move_to to ensure + * that the last_move_point state is updated properly. + */ + if (! path->has_current_point) + return _cairo_path_fixed_move_to (path, point.x, point.y); + + /* If the previous op was but the initial MOVE_TO and this segment + * is degenerate, then we can simply skip this point. Note that + * a move-to followed by a degenerate line-to is a valid path for + * stroking, but at all other times is simply a degenerate segment. + */ + if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { + if (x == path->current_point.x && y == path->current_point.y) + return CAIRO_STATUS_SUCCESS; + } + + /* If the previous op was also a LINE_TO with the same gradient, + * then just change its end-point rather than adding a new op. + */ + if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { + cairo_path_buf_t *buf; + const cairo_point_t *p; + + buf = cairo_path_tail (path); + if (likely (buf->num_points >= 2)) { + p = &buf->points[buf->num_points-2]; + } else { + cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); + p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; + } + + if (p->x == path->current_point.x && p->y == path->current_point.y) { + /* previous line element was degenerate, replace */ + buf->points[buf->num_points - 1] = point; + goto FLAGS; + } else { + cairo_slope_t prev, self; + + _cairo_slope_init (&prev, p, &path->current_point); + _cairo_slope_init (&self, &path->current_point, &point); + if (_cairo_slope_equal (&prev, &self) && + /* cannot trim anti-parallel segments whilst stroking */ + ! _cairo_slope_backwards (&prev, &self)) + { + buf->points[buf->num_points - 1] = point; + goto FLAGS; + } + } + } + + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); + if (unlikely (status)) + return status; + + FLAGS: + if (path->is_rectilinear) { + path->is_rectilinear = path->current_point.x == x || + path->current_point.y == y; + path->maybe_fill_region &= path->is_rectilinear; + } + if (path->maybe_fill_region) { + path->maybe_fill_region = _cairo_fixed_is_integer (x) && + _cairo_fixed_is_integer (y); + } + if (path->is_empty_fill) { + path->is_empty_fill = path->current_point.x == x && + path->current_point.y == y; + } + + path->current_point = point; + if (path->has_last_move_point) { + _cairo_path_fixed_extents_add (path, &path->last_move_point); + path->has_last_move_point = FALSE; + } + _cairo_path_fixed_extents_add (path, &point); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_line_to (path, + path->current_point.x + dx, + path->current_point.y + dy); +} + +cairo_status_t +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2) +{ + cairo_status_t status; + cairo_point_t point[3]; + + /* make sure subpaths are started properly */ + if (! path->has_current_point) { + status = _cairo_path_fixed_move_to (path, x0, y0); + if (unlikely (status)) + return status; + } + + point[0].x = x0; point[0].y = y0; + point[1].x = x1; point[1].y = y1; + point[2].x = x2; point[2].y = y2; + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + if (unlikely (status)) + return status; + + path->current_point = point[2]; + path->has_current_point = TRUE; + path->is_empty_fill = FALSE; + path->has_curve_to = TRUE; + path->is_rectilinear = FALSE; + path->maybe_fill_region = FALSE; + + /* coarse bounds */ + if (path->has_last_move_point) { + _cairo_path_fixed_extents_add (path, &path->last_move_point); + path->has_last_move_point = FALSE; + } + _cairo_path_fixed_extents_add (path, &point[0]); + _cairo_path_fixed_extents_add (path, &point[1]); + _cairo_path_fixed_extents_add (path, &point[2]); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_curve_to (path, + path->current_point.x + dx0, + path->current_point.y + dy0, + + path->current_point.x + dx1, + path->current_point.y + dy1, + + path->current_point.x + dx2, + path->current_point.y + dy2); +} + +cairo_status_t +_cairo_path_fixed_close_path (cairo_path_fixed_t *path) +{ + cairo_status_t status; + + if (! path->has_current_point) + return CAIRO_STATUS_SUCCESS; + + /* If the previous op was also a LINE_TO back to the start, discard it */ + if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { + if (path->current_point.x == path->last_move_point.x && + path->current_point.y == path->last_move_point.y) + { + cairo_path_buf_t *buf; + cairo_point_t *p; + + buf = cairo_path_tail (path); + if (likely (buf->num_points >= 2)) { + p = &buf->points[buf->num_points-2]; + } else { + cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); + p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; + } + + path->current_point = *p; + buf->num_ops--; + buf->num_points--; + } + } + + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_move_to (path, + path->last_move_point.x, + path->last_move_point.y); +} + +cairo_bool_t +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y) +{ + if (! path->has_current_point) + return FALSE; + + *x = path->current_point.x; + *y = path->current_point.y; + + return TRUE; +} + +static cairo_status_t +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + const cairo_point_t *points, + int num_points) +{ + cairo_path_buf_t *buf = cairo_path_tail (path); + + if (buf->num_ops + 1 > buf->size_ops || + buf->num_points + num_points > buf->size_points) + { + buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_path_fixed_add_buf (path, buf); + } + + if (WATCH_PATH) { + const char *op_str[] = { + "move-to", + "line-to", + "curve-to", + "close-path", + }; + char buf[1024]; + int len = 0; + int i; + + len += snprintf (buf + len, sizeof (buf), "["); + for (i = 0; i < num_points; i++) { + if (i != 0) + len += snprintf (buf + len, sizeof (buf), " "); + len += snprintf (buf + len, sizeof (buf), "(%f, %f)", + _cairo_fixed_to_double (points[i].x), + _cairo_fixed_to_double (points[i].y)); + } + len += snprintf (buf + len, sizeof (buf), "]"); + + fprintf (stderr, + "_cairo_path_fixed_add (%s, %s)\n", + op_str[(int) op], buf); + } + + _cairo_path_buf_add_op (buf, op); + _cairo_path_buf_add_points (buf, points, num_points); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, + cairo_path_buf_t *buf) +{ + cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link); +} + +COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1); +static cairo_path_buf_t * +_cairo_path_buf_create (int size_ops, int size_points) +{ + cairo_path_buf_t *buf; + + /* adjust size_ops to ensure that buf->points is naturally aligned */ + size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double)); + buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t)); + if (buf) { + buf->num_ops = 0; + buf->num_points = 0; + buf->size_ops = size_ops; + buf->size_points = size_points; + + buf->op = (cairo_path_op_t *) (buf + 1); + buf->points = (cairo_point_t *) (buf->op + size_ops); + } + + return buf; +} + +static void +_cairo_path_buf_destroy (cairo_path_buf_t *buf) +{ + free (buf); +} + +static void +_cairo_path_buf_add_op (cairo_path_buf_t *buf, + cairo_path_op_t op) +{ + buf->op[buf->num_ops++] = op; +} + +static void +_cairo_path_buf_add_points (cairo_path_buf_t *buf, + const cairo_point_t *points, + int num_points) +{ + memcpy (buf->points + buf->num_points, + points, + sizeof (points[0]) * num_points); + buf->num_points += num_points; +} + +cairo_status_t +_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_curve_to_func_t *curve_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure) +{ + const uint8_t num_args[] = { + 1, /* cairo_path_move_to */ + 1, /* cairo_path_op_line_to */ + 3, /* cairo_path_op_curve_to */ + 0, /* cairo_path_op_close_path */ + }; + cairo_status_t status; + const cairo_path_buf_t *buf, *first; + cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD); + int step = forward ? 1 : -1; + + buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path); + do { + cairo_point_t *points; + int start, stop, i; + + if (forward) { + start = 0; + stop = buf->num_ops; + points = buf->points; + } else { + start = buf->num_ops - 1; + stop = -1; + points = buf->points + buf->num_points; + } + + for (i = start; i != stop; i += step) { + cairo_path_op_t op = buf->op[i]; + + if (! forward) + points -= num_args[(int) op]; + + switch (op) { + case CAIRO_PATH_OP_MOVE_TO: + status = (*move_to) (closure, &points[0]); + break; + case CAIRO_PATH_OP_LINE_TO: + status = (*line_to) (closure, &points[0]); + break; + case CAIRO_PATH_OP_CURVE_TO: + status = (*curve_to) (closure, &points[0], &points[1], &points[2]); + break; + default: + ASSERT_NOT_REACHED; + case CAIRO_PATH_OP_CLOSE_PATH: + status = (*close_path) (closure); + break; + } + if (unlikely (status)) + return status; + + if (forward) + points += num_args[(int) op]; + } + } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first); + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_path_fixed_append_closure { + cairo_point_t offset; + cairo_path_fixed_t *path; +} cairo_path_fixed_append_closure_t; + +static cairo_status_t +_append_move_to (void *abstract_closure, + const cairo_point_t *point) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_move_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_append_line_to (void *abstract_closure, + const cairo_point_t *point) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_line_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_append_curve_to (void *abstract_closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_curve_to (closure->path, + p0->x + closure->offset.x, + p0->y + closure->offset.y, + p1->x + closure->offset.x, + p1->y + closure->offset.y, + p2->x + closure->offset.x, + p2->y + closure->offset.y); +} + +static cairo_status_t +_append_close_path (void *abstract_closure) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_close_path (closure->path); +} + +cairo_status_t +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_direction_t dir, + cairo_fixed_t tx, + cairo_fixed_t ty) +{ + cairo_path_fixed_append_closure_t closure; + + closure.path = path; + closure.offset.x = tx; + closure.offset.y = ty; + + return _cairo_path_fixed_interpret (other, dir, + _append_move_to, + _append_line_to, + _append_curve_to, + _append_close_path, + &closure); +} + +static void +_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy, + cairo_fixed_t scalex, + cairo_fixed_t scaley) +{ + cairo_path_buf_t *buf; + unsigned int i; + + if (path->maybe_fill_region) { + path->maybe_fill_region = _cairo_fixed_is_integer (offx) && + _cairo_fixed_is_integer (offy) && + _cairo_fixed_is_integer (scalex) && + _cairo_fixed_is_integer (scaley); + } + + cairo_path_foreach_buf_start (buf, path) { + for (i = 0; i < buf->num_points; i++) { + if (scalex != CAIRO_FIXED_ONE) + buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex); + buf->points[i].x += offx; + + if (scaley != CAIRO_FIXED_ONE) + buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley); + buf->points[i].y += offy; + } + } cairo_path_foreach_buf_end (buf, path); + + path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx; + path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx; + + path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy; + path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy; +} + +void +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy) +{ + cairo_path_buf_t *buf; + unsigned int i; + + if (offx == 0 && offy == 0) + return; + + if (path->maybe_fill_region && + ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy))) + { + path->maybe_fill_region = FALSE; + } + + path->last_move_point.x += offx; + path->last_move_point.y += offy; + path->current_point.x += offx; + path->current_point.y += offy; + + cairo_path_foreach_buf_start (buf, path) { + for (i = 0; i < buf->num_points; i++) { + buf->points[i].x += offx; + buf->points[i].y += offy; + } + } cairo_path_foreach_buf_end (buf, path); + + path->extents.p1.x += offx; + path->extents.p1.y += offy; + path->extents.p2.x += offx; + path->extents.p2.y += offy; +} + +/** + * _cairo_path_fixed_transform: + * @path: a #cairo_path_fixed_t to be transformed + * @matrix: a #cairo_matrix_t + * + * Transform the fixed-point path according to the given matrix. + * There is a fast path for the case where @matrix has no rotation + * or shear. + **/ +void +_cairo_path_fixed_transform (cairo_path_fixed_t *path, + const cairo_matrix_t *matrix) +{ + cairo_path_buf_t *buf; + unsigned int i; + double dx, dy; + + /* XXX current_point, last_move_to */ + + if (matrix->yx == 0.0 && matrix->xy == 0.0) { + /* Fast path for the common case of scale+transform */ + if (matrix->xx == 1. && matrix->yy == 1.) { + _cairo_path_fixed_translate (path, + _cairo_fixed_from_double (matrix->x0), + _cairo_fixed_from_double (matrix->y0)); + } else { + _cairo_path_fixed_offset_and_scale (path, + _cairo_fixed_from_double (matrix->x0), + _cairo_fixed_from_double (matrix->y0), + _cairo_fixed_from_double (matrix->xx), + _cairo_fixed_from_double (matrix->yy)); + } + return; + } + + path->extents.p1.x = path->extents.p1.y = INT_MAX; + path->extents.p2.x = path->extents.p2.y = INT_MIN; + path->maybe_fill_region = FALSE; + cairo_path_foreach_buf_start (buf, path) { + for (i = 0; i < buf->num_points; i++) { + dx = _cairo_fixed_to_double (buf->points[i].x); + dy = _cairo_fixed_to_double (buf->points[i].y); + + cairo_matrix_transform_point (matrix, &dx, &dy); + + buf->points[i].x = _cairo_fixed_from_double (dx); + buf->points[i].y = _cairo_fixed_from_double (dy); + + /* XXX need to eliminate surplus move-to's? */ + _cairo_path_fixed_extents_add (path, &buf->points[i]); + } + } cairo_path_foreach_buf_end (buf, path); +} + +cairo_bool_t +_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, + const cairo_path_fixed_t *other) +{ + const cairo_path_buf_t *path_buf, *other_buf; + + if (path->current_point.x != other->current_point.x || + path->current_point.y != other->current_point.y || + path->has_current_point != other->has_current_point || + path->has_curve_to != other->has_curve_to || + path->is_rectilinear != other->is_rectilinear || + path->maybe_fill_region != other->maybe_fill_region || + path->last_move_point.x != other->last_move_point.x || + path->last_move_point.y != other->last_move_point.y) + { + return FALSE; + } + + other_buf = cairo_path_head (other); + cairo_path_foreach_buf_start (path_buf, path) { + if (path_buf->num_ops != other_buf->num_ops || + path_buf->num_points != other_buf->num_points || + memcmp (path_buf->op, other_buf->op, + sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 || + memcmp (path_buf->points, other_buf->points, + sizeof (cairo_point_t) * path_buf->num_points) != 0) + { + return FALSE; + } + other_buf = cairo_path_buf_next (other_buf); + } cairo_path_foreach_buf_end (path_buf, path); + + return TRUE; +} + +/* Closure for path flattening */ +typedef struct cairo_path_flattener { + double tolerance; + cairo_point_t current_point; + cairo_path_fixed_move_to_func_t *move_to; + cairo_path_fixed_line_to_func_t *line_to; + cairo_path_fixed_close_path_func_t *close_path; + void *closure; +} cpf_t; + +static cairo_status_t +_cpf_move_to (void *closure, + const cairo_point_t *point) +{ + cpf_t *cpf = closure; + + cpf->current_point = *point; + + return cpf->move_to (cpf->closure, point); +} + +static cairo_status_t +_cpf_line_to (void *closure, + const cairo_point_t *point) +{ + cpf_t *cpf = closure; + + cpf->current_point = *point; + + return cpf->line_to (cpf->closure, point); +} + +static cairo_status_t +_cpf_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpf_t *cpf = closure; + cairo_spline_t spline; + + cairo_point_t *p0 = &cpf->current_point; + + if (! _cairo_spline_init (&spline, + cpf->line_to, + cpf->closure, + p0, p1, p2, p3)) + { + return _cpf_line_to (closure, p3); + } + + cpf->current_point = *p3; + + return _cairo_spline_decompose (&spline, cpf->tolerance); +} + +static cairo_status_t +_cpf_close_path (void *closure) +{ + cpf_t *cpf = closure; + + return cpf->close_path (cpf->closure); +} + +cairo_status_t +_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure, + double tolerance) +{ + cpf_t flattener; + + if (! path->has_curve_to) { + return _cairo_path_fixed_interpret (path, dir, + move_to, + line_to, + NULL, + close_path, + closure); + } + + flattener.tolerance = tolerance; + flattener.move_to = move_to; + flattener.line_to = line_to; + flattener.close_path = close_path; + flattener.closure = closure; + return _cairo_path_fixed_interpret (path, dir, + _cpf_move_to, + _cpf_line_to, + _cpf_curve_to, + _cpf_close_path, + &flattener); +} + +static inline void +_canonical_box (cairo_box_t *box, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + if (p1->x <= p2->x) { + box->p1.x = p1->x; + box->p2.x = p2->x; + } else { + box->p1.x = p2->x; + box->p2.x = p1->x; + } + + if (p1->y <= p2->y) { + box->p1.y = p1->y; + box->p2.y = p2->y; + } else { + box->p1.y = p2->y; + box->p2.y = p1->y; + } +} + +/* + * Check whether the given path contains a single rectangle. + */ +cairo_bool_t +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + const cairo_path_buf_t *buf = cairo_path_head (path); + + if (! path->is_rectilinear) + return FALSE; + + /* Do we have the right number of ops? */ + if (buf->num_ops < 4 || buf->num_ops > 6) + return FALSE; + + /* Check whether the ops are those that would be used for a rectangle */ + if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO || + buf->op[1] != CAIRO_PATH_OP_LINE_TO || + buf->op[2] != CAIRO_PATH_OP_LINE_TO || + buf->op[3] != CAIRO_PATH_OP_LINE_TO) + { + return FALSE; + } + + /* we accept an implicit close for filled paths */ + if (buf->num_ops > 4) { + /* Now, there are choices. The rectangle might end with a LINE_TO + * (to the original point), but this isn't required. If it + * doesn't, then it must end with a CLOSE_PATH. */ + if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { + if (buf->points[4].x != buf->points[0].x || + buf->points[4].y != buf->points[0].y) + return FALSE; + } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { + return FALSE; + } + + if (buf->num_ops == 6) { + /* A trailing CLOSE_PATH or MOVE_TO is ok */ + if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && + buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) + return FALSE; + } + } + + /* Ok, we may have a box, if the points line up */ + if (buf->points[0].y == buf->points[1].y && + buf->points[1].x == buf->points[2].x && + buf->points[2].y == buf->points[3].y && + buf->points[3].x == buf->points[0].x) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + if (buf->points[0].x == buf->points[1].x && + buf->points[1].y == buf->points[2].y && + buf->points[2].x == buf->points[3].x && + buf->points[3].y == buf->points[0].y) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + return FALSE; +} + +/* + * Check whether the given path contains a single rectangle + * that is logically equivalent to: + * + * cairo_move_to (cr, x, y); + * cairo_rel_line_to (cr, width, 0); + * cairo_rel_line_to (cr, 0, height); + * cairo_rel_line_to (cr, -width, 0); + * cairo_close_path (cr); + * + */ +cairo_bool_t +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + const cairo_path_buf_t *buf; + + if (! _cairo_path_fixed_is_box (path, box)) + return FALSE; + + buf = cairo_path_head (path); + if (buf->points[0].y == buf->points[1].y) + return TRUE; + + return FALSE; +} + +void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + const cairo_path_fixed_t *path) +{ + iter->first = iter->buf = cairo_path_head (path); + iter->n_op = 0; + iter->n_point = 0; +} + +static cairo_bool_t +_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) +{ + if (++iter->n_op >= iter->buf->num_ops) { + iter->buf = cairo_path_buf_next (iter->buf); + if (iter->buf == iter->first) { + iter->buf = NULL; + return FALSE; + } + + iter->n_op = 0; + iter->n_point = 0; + } + + return TRUE; +} + +cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box) +{ + cairo_point_t points[5]; + cairo_path_fixed_iter_t iter; + + if (_iter->buf == NULL) + return FALSE; + + iter = *_iter; + + if (iter.n_op == iter.buf->num_ops && + ! _cairo_path_fixed_iter_next_op (&iter)) + { + return FALSE; + } + + /* Check whether the ops are those that would be used for a rectangle */ + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) + return FALSE; + points[0] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[1] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[2] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[3] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* Now, there are choices. The rectangle might end with a LINE_TO + * (to the original point), but this isn't required. If it + * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */ + if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) + { + points[4] = iter.buf->points[iter.n_point++]; + if (points[4].x != points[0].x || points[4].y != points[0].y) + return FALSE; + } + else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH || + iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO)) + { + return FALSE; + } + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* Ok, we may have a box, if the points line up */ + if (points[0].y == points[1].y && + points[1].x == points[2].x && + points[2].y == points[3].y && + points[3].x == points[0].x) + { + box->p1 = points[0]; + box->p2 = points[2]; + *_iter = iter; + return TRUE; + } + + if (points[0].x == points[1].x && + points[1].y == points[2].y && + points[2].x == points[3].x && + points[3].y == points[0].y) + { + box->p1 = points[1]; + box->p2 = points[3]; + *_iter = iter; + return TRUE; + } + + return FALSE; +} + +cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) +{ + if (iter->buf == NULL) + return TRUE; + + if (iter->n_op == iter->buf->num_ops) + return TRUE; + + if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO && + iter->buf->num_ops == iter->n_op + 1) + { + return TRUE; + } + + return FALSE; +} diff --git a/libs/cairo/src/cairo-path-in-fill.c b/libs/cairo/src/cairo-path-in-fill.c new file mode 100644 index 000000000..c303d8596 --- /dev/null +++ b/libs/cairo/src/cairo-path-in-fill.c @@ -0,0 +1,260 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef struct cairo_in_fill { + double tolerance; + cairo_bool_t on_edge; + int winding; + + cairo_fixed_t x, y; + + cairo_bool_t has_current_point; + cairo_point_t current_point; + cairo_point_t first_point; +} cairo_in_fill_t; + +static void +_cairo_in_fill_init (cairo_in_fill_t *in_fill, + double tolerance, + double x, + double y) +{ + in_fill->on_edge = FALSE; + in_fill->winding = 0; + in_fill->tolerance = tolerance; + + in_fill->x = _cairo_fixed_from_double (x); + in_fill->y = _cairo_fixed_from_double (y); + + in_fill->has_current_point = FALSE; + in_fill->current_point.x = 0; + in_fill->current_point.y = 0; +} + +static void +_cairo_in_fill_fini (cairo_in_fill_t *in_fill) +{ +} + +static int +edge_compare_for_y_against_x (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t y, + cairo_fixed_t x) +{ + cairo_fixed_t adx, ady; + cairo_fixed_t dx, dy; + cairo_int64_t L, R; + + adx = p2->x - p1->x; + dx = x - p1->x; + + if (adx == 0) + return -dx; + if ((adx ^ dx) < 0) + return adx; + + dy = y - p1->y; + ady = p2->y - p1->y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static void +_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int dir; + + if (in_fill->on_edge) + return; + + /* count the number of edge crossing to -∞ */ + + dir = 1; + if (p2->y < p1->y) { + const cairo_point_t *tmp; + + tmp = p1; + p1 = p2; + p2 = tmp; + + dir = -1; + } + + /* First check whether the query is on an edge */ + if ((p1->x == in_fill->x && p1->y == in_fill->y) || + (p2->x == in_fill->x && p2->y == in_fill->y) || + (! (p2->y < in_fill->y || p1->y > in_fill->y || + (p1->x > in_fill->x && p2->x > in_fill->x) || + (p1->x < in_fill->x && p2->x < in_fill->x)) && + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0)) + { + in_fill->on_edge = TRUE; + return; + } + + /* edge is entirely above or below, note the shortening rule */ + if (p2->y <= in_fill->y || p1->y > in_fill->y) + return; + + /* edge lies wholly to the right */ + if (p1->x >= in_fill->x && p2->x >= in_fill->x) + return; + + if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0) + { + in_fill->winding += dir; + } +} + +static cairo_status_t +_cairo_in_fill_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + /* implicit close path */ + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + } + + in_fill->first_point = *point; + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) + _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point); + + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_in_fill_t *in_fill = closure; + cairo_spline_t spline; + cairo_fixed_t top, bot, left; + + /* first reject based on bbox */ + bot = top = in_fill->current_point.y; + if (b->y < top) top = b->y; + if (b->y > bot) bot = b->y; + if (c->y < top) top = c->y; + if (c->y > bot) bot = c->y; + if (d->y < top) top = d->y; + if (d->y > bot) bot = d->y; + if (bot < in_fill->y || top > in_fill->y) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + left = in_fill->current_point.x; + if (b->x < left) left = b->x; + if (c->x < left) left = c->x; + if (d->x < left) left = d->x; + if (left > in_fill->x) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + /* XXX Investigate direct inspection of the inflections? */ + if (! _cairo_spline_init (&spline, + _cairo_in_fill_line_to, + in_fill, + &in_fill->current_point, b, c, d)) + { + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_spline_decompose (&spline, in_fill->tolerance); +} + +static cairo_status_t +_cairo_in_fill_close_path (void *closure) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + + in_fill->has_current_point = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y) +{ + cairo_in_fill_t in_fill; + cairo_status_t status; + cairo_bool_t is_inside; + + if (path->is_empty_fill) + return FALSE; + + _cairo_in_fill_init (&in_fill, tolerance, x, y); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_in_fill_move_to, + _cairo_in_fill_line_to, + _cairo_in_fill_curve_to, + _cairo_in_fill_close_path, + &in_fill); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_in_fill_close_path (&in_fill); + + if (in_fill.on_edge) { + is_inside = TRUE; + } else switch (fill_rule) { + case CAIRO_FILL_RULE_EVEN_ODD: + is_inside = in_fill.winding & 1; + break; + case CAIRO_FILL_RULE_WINDING: + is_inside = in_fill.winding != 0; + break; + default: + ASSERT_NOT_REACHED; + is_inside = FALSE; + break; + } + + _cairo_in_fill_fini (&in_fill); + + return is_inside; +} diff --git a/libs/cairo/src/cairo-path-private.h b/libs/cairo/src/cairo-path-private.h new file mode 100644 index 000000000..c28612a32 --- /dev/null +++ b/libs/cairo/src/cairo-path-private.h @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PATH_PRIVATE_H +#define CAIRO_PATH_PRIVATE_H + +#include "cairoint.h" + +cairo_private cairo_path_t * +_cairo_path_create (cairo_path_fixed_t *path, + cairo_gstate_t *gstate); + +cairo_private cairo_path_t * +_cairo_path_create_flat (cairo_path_fixed_t *path, + cairo_gstate_t *gstate); + +cairo_private cairo_path_t * +_cairo_path_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_path_append_to_context (const cairo_path_t *path, + cairo_t *cr); + +#endif /* CAIRO_PATH_DATA_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-path-stroke.c b/libs/cairo/src/cairo-path-stroke.c new file mode 100644 index 000000000..6d1d6f205 --- /dev/null +++ b/libs/cairo/src/cairo-path-stroke.c @@ -0,0 +1,2109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for hypot() */ +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" + +typedef struct _cairo_stroker_dash { + cairo_bool_t dashed; + unsigned int dash_index; + cairo_bool_t dash_on; + cairo_bool_t dash_starts_on; + double dash_remain; + + double dash_offset; + const double *dashes; + unsigned int num_dashes; +} cairo_stroker_dash_t; + +typedef struct cairo_stroker { + cairo_stroke_style_t style; + + const cairo_matrix_t *ctm; + const cairo_matrix_t *ctm_inverse; + double tolerance; + double ctm_determinant; + cairo_bool_t ctm_det_positive; + + void *closure; + cairo_status_t (*add_external_edge) (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2); + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]); + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints); + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]); + + cairo_pen_t pen; + + cairo_point_t current_point; + cairo_point_t first_point; + + cairo_bool_t has_initial_sub_path; + + cairo_bool_t has_current_face; + cairo_stroke_face_t current_face; + + cairo_bool_t has_first_face; + cairo_stroke_face_t first_face; + + cairo_stroker_dash_t dash; + + cairo_bool_t has_bounds; + cairo_box_t bounds; +} cairo_stroker_t; + +static void +_cairo_stroker_dash_start (cairo_stroker_dash_t *dash) +{ + double offset; + cairo_bool_t on = TRUE; + unsigned int i = 0; + + if (! dash->dashed) + return; + + offset = dash->dash_offset; + + /* We stop searching for a starting point as soon as the + offset reaches zero. Otherwise when an initial dash + segment shrinks to zero it will be skipped over. */ + while (offset > 0.0 && offset >= dash->dashes[i]) { + offset -= dash->dashes[i]; + on = !on; + if (++i == dash->num_dashes) + i = 0; + } + + dash->dash_index = i; + dash->dash_on = dash->dash_starts_on = on; + dash->dash_remain = dash->dashes[i] - offset; +} + +static void +_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) +{ + dash->dash_remain -= step; + if (dash->dash_remain <= 0.) { + if (++dash->dash_index == dash->num_dashes) + dash->dash_index = 0; + + dash->dash_on = ! dash->dash_on; + dash->dash_remain = dash->dashes[dash->dash_index]; + } +} + +static void +_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, + const cairo_stroke_style_t *style) +{ + dash->dashed = style->dash != NULL; + if (! dash->dashed) + return; + + dash->dashes = style->dash; + dash->num_dashes = style->num_dashes; + dash->dash_offset = style->dash_offset; + + _cairo_stroker_dash_start (dash); +} + +static cairo_status_t +_cairo_stroker_init (cairo_stroker_t *stroker, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance) +{ + cairo_status_t status; + + stroker->style = *stroke_style; + stroker->ctm = ctm; + stroker->ctm_inverse = ctm_inverse; + stroker->tolerance = tolerance; + + stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); + stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; + + status = _cairo_pen_init (&stroker->pen, + stroke_style->line_width / 2.0, + tolerance, ctm); + if (unlikely (status)) + return status; + + stroker->has_bounds = FALSE; + + stroker->has_current_face = FALSE; + stroker->has_first_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + _cairo_stroker_dash_init (&stroker->dash, stroke_style); + + stroker->add_external_edge = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_stroker_limit (cairo_stroker_t *stroker, + const cairo_box_t *boxes, + int num_boxes) +{ + double dx, dy; + cairo_fixed_t fdx, fdy; + + stroker->has_bounds = TRUE; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); + + /* Extend the bounds in each direction to account for the maximum area + * we might generate trapezoids, to capture line segments that are outside + * of the bounds but which might generate rendering that's within bounds. + */ + + _cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm, + &dx, &dy); + + fdx = _cairo_fixed_from_double (dx); + fdy = _cairo_fixed_from_double (dy); + + stroker->bounds.p1.x -= fdx; + stroker->bounds.p2.x += fdx; + + stroker->bounds.p1.y -= fdy; + stroker->bounds.p2.y += fdy; +} + +static void +_cairo_stroker_fini (cairo_stroker_t *stroker) +{ + _cairo_pen_fini (&stroker->pen); +} + +static void +_translate_point (cairo_point_t *point, const cairo_point_t *offset) +{ + point->x += offset->x; + point->y += offset->y; +} + +static int +_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + cairo_slope_t in_slope, out_slope; + + _cairo_slope_init (&in_slope, &in->point, &in->cw); + _cairo_slope_init (&out_slope, &out->point, &out->cw); + + return _cairo_slope_compare (&in_slope, &out_slope) < 0; +} + +/** + * _cairo_slope_compare_sgn + * + * Return -1, 0 or 1 depending on the relative slopes of + * two lines. + */ +static int +_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) +{ + double c = (dx1 * dy2 - dx2 * dy1); + + if (c > 0) return 1; + if (c < 0) return -1; + return 0; +} + +static inline int +_range_step (int i, int step, int max) +{ + i += step; + if (i < 0) + i = max - 1; + if (i >= max) + i = 0; + return i; +} + +/* + * Construct a fan around the midpoint using the vertices from pen between + * inpt and outpt. + */ +static cairo_status_t +_tessellate_fan (cairo_stroker_t *stroker, + const cairo_slope_t *in_vector, + const cairo_slope_t *out_vector, + const cairo_point_t *midpt, + const cairo_point_t *inpt, + const cairo_point_t *outpt, + cairo_bool_t clockwise) +{ + cairo_point_t stack_points[64], *points = stack_points; + int start, stop, step, i, npoints; + cairo_status_t status; + + if (clockwise) { + step = -1; + + start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, + in_vector) < 0) + start = _range_step (start, -1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + out_vector) > 0) + { + stop = _range_step (stop, 1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + in_vector) < 0) + { + goto BEVEL; + } + } + + npoints = start - stop; + } else { + step = 1; + + start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, + in_vector) < 0) + start = _range_step (start, 1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + out_vector) > 0) + { + stop = _range_step (stop, -1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + in_vector) < 0) + { + goto BEVEL; + } + } + + npoints = stop - start; + } + stop = _range_step (stop, step, stroker->pen.num_vertices); + + if (npoints < 0) + npoints += stroker->pen.num_vertices; + npoints += 3; + + if (npoints <= 1) + goto BEVEL; + + if (npoints > ARRAY_LENGTH (stack_points)) { + points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); + if (unlikely (points == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + + /* Construct the fan. */ + npoints = 0; + points[npoints++] = *inpt; + for (i = start; + i != stop; + i = _range_step (i, step, stroker->pen.num_vertices)) + { + points[npoints] = *midpt; + _translate_point (&points[npoints], &stroker->pen.vertices[i].point); + npoints++; + } + points[npoints++] = *outpt; + + if (stroker->add_external_edge != NULL) { + for (i = 0; i < npoints - 1; i++) { + if (clockwise) { + status = stroker->add_external_edge (stroker->closure, + &points[i], &points[i+1]); + } else { + status = stroker->add_external_edge (stroker->closure, + &points[i+1], &points[i]); + } + if (unlikely (status)) + break; + } + } else { + status = stroker->add_triangle_fan (stroker->closure, + midpt, points, npoints); + } + + if (points != stack_points) + free (points); + + return status; + +BEVEL: + /* Ensure a leak free connection... */ + if (stroker->add_external_edge != NULL) { + if (clockwise) + return stroker->add_external_edge (stroker->closure, inpt, outpt); + else + return stroker->add_external_edge (stroker->closure, outpt, inpt); + } else { + stack_points[0] = *midpt; + stack_points[1] = *inpt; + stack_points[2] = *outpt; + return stroker->add_triangle (stroker->closure, stack_points); + } +} + +static cairo_status_t +_cairo_stroker_join (cairo_stroker_t *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + int clockwise = _cairo_stroker_join_is_clockwise (out, in); + const cairo_point_t *inpt, *outpt; + cairo_point_t points[4]; + cairo_status_t status; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) + { + return CAIRO_STATUS_SUCCESS; + } + + if (clockwise) { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &out->cw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &in->cw); + if (unlikely (status)) + return status; + } + + inpt = &in->ccw; + outpt = &out->ccw; + } else { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &in->ccw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &out->ccw); + if (unlikely (status)) + return status; + } + + inpt = &in->cw; + outpt = &out->cw; + } + + switch (stroker->style.line_join) { + case CAIRO_LINE_JOIN_ROUND: + /* construct a fan around the common midpoint */ + return _tessellate_fan (stroker, + &in->dev_vector, + &out->dev_vector, + &in->point, inpt, outpt, + clockwise); + + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = -in->usr_vector.x * out->usr_vector.x + + -in->usr_vector.y * out->usr_vector.y; + double ml = stroker->style.miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 - in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + if (stroker->add_external_edge != NULL) { + points[0].x = _cairo_fixed_from_double (mx); + points[0].y = _cairo_fixed_from_double (my); + + if (clockwise) { + status = stroker->add_external_edge (stroker->closure, + inpt, &points[0]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &points[0], outpt); + if (unlikely (status)) + return status; + } else { + status = stroker->add_external_edge (stroker->closure, + outpt, &points[0]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &points[0], inpt); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; + } else { + points[0] = in->point; + points[1] = *inpt; + points[2].x = _cairo_fixed_from_double (mx); + points[2].y = _cairo_fixed_from_double (my); + points[3] = *outpt; + + return stroker->add_convex_quad (stroker->closure, points); + } + } + } + } + + /* fall through ... */ + + case CAIRO_LINE_JOIN_BEVEL: + if (stroker->add_external_edge != NULL) { + if (clockwise) { + return stroker->add_external_edge (stroker->closure, + inpt, outpt); + } else { + return stroker->add_external_edge (stroker->closure, + outpt, inpt); + } + } else { + points[0] = in->point; + points[1] = *inpt; + points[2] = *outpt; + + return stroker->add_triangle (stroker->closure, points); + } + } +} + +static cairo_status_t +_cairo_stroker_add_cap (cairo_stroker_t *stroker, + const cairo_stroke_face_t *f) +{ + switch (stroker->style.line_cap) { + case CAIRO_LINE_CAP_ROUND: { + cairo_slope_t slope; + + slope.dx = -f->dev_vector.dx; + slope.dy = -f->dev_vector.dy; + + return _tessellate_fan (stroker, + &f->dev_vector, + &slope, + &f->point, &f->cw, &f->ccw, + FALSE); + + } + + case CAIRO_LINE_CAP_SQUARE: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t quad[4]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->style.line_width / 2.0; + dy *= stroker->style.line_width / 2.0; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + quad[0] = f->ccw; + quad[1].x = f->ccw.x + fvector.dx; + quad[1].y = f->ccw.y + fvector.dy; + quad[2].x = f->cw.x + fvector.dx; + quad[2].y = f->cw.y + fvector.dy; + quad[3] = f->cw; + + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &quad[0], &quad[1]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[1], &quad[2]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[2], &quad[3]); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + return stroker->add_convex_quad (stroker->closure, quad); + } + } + + case CAIRO_LINE_CAP_BUTT: + default: + if (stroker->add_external_edge != NULL) { + return stroker->add_external_edge (stroker->closure, + &f->ccw, &f->cw); + } else { + return CAIRO_STATUS_SUCCESS; + } + } +} + +static cairo_status_t +_cairo_stroker_add_leading_cap (cairo_stroker_t *stroker, + const cairo_stroke_face_t *face) +{ + cairo_stroke_face_t reversed; + cairo_point_t t; + + reversed = *face; + + /* The initial cap needs an outward facing vector. Reverse everything */ + reversed.usr_vector.x = -reversed.usr_vector.x; + reversed.usr_vector.y = -reversed.usr_vector.y; + reversed.dev_vector.dx = -reversed.dev_vector.dx; + reversed.dev_vector.dy = -reversed.dev_vector.dy; + t = reversed.cw; + reversed.cw = reversed.ccw; + reversed.ccw = t; + + return _cairo_stroker_add_cap (stroker, &reversed); +} + +static cairo_status_t +_cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, + const cairo_stroke_face_t *face) +{ + return _cairo_stroker_add_cap (stroker, face); +} + +static inline cairo_bool_t +_compute_normalized_device_slope (double *dx, double *dy, + const cairo_matrix_t *ctm_inverse, + double *mag_out) +{ + double dx0 = *dx, dy0 = *dy; + double mag; + + cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0); + + if (dx0 == 0.0 && dy0 == 0.0) { + if (mag_out) + *mag_out = 0.0; + return FALSE; + } + + if (dx0 == 0.0) { + *dx = 0.0; + if (dy0 > 0.0) { + mag = dy0; + *dy = 1.0; + } else { + mag = -dy0; + *dy = -1.0; + } + } else if (dy0 == 0.0) { + *dy = 0.0; + if (dx0 > 0.0) { + mag = dx0; + *dx = 1.0; + } else { + mag = -dx0; + *dx = -1.0; + } + } else { + mag = hypot (dx0, dy0); + *dx = dx0 / mag; + *dy = dy0 / mag; + } + + if (mag_out) + *mag_out = mag; + + return TRUE; +} + +static void +_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, + double slope_dx, double slope_dy, + cairo_stroker_t *stroker, cairo_stroke_face_t *face) +{ + double face_dx, face_dy; + cairo_point_t offset_ccw, offset_cw; + + /* + * rotate to get a line_width/2 vector along the face, note that + * the vector must be rotated the right direction in device space, + * but by 90° in user space. So, the rotation depends on + * whether the ctm reflects or not, and that can be determined + * by looking at the determinant of the matrix. + */ + if (stroker->ctm_det_positive) + { + face_dx = - slope_dy * (stroker->style.line_width / 2.0); + face_dy = slope_dx * (stroker->style.line_width / 2.0); + } + else + { + face_dx = slope_dy * (stroker->style.line_width / 2.0); + face_dy = - slope_dx * (stroker->style.line_width / 2.0); + } + + /* back to device space */ + cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); + + offset_ccw.x = _cairo_fixed_from_double (face_dx); + offset_ccw.y = _cairo_fixed_from_double (face_dy); + offset_cw.x = -offset_ccw.x; + offset_cw.y = -offset_ccw.y; + + face->ccw = *point; + _translate_point (&face->ccw, &offset_ccw); + + face->point = *point; + + face->cw = *point; + _translate_point (&face->cw, &offset_cw); + + face->usr_vector.x = slope_dx; + face->usr_vector.y = slope_dy; + + face->dev_vector = *dev_slope; +} + +static cairo_status_t +_cairo_stroker_add_caps (cairo_stroker_t *stroker) +{ + cairo_status_t status; + + /* check for a degenerative sub_path */ + if (stroker->has_initial_sub_path + && ! stroker->has_first_face + && ! stroker->has_current_face + && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND) + { + /* pick an arbitrary slope to use */ + double dx = 1.0, dy = 0.0; + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + _compute_normalized_device_slope (&dx, &dy, + stroker->ctm_inverse, NULL); + + /* arbitrarily choose first_point + * first_point and current_point should be the same */ + _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); + + status = _cairo_stroker_add_leading_cap (stroker, &face); + if (unlikely (status)) + return status; + + status = _cairo_stroker_add_trailing_cap (stroker, &face); + if (unlikely (status)) + return status; + } + + if (stroker->has_first_face) { + status = _cairo_stroker_add_leading_cap (stroker, + &stroker->first_face); + if (unlikely (status)) + return status; + } + + if (stroker->has_current_face) { + status = _cairo_stroker_add_trailing_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_slope_t *dev_slope, + double slope_dx, double slope_dy, + cairo_stroke_face_t *start, + cairo_stroke_face_t *end) +{ + _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); + *end = *start; + + if (p1->x == p2->x && p1->y == p2->y) + return CAIRO_STATUS_SUCCESS; + + end->point = *p2; + end->ccw.x += p2->x - p1->x; + end->ccw.y += p2->y - p1->y; + end->cw.x += p2->x - p1->x; + end->cw.y += p2->y - p1->y; + + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &end->cw, &start->cw); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &start->ccw, &end->ccw); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + cairo_point_t quad[4]; + + quad[0] = start->cw; + quad[1] = end->cw; + quad[2] = end->ccw; + quad[3] = start->ccw; + + return stroker->add_convex_quad (stroker->closure, quad); + } +} + +static cairo_status_t +_cairo_stroker_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + cairo_status_t status; + + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + + /* Cap the start and end of the previous sub path as needed */ + status = _cairo_stroker_add_caps (stroker); + if (unlikely (status)) + return status; + + stroker->first_point = *point; + stroker->current_point = *point; + + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + cairo_stroke_face_t start, end; + cairo_point_t *p1 = &stroker->current_point; + cairo_slope_t dev_slope; + double slope_dx, slope_dy; + cairo_status_t status; + + stroker->has_initial_sub_path = TRUE; + + if (p1->x == point->x && p1->y == point->y) + return CAIRO_STATUS_SUCCESS; + + _cairo_slope_init (&dev_slope, p1, point); + slope_dx = _cairo_fixed_to_double (point->x - p1->x); + slope_dy = _cairo_fixed_to_double (point->y - p1->y); + _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL); + + status = _cairo_stroker_add_sub_edge (stroker, + p1, point, + &dev_slope, + slope_dx, slope_dy, + &start, &end); + if (unlikely (status)) + return status; + + if (stroker->has_current_face) { + /* Join with final face from previous segment */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &start); + if (unlikely (status)) + return status; + } else if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = start; + stroker->has_first_face = TRUE; + } + stroker->current_face = end; + stroker->has_current_face = TRUE; + + stroker->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Dashed lines. Cap each dash end, join around turns when on + */ +static cairo_status_t +_cairo_stroker_line_to_dashed (void *closure, + const cairo_point_t *p2) +{ + cairo_stroker_t *stroker = closure; + double mag, remain, step_length = 0; + double slope_dx, slope_dy; + double dx2, dy2; + cairo_stroke_face_t sub_start, sub_end; + cairo_point_t *p1 = &stroker->current_point; + cairo_slope_t dev_slope; + cairo_line_t segment; + cairo_bool_t fully_in_bounds; + cairo_status_t status; + + stroker->has_initial_sub_path = stroker->dash.dash_starts_on; + + if (p1->x == p2->x && p1->y == p2->y) + return CAIRO_STATUS_SUCCESS; + + fully_in_bounds = TRUE; + if (stroker->has_bounds && + (! _cairo_box_contains_point (&stroker->bounds, p1) || + ! _cairo_box_contains_point (&stroker->bounds, p2))) + { + fully_in_bounds = FALSE; + } + + _cairo_slope_init (&dev_slope, p1, p2); + + slope_dx = _cairo_fixed_to_double (p2->x - p1->x); + slope_dy = _cairo_fixed_to_double (p2->y - p1->y); + + if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, &mag)) + { + return CAIRO_STATUS_SUCCESS; + } + + remain = mag; + segment.p1 = *p1; + while (remain) { + step_length = MIN (stroker->dash.dash_remain, remain); + remain -= step_length; + dx2 = slope_dx * (mag - remain); + dy2 = slope_dy * (mag - remain); + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; + segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; + + if (stroker->dash.dash_on && + (fully_in_bounds || + (! stroker->has_first_face && stroker->dash.dash_starts_on) || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + status = _cairo_stroker_add_sub_edge (stroker, + &segment.p1, &segment.p2, + &dev_slope, + slope_dx, slope_dy, + &sub_start, &sub_end); + if (unlikely (status)) + return status; + + if (stroker->has_current_face) + { + /* Join with final face from previous segment */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &sub_start); + if (unlikely (status)) + return status; + + stroker->has_current_face = FALSE; + } + else if (! stroker->has_first_face && + stroker->dash.dash_starts_on) + { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = sub_start; + stroker->has_first_face = TRUE; + } + else + { + /* Cap dash start if not connecting to a previous segment */ + status = _cairo_stroker_add_leading_cap (stroker, &sub_start); + if (unlikely (status)) + return status; + } + + if (remain) { + /* Cap dash end if not at end of segment */ + status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); + if (unlikely (status)) + return status; + } else { + stroker->current_face = sub_end; + stroker->has_current_face = TRUE; + } + } else { + if (stroker->has_current_face) { + /* Cap final face from previous segment */ + status = _cairo_stroker_add_trailing_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + + stroker->has_current_face = FALSE; + } + } + + _cairo_stroker_dash_step (&stroker->dash, step_length); + segment.p1 = segment.p2; + } + + if (stroker->dash.dash_on && ! stroker->has_current_face) { + /* This segment ends on a transition to dash_on, compute a new face + * and add cap for the beginning of the next dash_on step. + * + * Note: this will create a degenerate cap if this is not the last line + * in the path. Whether this behaviour is desirable or not is debatable. + * On one side these degenerate caps can not be reproduced with regular + * path stroking. + * On the other hand, Acroread 7 also produces the degenerate caps. + */ + _compute_face (p2, &dev_slope, + slope_dx, slope_dy, + stroker, + &stroker->current_face); + + status = _cairo_stroker_add_leading_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + + stroker->has_current_face = TRUE; + } + + stroker->current_point = *p2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_stroker_t *stroker = closure; + cairo_spline_t spline; + cairo_line_join_t line_join_save; + cairo_stroke_face_t face; + double slope_dx, slope_dy; + cairo_path_fixed_line_to_func_t *line_to; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + line_to = stroker->dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to; + + if (! _cairo_spline_init (&spline, + line_to, stroker, + &stroker->current_point, b, c, d)) + { + return line_to (closure, d); + } + + /* If the line width is so small that the pen is reduced to a + single point, then we have nothing to do. */ + if (stroker->pen.num_vertices <= 1) + return CAIRO_STATUS_SUCCESS; + + /* Compute the initial face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.initial_slope, + slope_dx, slope_dy, + stroker, &face); + } + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, + &stroker->current_face, &face); + if (unlikely (status)) + return status; + } else if (! stroker->has_first_face) { + stroker->first_face = face; + stroker->has_first_face = TRUE; + } + + stroker->current_face = face; + stroker->has_current_face = TRUE; + } + + /* Temporarily modify the stroker to use round joins to guarantee + * smooth stroked curves. */ + line_join_save = stroker->style.line_join; + stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; + + status = _cairo_spline_decompose (&spline, stroker->tolerance); + if (unlikely (status)) + return status; + + /* And join the final face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.final_slope, + slope_dx, slope_dy, + stroker, &face); + } + + status = _cairo_stroker_join (stroker, &stroker->current_face, &face); + if (unlikely (status)) + return status; + + stroker->current_face = face; + } + + stroker->style.line_join = line_join_save; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_close_path (void *closure) +{ + cairo_stroker_t *stroker = closure; + cairo_status_t status; + + if (stroker->dash.dashed) + status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); + else + status = _cairo_stroker_line_to (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + if (stroker->has_first_face && stroker->has_current_face) { + /* Join first and final faces of sub path */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &stroker->first_face); + if (unlikely (status)) + return status; + } else { + /* Cap the start and end of the sub path as needed */ + status = _cairo_stroker_add_caps (stroker); + if (unlikely (status)) + return status; + } + + stroker->has_initial_sub_path = FALSE; + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure) +{ + cairo_stroker_t stroker; + cairo_status_t status; + + status = _cairo_stroker_init (&stroker, stroke_style, + ctm, ctm_inverse, tolerance); + if (unlikely (status)) + return status; + + stroker.add_triangle = add_triangle; + stroker.add_triangle_fan = add_triangle_fan; + stroker.add_convex_quad = add_convex_quad; + stroker.closure = closure; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + + if (unlikely (status)) + goto BAIL; + + /* Cap the start and end of the final sub path as needed */ + status = _cairo_stroker_add_caps (&stroker); + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} + +cairo_status_t +_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_polygon_t *polygon) +{ + cairo_stroker_t stroker; + cairo_status_t status; + + status = _cairo_stroker_init (&stroker, stroke_style, + ctm, ctm_inverse, tolerance); + if (unlikely (status)) + return status; + + stroker.add_external_edge = _cairo_polygon_add_external_edge, + stroker.closure = polygon; + + if (polygon->num_limits) + _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + + if (unlikely (status)) + goto BAIL; + + /* Cap the start and end of the final sub path as needed */ + status = _cairo_stroker_add_caps (&stroker); + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} + +cairo_status_t +_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_traps_t *traps) +{ + cairo_status_t status; + cairo_polygon_t polygon; + + /* Before we do anything else, we attempt the rectilinear + * stroker. It's careful to generate trapezoids that align to + * device-pixel boundaries when possible. Many backends can render + * those much faster than non-aligned trapezoids, (by using clip + * regions, etc.) */ + if (path->is_rectilinear) { + status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, + stroke_style, + ctm, + traps); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + _cairo_polygon_init (&polygon); + if (traps->num_limits) + _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, + ctm_inverse, + tolerance, + &polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_polygon_status (&polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, + CAIRO_FILL_RULE_WINDING); + +BAIL: + _cairo_polygon_fini (&polygon); + + return status; +} + +typedef struct _segment_t { + cairo_point_t p1, p2; + cairo_bool_t is_horizontal; + cairo_bool_t has_join; +} segment_t; + +typedef struct _cairo_rectilinear_stroker { + const cairo_stroke_style_t *stroke_style; + const cairo_matrix_t *ctm; + + cairo_fixed_t half_line_width; + cairo_bool_t do_traps; + void *container; + cairo_point_t current_point; + cairo_point_t first_point; + cairo_bool_t open_sub_path; + + cairo_stroker_dash_t dash; + + cairo_bool_t has_bounds; + cairo_box_t bounds; + + int num_segments; + int segments_size; + segment_t *segments; + segment_t segments_embedded[8]; /* common case is a single rectangle */ +} cairo_rectilinear_stroker_t; + +static void +_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, + const cairo_box_t *boxes, + int num_boxes) +{ + stroker->has_bounds = TRUE; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); + + stroker->bounds.p1.x -= stroker->half_line_width; + stroker->bounds.p2.x += stroker->half_line_width; + + stroker->bounds.p1.y -= stroker->half_line_width; + stroker->bounds.p2.y += stroker->half_line_width; +} + +static cairo_bool_t +_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_bool_t do_traps, + void *container) +{ + /* This special-case rectilinear stroker only supports + * miter-joined lines (not curves) and a translation-only matrix + * (though it could probably be extended to support a matrix with + * uniform, integer scaling). + * + * It also only supports horizontal and vertical line_to + * elements. But we don't catch that here, but instead return + * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any + * non-rectilinear line_to is encountered. + */ + if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) + return FALSE; + + /* If the miter limit turns right angles into bevels, then we + * can't use this optimization. Remember, the ratio is + * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, + * which we round for safety. */ + if (stroke_style->miter_limit < M_SQRT2) + return FALSE; + + if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || + stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) + { + return FALSE; + } + + if (! _cairo_matrix_has_unity_scale (ctm)) + return FALSE; + + stroker->stroke_style = stroke_style; + stroker->ctm = ctm; + + stroker->half_line_width = + _cairo_fixed_from_double (stroke_style->line_width / 2.0); + stroker->open_sub_path = FALSE; + stroker->segments = stroker->segments_embedded; + stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); + stroker->num_segments = 0; + + _cairo_stroker_dash_init (&stroker->dash, stroke_style); + + stroker->has_bounds = FALSE; + + stroker->do_traps = do_traps; + stroker->container = container; + + return TRUE; +} + +static void +_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) +{ + if (stroker->segments != stroker->segments_embedded) + free (stroker->segments); +} + +static cairo_status_t +_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_bool_t is_horizontal, + cairo_bool_t has_join) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (stroker->num_segments == stroker->segments_size) { + int new_size = stroker->segments_size * 2; + segment_t *new_segments; + + if (stroker->segments == stroker->segments_embedded) { + new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_segments, stroker->segments, + stroker->num_segments * sizeof (segment_t)); + } else { + new_segments = _cairo_realloc_ab (stroker->segments, + new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + stroker->segments_size = new_size; + stroker->segments = new_segments; + } + + stroker->segments[stroker->num_segments].p1 = *p1; + stroker->segments[stroker->num_segments].p2 = *p2; + stroker->segments[stroker->num_segments].has_join = has_join; + stroker->segments[stroker->num_segments].is_horizontal = is_horizontal; + stroker->num_segments++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) +{ + cairo_status_t status; + cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; + cairo_fixed_t half_line_width = stroker->half_line_width; + int i; + + for (i = 0; i < stroker->num_segments; i++) { + cairo_point_t *a, *b; + cairo_bool_t lengthen_initial, shorten_final, lengthen_final; + + a = &stroker->segments[i].p1; + b = &stroker->segments[i].p2; + + /* For each segment we generate a single rectangular + * trapezoid. This rectangle is based on a perpendicular + * extension (by half the line width) of the segment endpoints + * after some adjustments of the endpoints to account for caps + * and joins. + */ + + /* We adjust the initial point of the segment to extend the + * rectangle to include the previous cap or join, (this + * adjustment applies to all segments except for the first + * segment of open, butt-capped paths). + */ + lengthen_initial = TRUE; + if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) + lengthen_initial = FALSE; + + /* The adjustment of the final point is trickier. For all but + * the last segment we shorten the segment at the final + * endpoint to not overlap with the subsequent join. For the + * last segment we do the same shortening if the path is + * closed. If the path is open and butt-capped we do no + * adjustment, while if it's open and square-capped we do a + * lengthening adjustment instead to include the cap. + */ + shorten_final = TRUE; + lengthen_final = FALSE; + if (i == stroker->num_segments - 1 && stroker->open_sub_path) { + shorten_final = FALSE; + if (line_cap == CAIRO_LINE_CAP_SQUARE) + lengthen_final = TRUE; + } + + /* Perform the adjustments of the endpoints. */ + if (a->y == b->y) { + if (a->x < b->x) { + if (lengthen_initial) + a->x -= half_line_width; + if (shorten_final) + b->x -= half_line_width; + else if (lengthen_final) + b->x += half_line_width; + } else { + if (lengthen_initial) + a->x += half_line_width; + if (shorten_final) + b->x += half_line_width; + else if (lengthen_final) + b->x -= half_line_width; + } + + if (a->x > b->x) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + } else { + if (a->y < b->y) { + if (lengthen_initial) + a->y -= half_line_width; + if (shorten_final) + b->y -= half_line_width; + else if (lengthen_final) + b->y += half_line_width; + } else { + if (lengthen_initial) + a->y += half_line_width; + if (shorten_final) + b->y += half_line_width; + else if (lengthen_final) + b->y -= half_line_width; + } + + if (a->y > b->y) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + } + + /* Form the rectangle by expanding by half the line width in + * either perpendicular direction. */ + if (a->y == b->y) { + a->y -= half_line_width; + b->y += half_line_width; + } else { + a->x -= half_line_width; + b->x += half_line_width; + } + + if (stroker->do_traps) { + status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); + } else { + cairo_box_t box; + + box.p1 = *a; + box.p2 = *b; + + status = _cairo_boxes_add (stroker->container, &box); + } + if (unlikely (status)) + return status; + } + + stroker->num_segments = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) +{ + cairo_status_t status; + cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; + cairo_fixed_t half_line_width = stroker->half_line_width; + int i; + + for (i = 0; i < stroker->num_segments; i++) { + cairo_point_t *a, *b; + cairo_bool_t is_horizontal; + + a = &stroker->segments[i].p1; + b = &stroker->segments[i].p2; + + is_horizontal = stroker->segments[i].is_horizontal; + + /* Handle the joins for a potentially degenerate segment. */ + if (line_cap == CAIRO_LINE_CAP_BUTT && + stroker->segments[i].has_join && + (i != stroker->num_segments - 1 || + (! stroker->open_sub_path && stroker->dash.dash_starts_on))) + { + cairo_point_t p1 = stroker->segments[i].p1; + cairo_point_t p2 = stroker->segments[i].p2; + cairo_slope_t out_slope; + int j = (i + 1) % stroker->num_segments; + + _cairo_slope_init (&out_slope, + &stroker->segments[j].p1, + &stroker->segments[j].p2); + + if (is_horizontal) { + if (p1.x <= p2.x) { + p1.x = p2.x; + p2.x += half_line_width; + } else { + p1.x = p2.x - half_line_width; + } + if (out_slope.dy >= 0) + p1.y -= half_line_width; + if (out_slope.dy <= 0) + p2.y += half_line_width; + } else { + if (p1.y <= p2.y) { + p1.y = p2.y; + p2.y += half_line_width; + } else { + p1.y = p2.y - half_line_width; + } + if (out_slope.dx >= 0) + p1.x -= half_line_width; + if (out_slope.dx <= 0) + p2.x += half_line_width; + } + + if (stroker->do_traps) { + status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2); + } else { + cairo_box_t box; + + box.p1 = p1; + box.p2 = p2; + + status = _cairo_boxes_add (stroker->container, &box); + } + if (unlikely (status)) + return status; + } + + /* Perform the adjustments of the endpoints. */ + if (is_horizontal) { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->x <= b->x) { + a->x -= half_line_width; + b->x += half_line_width; + } else { + a->x += half_line_width; + b->x -= half_line_width; + } + } + + if (a->x > b->x) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + + a->y -= half_line_width; + b->y += half_line_width; + } else { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->y <= b->y) { + a->y -= half_line_width; + b->y += half_line_width; + } else { + a->y += half_line_width; + b->y -= half_line_width; + } + } + + if (a->y > b->y) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + + a->x -= half_line_width; + b->x += half_line_width; + } + + if (a->x == b->x && a->y == b->y) + continue; + + if (stroker->do_traps) { + status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); + } else { + cairo_box_t box; + + box.p1 = *a; + box.p2 = *b; + + status = _cairo_boxes_add (stroker->container, &box); + } + if (unlikely (status)) + return status; + } + + stroker->num_segments = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_status_t status; + + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) + return status; + + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + + stroker->current_point = *point; + stroker->first_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_line_to (void *closure, + const cairo_point_t *b) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_point_t *a = &stroker->current_point; + cairo_status_t status; + + /* We only support horizontal or vertical elements. */ + assert (a->x == b->x || a->y == b->y); + + /* We don't draw anything for degenerate paths. */ + if (a->x == b->x && a->y == b->y) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, + a->y == b->y, + TRUE); + + stroker->current_point = *b; + stroker->open_sub_path = TRUE; + + return status; +} + +static cairo_status_t +_cairo_rectilinear_stroker_line_to_dashed (void *closure, + const cairo_point_t *point) +{ + cairo_rectilinear_stroker_t *stroker = closure; + const cairo_point_t *a = &stroker->current_point; + const cairo_point_t *b = point; + cairo_bool_t fully_in_bounds; + double sign, remain; + cairo_fixed_t mag; + cairo_status_t status; + cairo_line_t segment; + cairo_bool_t dash_on = FALSE; + cairo_bool_t is_horizontal; + + /* We don't draw anything for degenerate paths. */ + if (a->x == b->x && a->y == b->y) + return CAIRO_STATUS_SUCCESS; + + /* We only support horizontal or vertical elements. */ + assert (a->x == b->x || a->y == b->y); + + fully_in_bounds = TRUE; + if (stroker->has_bounds && + (! _cairo_box_contains_point (&stroker->bounds, a) || + ! _cairo_box_contains_point (&stroker->bounds, b))) + { + fully_in_bounds = FALSE; + } + + is_horizontal = a->y == b->y; + if (is_horizontal) + mag = b->x - a->x; + else + mag = b->y - a->y; + if (mag < 0) { + remain = _cairo_fixed_to_double (-mag); + sign = 1.; + } else { + remain = _cairo_fixed_to_double (mag); + sign = -1.; + } + + segment.p2 = segment.p1 = *a; + while (remain > 0.) { + double step_length; + + step_length = MIN (stroker->dash.dash_remain, remain); + remain -= step_length; + + mag = _cairo_fixed_from_double (sign*remain); + if (is_horizontal) + segment.p2.x = b->x + mag; + else + segment.p2.y = b->y + mag; + + if (stroker->dash.dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p2, + is_horizontal, + remain <= 0.); + if (unlikely (status)) + return status; + + dash_on = TRUE; + } + else + { + dash_on = FALSE; + } + + _cairo_stroker_dash_step (&stroker->dash, step_length); + segment.p1 = segment.p2; + } + + if (stroker->dash.dash_on && ! dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + + /* This segment ends on a transition to dash_on, compute a new face + * and add cap for the beginning of the next dash_on step. + */ + + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p1, + is_horizontal, + TRUE); + if (unlikely (status)) + return status; + } + + stroker->current_point = *point; + stroker->open_sub_path = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_close_path (void *closure) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_status_t status; + + /* We don't draw anything for degenerate paths. */ + if (! stroker->open_sub_path) + return CAIRO_STATUS_SUCCESS; + + if (stroker->dash.dashed) { + status = _cairo_rectilinear_stroker_line_to_dashed (stroker, + &stroker->first_point); + } else { + status = _cairo_rectilinear_stroker_line_to (stroker, + &stroker->first_point); + } + if (unlikely (status)) + return status; + + stroker->open_sub_path = FALSE; + + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_traps_t *traps) +{ + cairo_rectilinear_stroker_t rectilinear_stroker; + cairo_int_status_t status; + + assert (path->is_rectilinear); + + if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, + stroke_style, ctm, + TRUE, traps)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (traps->num_limits) { + _cairo_rectilinear_stroker_limit (&rectilinear_stroker, + traps->limits, + traps->num_limits); + } + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_rectilinear_stroker_move_to, + rectilinear_stroker.dash.dashed ? + _cairo_rectilinear_stroker_line_to_dashed : + _cairo_rectilinear_stroker_line_to, + NULL, + _cairo_rectilinear_stroker_close_path, + &rectilinear_stroker); + if (unlikely (status)) + goto BAIL; + + if (rectilinear_stroker.dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); + + traps->is_rectilinear = 1; + traps->is_rectangular = 1; + /* As we incrementally tessellate, we do not eliminate self-intersections */ + traps->has_intersections = traps->num_traps > 1; +BAIL: + _cairo_rectilinear_stroker_fini (&rectilinear_stroker); + + if (unlikely (status)) + _cairo_traps_clear (traps); + + return status; +} + +cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_boxes_t *boxes) +{ + cairo_rectilinear_stroker_t rectilinear_stroker; + cairo_int_status_t status; + + assert (path->is_rectilinear); + + if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, + stroke_style, ctm, + FALSE, boxes)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (boxes->num_limits) { + _cairo_rectilinear_stroker_limit (&rectilinear_stroker, + boxes->limits, + boxes->num_limits); + } + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_rectilinear_stroker_move_to, + rectilinear_stroker.dash.dashed ? + _cairo_rectilinear_stroker_line_to_dashed : + _cairo_rectilinear_stroker_line_to, + NULL, + _cairo_rectilinear_stroker_close_path, + &rectilinear_stroker); + if (unlikely (status)) + goto BAIL; + + if (rectilinear_stroker.dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); + if (unlikely (status)) + goto BAIL; + + /* As we incrementally tessellate, we do not eliminate self-intersections */ + status = _cairo_bentley_ottmann_tessellate_boxes (boxes, + CAIRO_FILL_RULE_WINDING, + boxes); + if (unlikely (status)) + goto BAIL; + + _cairo_rectilinear_stroker_fini (&rectilinear_stroker); + + return CAIRO_STATUS_SUCCESS; + +BAIL: + _cairo_rectilinear_stroker_fini (&rectilinear_stroker); + _cairo_boxes_clear (boxes); + return status; +} diff --git a/libs/cairo/src/cairo-path.c b/libs/cairo/src/cairo-path.c new file mode 100644 index 000000000..49b479a99 --- /dev/null +++ b/libs/cairo/src/cairo-path.c @@ -0,0 +1,504 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-private.h" +#include "cairo-error-private.h" +#include "cairo-path-private.h" +#include "cairo-path-fixed-private.h" + +/** + * SECTION:cairo-paths + * @Title: Paths + * @Short_Description: Creating paths and manipulating path data + * + * Paths are the most basic drawing tools and are primarily used to implicitly + * generate simple masks. + */ + +static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; + +/* Closure for path interpretation. */ +typedef struct cairo_path_count { + int count; + cairo_point_t current_point; +} cpc_t; + +static cairo_status_t +_cpc_move_to (void *closure, + const cairo_point_t *point) +{ + cpc_t *cpc = closure; + + cpc->count += 2; + + cpc->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_line_to (void *closure, + const cairo_point_t *point) +{ + cpc_t *cpc = closure; + + cpc->count += 2; + + cpc->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpc_t *cpc = closure; + + cpc->count += 4; + + cpc->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_close_path (void *closure) +{ + cpc_t *cpc = closure; + + cpc->count += 1; + + return CAIRO_STATUS_SUCCESS; +} + +static int +_cairo_path_count (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + double tolerance, + cairo_bool_t flatten) +{ + cairo_status_t status; + cpc_t cpc; + + cpc.count = 0; + cpc.current_point.x = 0; + cpc.current_point.y = 0; + + if (flatten) { + status = _cairo_path_fixed_interpret_flat (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpc_move_to, + _cpc_line_to, + _cpc_close_path, + &cpc, + tolerance); + } else { + status = _cairo_path_fixed_interpret (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpc_move_to, + _cpc_line_to, + _cpc_curve_to, + _cpc_close_path, + &cpc); + } + + if (unlikely (status)) + return -1; + + return cpc.count; +} + +/* Closure for path interpretation. */ +typedef struct cairo_path_populate { + cairo_path_data_t *data; + cairo_gstate_t *gstate; + cairo_point_t current_point; +} cpp_t; + +static cairo_status_t +_cpp_move_to (void *closure, + const cairo_point_t *point) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); + + data->header.type = CAIRO_PATH_MOVE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpp->data += data->header.length; + + cpp->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_line_to (void *closure, + const cairo_point_t *point) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); + + data->header.type = CAIRO_PATH_LINE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpp->data += data->header.length; + + cpp->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x1, y1; + double x2, y2; + double x3, y3; + + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + _cairo_gstate_backend_to_user (cpp->gstate, &x1, &y1); + + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + _cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2); + + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + _cairo_gstate_backend_to_user (cpp->gstate, &x3, &y3); + + data->header.type = CAIRO_PATH_CURVE_TO; + data->header.length = 4; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x1; + data[1].point.y = y1; + + data[2].point.x = x2; + data[2].point.y = y2; + + data[3].point.x = x3; + data[3].point.y = y3; + + cpp->data += data->header.length; + + cpp->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_close_path (void *closure) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + + data->header.type = CAIRO_PATH_CLOSE_PATH; + data->header.length = 1; + + cpp->data += data->header.length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_populate (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + cairo_gstate_t *gstate, + cairo_bool_t flatten) +{ + cairo_status_t status; + cpp_t cpp; + + cpp.data = path->data; + cpp.gstate = gstate; + cpp.current_point.x = 0; + cpp.current_point.y = 0; + + if (flatten) { + double tolerance = _cairo_gstate_get_tolerance (gstate); + status = _cairo_path_fixed_interpret_flat (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpp_move_to, + _cpp_line_to, + _cpp_close_path, + &cpp, + tolerance); + } else { + status = _cairo_path_fixed_interpret (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpp_move_to, + _cpp_line_to, + _cpp_curve_to, + _cpp_close_path, + &cpp); + } + + if (unlikely (status)) + return status; + + /* Sanity check the count */ + assert (cpp.data - path->data == path->num_data); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_path_t * +_cairo_path_create_in_error (cairo_status_t status) +{ + cairo_path_t *path; + + /* special case NO_MEMORY so as to avoid allocations */ + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_path_t*) &_cairo_path_nil; + + path = malloc (sizeof (cairo_path_t)); + if (unlikely (path == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_path_t*) &_cairo_path_nil; + } + + path->num_data = 0; + path->data = NULL; + path->status = status; + + return path; +} + +static cairo_path_t * +_cairo_path_create_internal (cairo_path_fixed_t *path_fixed, + cairo_gstate_t *gstate, + cairo_bool_t flatten) +{ + cairo_path_t *path; + + path = malloc (sizeof (cairo_path_t)); + if (unlikely (path == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_path_t*) &_cairo_path_nil; + } + + path->num_data = _cairo_path_count (path, path_fixed, + _cairo_gstate_get_tolerance (gstate), + flatten); + if (path->num_data < 0) { + free (path); + return (cairo_path_t*) &_cairo_path_nil; + } + + if (path->num_data) { + path->data = _cairo_malloc_ab (path->num_data, + sizeof (cairo_path_data_t)); + if (unlikely (path->data == NULL)) { + free (path); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_path_t*) &_cairo_path_nil; + } + + path->status = _cairo_path_populate (path, path_fixed, + gstate, flatten); + } else { + path->data = NULL; + path->status = CAIRO_STATUS_SUCCESS; + } + + return path; +} + +/** + * cairo_path_destroy: + * @path: a path previously returned by either cairo_copy_path() or + * cairo_copy_path_flat(). + * + * Immediately releases all memory associated with @path. After a call + * to cairo_path_destroy() the @path pointer is no longer valid and + * should not be used further. + * + * Note: cairo_path_destroy() should only be called with a + * pointer to a #cairo_path_t returned by a cairo function. Any path + * that is created manually (ie. outside of cairo) should be destroyed + * manually as well. + **/ +void +cairo_path_destroy (cairo_path_t *path) +{ + if (path == NULL || path == &_cairo_path_nil) + return; + + if (path->data) + free (path->data); + + free (path); +} + +/** + * _cairo_path_create: + * @path: a fixed-point, device-space path to be converted and copied + * @gstate: the current graphics state + * + * Creates a user-space #cairo_path_t copy of the given device-space + * @path. The @gstate parameter provides the inverse CTM for the + * conversion. + * + * Return value: the new copy of the path. If there is insufficient + * memory a pointer to a special static nil #cairo_path_t will be + * returned instead with status==%CAIRO_STATUS_NO_MEMORY and + * data==%NULL. + **/ +cairo_path_t * +_cairo_path_create (cairo_path_fixed_t *path, + cairo_gstate_t *gstate) +{ + return _cairo_path_create_internal (path, gstate, FALSE); +} + +/** + * _cairo_path_create_flat: + * @path: a fixed-point, device-space path to be flattened, converted and copied + * @gstate: the current graphics state + * + * Creates a flattened, user-space #cairo_path_t copy of the given + * device-space @path. The @gstate parameter provide the inverse CTM + * for the conversion, as well as the tolerance value to control the + * accuracy of the flattening. + * + * Return value: the flattened copy of the path. If there is insufficient + * memory a pointer to a special static nil #cairo_path_t will be + * returned instead with status==%CAIRO_STATUS_NO_MEMORY and + * data==%NULL. + **/ +cairo_path_t * +_cairo_path_create_flat (cairo_path_fixed_t *path, + cairo_gstate_t *gstate) +{ + return _cairo_path_create_internal (path, gstate, TRUE); +} + +/** + * _cairo_path_append_to_context: + * @path: the path data to be appended + * @cr: a cairo context + * + * Append @path to the current path within @cr. + * + * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path + * is invalid, and %CAIRO_STATUS_SUCCESS otherwise. + **/ +cairo_status_t +_cairo_path_append_to_context (const cairo_path_t *path, + cairo_t *cr) +{ + const cairo_path_data_t *p, *end; + cairo_fixed_t x1_fixed, y1_fixed; + cairo_fixed_t x2_fixed, y2_fixed; + cairo_fixed_t x3_fixed, y3_fixed; + cairo_matrix_t user_to_backend; + cairo_status_t status; + double x, y; + + user_to_backend = cr->gstate->ctm; + cairo_matrix_multiply (&user_to_backend, + &user_to_backend, + &cr->gstate->target->device_transform); + + end = &path->data[path->num_data]; + for (p = &path->data[0]; p < end; p += p->header.length) { + switch (p->header.type) { + case CAIRO_PATH_MOVE_TO: + if (unlikely (p->header.length < 2)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed); + break; + + case CAIRO_PATH_LINE_TO: + if (unlikely (p->header.length < 2)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed); + break; + + case CAIRO_PATH_CURVE_TO: + if (unlikely (p->header.length < 4)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + x = p[2].point.x, y = p[2].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x2_fixed = _cairo_fixed_from_double (x); + y2_fixed = _cairo_fixed_from_double (y); + + x = p[3].point.x, y = p[3].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x3_fixed = _cairo_fixed_from_double (x); + y3_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_curve_to (cr->path, + x1_fixed, y1_fixed, + x2_fixed, y2_fixed, + x3_fixed, y3_fixed); + break; + + case CAIRO_PATH_CLOSE_PATH: + if (unlikely (p->header.length < 1)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + status = _cairo_path_fixed_close_path (cr->path); + break; + + default: + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + } + + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-pattern.c b/libs/cairo/src/cairo-pattern.c new file mode 100644 index 000000000..502344f3c --- /dev/null +++ b/libs/cairo/src/cairo-pattern.c @@ -0,0 +1,3202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" + +/** + * SECTION:cairo-pattern + * @Title: cairo_pattern_t + * @Short_Description: Sources for drawing + * @See_Also: #cairo_t, #cairo_surface_t + * + * #cairo_pattern_t is the paint with which cairo draws. + * The primary use of patterns is as the source for all cairo drawing + * operations, although they can also be used as masks, that is, as the + * brush too. + * + * A cairo pattern is created by using one of the many constructors, + * of the form cairo_pattern_create_type() + * or implicitly through + * cairo_set_source_type() functions. + */ + +#if HAS_FREED_POOL +static freed_pool_t freed_pattern_pool[4]; +#endif + +static const cairo_solid_pattern_t _cairo_pattern_nil = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NO_MEMORY, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + CAIRO_FILTER_DEFAULT, /* filter */ + CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ +}; + +static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NULL_POINTER, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + CAIRO_FILTER_DEFAULT, /* filter */ + CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ +}; + +const cairo_solid_pattern_t _cairo_pattern_black = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_SUCCESS, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + CAIRO_FILTER_DEFAULT, /* filter */ + CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ +}; + +const cairo_solid_pattern_t _cairo_pattern_clear = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_SUCCESS, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + CAIRO_FILTER_DEFAULT, /* filter */ + CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */ +}; + +const cairo_solid_pattern_t _cairo_pattern_white = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_SUCCESS, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + CAIRO_FILTER_DEFAULT, /* filter */ + CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */ +}; + +/** + * _cairo_pattern_set_error: + * @pattern: a pattern + * @status: a status value indicating an error + * + * Atomically sets pattern->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to pattern->status should happen + * through _cairo_pattern_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the nil + * objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +static cairo_status_t +_cairo_pattern_set_error (cairo_pattern_t *pattern, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&pattern->status, status); + + return _cairo_error (status); +} + +static void +_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) +{ +#if HAVE_VALGRIND + switch (type) { + case CAIRO_PATTERN_TYPE_SOLID: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); + break; + } +#endif + + pattern->type = type; + pattern->status = CAIRO_STATUS_SUCCESS; + + /* Set the reference count to zero for on-stack patterns. + * Callers needs to explicitly increment the count for heap allocations. */ + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); + + _cairo_user_data_array_init (&pattern->user_data); + + if (type == CAIRO_PATTERN_TYPE_SURFACE) + pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT; + else + pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT; + + pattern->filter = CAIRO_FILTER_DEFAULT; + + pattern->has_component_alpha = FALSE; + + cairo_matrix_init_identity (&pattern->matrix); +} + +static cairo_status_t +_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, + const cairo_gradient_pattern_t *other) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; + cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; + + *dst = *src; + } + else + { + cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; + cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; + + *dst = *src; + } + + if (other->stops == other->stops_embedded) + pattern->stops = pattern->stops_embedded; + else if (other->stops) + { + pattern->stops = _cairo_malloc_ab (other->stops_size, + sizeof (cairo_gradient_stop_t)); + if (unlikely (pattern->stops == NULL)) { + pattern->stops_size = 0; + pattern->n_stops = 0; + return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); + } + + memcpy (pattern->stops, other->stops, + other->n_stops * sizeof (cairo_gradient_stop_t)); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + if (other->status) + return _cairo_pattern_set_error (pattern, other->status); + + switch (other->type) { + case CAIRO_PATTERN_TYPE_SOLID: { + cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); + + *dst = *src; + } break; + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); + + *dst = *src; + cairo_surface_reference (dst->surface); + } break; + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; + cairo_status_t status; + + if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); + } else { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); + } + + status = _cairo_gradient_pattern_init_copy (dst, src); + if (unlikely (status)) + return status; + + } break; + } + + /* The reference count and user_data array are unique to the copy. */ + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); + _cairo_user_data_array_init (&pattern->user_data); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + int size; + + assert (other->status == CAIRO_STATUS_SUCCESS); + + switch (other->type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + size = sizeof (cairo_solid_pattern_t); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + size = sizeof (cairo_surface_pattern_t); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + size = sizeof (cairo_linear_pattern_t); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + size = sizeof (cairo_radial_pattern_t); + break; + } + + memcpy (pattern, other, size); + + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); + _cairo_user_data_array_init (&pattern->user_data); +} + +cairo_status_t +_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + cairo_status_t status; + + /* We don't bother doing any fancy copy-on-write implementation + * for the pattern's data. It's generally quite tiny. */ + status = _cairo_pattern_init_copy (pattern, other); + if (unlikely (status)) + return status; + + /* But we do let the surface snapshot stuff be as fancy as it + * would like to be. */ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = + (cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + surface_pattern->surface = _cairo_surface_snapshot (surface); + + cairo_surface_destroy (surface); + + if (surface_pattern->surface->status) + return surface_pattern->surface->status; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_fini (cairo_pattern_t *pattern) +{ + _cairo_user_data_array_fini (&pattern->user_data); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + break; + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surface_pattern = + (cairo_surface_pattern_t *) pattern; + + cairo_surface_destroy (surface_pattern->surface); + } break; + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + + if (gradient->stops && gradient->stops != gradient->stops_embedded) + free (gradient->stops); + } break; + } + +#if HAVE_VALGRIND + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); + break; + } +#endif +} + +cairo_status_t +_cairo_pattern_create_copy (cairo_pattern_t **pattern_out, + const cairo_pattern_t *other) +{ + cairo_pattern_t *pattern; + cairo_status_t status; + + if (other->status) + return other->status; + + switch (other->type) { + case CAIRO_PATTERN_TYPE_SOLID: + pattern = malloc (sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + pattern = malloc (sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + pattern = malloc (sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + pattern = malloc (sizeof (cairo_radial_pattern_t)); + break; + default: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + } + if (unlikely (pattern == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pattern_init_copy (pattern, other); + if (unlikely (status)) { + free (pattern); + return status; + } + + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); + *pattern_out = pattern; + return CAIRO_STATUS_SUCCESS; +} + + +void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + const cairo_color_t *color) +{ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); + pattern->color = *color; +} + +void +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface) +{ + if (surface->status) { + /* Force to solid to simplify the pattern_fini process. */ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); + _cairo_pattern_set_error (&pattern->base, surface->status); + return; + } + + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); + + pattern->surface = cairo_surface_reference (surface); +} + +static void +_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, + cairo_pattern_type_t type) +{ + _cairo_pattern_init (&pattern->base, type); + + pattern->n_stops = 0; + pattern->stops_size = 0; + pattern->stops = NULL; +} + +void +_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, + double x0, double y0, double x1, double y1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR); + + pattern->p1.x = _cairo_fixed_from_double (x0); + pattern->p1.y = _cairo_fixed_from_double (y0); + pattern->p2.x = _cairo_fixed_from_double (x1); + pattern->p2.y = _cairo_fixed_from_double (y1); +} + +void +_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, + double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL); + + pattern->c1.x = _cairo_fixed_from_double (cx0); + pattern->c1.y = _cairo_fixed_from_double (cy0); + pattern->r1 = _cairo_fixed_from_double (fabs (radius0)); + pattern->c2.x = _cairo_fixed_from_double (cx1); + pattern->c2.y = _cairo_fixed_from_double (cy1); + pattern->r2 = _cairo_fixed_from_double (fabs (radius1)); +} + +cairo_pattern_t * +_cairo_pattern_create_solid (const cairo_color_t *color) +{ + cairo_solid_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); + if (unlikely (pattern == NULL)) { + /* None cached, need to create a new pattern. */ + pattern = malloc (sizeof (cairo_solid_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil; + } + } + + _cairo_pattern_init_solid (pattern, color); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; +} + +cairo_pattern_t * +_cairo_pattern_create_in_error (cairo_status_t status) +{ + cairo_pattern_t *pattern; + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_pattern_t *)&_cairo_pattern_nil.base; + + CAIRO_MUTEX_INITIALIZE (); + + pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + if (pattern->status == CAIRO_STATUS_SUCCESS) + status = _cairo_pattern_set_error (pattern, status); + + return pattern; +} + +/** + * cairo_pattern_create_rgb: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * + * Creates a new #cairo_pattern_t corresponding to an opaque color. The + * color components are floating point numbers in the range 0 to 1. + * If the values passed in are outside that range, they will be + * clamped. + * + * Return value: the newly created #cairo_pattern_t if successful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue) +{ + cairo_color_t color; + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + + _cairo_color_init_rgb (&color, red, green, blue); + + CAIRO_MUTEX_INITIALIZE (); + + return _cairo_pattern_create_solid (&color); +} +slim_hidden_def (cairo_pattern_create_rgb); + +/** + * cairo_pattern_create_rgba: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * @alpha: alpha component of the color + * + * Creates a new #cairo_pattern_t corresponding to a translucent color. + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. + * + * Return value: the newly created #cairo_pattern_t if successful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha) +{ + cairo_color_t color; + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + + CAIRO_MUTEX_INITIALIZE (); + + return _cairo_pattern_create_solid (&color); +} +slim_hidden_def (cairo_pattern_create_rgba); + +/** + * cairo_pattern_create_for_surface: + * @surface: the surface + * + * Create a new #cairo_pattern_t for the given surface. + * + * Return value: the newly created #cairo_pattern_t if successful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_surface_pattern_t *pattern; + + if (surface == NULL) { + _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); + return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer; + } + + if (surface->status) + return _cairo_pattern_create_in_error (surface->status); + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_surface_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *)&_cairo_pattern_nil.base; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_for_surface (pattern, surface); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; +} +slim_hidden_def (cairo_pattern_create_for_surface); + +/** + * cairo_pattern_create_linear: + * @x0: x coordinate of the start point + * @y0: y coordinate of the start point + * @x1: x coordinate of the end point + * @y1: y coordinate of the end point + * + * Create a new linear gradient #cairo_pattern_t along the line defined + * by (x0, y0) and (x1, y1). Before using the gradient pattern, a + * number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Note: The coordinates here are in pattern space. For a new pattern, + * pattern space is identical to user space, but the relationship + * between the spaces can be changed with cairo_pattern_set_matrix(). + * + * Return value: the newly created #cairo_pattern_t if successful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_linear_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_linear_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil.base; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); + + return &pattern->base.base; +} + +/** + * cairo_pattern_create_radial: + * @cx0: x coordinate for the center of the start circle + * @cy0: y coordinate for the center of the start circle + * @radius0: radius of the start circle + * @cx1: x coordinate for the center of the end circle + * @cy1: y coordinate for the center of the end circle + * @radius1: radius of the end circle + * + * Creates a new radial gradient #cairo_pattern_t between the two + * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the + * gradient pattern, a number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Note: The coordinates here are in pattern space. For a new pattern, + * pattern space is identical to user space, but the relationship + * between the spaces can be changed with cairo_pattern_set_matrix(). + * + * Return value: the newly created #cairo_pattern_t if successful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_radial_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_radial_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil.base; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); + + return &pattern->base.base; +} + +/** + * cairo_pattern_reference: + * @pattern: a #cairo_pattern_t + * + * Increases the reference count on @pattern by one. This prevents + * @pattern from being destroyed until a matching call to + * cairo_pattern_destroy() is made. + * + * The number of references to a #cairo_pattern_t can be get using + * cairo_pattern_get_reference_count(). + * + * Return value: the referenced #cairo_pattern_t. + **/ +cairo_pattern_t * +cairo_pattern_reference (cairo_pattern_t *pattern) +{ + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return pattern; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); + + _cairo_reference_count_inc (&pattern->ref_count); + + return pattern; +} +slim_hidden_def (cairo_pattern_reference); + +/** + * cairo_pattern_get_type: + * @pattern: a #cairo_pattern_t + * + * This function returns the type a pattern. + * See #cairo_pattern_type_t for available types. + * + * Return value: The type of @pattern. + * + * Since: 1.2 + **/ +cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern) +{ + return pattern->type; +} + +/** + * cairo_pattern_status: + * @pattern: a #cairo_pattern_t + * + * Checks whether an error has previously occurred for this + * pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. + **/ +cairo_status_t +cairo_pattern_status (cairo_pattern_t *pattern) +{ + return pattern->status; +} + +/** + * cairo_pattern_destroy: + * @pattern: a #cairo_pattern_t + * + * Decreases the reference count on @pattern by one. If the result is + * zero, then @pattern and all associated resources are freed. See + * cairo_pattern_reference(). + **/ +void +cairo_pattern_destroy (cairo_pattern_t *pattern) +{ + cairo_pattern_type_t type; + + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) + return; + + type = pattern->type; + _cairo_pattern_fini (pattern); + + /* maintain a small cache of freed patterns */ + _freed_pool_put (&freed_pattern_pool[type], pattern); +} +slim_hidden_def (cairo_pattern_destroy); + +/** + * cairo_pattern_get_reference_count: + * @pattern: a #cairo_pattern_t + * + * Returns the current reference count of @pattern. + * + * Return value: the current reference count of @pattern. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_pattern_get_reference_count (cairo_pattern_t *pattern) +{ + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count); +} + +/** + * cairo_pattern_get_user_data: + * @pattern: a #cairo_pattern_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @pattern using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +void * +cairo_pattern_get_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&pattern->user_data, + key); +} + +/** + * cairo_pattern_set_user_data: + * @pattern: a #cairo_pattern_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_pattern_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @pattern. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_set_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return pattern->status; + + return _cairo_user_data_array_set_data (&pattern->user_data, + key, user_data, destroy); +} + +/* make room for at least one more color stop */ +static cairo_status_t +_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) +{ + cairo_gradient_stop_t *new_stops; + int old_size = pattern->stops_size; + int embedded_size = ARRAY_LENGTH (pattern->stops_embedded); + int new_size = 2 * MAX (old_size, 4); + + /* we have a local buffer at pattern->stops_embedded. try to fulfill the request + * from there. */ + if (old_size < embedded_size) { + pattern->stops = pattern->stops_embedded; + pattern->stops_size = embedded_size; + return CAIRO_STATUS_SUCCESS; + } + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + assert (pattern->n_stops <= pattern->stops_size); + + if (pattern->stops == pattern->stops_embedded) { + new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t)); + if (new_stops) + memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t)); + } else { + new_stops = _cairo_realloc_ab (pattern->stops, + new_size, + sizeof (cairo_gradient_stop_t)); + } + + if (unlikely (new_stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pattern->stops = new_stops; + pattern->stops_size = new_size; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) +{ + cairo_gradient_stop_t *stops; + unsigned int i; + + if (pattern->n_stops >= pattern->stops_size) { + cairo_status_t status = _cairo_pattern_gradient_grow (pattern); + if (unlikely (status)) { + status = _cairo_pattern_set_error (&pattern->base, status); + return; + } + } + + stops = pattern->stops; + + for (i = 0; i < pattern->n_stops; i++) + { + if (offset < stops[i].offset) + { + memmove (&stops[i + 1], &stops[i], + sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i)); + + break; + } + } + + stops[i].offset = offset; + + stops[i].color.red = red; + stops[i].color.green = green; + stops[i].color.blue = blue; + stops[i].color.alpha = alpha; + + stops[i].color.red_short = _cairo_color_double_to_short (red); + stops[i].color.green_short = _cairo_color_double_to_short (green); + stops[i].color.blue_short = _cairo_color_double_to_short (blue); + stops[i].color.alpha_short = _cairo_color_double_to_short (alpha); + + pattern->n_stops++; +} + +/** + * cairo_pattern_add_color_stop_rgb: + * @pattern: a #cairo_pattern_t + * @offset: an offset in the range [0.0 .. 1.0] + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Adds an opaque color stop to a gradient pattern. The offset + * specifies the location along the gradient's control vector. For + * example, a linear gradient's control vector is from (x0,y0) to + * (x1,y1) while a radial gradient's control vector is from any point + * on the start circle to the corresponding point on the end circle. + * + * The color is specified in the same way as in cairo_set_source_rgb(). + * + * If two (or more) stops are specified with identical offset values, + * they will be sorted according to the order in which the stops are + * added, (stops added earlier will compare less than stops added + * later). This can be useful for reliably making sharp color + * transitions instead of the typical blend. + * + * + * Note: If the pattern is not a gradient pattern, (eg. a linear or + * radial pattern), then the pattern will be put into an error status + * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. + **/ +void +cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue) +{ + if (pattern->status) + return; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + offset = _cairo_restrict_value (offset, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + + _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, + offset, red, green, blue, 1.0); +} + +/** + * cairo_pattern_add_color_stop_rgba: + * @pattern: a #cairo_pattern_t + * @offset: an offset in the range [0.0 .. 1.0] + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Adds a translucent color stop to a gradient pattern. The offset + * specifies the location along the gradient's control vector. For + * example, a linear gradient's control vector is from (x0,y0) to + * (x1,y1) while a radial gradient's control vector is from any point + * on the start circle to the corresponding point on the end circle. + * + * The color is specified in the same way as in cairo_set_source_rgba(). + * + * If two (or more) stops are specified with identical offset values, + * they will be sorted according to the order in which the stops are + * added, (stops added earlier will compare less than stops added + * later). This can be useful for reliably making sharp color + * transitions instead of the typical blend. + * + * Note: If the pattern is not a gradient pattern, (eg. a linear or + * radial pattern), then the pattern will be put into an error status + * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. + */ +void +cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) +{ + if (pattern->status) + return; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + offset = _cairo_restrict_value (offset, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, + offset, red, green, blue, alpha); +} + +/** + * cairo_pattern_set_matrix: + * @pattern: a #cairo_pattern_t + * @matrix: a #cairo_matrix_t + * + * Sets the pattern's transformation matrix to @matrix. This matrix is + * a transformation from user space to pattern space. + * + * When a pattern is first created it always has the identity matrix + * for its transformation matrix, which means that pattern space is + * initially identical to user space. + * + * Important: Please note that the direction of this transformation + * matrix is from user space to pattern space. This means that if you + * imagine the flow from a pattern to user space (and on to device + * space), then coordinates in that flow will be transformed by the + * inverse of the pattern matrix. + * + * For example, if you want to make a pattern appear twice as large as + * it does by default the correct code to use is: + * + * + * cairo_matrix_init_scale (&matrix, 0.5, 0.5); + * cairo_pattern_set_matrix (pattern, &matrix); + * + * + * Meanwhile, using values of 2.0 rather than 0.5 in the code above + * would cause the pattern to appear at half of its default size. + * + * Also, please note the discussion of the user-space locking + * semantics of cairo_set_source(). + **/ +void +cairo_pattern_set_matrix (cairo_pattern_t *pattern, + const cairo_matrix_t *matrix) +{ + cairo_matrix_t inverse; + cairo_status_t status; + + if (pattern->status) + return; + + if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) + return; + + pattern->matrix = *matrix; + + inverse = *matrix; + status = cairo_matrix_invert (&inverse); + if (unlikely (status)) + status = _cairo_pattern_set_error (pattern, status); +} +slim_hidden_def (cairo_pattern_set_matrix); + +/** + * cairo_pattern_get_matrix: + * @pattern: a #cairo_pattern_t + * @matrix: return value for the matrix + * + * Stores the pattern's transformation matrix into @matrix. + **/ +void +cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +{ + *matrix = pattern->matrix; +} + +/** + * cairo_pattern_set_filter: + * @pattern: a #cairo_pattern_t + * @filter: a #cairo_filter_t describing the filter to use for resizing + * the pattern + * + * Sets the filter to be used for resizing when using this pattern. + * See #cairo_filter_t for details on each filter. + * + * * Note that you might want to control filtering even when you do not + * have an explicit #cairo_pattern_t object, (for example when using + * cairo_set_source_surface()). In these cases, it is convenient to + * use cairo_get_source() to get access to the pattern that cairo + * creates implicitly. For example: + * + * + * cairo_set_source_surface (cr, image, x, y); + * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); + * + **/ +void +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) +{ + if (pattern->status) + return; + + pattern->filter = filter; +} + +/** + * cairo_pattern_get_filter: + * @pattern: a #cairo_pattern_t + * + * Gets the current filter for a pattern. See #cairo_filter_t + * for details on each filter. + * + * Return value: the current filter used for resizing the pattern. + **/ +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern) +{ + return pattern->filter; +} + +/** + * cairo_pattern_set_extend: + * @pattern: a #cairo_pattern_t + * @extend: a #cairo_extend_t describing how the area outside of the + * pattern will be drawn + * + * Sets the mode to be used for drawing outside the area of a pattern. + * See #cairo_extend_t for details on the semantics of each extend + * strategy. + * + * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns + * and %CAIRO_EXTEND_PAD for gradient patterns. + **/ +void +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) +{ + if (pattern->status) + return; + + pattern->extend = extend; +} + +/** + * cairo_pattern_get_extend: + * @pattern: a #cairo_pattern_t + * + * Gets the current extend mode for a pattern. See #cairo_extend_t + * for details on the semantics of each extend strategy. + * + * Return value: the current extend strategy used for drawing the + * pattern. + **/ +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern) +{ + return pattern->extend; +} +slim_hidden_def (cairo_pattern_get_extend); + +void +_cairo_pattern_transform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm_inverse) +{ + if (pattern->status) + return; + + cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); +} + +static void +_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, + double offset_x, + double offset_y, + int width, + int height, + cairo_bool_t *is_horizontal, + cairo_bool_t *is_vertical) +{ + cairo_point_double_t point0, point1; + double a, b, c, d, tx, ty; + double scale, start, dx, dy; + cairo_fixed_t factors[3]; + int i; + + /* To classify a pattern as horizontal or vertical, we first + * compute the (fixed point) factors at the corners of the + * pattern. We actually only need 3/4 corners, so we skip the + * fourth. + */ + point0.x = _cairo_fixed_to_double (pattern->p1.x); + point0.y = _cairo_fixed_to_double (pattern->p1.y); + point1.x = _cairo_fixed_to_double (pattern->p2.x); + point1.y = _cairo_fixed_to_double (pattern->p2.y); + + _cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); + + dx = point1.x - point0.x; + dy = point1.y - point0.y; + scale = dx * dx + dy * dy; + scale = (scale) ? 1.0 / scale : 1.0; + + start = dx * point0.x + dy * point0.y; + + for (i = 0; i < 3; i++) { + double qx_device = (i % 2) * (width - 1) + offset_x; + double qy_device = (i / 2) * (height - 1) + offset_y; + + /* transform fragment into pattern space */ + double qx = a * qx_device + c * qy_device + tx; + double qy = b * qx_device + d * qy_device + ty; + + factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); + } + + /* We consider a pattern to be vertical if the fixed point factor + * at the two upper corners is the same. We could accept a small + * change, but determining what change is acceptable would require + * sorting the stops in the pattern and looking at the differences. + * + * Horizontal works the same way with the two left corners. + */ + + *is_vertical = factors[1] == factors[0]; + *is_horizontal = factors[2] == factors[0]; +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) +{ + cairo_image_surface_t *image; + pixman_image_t *pixman_image; + pixman_transform_t pixman_transform; + cairo_status_t status; + cairo_bool_t repeat = FALSE; + cairo_bool_t opaque = TRUE; + + pixman_gradient_stop_t pixman_stops_static[2]; + pixman_gradient_stop_t *pixman_stops = pixman_stops_static; + unsigned int i; + int clone_offset_x, clone_offset_y; + cairo_matrix_t matrix = pattern->base.matrix; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof(pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < pattern->n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); + pixman_stops[i].color.red = pattern->stops[i].color.red_short; + pixman_stops[i].color.green = pattern->stops[i].color.green_short; + pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; + pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha)) + opaque = FALSE; + } + + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + pixman_point_fixed_t p1, p2; + double x0, y0, x1, y1, maxabs; + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ + x0 = _cairo_fixed_to_double (linear->p1.x); + y0 = _cairo_fixed_to_double (linear->p1.y); + x1 = _cairo_fixed_to_double (linear->p2.x); + y1 = _cairo_fixed_to_double (linear->p2.y); + cairo_matrix_transform_point (&matrix, &x0, &y0); + cairo_matrix_transform_point (&matrix, &x1, &y1); + maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + if (maxabs > PIXMAN_MAX_INT) + { + double sf; + cairo_matrix_t scale; + + sf = PIXMAN_MAX_INT / maxabs; + + p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); + p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); + p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); + p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); + + /* cairo_matrix_scale does a pre-scale, we want a post-scale */ + cairo_matrix_init_scale (&scale, sf, sf); + cairo_matrix_multiply (&matrix, &matrix, &scale); + } + else + { + p1.x = _cairo_fixed_to_16_16 (linear->p1.x); + p1.y = _cairo_fixed_to_16_16 (linear->p1.y); + p2.x = _cairo_fixed_to_16_16 (linear->p2.x); + p2.y = _cairo_fixed_to_16_16 (linear->p2.y); + } + + pixman_image = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + pattern->n_stops); + } + else + { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + pixman_point_fixed_t c1, c2; + pixman_fixed_t r1, r2; + + c1.x = _cairo_fixed_to_16_16 (radial->c1.x); + c1.y = _cairo_fixed_to_16_16 (radial->c1.y); + r1 = _cairo_fixed_to_16_16 (radial->r1); + + c2.x = _cairo_fixed_to_16_16 (radial->c2.x); + c2.y = _cairo_fixed_to_16_16 (radial->c2.y); + r2 = _cairo_fixed_to_16_16 (radial->r2); + + pixman_image = pixman_image_create_radial_gradient (&c1, &c2, + r1, r2, + pixman_stops, + pattern->n_stops); + } + + if (pixman_stops != pixman_stops_static) + free (pixman_stops); + + if (unlikely (pixman_image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (_cairo_surface_is_image (dst)) + { + image = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (pixman_image, + PIXMAN_a8r8g8b8); + if (image->base.status) + { + pixman_image_unref (pixman_image); + return image->base.status; + } + + attr->x_offset = attr->y_offset = 0; + attr->matrix = matrix; + attr->extend = pattern->base.extend; + attr->filter = CAIRO_FILTER_NEAREST; + attr->has_component_alpha = pattern->base.has_component_alpha; + + *out = &image->base; + + return CAIRO_STATUS_SUCCESS; + } + + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_bool_t is_horizontal; + cairo_bool_t is_vertical; + + _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, + x, y, width, height, + &is_horizontal, &is_vertical); + if (is_horizontal) { + height = 1; + repeat = TRUE; + } + /* width-1 repeating patterns are quite slow with scan-line based + * compositing code, so we use a wider strip and spend some extra + * expense in computing the gradient. It's possible that for narrow + * gradients we'd be better off using a 2 or 4 pixel strip; the + * wider the gradient, the more it's worth spending extra time + * computing a sample. + */ + if (is_vertical && width > 8) { + width = 8; + repeat = TRUE; + } + } + + if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, + NULL, 0)) + { + pixman_image_unref (pixman_image); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + image = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + if (image->base.status) { + pixman_image_unref (pixman_image); + return image->base.status; + } + + _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform, + width/2., height/2.); + if (!pixman_image_set_transform (pixman_image, &pixman_transform)) { + cairo_surface_destroy (&image->base); + pixman_image_unref (pixman_image); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + switch (pattern->base.extend) { + case CAIRO_EXTEND_NONE: + pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE); + break; + case CAIRO_EXTEND_REPEAT: + pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL); + break; + case CAIRO_EXTEND_REFLECT: + pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT); + break; + case CAIRO_EXTEND_PAD: + pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD); + break; + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + pixman_image, + NULL, + image->pixman_image, + x, y, + 0, 0, + 0, 0, + width, height); + + pixman_image_unref (pixman_image); + + _cairo_debug_check_image_surface_is_defined (&image->base); + + status = _cairo_surface_clone_similar (dst, &image->base, + 0, 0, width, height, + &clone_offset_x, + &clone_offset_y, + out); + + cairo_surface_destroy (&image->base); + + attr->x_offset = -x; + attr->y_offset = -y; + cairo_matrix_init_identity (&attr->matrix); + attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; + attr->filter = CAIRO_FILTER_NEAREST; + attr->has_component_alpha = pattern->base.has_component_alpha; + + return status; +} + +/* We maintain a small cache here, because we don't want to constantly + * recreate surfaces for simple solid colors. */ +#define MAX_SURFACE_CACHE_SIZE 16 +static struct { + struct _cairo_pattern_solid_surface_cache{ + cairo_color_t color; + cairo_surface_t *surface; + } cache[MAX_SURFACE_CACHE_SIZE]; + int size; +} solid_surface_cache; + +static cairo_bool_t +_cairo_pattern_solid_surface_matches ( + const struct _cairo_pattern_solid_surface_cache *cache, + const cairo_solid_pattern_t *pattern, + cairo_surface_t *dst) +{ + if (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color)) + return FALSE; + + if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1) + return FALSE; + + if (! _cairo_surface_is_similar (cache->surface, dst)) + return FALSE; + + return TRUE; +} + +static cairo_bool_t +_cairo_pattern_solid_surface_matches_color ( + const struct _cairo_pattern_solid_surface_cache *cache, + const cairo_solid_pattern_t *pattern, + cairo_surface_t *dst) +{ + if (! _cairo_color_equal (&cache->color, &pattern->color)) + return FALSE; + + return _cairo_pattern_solid_surface_matches (cache, pattern, dst); +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attribs) +{ + static int i; + + cairo_surface_t *surface, *to_destroy = NULL; + cairo_status_t status; + + CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); + + /* Check cache first */ + if (i < solid_surface_cache.size && + _cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], + pattern, + dst)) + { + goto DONE; + } + + for (i = 0 ; i < solid_surface_cache.size; i++) { + if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], + pattern, + dst)) + { + goto DONE; + } + } + + /* Choose a surface to repaint/evict */ + surface = NULL; + if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) { + i = rand () % MAX_SURFACE_CACHE_SIZE; + surface = solid_surface_cache.cache[i].surface; + + if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i], + pattern, + dst)) + { + /* Reuse the surface instead of evicting */ + status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); + if (unlikely (status)) + goto EVICT; + + cairo_surface_reference (surface); + } + else + { + EVICT: + surface = NULL; + } + } + + if (surface == NULL) { + /* Not cached, need to create new */ + surface = _cairo_surface_create_solid_pattern_surface (dst, pattern); + if (surface == NULL) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto UNLOCK; + } + if (unlikely (surface->status)) { + status = surface->status; + goto UNLOCK; + } + + if (unlikely (! _cairo_surface_is_similar (surface, dst))) + { + /* In the rare event of a substitute surface being returned, + * don't cache the fallback. + */ + *out = surface; + goto NOCACHE; + } + } + + if (i == solid_surface_cache.size) + solid_surface_cache.size++; + + to_destroy = solid_surface_cache.cache[i].surface; + solid_surface_cache.cache[i].surface = surface; + solid_surface_cache.cache[i].color = pattern->color; + +DONE: + *out = cairo_surface_reference (solid_surface_cache.cache[i].surface); + +NOCACHE: + attribs->x_offset = attribs->y_offset = 0; + cairo_matrix_init_identity (&attribs->matrix); + attribs->extend = CAIRO_EXTEND_REPEAT; + attribs->filter = CAIRO_FILTER_NEAREST; + attribs->has_component_alpha = pattern->base.has_component_alpha; + + status = CAIRO_STATUS_SUCCESS; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); + + if (to_destroy) + cairo_surface_destroy (to_destroy); + + return status; +} + +static void +_cairo_pattern_reset_solid_surface_cache (void) +{ + CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); + + /* remove surfaces starting from the end so that solid_surface_cache.cache + * is always in a consistent state when we release the mutex. */ + while (solid_surface_cache.size) { + cairo_surface_t *surface; + + solid_surface_cache.size--; + surface = solid_surface_cache.cache[solid_surface_cache.size].surface; + solid_surface_cache.cache[solid_surface_cache.size].surface = NULL; + + /* release the lock to avoid the possibility of a recursive + * deadlock when the surface destroy closure gets called */ + CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); + cairo_surface_destroy (surface); + CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); + } + + CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); +} + +static void +_extents_to_linear_parameter (const cairo_linear_pattern_t *linear, + const cairo_rectangle_int_t *extents, + double t[2]) +{ + double t0, tdx, tdy; + double p1x, p1y, pdx, pdy, invsqnorm; + + p1x = _cairo_fixed_to_double (linear->p1.x); + p1y = _cairo_fixed_to_double (linear->p1.y); + pdx = _cairo_fixed_to_double (linear->p2.x) - p1x; + pdy = _cairo_fixed_to_double (linear->p2.y) - p1y; + invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); + pdx *= invsqnorm; + pdy *= invsqnorm; + + t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy; + tdx = extents->width * pdx; + tdy = extents->height * pdy; + + t[0] = t[1] = t0; + if (tdx < 0) + t[0] += tdx; + else + t[1] += tdx; + + if (tdy < 0) + t[0] += tdy; + else + t[1] += tdy; +} + +static cairo_bool_t +_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) +{ + return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y; +} + +static cairo_bool_t +_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial) +{ + return radial->r1 == radial->r2 && + (radial->r1 == 0 /* && radial->r2 == 0 */ || + (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y)); +} + +static cairo_bool_t +_gradient_is_clear (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->n_stops == 0 || + (gradient->base.extend == CAIRO_EXTEND_NONE && + gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) + return TRUE; + + /* Check if the extents intersect the drawn part of the pattern. */ + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + if (gradient->base.extend == CAIRO_EXTEND_NONE) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + /* EXTEND_NONE degenerate linear gradients are clear */ + if (_linear_pattern_is_degenerate (linear)) + return TRUE; + + if (extents != NULL) { + double t[2]; + _extents_to_linear_parameter (linear, extents, t); + if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0)) + return TRUE; + } + } + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; + /* degenerate radial gradients are clear */ + if (_radial_pattern_is_degenerate (radial) && FALSE) + return TRUE; + /* TODO: check actual intersection */ + } + + for (i = 0; i < gradient->n_stops; i++) + if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color)) + return FALSE; + + return TRUE; +} + +/** + * _cairo_gradient_pattern_is_solid + * + * Convenience function to determine whether a gradient pattern is + * a solid color within the given extents. In this case the color + * argument is initialized to the color the pattern represents. + * This functions doesn't handle completely transparent gradients, + * thus it should be called only after _cairo_pattern_is_clear has + * returned FALSE. + * + * Return value: %TRUE if the pattern is a solid color. + **/ +cairo_bool_t +_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents, + cairo_color_t *color) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + /* TODO: radial, degenerate linear */ + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + if (gradient->base.extend == CAIRO_EXTEND_NONE) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + double t[2]; + + /* We already know that the pattern is not clear, thus if some + * part of it is clear, the whole is not solid. + */ + + if (extents == NULL) + return FALSE; + + _extents_to_linear_parameter (linear, extents, t); + if (t[0] < 0.0 || t[1] > 1.0) + return FALSE; + } + } + + for (i = 1; i < gradient->n_stops; i++) + if (! _cairo_color_stop_equal (&gradient->stops[0].color, + &gradient->stops[i].color)) + return FALSE; + + _cairo_color_init_rgba (color, + gradient->stops[0].color.red, + gradient->stops[0].color.green, + gradient->stops[0].color.blue, + gradient->stops[0].color.alpha); + + return TRUE; +} + +/** + * _cairo_pattern_is_opaque_solid + * + * Convenience function to determine whether a pattern is an opaque + * (alpha==1.0) solid color pattern. This is done by testing whether + * the pattern's alpha value when converted to a byte is 255, so if a + * backend actually supported deep alpha channels this function might + * not do the right thing. + * + * Return value: %TRUE if the pattern is an opaque, solid color. + **/ +cairo_bool_t +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + solid = (cairo_solid_pattern_t *) pattern; + + return CAIRO_COLOR_IS_OPAQUE (&solid->color); +} + +static cairo_bool_t +_surface_is_opaque (const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *r) +{ + if (pattern->surface->content & CAIRO_CONTENT_ALPHA) + return FALSE; + + if (pattern->base.extend != CAIRO_EXTEND_NONE) + return TRUE; + + if (r != NULL) { + cairo_rectangle_int_t extents; + + if (! _cairo_surface_get_extents (pattern->surface, &extents)) + return TRUE; + + if (r->x >= extents.x && + r->y >= extents.y && + r->x + r->width <= extents.x + extents.width && + r->y + r->height <= extents.y + extents.height) + { + return TRUE; + } + } + + return FALSE; +} + +static cairo_bool_t +_surface_is_clear (const cairo_surface_pattern_t *pattern) +{ + cairo_rectangle_int_t extents; + + if (_cairo_surface_get_extents (pattern->surface, &extents) && + (extents.width == 0 || extents.height == 0)) + return TRUE; + + return pattern->surface->is_clear && + pattern->surface->content & CAIRO_CONTENT_ALPHA; +} + +static cairo_bool_t +_gradient_is_opaque (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->n_stops == 0 || + (gradient->base.extend == CAIRO_EXTEND_NONE && + gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) + return FALSE; + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + if (gradient->base.extend == CAIRO_EXTEND_NONE) { + double t[2]; + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + + /* EXTEND_NONE degenerate radial gradients are clear */ + if (_linear_pattern_is_degenerate (linear)) + return FALSE; + + if (extents == NULL) + return FALSE; + + _extents_to_linear_parameter (linear, extents, t); + if (t[0] < 0.0 || t[1] > 1.0) + return FALSE; + } + } + + for (i = 0; i < gradient->n_stops; i++) + if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) + return FALSE; + + return TRUE; +} + +/** + * _cairo_pattern_is_opaque + * + * Convenience function to determine whether a pattern is an opaque + * pattern (of any type). The same caveats that apply to + * _cairo_pattern_is_opaque_solid apply here as well. + * + * Return value: %TRUE if the pattern is a opaque. + **/ +cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, + const cairo_rectangle_int_t *extents) +{ + const cairo_pattern_union_t *pattern; + + if (abstract_pattern->has_component_alpha) + return FALSE; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->base.type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_pattern_is_opaque_solid (abstract_pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _surface_is_opaque (&pattern->surface, extents); + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return _gradient_is_opaque (&pattern->gradient.base, extents); + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +cairo_bool_t +_cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) +{ + const cairo_pattern_union_t *pattern; + + if (abstract_pattern->has_component_alpha) + return FALSE; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color); + case CAIRO_PATTERN_TYPE_SURFACE: + return _surface_is_clear (&pattern->surface); + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return _gradient_is_clear (&pattern->gradient.base, NULL); + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +/** + * _cairo_pattern_analyze_filter: + * @pattern: surface pattern + * @pad_out: location to store necessary padding in the source image, or %NULL + * Returns: the optimized #cairo_filter_t to use with @pattern. + * + * Analyze the filter to determine how much extra needs to be sampled + * from the source image to account for the filter radius and whether + * we can optimize the filter to a simpler value. + * + * XXX: We don't actually have any way of querying the backend for + * the filter radius, so we just guess base on what we know that + * backends do currently (see bug #10508) + */ +cairo_filter_t +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, + double *pad_out) +{ + double pad; + cairo_filter_t optimized_filter; + + switch (pattern->filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + /* If source pixels map 1:1 onto destination pixels, we do + * not need to filter (and do not want to filter, since it + * will cause blurriness) + */ + if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { + pad = 0.; + optimized_filter = CAIRO_FILTER_NEAREST; + } else { + /* 0.5 is enough for a bilinear filter. It's possible we + * should defensively use more for CAIRO_FILTER_BEST, but + * without a single example, it's hard to know how much + * more would be defensive... + */ + pad = 0.5; + optimized_filter = pattern->filter; + } + break; + + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + default: + pad = 0.; + optimized_filter = pattern->filter; + break; + } + + if (pad_out) + *pad_out = pad; + + return optimized_filter; +} + + +static double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned int flags, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) +{ + cairo_surface_t *surface; + cairo_rectangle_int_t extents; + cairo_rectangle_int_t sampled_area; + double x1, y1, x2, y2; + int tx, ty; + double pad; + cairo_bool_t is_identity; + cairo_bool_t is_empty; + cairo_bool_t is_bounded; + cairo_int_status_t status; + + surface = cairo_surface_reference (pattern->surface); + + is_identity = FALSE; + attr->matrix = pattern->base.matrix; + attr->extend = pattern->base.extend; + attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); + attr->has_component_alpha = pattern->base.has_component_alpha; + + attr->x_offset = attr->y_offset = tx = ty = 0; + if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { + cairo_matrix_init_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + is_identity = TRUE; + } else if (attr->filter == CAIRO_FILTER_NEAREST) { + /* + * For NEAREST, we can remove the fractional translation component + * from the transformation - this ensures that the pattern will always + * hit fast-paths in the backends for simple transformations that + * become (almost) identity, without loss of quality. + */ + attr->matrix.x0 = 0; + attr->matrix.y0 = 0; + if (_cairo_matrix_is_pixel_exact (&attr->matrix)) { + /* The rounding here is rather peculiar as it needs to match the + * rounding performed on the sample coordinate used by pixman. + */ + attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0); + attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0); + } else { + attr->matrix.x0 = pattern->base.matrix.x0; + attr->matrix.y0 = pattern->base.matrix.y0; + } + + if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { + cairo_matrix_init_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + is_identity = TRUE; + } + } + + /* XXX: Hack: + * + * The way we currently support CAIRO_EXTEND_REFLECT is to create + * an image twice bigger on each side, and create a pattern of four + * images such that the new image, when repeated, has the same effect + * of reflecting the original pattern. + */ + if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT && + attr->extend == CAIRO_EXTEND_REFLECT) + { + cairo_t *cr; + cairo_surface_t *src; + int w, h; + + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded); + + status = _cairo_surface_clone_similar (dst, surface, + extents.x, extents.y, + extents.width, extents.height, + &extents.x, &extents.y, &src); + if (unlikely (status)) + goto BAIL; + + w = 2 * extents.width; + h = 2 * extents.height; + + if (is_identity) { + attr->x_offset = -x; + x += tx; + while (x <= -w) + x += w; + while (x >= w) + x -= w; + extents.x += x; + tx = x = 0; + + attr->y_offset = -y; + y += ty; + while (y <= -h) + y += h; + while (y >= h) + y -= h; + extents.y += y; + ty = y = 0; + } + + cairo_surface_destroy (surface); + surface = _cairo_surface_create_similar_solid (dst, + dst->content, w, h, + CAIRO_COLOR_TRANSPARENT, + FALSE); + if (surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (surface->status)) { + cairo_surface_destroy (src); + return surface->status; + } + + surface->device_transform = pattern->surface->device_transform; + surface->device_transform_inverse = pattern->surface->device_transform_inverse; + + cr = cairo_create (surface); + + cairo_set_source_surface (cr, src, -extents.x, -extents.y); + cairo_paint (cr); + + cairo_scale (cr, -1, +1); + cairo_set_source_surface (cr, src, extents.x-w, -extents.y); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, -extents.y); + cairo_paint (cr); + + cairo_scale (cr, +1, -1); + cairo_set_source_surface (cr, src, extents.x-w, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x-w, extents.y); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, extents.y); + cairo_paint (cr); + + cairo_scale (cr, -1, +1); + cairo_set_source_surface (cr, src, -extents.x, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, -extents.x, extents.y); + cairo_paint (cr); + + status = cairo_status (cr); + cairo_destroy (cr); + + cairo_surface_destroy (src); + + if (unlikely (status)) + goto BAIL; + + attr->extend = CAIRO_EXTEND_REPEAT; + } + + /* We first transform the rectangle to the coordinate space of the + * source surface so that we only need to clone that portion of the + * surface that will be read. + */ + x1 = x; + y1 = y; + x2 = x + (int) width; + y2 = y + (int) height; + if (! is_identity) { + _cairo_matrix_transform_bounding_box (&attr->matrix, + &x1, &y1, &x2, &y2, + NULL); + } + + sampled_area.x = floor (x1 - pad); + sampled_area.y = floor (y1 - pad); + sampled_area.width = ceil (x2 + pad) - sampled_area.x; + sampled_area.height = ceil (y2 + pad) - sampled_area.y; + + sampled_area.x += tx; + sampled_area.y += ty; + + if ( _cairo_surface_get_extents (surface, &extents)) { + if (attr->extend == CAIRO_EXTEND_NONE) { + /* Never acquire a larger area than the source itself */ + is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); + } else { + int trim = 0; + + if (sampled_area.x >= extents.x && + sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) + { + /* source is horizontally contained within extents, trim */ + extents.x = sampled_area.x; + extents.width = sampled_area.width; + trim |= 0x1; + } + + if (sampled_area.y >= extents.y && + sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) + { + /* source is vertically contained within extents, trim */ + extents.y = sampled_area.y; + extents.height = sampled_area.height; + trim |= 0x2; + } + + if (trim == 0x3) { + /* source is wholly contained within extents, drop the REPEAT */ + attr->extend = CAIRO_EXTEND_NONE; + } + + is_empty = extents.width == 0 || extents.height == 0; + } + } + + /* XXX can we use is_empty? */ + + status = _cairo_surface_clone_similar (dst, surface, + extents.x, extents.y, + extents.width, extents.height, + &x, &y, out); + if (unlikely (status)) + goto BAIL; + + if (x != 0 || y != 0) { + if (is_identity) { + attr->x_offset -= x; + attr->y_offset -= y; + } else { + cairo_matrix_t m; + + x -= attr->x_offset; + y -= attr->y_offset; + attr->x_offset = 0; + attr->y_offset = 0; + + cairo_matrix_init_translate (&m, -x, -y); + cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); + } + } + + /* reduce likelihood of range overflow with large downscaling */ + if (! is_identity) { + cairo_matrix_t m; + cairo_status_t invert_status; + + m = attr->matrix; + invert_status = cairo_matrix_invert (&m); + assert (invert_status == CAIRO_STATUS_SUCCESS); + + if (m.x0 != 0. || m.y0 != 0.) { + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + x = floor (m.x0 / 2); + y = floor (m.y0 / 2); + attr->x_offset -= x; + attr->y_offset -= y; + cairo_matrix_init_translate (&m, x, y); + cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix); + } + } + + BAIL: + cairo_surface_destroy (surface); + return status; +} + +/** + * _cairo_pattern_acquire_surface: + * @pattern: a #cairo_pattern_t + * @dst: destination surface + * @x: X coordinate in source corresponding to left side of destination area + * @y: Y coordinate in source corresponding to top side of destination area + * @width: width of destination area + * @height: height of destination area + * @surface_out: location to store a pointer to a surface + * @attributes: surface attributes that destination backend should apply to + * the returned surface + * + * A convenience function to obtain a surface to use as the source for + * drawing on @dst. + * + * Note that this function is only suitable for use when the destination + * surface is pixel based and 1 device unit maps to one pixel. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. + **/ +cairo_int_status_t +_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned int flags, + cairo_surface_t **surface_out, + cairo_surface_attributes_t *attributes) +{ + if (unlikely (pattern->status)) { + *surface_out = NULL; + return pattern->status; + } + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern, + dst, x, y, width, height, + surface_out, + attributes); + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern, + dst, x, y, width, height, + surface_out, + attributes); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern, + dst, x, y, width, height, + flags, + surface_out, + attributes); + + default: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + } +} + +/** + * _cairo_pattern_release_surface: + * @pattern: a #cairo_pattern_t + * @surface: a surface obtained by _cairo_pattern_acquire_surface + * @attributes: attributes obtained by _cairo_pattern_acquire_surface + * + * Releases resources obtained by _cairo_pattern_acquire_surface. + **/ +void +_cairo_pattern_release_surface (const cairo_pattern_t *pattern, + cairo_surface_t *surface, + cairo_surface_attributes_t *attributes) +{ + cairo_surface_destroy (surface); +} + +cairo_int_status_t +_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + unsigned int flags, + cairo_surface_t **src_out, + cairo_surface_t **mask_out, + cairo_surface_attributes_t *src_attributes, + cairo_surface_attributes_t *mask_attributes) +{ + cairo_int_status_t status; + cairo_pattern_union_t src_tmp; + + if (unlikely (src->status)) + return src->status; + if (unlikely (mask != NULL && mask->status)) + return mask->status; + + /* If src and mask are both solid, then the mask alpha can be + * combined into src and mask can be ignored. */ + + if (src->type == CAIRO_PATTERN_TYPE_SOLID && + mask && + ! mask->has_component_alpha && + mask->type == CAIRO_PATTERN_TYPE_SOLID) + { + cairo_color_t combined; + cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; + cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; + + combined = src_solid->color; + _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); + + _cairo_pattern_init_solid (&src_tmp.solid, &combined); + + src = &src_tmp.base; + mask = NULL; + } + + status = _cairo_pattern_acquire_surface (src, dst, + src_x, src_y, + width, height, + flags, + src_out, src_attributes); + if (unlikely (status)) + goto BAIL; + + if (mask == NULL) { + *mask_out = NULL; + goto BAIL; + } + + status = _cairo_pattern_acquire_surface (mask, dst, + mask_x, mask_y, + width, height, + flags, + mask_out, mask_attributes); + if (unlikely (status)) + _cairo_pattern_release_surface (src, *src_out, src_attributes); + + BAIL: + if (src == &src_tmp.base) + _cairo_pattern_fini (&src_tmp.base); + + return status; +} + +/** + * _cairo_pattern_get_extents: + * + * Return the "target-space" extents of @pattern in @extents. + * + * For unbounded patterns, the @extents will be initialized with + * "infinite" extents, (minimum and maximum fixed-point values). + * + * XXX: Currently, bounded gradient patterns will also return + * "infinite" extents, though it would be possible to optimize these + * with a little more work. + **/ +void +_cairo_pattern_get_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) +{ + double x1, y1, x2, y2; + cairo_status_t status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + goto UNBOUNDED; + + case CAIRO_PATTERN_TYPE_SURFACE: + { + cairo_rectangle_int_t surface_extents; + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + double pad; + + if (! _cairo_surface_get_extents (surface, &surface_extents)) + goto UNBOUNDED; + + if (surface_extents.width == 0 || surface_extents.height == 0) + goto EMPTY; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + /* The filter can effectively enlarge the extents of the + * pattern, so extend as necessary. + */ + _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); + x1 = surface_extents.x - pad; + y1 = surface_extents.y - pad; + x2 = surface_extents.x + (int) surface_extents.width + pad; + y2 = surface_extents.y + (int) surface_extents.height + pad; + } + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + { + const cairo_radial_pattern_t *radial = + (const cairo_radial_pattern_t *) pattern; + double cx1, cy1; + double cx2, cy2; + double r, D; + + if (radial->r1 == 0 && radial->r2 == 0) + goto EMPTY; + + cx1 = _cairo_fixed_to_double (radial->c1.x); + cy1 = _cairo_fixed_to_double (radial->c1.y); + r = _cairo_fixed_to_double (radial->r1); + x1 = cx1 - r; x2 = cx1 + r; + y1 = cy1 - r; y2 = cy1 + r; + + cx2 = _cairo_fixed_to_double (radial->c2.x); + cy2 = _cairo_fixed_to_double (radial->c2.y); + r = fabs (_cairo_fixed_to_double (radial->r2)); + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + /* We need to be careful, as if the circles are not + * self-contained, then the solution is actually unbounded. + */ + D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2); + if (D > r*r - 1e-5) + goto UNBOUNDED; + + if (cx2 - r < x1) + x1 = cx2 - r; + if (cx2 + r > x2) + x2 = cx2 + r; + + if (cy2 - r < y1) + y1 = cy2 - r; + if (cy2 + r > y2) + y2 = cy2 + r; + } + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + { + const cairo_linear_pattern_t *linear = + (const cairo_linear_pattern_t *) pattern; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y) + goto EMPTY; + + if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) + goto UNBOUNDED; + + if (linear->p1.x == linear->p2.x) { + x1 = -HUGE_VAL; + x2 = HUGE_VAL; + y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y)); + y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y)); + } else if (linear->p1.y == linear->p2.y) { + x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x)); + x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x)); + y1 = -HUGE_VAL; + y2 = HUGE_VAL; + } else { + goto UNBOUNDED; + } + } + break; + + default: + ASSERT_NOT_REACHED; + } + + if (_cairo_matrix_is_translation (&pattern->matrix)) { + x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0; + y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0; + } else { + cairo_matrix_t imatrix; + + imatrix = pattern->matrix; + status = cairo_matrix_invert (&imatrix); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_matrix_transform_bounding_box (&imatrix, + &x1, &y1, &x2, &y2, + NULL); + } + + x1 = floor (x1); + if (x1 < CAIRO_RECT_INT_MIN) + x1 = CAIRO_RECT_INT_MIN; + y1 = floor (y1); + if (y1 < CAIRO_RECT_INT_MIN) + y1 = CAIRO_RECT_INT_MIN; + + x2 = ceil (x2); + if (x2 > CAIRO_RECT_INT_MAX) + x2 = CAIRO_RECT_INT_MAX; + y2 = ceil (y2); + if (y2 > CAIRO_RECT_INT_MAX) + y2 = CAIRO_RECT_INT_MAX; + + extents->x = x1; extents->width = x2 - x1; + extents->y = y1; extents->height = y2 - y1; + return; + + UNBOUNDED: + /* unbounded patterns -> 'infinite' extents */ + _cairo_unbounded_rectangle_init (extents); + return; + + EMPTY: + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; +} + + +static unsigned long +_cairo_solid_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); + + return hash; +} + +static unsigned long +_cairo_gradient_color_stops_hash (unsigned long hash, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int n; + + hash = _cairo_hash_bytes (hash, + &gradient->n_stops, + sizeof (gradient->n_stops)); + + for (n = 0; n < gradient->n_stops; n++) { + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].offset, + sizeof (double)); + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].color, + sizeof (cairo_color_t)); + } + + return hash; +} + +unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_linear_pattern_t *linear) +{ + hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); + hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); + + return _cairo_gradient_color_stops_hash (hash, &linear->base); +} + +unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial) +{ + hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); + hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); + hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); + hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2)); + + return _cairo_gradient_color_stops_hash (hash, &radial->base); +} + +static unsigned long +_cairo_surface_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + + hash ^= surface->surface->unique_id; + + return hash; +} + +unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + if (pattern->status) + return 0; + + hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + hash = _cairo_hash_bytes (hash, + &pattern->matrix, sizeof (pattern->matrix)); + hash = _cairo_hash_bytes (hash, + &pattern->filter, sizeof (pattern->filter)); + hash = _cairo_hash_bytes (hash, + &pattern->extend, sizeof (pattern->extend)); + hash = _cairo_hash_bytes (hash, + &pattern->has_component_alpha, + sizeof (pattern->has_component_alpha)); + } + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_hash (hash, pattern); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static unsigned long +_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + + return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t)); +} + +unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern) +{ + if (pattern->status) + return 0; + + /* XXX */ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return sizeof (cairo_solid_pattern_t); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + return sizeof (cairo_surface_pattern_t); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + return sizeof (cairo_linear_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + return sizeof (cairo_radial_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + default: + ASSERT_NOT_REACHED; + return 0; + } +} + + +static cairo_bool_t +_cairo_solid_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A; + const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B; + + return _cairo_color_equal (&a->color, &b->color); +} + +static cairo_bool_t +_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, + const cairo_gradient_pattern_t *b) +{ + unsigned int n; + + if (a->n_stops != b->n_stops) + return FALSE; + + for (n = 0; n < a->n_stops; n++) { + if (a->stops[n].offset != b->stops[n].offset) + return FALSE; + if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) + return FALSE; + } + + return TRUE; +} + +cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b) +{ + if (a->p1.x != b->p1.x) + return FALSE; + + if (a->p1.y != b->p1.y) + return FALSE; + + if (a->p2.x != b->p2.x) + return FALSE; + + if (a->p2.y != b->p2.y) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b) +{ + if (a->c1.x != b->c1.x) + return FALSE; + + if (a->c1.y != b->c1.y) + return FALSE; + + if (a->r1 != b->r1) + return FALSE; + + if (a->c2.x != b->c2.x) + return FALSE; + + if (a->c2.y != b->c2.y) + return FALSE; + + if (a->r2 != b->r2) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_surface_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A; + const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B; + + return a->surface->unique_id == b->surface->unique_id; +} + +cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) +{ + if (a->status || b->status) + return FALSE; + + if (a == b) + return TRUE; + + if (a->type != b->type) + return FALSE; + + if (a->has_component_alpha != b->has_component_alpha) + return FALSE; + + if (a->type != CAIRO_PATTERN_TYPE_SOLID) { + if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + return FALSE; + + if (a->filter != b->filter) + return FALSE; + + if (a->extend != b->extend) + return FALSE; + } + + switch (a->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, + (cairo_linear_pattern_t *) b); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, + (cairo_radial_pattern_t *) b); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_equal (a, b); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +/** + * cairo_pattern_get_rgba + * @pattern: a #cairo_pattern_t + * @red: return value for red component of color, or %NULL + * @green: return value for green component of color, or %NULL + * @blue: return value for blue component of color, or %NULL + * @alpha: return value for alpha component of color, or %NULL + * + * Gets the solid color for a solid color pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid + * color pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_rgba (cairo_pattern_t *pattern, + double *red, double *green, + double *blue, double *alpha) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; + double r0, g0, b0, a0; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0); + + if (red) + *red = r0; + if (green) + *green = g0; + if (blue) + *blue = b0; + if (alpha) + *alpha = a0; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_surface + * @pattern: a #cairo_pattern_t + * @surface: return value for surface of pattern, or %NULL + * + * Gets the surface of a surface pattern. The reference returned in + * @surface is owned by the pattern; the caller should call + * cairo_surface_reference() if the surface is to be retained. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface + * pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_surface (cairo_pattern_t *pattern, + cairo_surface_t **surface) +{ + cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_STATUS_PATTERN_TYPE_MISMATCH; + + if (surface) + *surface = spat->surface; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_color_stop_rgba + * @pattern: a #cairo_pattern_t + * @index: index of the stop to return data for + * @offset: return value for the offset of the stop, or %NULL + * @red: return value for red component of color, or %NULL + * @green: return value for green component of color, or %NULL + * @blue: return value for blue component of color, or %NULL + * @alpha: return value for alpha component of color, or %NULL + * + * Gets the color and offset information at the given @index for a + * gradient pattern. Values of @index are 0 to 1 less than the number + * returned by cairo_pattern_get_color_stop_count(). + * + * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX + * if @index is not valid for the given pattern. If the pattern is + * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is + * returned. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, + int index, double *offset, + double *red, double *green, + double *blue, double *alpha) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (index < 0 || (unsigned int) index >= gradient->n_stops) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + if (offset) + *offset = gradient->stops[index].offset; + if (red) + *red = gradient->stops[index].color.red; + if (green) + *green = gradient->stops[index].color.green; + if (blue) + *blue = gradient->stops[index].color.blue; + if (alpha) + *alpha = gradient->stops[index].color.alpha; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_color_stop_count + * @pattern: a #cairo_pattern_t + * @count: return value for the number of color stops, or %NULL + * + * Gets the number of color stops specified in the given gradient + * pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient + * pattern. + * + * Since: 1.4 + */ +cairo_status_t +cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, + int *count) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (count) + *count = gradient->n_stops; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_linear_points + * @pattern: a #cairo_pattern_t + * @x0: return value for the x coordinate of the first point, or %NULL + * @y0: return value for the y coordinate of the first point, or %NULL + * @x1: return value for the x coordinate of the second point, or %NULL + * @y1: return value for the y coordinate of the second point, or %NULL + * + * Gets the gradient endpoints for a linear gradient. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear + * gradient pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_linear_points (cairo_pattern_t *pattern, + double *x0, double *y0, + double *x1, double *y1) +{ + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (x0) + *x0 = _cairo_fixed_to_double (linear->p1.x); + if (y0) + *y0 = _cairo_fixed_to_double (linear->p1.y); + if (x1) + *x1 = _cairo_fixed_to_double (linear->p2.x); + if (y1) + *y1 = _cairo_fixed_to_double (linear->p2.y); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_radial_circles + * @pattern: a #cairo_pattern_t + * @x0: return value for the x coordinate of the center of the first circle, or %NULL + * @y0: return value for the y coordinate of the center of the first circle, or %NULL + * @r0: return value for the radius of the first circle, or %NULL + * @x1: return value for the x coordinate of the center of the second circle, or %NULL + * @y1: return value for the y coordinate of the center of the second circle, or %NULL + * @r1: return value for the radius of the second circle, or %NULL + * + * Gets the gradient endpoint circles for a radial gradient, each + * specified as a center coordinate and a radius. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial + * gradient pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, + double *x0, double *y0, double *r0, + double *x1, double *y1, double *r1) +{ + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (x0) + *x0 = _cairo_fixed_to_double (radial->c1.x); + if (y0) + *y0 = _cairo_fixed_to_double (radial->c1.y); + if (r0) + *r0 = _cairo_fixed_to_double (radial->r1); + if (x1) + *x1 = _cairo_fixed_to_double (radial->c2.x); + if (y1) + *y1 = _cairo_fixed_to_double (radial->c2.y); + if (r1) + *r1 = _cairo_fixed_to_double (radial->r2); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_reset_static_data (void) +{ +#if HAS_FREED_POOL + int i; + + for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) + _freed_pool_reset (&freed_pattern_pool[i]); +#endif + + _cairo_pattern_reset_solid_surface_cache (); +} diff --git a/libs/cairo/src/cairo-pdf-operators-private.h b/libs/cairo/src/cairo-pdf-operators-private.h new file mode 100644 index 000000000..d0051433b --- /dev/null +++ b/libs/cairo/src/cairo-pdf-operators-private.h @@ -0,0 +1,134 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PDF_OPERATORS_H +#define CAIRO_PDF_OPERATORS_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +/* The glyph buffer size is based on the expected maximum glyphs in a + * line so that an entire line can be emitted in as one string. If the + * glyphs in a line exceeds this size the only downside is the slight + * overhead of emitting two strings. + */ +#define PDF_GLYPH_BUFFER_SIZE 200 + +typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id, + unsigned int subset_id, + void *closure); + +typedef struct _cairo_pdf_glyph { + unsigned int glyph_index; + double x_position; + double x_advance; +} cairo_pdf_glyph_t; + +typedef struct _cairo_pdf_operators { + cairo_output_stream_t *stream; + cairo_matrix_t cairo_to_pdf; + cairo_scaled_font_subsets_t *font_subsets; + cairo_pdf_operators_use_font_subset_t use_font_subset; + void *use_font_subset_closure; + cairo_bool_t use_actual_text; + cairo_bool_t in_text_object; /* inside BT/ET pair */ + + /* PDF text state */ + cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */ + unsigned int font_id; + unsigned int subset_id; + cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */ + cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */ + cairo_matrix_t font_matrix_inverse; + double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */ + double cur_y; + int hex_width; + int num_glyphs; + double glyph_buf_x_pos; + cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE]; + + /* PDF line style */ + cairo_bool_t has_line_style; + double line_width; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + double miter_limit; + cairo_bool_t has_dashes; +} cairo_pdf_operators_t; + +cairo_private void +_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream, + cairo_matrix_t *cairo_to_pdf, + cairo_scaled_font_subsets_t *font_subsets); + +cairo_private cairo_status_t +_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators); + +cairo_private void +_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure); + +cairo_private void +_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream); + + +cairo_private void +_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *cairo_to_pdf); + +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable); + +cairo_private cairo_status_t +_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators); + +cairo_private void +_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, + const cairo_stroke_style_t *style, + double scale); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font); + +#endif /* CAIRO_PDF_OPERATORS_H */ diff --git a/libs/cairo/src/cairo-pdf-operators.c b/libs/cairo/src/cairo-pdf-operators.c new file mode 100644 index 000000000..4da9d573c --- /dev/null +++ b/libs/cairo/src/cairo-pdf-operators.c @@ -0,0 +1,1433 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#if CAIRO_HAS_PDF_OPERATORS + +#include "cairo-error-private.h" +#include "cairo-pdf-operators-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-scaled-font-subsets-private.h" + +static cairo_status_t +_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); + + +void +_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream, + cairo_matrix_t *cairo_to_pdf, + cairo_scaled_font_subsets_t *font_subsets) +{ + pdf_operators->stream = stream; + pdf_operators->cairo_to_pdf = *cairo_to_pdf; + pdf_operators->font_subsets = font_subsets; + pdf_operators->use_font_subset = NULL; + pdf_operators->use_font_subset_closure = NULL; + pdf_operators->in_text_object = FALSE; + pdf_operators->num_glyphs = 0; + pdf_operators->has_line_style = FALSE; + pdf_operators->use_actual_text = FALSE; +} + +cairo_status_t +_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators) +{ + return _cairo_pdf_operators_flush (pdf_operators); +} + +void +_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure) +{ + pdf_operators->use_font_subset = use_font_subset; + pdf_operators->use_font_subset_closure = closure; +} + +/* Change the output stream to a different stream. + * _cairo_pdf_operators_flush() should always be called before calling + * this function. + */ +void +_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + pdf_operators->stream = stream; + pdf_operators->has_line_style = FALSE; +} + +void +_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *cairo_to_pdf) +{ + pdf_operators->cairo_to_pdf = *cairo_to_pdf; + pdf_operators->has_line_style = FALSE; +} + +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable) +{ + pdf_operators->use_actual_text = enable; +} + +/* Finish writing out any pending commands to the stream. This + * function must be called by the surface before emitting anything + * into the PDF stream. + * + * pdf_operators may leave the emitted PDF for some operations + * unfinished in case subsequent operations can be merged. This + * function will finish off any incomplete operation so the stream + * will be in a state where the surface may emit its own PDF + * operations (eg changing patterns). + * + */ +cairo_status_t +_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (pdf_operators->in_text_object) + status = _cairo_pdf_operators_end_text (pdf_operators); + + return status; +} + +/* Reset the known graphics state of the PDF consumer. ie no + * assumptions will be made about the state. The next time a + * particular graphics state is required (eg line width) the state + * operator is always emitted and then remembered for subsequent + * operatations. + * + * This should be called when starting a new stream or after emitting + * the 'Q' operator (where pdf-operators functions were called inside + * the q/Q pair). + */ +void +_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) +{ + pdf_operators->has_line_style = FALSE; +} + +/* A word wrap stream can be used as a filter to do word wrapping on + * top of an existing output stream. The word wrapping is quite + * simple, using isspace to determine characters that separate + * words. Any word that will cause the column count exceed the given + * max_column will have a '\n' character emitted before it. + * + * The stream is careful to maintain integrity for words that cross + * the boundary from one call to write to the next. + * + * Note: This stream does not guarantee that the output will never + * exceed max_column. In particular, if a single word is larger than + * max_column it will not be broken up. + */ +typedef struct _word_wrap_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + int max_column; + int column; + cairo_bool_t last_write_was_space; + cairo_bool_t in_hexstring; + cairo_bool_t empty_hexstring; +} word_wrap_stream_t; + +static int +_count_word_up_to (const unsigned char *s, int length) +{ + int word = 0; + + while (length--) { + if (! (_cairo_isspace (*s) || *s == '<')) { + s++; + word++; + } else { + return word; + } + } + + return word; +} + + +/* Count up to either the end of the ASCII hexstring or the number + * of columns remaining. + */ +static int +_count_hexstring_up_to (const unsigned char *s, int length, int columns) +{ + int word = 0; + + while (length--) { + if (*s++ != '>') + word++; + else + return word; + + columns--; + if (columns < 0 && word > 1) + return word; + } + + return word; +} + +static cairo_status_t +_word_wrap_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; + cairo_bool_t newline; + int word; + + while (length) { + if (*data == '<') { + stream->in_hexstring = TRUE; + stream->empty_hexstring = TRUE; + stream->last_write_was_space = FALSE; + data++; + length--; + _cairo_output_stream_printf (stream->output, "<"); + stream->column++; + } else if (*data == '>') { + stream->in_hexstring = FALSE; + stream->last_write_was_space = FALSE; + data++; + length--; + _cairo_output_stream_printf (stream->output, ">"); + stream->column++; + } else if (_cairo_isspace (*data)) { + newline = (*data == '\n' || *data == '\r'); + if (! newline && stream->column >= stream->max_column) { + _cairo_output_stream_printf (stream->output, "\n"); + stream->column = 0; + } + _cairo_output_stream_write (stream->output, data, 1); + data++; + length--; + if (newline) { + stream->column = 0; + } + else + stream->column++; + stream->last_write_was_space = TRUE; + } else { + if (stream->in_hexstring) { + word = _count_hexstring_up_to (data, length, + MAX (stream->max_column - stream->column, 0)); + } else { + word = _count_word_up_to (data, length); + } + /* Don't wrap if this word is a continuation of a non hex + * string word from a previous call to write. */ + if (stream->column + word >= stream->max_column) { + if (stream->last_write_was_space || + (stream->in_hexstring && !stream->empty_hexstring)) + { + _cairo_output_stream_printf (stream->output, "\n"); + stream->column = 0; + } + } + _cairo_output_stream_write (stream->output, data, word); + data += word; + length -= word; + stream->column += word; + stream->last_write_was_space = FALSE; + if (stream->in_hexstring) + stream->empty_hexstring = FALSE; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_word_wrap_stream_close (cairo_output_stream_t *base) +{ + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_output_stream_t * +_word_wrap_stream_create (cairo_output_stream_t *output, int max_column) +{ + word_wrap_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + stream = malloc (sizeof (word_wrap_stream_t)); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + _word_wrap_stream_write, + NULL, + _word_wrap_stream_close); + stream->output = output; + stream->max_column = max_column; + stream->column = 0; + stream->last_write_was_space = FALSE; + stream->in_hexstring = FALSE; + stream->empty_hexstring = TRUE; + + return &stream->base; +} + +typedef struct _pdf_path_info { + cairo_output_stream_t *output; + cairo_matrix_t *path_transform; + cairo_line_cap_t line_cap; + cairo_point_t last_move_to_point; + cairo_bool_t has_sub_path; +} pdf_path_info_t; + +static cairo_status_t +_cairo_pdf_path_move_to (void *closure, + const cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + info->last_move_to_point = *point; + info->has_sub_path = FALSE; + cairo_matrix_transform_point (info->path_transform, &x, &y); + _cairo_output_stream_printf (info->output, + "%g %g m ", x, y); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_line_to (void *closure, + const cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->line_cap != CAIRO_LINE_CAP_ROUND && + ! info->has_sub_path && + point->x == info->last_move_to_point.x && + point->y == info->last_move_to_point.y) + { + return CAIRO_STATUS_SUCCESS; + } + + info->has_sub_path = TRUE; + cairo_matrix_transform_point (info->path_transform, &x, &y); + _cairo_output_stream_printf (info->output, + "%g %g l ", x, y); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + pdf_path_info_t *info = closure; + double bx = _cairo_fixed_to_double (b->x); + double by = _cairo_fixed_to_double (b->y); + double cx = _cairo_fixed_to_double (c->x); + double cy = _cairo_fixed_to_double (c->y); + double dx = _cairo_fixed_to_double (d->x); + double dy = _cairo_fixed_to_double (d->y); + + info->has_sub_path = TRUE; + cairo_matrix_transform_point (info->path_transform, &bx, &by); + cairo_matrix_transform_point (info->path_transform, &cx, &cy); + cairo_matrix_transform_point (info->path_transform, &dx, &dy); + _cairo_output_stream_printf (info->output, + "%g %g %g %g %g %g c ", + bx, by, cx, cy, dx, dy); + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_close_path (void *closure) +{ + pdf_path_info_t *info = closure; + + if (info->line_cap != CAIRO_LINE_CAP_ROUND && + ! info->has_sub_path) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (info->output, + "h\n"); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) +{ + double x1 = _cairo_fixed_to_double (box->p1.x); + double y1 = _cairo_fixed_to_double (box->p1.y); + double x2 = _cairo_fixed_to_double (box->p2.x); + double y2 = _cairo_fixed_to_double (box->p2.y); + + cairo_matrix_transform_point (info->path_transform, &x1, &y1); + cairo_matrix_transform_point (info->path_transform, &x2, &y2); + _cairo_output_stream_printf (info->output, + "%g %g %g %g re ", + x1, y1, x2 - x1, y2 - y1); + + return _cairo_output_stream_get_status (info->output); +} + +/* The line cap value is needed to workaround the fact that PostScript + * and PDF semantics for stroking degenerate sub-paths do not match + * cairo semantics. (PostScript draws something for any line cap + * value, while cairo draws something only for round caps). + * + * When using this function to emit a path to be filled, rather than + * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that + * the stroke workaround will not modify the path being emitted. + */ +static cairo_status_t +_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_matrix_t *path_transform, + cairo_line_cap_t line_cap) +{ + cairo_output_stream_t *word_wrap; + cairo_status_t status, status2; + pdf_path_info_t info; + cairo_box_t box; + + word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); + status = _cairo_output_stream_get_status (word_wrap); + if (unlikely (status)) + return _cairo_output_stream_destroy (word_wrap); + + info.output = word_wrap; + info.path_transform = path_transform; + info.line_cap = line_cap; + if (_cairo_path_fixed_is_rectangle (path, &box)) { + status = _cairo_pdf_path_rectangle (&info, &box); + } else { + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_pdf_path_move_to, + _cairo_pdf_path_line_to, + _cairo_pdf_path_curve_to, + _cairo_pdf_path_close_path, + &info); + } + + status2 = _cairo_output_stream_destroy (word_wrap); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +cairo_int_status_t +_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule) +{ + const char *pdf_operator; + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + if (! path->has_current_point) { + /* construct an empty path */ + _cairo_output_stream_printf (pdf_operators->stream, "0 0 m "); + } else { + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &pdf_operators->cairo_to_pdf, + CAIRO_LINE_CAP_ROUND); + if (unlikely (status)) + return status; + } + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "W"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "W*"; + break; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "%s n\n", + pdf_operator); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static int +_cairo_pdf_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return 0; + case CAIRO_LINE_CAP_ROUND: + return 1; + case CAIRO_LINE_CAP_SQUARE: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_pdf_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return 0; + case CAIRO_LINE_JOIN_ROUND: + return 1; + case CAIRO_LINE_JOIN_BEVEL: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +cairo_int_status_t +_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, + const cairo_stroke_style_t *style, + double scale) +{ + double *dash = style->dash; + int num_dashes = style->num_dashes; + double dash_offset = style->dash_offset; + double line_width = style->line_width * scale; + + /* PostScript has "special needs" when it comes to zero-length + * dash segments with butt caps. It apparently (at least + * according to ghostscript) draws hairlines for this + * case. That's not what the cairo semantics want, so we first + * touch up the array to eliminate any 0.0 values that will + * result in "on" segments. + */ + if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { + int i; + + /* If there's an odd number of dash values they will each get + * interpreted as both on and off. So we first explicitly + * expand the array to remove the duplicate usage so that we + * can modify some of the values. + */ + if (num_dashes % 2) { + dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); + if (unlikely (dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (dash, style->dash, num_dashes * sizeof (double)); + memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double)); + + num_dashes *= 2; + } + + for (i = 0; i < num_dashes; i += 2) { + if (dash[i] == 0.0) { + /* Do not modify the dashes in-place, as we may need to also + * replay this stroke to an image fallback. + */ + if (dash == style->dash) { + dash = _cairo_malloc_ab (num_dashes, sizeof (double)); + if (unlikely (dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + memcpy (dash, style->dash, num_dashes * sizeof (double)); + } + + /* If we're at the front of the list, we first rotate + * two elements from the end of the list to the front + * of the list before folding away the 0.0. Or, if + * there are only two dash elements, then there is + * nothing at all to draw. + */ + if (i == 0) { + double last_two[2]; + + if (num_dashes == 2) { + free (dash); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + /* The cases of num_dashes == 0, 1, or 3 elements + * cannot exist, so the rotation of 2 elements + * will always be safe */ + memcpy (last_two, dash + num_dashes - 2, sizeof (last_two)); + memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double)); + memcpy (dash, last_two, sizeof (last_two)); + dash_offset += dash[0] + dash[1]; + i = 2; + } + dash[i-1] += dash[i+1]; + num_dashes -= 2; + memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); + /* If we might have just rotated, it's possible that + * we rotated a 0.0 value to the front of the list. + * Set i to -2 so it will get incremented to 0. */ + if (i == 2) + i = -2; + } + } + } + + if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) { + _cairo_output_stream_printf (pdf_operators->stream, + "%f w\n", + line_width); + pdf_operators->line_width = line_width; + } + + if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) { + _cairo_output_stream_printf (pdf_operators->stream, + "%d J\n", + _cairo_pdf_line_cap (style->line_cap)); + pdf_operators->line_cap = style->line_cap; + } + + if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) { + _cairo_output_stream_printf (pdf_operators->stream, + "%d j\n", + _cairo_pdf_line_join (style->line_join)); + pdf_operators->line_join = style->line_join; + } + + if (num_dashes) { + int d; + + _cairo_output_stream_printf (pdf_operators->stream, "["); + for (d = 0; d < num_dashes; d++) + _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale); + _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n", + dash_offset * scale); + pdf_operators->has_dashes = TRUE; + } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) { + _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n"); + pdf_operators->has_dashes = FALSE; + } + if (dash != style->dash) + free (dash); + + if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) { + _cairo_output_stream_printf (pdf_operators->stream, + "%f M ", + style->miter_limit < 1.0 ? 1.0 : style->miter_limit); + pdf_operators->miter_limit = style->miter_limit; + } + pdf_operators->has_line_style = TRUE; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Scale the matrix so the largest absolute value of the non + * translation components is 1.0. Return the scale required to restore + * the matrix to the original values. + * + * eg the matrix [ 100 0 0 50 20 10 ] + * + * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] + * and the scale returned is 100 + */ +static void +_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) +{ + double s; + + s = fabs (m->xx); + if (fabs (m->xy) > s) + s = fabs (m->xy); + if (fabs (m->yx) > s) + s = fabs (m->yx); + if (fabs (m->yy) > s) + s = fabs (m->yy); + *scale = s; + s = 1.0/s; + cairo_matrix_scale (m, s, s); +} + +static cairo_int_status_t +_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const char *pdf_operator) +{ + cairo_status_t status; + cairo_matrix_t m, path_transform; + cairo_bool_t has_ctm = TRUE; + double scale = 1.0; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + /* Optimize away the stroke ctm when it does not affect the + * stroke. There are other ctm cases that could be optimized + * however this is the most common. + */ + if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 && + fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0) + { + has_ctm = FALSE; + } + + /* The PDF CTM is transformed to the user space CTM when stroking + * so the correct pen shape will be used. This also requires that + * the path be transformed to user space when emitted. The + * conversion of path coordinates to user space may cause rounding + * errors. For example the device space point (1.234, 3.142) when + * transformed to a user space CTM of [100 0 0 100 0 0] will be + * emitted as (0.012, 0.031). + * + * To avoid the rounding problem we scale the user space CTM + * matrix so that all the non translation components of the matrix + * are <= 1. The line width and and dashes are scaled by the + * inverse of the scale applied to the CTM. This maintains the + * shape of the stroke pen while keeping the user space CTM within + * the range that maximizes the precision of the emitted path. + */ + if (has_ctm) { + m = *ctm; + /* Zero out the translation since it does not affect the pen + * shape however it may cause unnecessary digits to be emitted. + */ + m.x0 = 0.0; + m.y0 = 0.0; + _cairo_matrix_factor_out_scale (&m, &scale); + path_transform = m; + status = cairo_matrix_invert (&path_transform); + if (unlikely (status)) + return status; + + cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); + } + + status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + if (has_ctm) { + _cairo_output_stream_printf (pdf_operators->stream, + "q %f %f %f %f %f %f cm\n", + m.xx, m.yx, m.xy, m.yy, + m.x0, m.y0); + } else { + path_transform = pdf_operators->cairo_to_pdf; + } + + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &path_transform, + style->line_cap); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); + if (has_ctm) + _cairo_output_stream_printf (pdf_operators->stream, " Q"); + + _cairo_output_stream_printf (pdf_operators->stream, "\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse) +{ + return _cairo_pdf_operators_emit_stroke (pdf_operators, + path, + style, + ctm, + ctm_inverse, + "S"); +} + +cairo_int_status_t +_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule) +{ + const char *pdf_operator; + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &pdf_operators->cairo_to_pdf, + CAIRO_LINE_CAP_ROUND); + if (unlikely (status)) + return status; + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "f"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "f*"; + break; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "%s\n", + pdf_operator); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse) +{ + const char *operator; + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + operator = "B"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + operator = "B*"; + break; + } + + return _cairo_pdf_operators_emit_stroke (pdf_operators, + path, + style, + ctm, + ctm_inverse, + operator); +} + +#define GLYPH_POSITION_TOLERANCE 0.001 + +/* Emit the string of glyphs using the 'Tj' operator. This requires + * that the glyphs are positioned at their natural glyph advances. */ +static cairo_status_t +_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + int i; + + _cairo_output_stream_printf (stream, "<"); + for (i = 0; i < pdf_operators->num_glyphs; i++) { + _cairo_output_stream_printf (stream, + "%0*x", + pdf_operators->hex_width, + pdf_operators->glyphs[i].glyph_index); + pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; + } + _cairo_output_stream_printf (stream, ">Tj\n"); + + return _cairo_output_stream_get_status (stream); +} + +/* Emit the string of glyphs using the 'TJ' operator. + * + * The TJ operator takes an array of strings of glyphs. Each string of + * glyphs is displayed using the glyph advances of each glyph to + * position the glyphs. A relative adjustment to the glyph advance may + * be specified by including the adjustment between two strings. The + * adjustment is in units of text space * -1000. + */ +static cairo_status_t +_cairo_pdf_operators_emit_glyph_string_with_positioning ( + cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + int i; + + _cairo_output_stream_printf (stream, "[<"); + for (i = 0; i < pdf_operators->num_glyphs; i++) { + if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) + { + double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; + int rounded_delta; + + delta = -1000.0*delta; + /* As the delta is in 1/1000 of a unit of text space, + * rounding to an integer should still provide sufficient + * precision. We round the delta before adding to Tm_x so + * that we keep track of the accumulated rounding error in + * the PDF interpreter and compensate for it when + * calculating subsequent deltas. + */ + rounded_delta = _cairo_lround (delta); + if (rounded_delta != 0) { + _cairo_output_stream_printf (stream, + ">%d<", + rounded_delta); + } + + /* Convert the rounded delta back to text + * space before adding to the current text + * position. */ + delta = rounded_delta/-1000.0; + pdf_operators->cur_x += delta; + } + + _cairo_output_stream_printf (stream, + "%0*x", + pdf_operators->hex_width, + pdf_operators->glyphs[i].glyph_index); + pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; + } + _cairo_output_stream_printf (stream, ">]TJ\n"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_status_t +_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) +{ + cairo_output_stream_t *word_wrap_stream; + cairo_status_t status, status2; + int i; + double x; + + if (pdf_operators->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); + status = _cairo_output_stream_get_status (word_wrap_stream); + if (unlikely (status)) + return _cairo_output_stream_destroy (word_wrap_stream); + + /* Check if glyph advance used to position every glyph */ + x = pdf_operators->cur_x; + for (i = 0; i < pdf_operators->num_glyphs; i++) { + if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) + break; + x += pdf_operators->glyphs[i].x_advance; + } + if (i == pdf_operators->num_glyphs) { + status = _cairo_pdf_operators_emit_glyph_string (pdf_operators, + word_wrap_stream); + } else { + status = _cairo_pdf_operators_emit_glyph_string_with_positioning ( + pdf_operators, word_wrap_stream); + } + + pdf_operators->num_glyphs = 0; + pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x; + status2 = _cairo_output_stream_destroy (word_wrap_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static cairo_status_t +_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, + cairo_scaled_font_subsets_glyph_t *glyph, + double x_position) +{ + double x, y; + + x = glyph->x_advance; + y = glyph->y_advance; + if (glyph->is_scaled) + cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); + + pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; + pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; + pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x; + pdf_operators->glyph_buf_x_pos += x; + pdf_operators->num_glyphs++; + if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE) + return _cairo_pdf_operators_flush_glyphs (pdf_operators); + + return CAIRO_STATUS_SUCCESS; +} + +/* Use 'Tm' operator to set the PDF text matrix. */ +static cairo_status_t +_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *matrix) +{ + cairo_matrix_t inverse; + cairo_status_t status; + + /* We require the matrix to be invertable. */ + inverse = *matrix; + status = cairo_matrix_invert (&inverse); + if (unlikely (status)) + return status; + + pdf_operators->text_matrix = *matrix; + pdf_operators->cur_x = 0; + pdf_operators->cur_y = 0; + pdf_operators->glyph_buf_x_pos = 0; + _cairo_output_stream_printf (pdf_operators->stream, + "%f %f %f %f %f %f Tm\n", + pdf_operators->text_matrix.xx, + pdf_operators->text_matrix.yx, + pdf_operators->text_matrix.xy, + pdf_operators->text_matrix.yy, + pdf_operators->text_matrix.x0, + pdf_operators->text_matrix.y0); + + pdf_operators->cairo_to_pdftext = *matrix; + status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, + &pdf_operators->cairo_to_pdf, + &pdf_operators->cairo_to_pdftext); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +#define TEXT_MATRIX_TOLERANCE 1e-6 + +/* Set the translation components of the PDF text matrix to x, y. The + * 'Td' operator is used to transform the text matrix. + */ +static cairo_status_t +_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, + double x, + double y) +{ + cairo_matrix_t translate, inverse; + cairo_status_t status; + + /* The Td operator transforms the text_matrix with: + * + * text_matrix' = T x text_matrix + * + * where T is a translation matrix with the translation components + * set to the Td operands tx and ty. + */ + inverse = pdf_operators->text_matrix; + status = cairo_matrix_invert (&inverse); + assert (status == CAIRO_STATUS_SUCCESS); + pdf_operators->text_matrix.x0 = x; + pdf_operators->text_matrix.y0 = y; + cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); + if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE) + translate.x0 = 0.0; + if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE) + translate.y0 = 0.0; + _cairo_output_stream_printf (pdf_operators->stream, + "%f %f Td\n", + translate.x0, + translate.y0); + pdf_operators->cur_x = 0; + pdf_operators->cur_y = 0; + pdf_operators->glyph_buf_x_pos = 0; + + pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix; + status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, + &pdf_operators->cairo_to_pdf, + &pdf_operators->cairo_to_pdftext); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Select the font using the 'Tf' operator. The font size is set to 1 + * as we use the 'Tm' operator to set the font scale. + */ +static cairo_status_t +_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_status_t status; + + _cairo_output_stream_printf (pdf_operators->stream, + "/f-%d-%d 1 Tf\n", + subset_glyph->font_id, + subset_glyph->subset_id); + if (pdf_operators->use_font_subset) { + status = pdf_operators->use_font_subset (subset_glyph->font_id, + subset_glyph->subset_id, + pdf_operators->use_font_subset_closure); + if (unlikely (status)) + return status; + } + pdf_operators->font_id = subset_glyph->font_id; + pdf_operators->subset_id = subset_glyph->subset_id; + + if (subset_glyph->is_composite) + pdf_operators->hex_width = 4; + else + pdf_operators->hex_width = 2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) +{ + _cairo_output_stream_printf (pdf_operators->stream, "BT\n"); + + pdf_operators->in_text_object = TRUE; + pdf_operators->num_glyphs = 0; + pdf_operators->glyph_buf_x_pos = 0; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status; + + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); + + pdf_operators->in_text_object = FALSE; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Compare the scale components of two matrices. The translation + * components are ignored. */ +static cairo_bool_t +_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) +{ + return (a->xx == b->xx && + a->xy == b->xy && + a->yx == b->yx && + a->yy == b->yy); +} + +static cairo_status_t +_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len) +{ + uint16_t *utf16; + int utf16_len; + cairo_status_t status; + int i; + + _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText stream, + "%04x", (int) (utf16[i])); + } + free (utf16); + } + _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) +{ + _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, + cairo_glyph_t *glyph, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + double x, y; + cairo_status_t status; + + if (pdf_operators->is_new_text_object || + pdf_operators->font_id != subset_glyph->font_id || + pdf_operators->subset_id != subset_glyph->subset_id) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); + if (unlikely (status)) + return status; + + pdf_operators->is_new_text_object = FALSE; + } + + x = glyph->x; + y = glyph->y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y); + + /* The TJ operator for displaying text strings can only set + * the horizontal position of the glyphs. If the y position + * (in text space) changes, use the Td operator to change the + * current position to the next glyph. We also use the Td + * operator to move the current position if the horizontal + * position changes by more than 10 (in text space + * units). This is becauses the horizontal glyph positioning + * in the TJ operator is intended for kerning and there may be + * PDF consumers that do not handle very large position + * adjustments in TJ. + */ + if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 || + fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + x = glyph->x; + y = glyph->y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); + status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); + if (unlikely (status)) + return status; + + x = 0.0; + y = 0.0; + } + + status = _cairo_pdf_operators_add_glyph (pdf_operators, + subset_glyph, + x); + return status; +} + +/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an + * empty string. + */ +static cairo_int_status_t +_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_subsets_glyph_t subset_glyph; + cairo_glyph_t *cur_glyph; + cairo_status_t status; + int i; + + /* If the cluster maps 1 glyph to 1 or more unicode characters, we + * first try _map_glyph() with the unicode string to see if it can + * use toUnicode to map our glyph to the unicode. This will fail + * if the glyph is already mapped to a different unicode string. + * + * We also go through this path if no unicode mapping was + * supplied (utf8_len < 0). + * + * Mapping a glyph to a zero length unicode string requires the + * use of ActualText. + */ + if (num_glyphs == 1 && utf8_len != 0) { + status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, + scaled_font, + glyphs->index, + utf8, + utf8_len, + &subset_glyph); + if (unlikely (status)) + return status; + + if (subset_glyph.utf8_is_mapped || utf8_len < 0) { + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + glyphs, + &subset_glyph); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } + } + + /* Fallback to using ActualText to map zero or more glyphs to a + * unicode string. */ + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); + if (unlikely (status)) + return status; + + cur_glyph = glyphs; + /* XXX + * If no glyphs, we should put *something* here for the text to be selectable. */ + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, + scaled_font, + cur_glyph->index, + NULL, -1, + &subset_glyph); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + cur_glyph, + &subset_glyph); + if (unlikely (status)) + return status; + + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph--; + else + cur_glyph++; + } + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_end_actualtext (pdf_operators); + + return status; +} + +cairo_int_status_t +_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + int i; + cairo_matrix_t text_matrix, invert_y_axis; + double x, y; + const char *cur_text; + cairo_glyph_t *cur_glyph; + + pdf_operators->font_matrix_inverse = scaled_font->font_matrix; + status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); + if (status == CAIRO_STATUS_INVALID_MATRIX) + return CAIRO_STATUS_SUCCESS; + assert (status == CAIRO_STATUS_SUCCESS); + + pdf_operators->is_new_text_object = FALSE; + if (pdf_operators->in_text_object == FALSE) { + status = _cairo_pdf_operators_begin_text (pdf_operators); + if (unlikely (status)) + return status; + + /* Force Tm and Tf to be emitted when starting a new text + * object.*/ + pdf_operators->is_new_text_object = TRUE; + } + + cairo_matrix_init_scale (&invert_y_axis, 1, -1); + text_matrix = scaled_font->scale; + + /* Invert y axis in font space */ + cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); + + /* Invert y axis in device space */ + cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); + + if (pdf_operators->is_new_text_object || + ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + x = glyphs[0].x; + y = glyphs[0].y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); + text_matrix.x0 = x; + text_matrix.y0 = y; + status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); + if (status == CAIRO_STATUS_INVALID_MATRIX) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + } + + if (num_clusters > 0) { + cur_text = utf8; + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph = glyphs + num_glyphs; + else + cur_glyph = glyphs; + for (i = 0; i < num_clusters; i++) { + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph -= clusters[i].num_glyphs; + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + cur_text, + clusters[i].num_bytes, + cur_glyph, + clusters[i].num_glyphs, + cluster_flags, + scaled_font); + if (unlikely (status)) + return status; + + cur_text += clusters[i].num_bytes; + if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph += clusters[i].num_glyphs; + } + } else { + for (i = 0; i < num_glyphs; i++) { + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + NULL, + -1, /* no unicode string available */ + &glyphs[i], + 1, + FALSE, + scaled_font); + if (unlikely (status)) + return status; + } + } + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +#endif /* CAIRO_HAS_PDF_OPERATORS */ diff --git a/libs/cairo/src/cairo-pdf-surface-private.h b/libs/cairo/src/cairo-pdf-surface-private.h new file mode 100644 index 000000000..ab5befa52 --- /dev/null +++ b/libs/cairo/src/cairo-pdf-surface-private.h @@ -0,0 +1,159 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PDF_SURFACE_PRIVATE_H +#define CAIRO_PDF_SURFACE_PRIVATE_H + +#include "cairo-pdf.h" + +#include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-pdf-operators-private.h" +#include "cairo-path-fixed-private.h" + +typedef struct _cairo_pdf_resource { + unsigned int id; +} cairo_pdf_resource_t; + +#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1) + +typedef struct _cairo_pdf_group_resources { + cairo_bool_t operators[CAIRO_NUM_OPERATORS]; + cairo_array_t alphas; + cairo_array_t smasks; + cairo_array_t patterns; + cairo_array_t xobjects; + cairo_array_t fonts; +} cairo_pdf_group_resources_t; + +typedef struct _cairo_pdf_source_surface_entry { + cairo_hash_entry_t base; + unsigned int id; + cairo_bool_t interpolate; + cairo_pdf_resource_t surface_res; + int width; + int height; +} cairo_pdf_source_surface_entry_t; + +typedef struct _cairo_pdf_source_surface { + cairo_surface_t *surface; + cairo_pdf_source_surface_entry_t *hash_entry; +} cairo_pdf_source_surface_t; + +typedef struct _cairo_pdf_pattern { + double width; + double height; + cairo_rectangle_int_t extents; + cairo_pattern_t *pattern; + cairo_pdf_resource_t pattern_res; + cairo_pdf_resource_t gstate_res; +} cairo_pdf_pattern_t; + +typedef enum _cairo_pdf_operation { + PDF_PAINT, + PDF_MASK, + PDF_FILL, + PDF_STROKE, + PDF_SHOW_GLYPHS +} cairo_pdf_operation_t; + +typedef struct _cairo_pdf_smask_group { + double width; + double height; + cairo_pdf_resource_t group_res; + cairo_pdf_operation_t operation; + cairo_pattern_t *source; + cairo_pdf_resource_t source_res; + cairo_pattern_t *mask; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + char *utf8; + int utf8_len; + cairo_glyph_t *glyphs; + int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_bool_t cluster_flags; + cairo_scaled_font_t *scaled_font; +} cairo_pdf_smask_group_t; + +typedef struct _cairo_pdf_surface cairo_pdf_surface_t; + +struct _cairo_pdf_surface { + cairo_surface_t base; + + /* Prefer the name "output" here to avoid confusion over the + * structure within a PDF document known as a "stream". */ + cairo_output_stream_t *output; + + double width; + double height; + cairo_matrix_t cairo_to_pdf; + + cairo_array_t objects; + cairo_array_t pages; + cairo_array_t rgb_linear_functions; + cairo_array_t alpha_linear_functions; + cairo_array_t page_patterns; + cairo_array_t page_surfaces; + cairo_hash_table_t *all_surfaces; + cairo_array_t smask_groups; + cairo_array_t knockout_group; + + cairo_scaled_font_subsets_t *font_subsets; + cairo_array_t fonts; + + cairo_pdf_resource_t next_available_resource; + cairo_pdf_resource_t pages_resource; + + cairo_pdf_version_t pdf_version; + cairo_bool_t compress_content; + + cairo_pdf_resource_t content; + cairo_pdf_resource_t content_resources; + cairo_pdf_group_resources_t resources; + cairo_bool_t has_fallback_images; + cairo_bool_t header_emitted; + + struct { + cairo_bool_t active; + cairo_pdf_resource_t self; + cairo_pdf_resource_t length; + long start_offset; + cairo_bool_t compressed; + cairo_output_stream_t *old_output; + } pdf_stream; + + struct { + cairo_bool_t active; + cairo_output_stream_t *stream; + cairo_output_stream_t *mem_stream; + cairo_output_stream_t *old_output; + cairo_pdf_resource_t resource; + cairo_bool_t is_knockout; + } group_stream; + + cairo_surface_clipper_t clipper; + + cairo_pdf_operators_t pdf_operators; + cairo_paginated_mode_t paginated_mode; + cairo_bool_t select_pattern_gstate_saved; + + cairo_bool_t force_fallbacks; + + cairo_operator_t current_operator; + cairo_bool_t current_pattern_is_solid_color; + cairo_bool_t current_color_is_stroke; + double current_color_red; + double current_color_green; + double current_color_blue; + double current_color_alpha; + + cairo_surface_t *paginated_surface; +}; + +#endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-pdf-surface.c b/libs/cairo/src/cairo-pdf-surface.c new file mode 100644 index 000000000..3dcf58859 --- /dev/null +++ b/libs/cairo/src/cairo-pdf-surface.c @@ -0,0 +1,6213 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for snprintf() */ +#include "cairoint.h" +#include "cairo-pdf.h" +#include "cairo-pdf-surface-private.h" +#include "cairo-pdf-operators-private.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-image-info-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-paginated-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-type3-glyph-surface-private.h" + +#include +#include + +/* Issues: + * + * - We embed an image in the stream each time it's composited. We + * could add generation counters to surfaces and remember the stream + * ID for a particular generation for a particular surface. + * + * - Backend specific meta data. + */ + +/* + * Page Structure of the Generated PDF: + * + * Each page requiring fallbacks images contains a knockout group at + * the top level. The first operation of the knockout group paints a + * group containing all the supported drawing operations. Fallback + * images (if any) are painted in the knockout group. This ensures + * that fallback images do not composite with any content under the + * fallback images. + * + * Streams: + * + * This PDF surface has three types of streams: + * - PDF Stream + * - Content Stream + * - Group Stream + * + * Calling _cairo_output_stream_printf (surface->output, ...) will + * write to the currently open stream. + * + * PDF Stream: + * A PDF Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open stream () + * _cairo_pdf_surface_close_stream () + * + * PDF Streams are written directly to the PDF file. They are used for + * fonts, images and patterns. + * + * Content Stream: + * The Content Stream is opened and closed with the following functions: + * _cairo_pdf_surface_open_content_stream () + * _cairo_pdf_surface_close_content_stream () + * + * The Content Stream contains the text and graphics operators. + * + * Group Stream: + * A Group Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open_group () + * _cairo_pdf_surface_close_group () + * + * A Group Stream is a Form XObject. It is used for short sequences + * of operators. As the content is very short the group is stored in + * memory until it is closed. This allows some optimization such as + * including the Resource dictionary and stream length inside the + * XObject instead of using an indirect object. + */ + +/** + * SECTION:cairo-pdf + * @Title: PDF Surfaces + * @Short_Description: Rendering PDF documents + * @See_Also: #cairo_surface_t + * + * The PDF surface is used to render cairo graphics to Adobe + * PDF files and is a multi-page vector surface backend. + */ + +/** + * CAIRO_HAS_PDF_SURFACE: + * + * Defined if the PDF surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +static const cairo_pdf_version_t _cairo_pdf_versions[] = +{ + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +}; + +#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions) + +static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = +{ + "PDF 1.4", + "PDF 1.5" +}; + +typedef struct _cairo_pdf_object { + long offset; +} cairo_pdf_object_t; + +typedef struct _cairo_pdf_font { + unsigned int font_id; + unsigned int subset_id; + cairo_pdf_resource_t subset_resource; +} cairo_pdf_font_t; + +typedef struct _cairo_pdf_rgb_linear_function { + cairo_pdf_resource_t resource; + double color1[3]; + double color2[3]; +} cairo_pdf_rgb_linear_function_t; + +typedef struct _cairo_pdf_alpha_linear_function { + cairo_pdf_resource_t resource; + double alpha1; + double alpha2; +} cairo_pdf_alpha_linear_function_t; + +static cairo_pdf_resource_t +_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); + +static void +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); + +static void +_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); + +static cairo_status_t +_cairo_pdf_surface_add_font (unsigned int font_id, + unsigned int subset_id, + void *closure); + +static void +_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res); + +static cairo_status_t +_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *resource, + cairo_bool_t compressed, + const char *fmt, + ...) CAIRO_PRINTF_FORMAT(4, 5); +static cairo_status_t +_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); + +static cairo_status_t +_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); + +static void +_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); + +static cairo_pdf_resource_t +_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface); + +static cairo_pdf_resource_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); + +static long +_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); + +static cairo_status_t +_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); + +static cairo_status_t +_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); + +static cairo_bool_t +_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); + +static const cairo_surface_backend_t cairo_pdf_surface_backend; +static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; + +static cairo_pdf_resource_t +_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t resource; + cairo_status_t status; + cairo_pdf_object_t object; + + object.offset = _cairo_output_stream_get_position (surface->output); + + status = _cairo_array_append (&surface->objects, &object); + if (unlikely (status)) { + resource.id = 0; + return resource; + } + + resource = surface->next_available_resource; + surface->next_available_resource.id++; + + return resource; +} + +static void +_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource) +{ + cairo_pdf_object_t *object; + + object = _cairo_array_index (&surface->objects, resource.id - 1); + object->offset = _cairo_output_stream_get_position (surface->output); +} + +static void +_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, + double width, + double height) +{ + surface->width = width; + surface->height = height; + cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, + &surface->cairo_to_pdf); +} + +static cairo_bool_t +_path_covers_bbox (cairo_pdf_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + return _cairo_path_fixed_is_box (path, &box) && + box.p1.x <= 0 && + box.p1.y <= 0 && + box.p2.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height); +} + +static cairo_status_t +_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = cairo_container_of (clipper, + cairo_pdf_surface_t, + clipper); + cairo_int_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (path == NULL) { + _cairo_output_stream_printf (surface->output, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); +} + +static cairo_surface_t * +_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, + double width, + double height) +{ + cairo_pdf_surface_t *surface; + cairo_status_t status, status_ignored; + + surface = malloc (sizeof (cairo_pdf_surface_t)); + if (unlikely (surface == NULL)) { + /* destroy stream on behalf of caller */ + status = _cairo_output_stream_destroy (output); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &cairo_pdf_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + surface->output = output; + surface->width = width; + surface->height = height; + cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); + + _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); + _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); + _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); + _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *)); + _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); + + _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); + _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); + surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); + if (unlikely (surface->all_surfaces == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL0; + } + + _cairo_pdf_group_resources_init (&surface->resources); + + surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); + if (! surface->font_subsets) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL1; + } + + surface->next_available_resource.id = 1; + surface->pages_resource = _cairo_pdf_surface_new_object (surface); + if (surface->pages_resource.id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL2; + } + + surface->pdf_version = CAIRO_PDF_VERSION_1_5; + surface->compress_content = TRUE; + surface->pdf_stream.active = FALSE; + surface->pdf_stream.old_output = NULL; + surface->group_stream.active = FALSE; + surface->group_stream.stream = NULL; + surface->group_stream.mem_stream = NULL; + + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + + surface->force_fallbacks = FALSE; + surface->select_pattern_gstate_saved = FALSE; + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + surface->header_emitted = FALSE; + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_pdf_surface_clipper_intersect_clip_path); + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->output, + &surface->cairo_to_pdf, + surface->font_subsets); + _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, + _cairo_pdf_surface_add_font, + surface); + _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); + + surface->paginated_surface = _cairo_paginated_surface_create ( + &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_pdf_surface_paginated_backend); + + status = surface->paginated_surface->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return surface->paginated_surface; + } + +BAIL2: + _cairo_scaled_font_subsets_destroy (surface->font_subsets); +BAIL1: + _cairo_hash_table_destroy (surface->all_surfaces); +BAIL0: + _cairo_array_fini (&surface->objects); + free (surface); + + /* destroy stream on behalf of caller */ + status_ignored = _cairo_output_stream_destroy (output); + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_pdf_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PDF surface of the specified size in points to be written + * incrementally to the stream represented by @write_func and @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + */ +cairo_surface_t * +cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *output; + + output = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (output)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); + + return _cairo_pdf_surface_create_for_stream_internal (output, + width_in_points, + height_in_points); +} + +/** + * cairo_pdf_surface_create: + * @filename: a filename for the PDF output (must be writable), %NULL may be + * used to specify no output. This will generate a PDF surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PDF surface of the specified size in points to be written + * to @filename. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *output; + + output = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (output)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); + + return _cairo_pdf_surface_create_for_stream_internal (output, + width_in_points, + height_in_points); +} + +static cairo_bool_t +_cairo_surface_is_pdf (cairo_surface_t *surface) +{ + return surface->backend == &cairo_pdf_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a pdf_surface, then set pdf_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_pdf_surface (cairo_surface_t *surface, + cairo_pdf_surface_t **pdf_surface) +{ + cairo_surface_t *target; + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + target = _cairo_paginated_surface_get_target (surface); + if (target->status) { + status_ignored = _cairo_surface_set_error (surface, + target->status); + return FALSE; + } + if (target->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_pdf (target)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *pdf_surface = (cairo_pdf_surface_t *) target; + return TRUE; +} + +/** + * cairo_pdf_surface_restrict_to_version: + * @surface: a PDF #cairo_surface_t + * @version: PDF version + * + * Restricts the generated PDF file to @version. See cairo_pdf_get_versions() + * for a list of available version values that can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.10 + **/ +void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_pdf_version_t version) +{ + cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (abstract_surface, &surface)) + return; + + if (version < CAIRO_PDF_VERSION_LAST) + surface->pdf_version = version; + + _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, + version >= CAIRO_PDF_VERSION_1_5); +} + +/** + * cairo_pdf_get_versions: + * @versions: supported version list + * @num_versions: list length + * + * Used to retrieve the list of supported versions. See + * cairo_pdf_surface_restrict_to_version(). + * + * Since: 1.10 + **/ +void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions) +{ + if (versions != NULL) + *versions = _cairo_pdf_versions; + + if (num_versions != NULL) + *num_versions = CAIRO_PDF_VERSION_LAST; +} + +/** + * cairo_pdf_version_to_string: + * @version: a version id + * + * Get the string representation of the given @version id. This function + * will return %NULL if @version isn't valid. See cairo_pdf_get_versions() + * for a way to get the list of valid version ids. + * + * Return value: the string associated to given version. + * + * Since: 1.10 + **/ +const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version) +{ + if (version >= CAIRO_PDF_VERSION_LAST) + return NULL; + + return _cairo_pdf_version_strings[version]; +} + +/** + * cairo_pdf_surface_set_size: + * @surface: a PDF #cairo_surface_t + * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) + * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) + * + * Changes the size of a PDF surface for the current (and + * subsequent) pages. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface or immediately after completing a page with either + * cairo_show_page() or cairo_copy_page(). + * + * Since: 1.2 + **/ +void +cairo_pdf_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + _cairo_pdf_surface_set_size_internal (pdf_surface, + width_in_points, + height_in_points); +} + +static void +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) +{ + int i, size; + cairo_pdf_pattern_t *pattern; + cairo_pdf_source_surface_t *src_surface; + cairo_pdf_smask_group_t *group; + + size = _cairo_array_num_elements (&surface->page_patterns); + for (i = 0; i < size; i++) { + pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i); + cairo_pattern_destroy (pattern->pattern); + } + _cairo_array_truncate (&surface->page_patterns, 0); + + size = _cairo_array_num_elements (&surface->page_surfaces); + for (i = 0; i < size; i++) { + src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); + cairo_surface_destroy (src_surface->surface); + } + _cairo_array_truncate (&surface->page_surfaces, 0); + + size = _cairo_array_num_elements (&surface->smask_groups); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->smask_groups, i, &group); + _cairo_pdf_smask_group_destroy (group); + } + _cairo_array_truncate (&surface->smask_groups, 0); + _cairo_array_truncate (&surface->knockout_group, 0); +} + +static void +_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) +{ + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + + _cairo_array_init (&res->alphas, sizeof (double)); + _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t)); +} + +static void +_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) +{ + _cairo_array_fini (&res->alphas); + _cairo_array_fini (&res->smasks); + _cairo_array_fini (&res->patterns); + _cairo_array_fini (&res->xobjects); + _cairo_array_fini (&res->fonts); +} + +static void +_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) +{ + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + + _cairo_array_truncate (&res->alphas, 0); + _cairo_array_truncate (&res->smasks, 0); + _cairo_array_truncate (&res->patterns, 0); + _cairo_array_truncate (&res->xobjects, 0); + _cairo_array_truncate (&res->fonts, 0); +} + +static void +_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_pdf_group_resources_t *res = &surface->resources; + + res->operators[op] = TRUE; +} + +static cairo_status_t +_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, + double alpha, + int *index) +{ + int num_alphas, i; + double other; + cairo_status_t status; + cairo_pdf_group_resources_t *res = &surface->resources; + + num_alphas = _cairo_array_num_elements (&res->alphas); + for (i = 0; i < num_alphas; i++) { + _cairo_array_copy_element (&res->alphas, i, &other); + if (alpha == other) { + *index = i; + return CAIRO_STATUS_SUCCESS; + } + } + + status = _cairo_array_append (&res->alphas, &alpha); + if (unlikely (status)) + return status; + + *index = _cairo_array_num_elements (&res->alphas) - 1; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t smask) +{ + return _cairo_array_append (&(surface->resources.smasks), &smask); +} + +static cairo_status_t +_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t pattern) +{ + return _cairo_array_append (&(surface->resources.patterns), &pattern); +} + +static cairo_status_t +_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t xobject) +{ + return _cairo_array_append (&(surface->resources.xobjects), &xobject); +} + +static cairo_status_t +_cairo_pdf_surface_add_font (unsigned int font_id, + unsigned int subset_id, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_pdf_font_t font; + int num_fonts, i; + cairo_status_t status; + cairo_pdf_group_resources_t *res = &surface->resources; + + num_fonts = _cairo_array_num_elements (&res->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&res->fonts, i, &font); + if (font.font_id == font_id && + font.subset_id == subset_id) + return CAIRO_STATUS_SUCCESS; + } + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &font); + if (font.font_id == font_id && + font.subset_id == subset_id) + return _cairo_array_append (&res->fonts, &font); + } + + font.font_id = font_id; + font.subset_id = subset_id; + font.subset_resource = _cairo_pdf_surface_new_object (surface); + if (font.subset_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&surface->fonts, &font); + if (unlikely (status)) + return status; + + return _cairo_array_append (&res->fonts, &font); +} + +static cairo_pdf_resource_t +_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, + unsigned int font_id, + unsigned int subset_id) +{ + cairo_pdf_font_t font; + int num_fonts, i; + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &font); + if (font.font_id == font_id && font.subset_id == subset_id) + return font.subset_resource; + } + + font.subset_resource.id = 0; + return font.subset_resource; +} + +static const char * +_cairo_operator_to_pdf_blend_mode (cairo_operator_t op) +{ + switch (op) { + /* The extend blend mode operators */ + case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; + case CAIRO_OPERATOR_SCREEN: return "Screen"; + case CAIRO_OPERATOR_OVERLAY: return "Overlay"; + case CAIRO_OPERATOR_DARKEN: return "Darken"; + case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; + case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; + case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; + case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; + case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; + case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; + case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; + case CAIRO_OPERATOR_HSL_HUE: return "Hue"; + case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; + case CAIRO_OPERATOR_HSL_COLOR: return "Color"; + case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; + + default: + /* The original Porter-Duff set */ + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return "Normal"; + } +} + +static void +_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, + cairo_pdf_group_resources_t *res) +{ + int num_alphas, num_smasks, num_resources, i; + double alpha; + cairo_pdf_resource_t *smask, *pattern, *xobject; + cairo_pdf_font_t *font; + + _cairo_output_stream_printf (surface->output, "<<\n"); + + num_alphas = _cairo_array_num_elements (&res->alphas); + num_smasks = _cairo_array_num_elements (&res->smasks); + if (num_alphas > 0 || num_smasks > 0) { + _cairo_output_stream_printf (surface->output, + " /ExtGState <<\n"); + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { + if (res->operators[i]) { + _cairo_output_stream_printf (surface->output, + " /b%d << /BM /%s >>\n", + i, _cairo_operator_to_pdf_blend_mode(i)); + } + } + + for (i = 0; i < num_alphas; i++) { + _cairo_array_copy_element (&res->alphas, i, &alpha); + _cairo_output_stream_printf (surface->output, + " /a%d << /CA %f /ca %f >>\n", + i, alpha, alpha); + } + + for (i = 0; i < num_smasks; i++) { + smask = _cairo_array_index (&res->smasks, i); + _cairo_output_stream_printf (surface->output, + " /s%d %d 0 R\n", + smask->id, smask->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + num_resources = _cairo_array_num_elements (&res->patterns); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output, + " /Pattern <<"); + for (i = 0; i < num_resources; i++) { + pattern = _cairo_array_index (&res->patterns, i); + _cairo_output_stream_printf (surface->output, + " /p%d %d 0 R", + pattern->id, pattern->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + num_resources = _cairo_array_num_elements (&res->xobjects); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output, + " /XObject <<"); + + for (i = 0; i < num_resources; i++) { + xobject = _cairo_array_index (&res->xobjects, i); + _cairo_output_stream_printf (surface->output, + " /x%d %d 0 R", + xobject->id, xobject->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + num_resources = _cairo_array_num_elements (&res->fonts); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output," /Font <<\n"); + for (i = 0; i < num_resources; i++) { + font = _cairo_array_index (&res->fonts, i); + _cairo_output_stream_printf (surface->output, + " /f-%d-%d %d 0 R\n", + font->font_id, + font->subset_id, + font->subset_resource.id); + } + _cairo_output_stream_printf (surface->output, " >>\n"); + } + + _cairo_output_stream_printf (surface->output, + ">>\n"); +} + +static cairo_pdf_smask_group_t * +_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) +{ + cairo_pdf_smask_group_t *group; + + group = calloc (1, sizeof (cairo_pdf_smask_group_t)); + if (unlikely (group == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + group->group_res = _cairo_pdf_surface_new_object (surface); + if (group->group_res.id == 0) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + free (group); + return NULL; + } + group->width = surface->width; + group->height = surface->height; + + return group; +} + +static void +_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) +{ + if (group->operation == PDF_FILL || group->operation == PDF_STROKE) + _cairo_path_fixed_fini (&group->path); + if (group->source) + cairo_pattern_destroy (group->source); + if (group->mask) + cairo_pattern_destroy (group->mask); + if (group->utf8) + free (group->utf8); + if (group->glyphs) + free (group->glyphs); + if (group->clusters) + free (group->clusters); + if (group->scaled_font) + cairo_scaled_font_destroy (group->scaled_font); + free (group); +} + +static cairo_status_t +_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, + cairo_pdf_smask_group_t *group) +{ + return _cairo_array_append (&surface->smask_groups, &group); +} + +static cairo_bool_t +_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_source_surface_entry_t *a = key_a; + const cairo_pdf_source_surface_entry_t *b = key_b; + + return (a->id == b->id) && (a->interpolate == b->interpolate); +} + +static void +_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) +{ + key->base.hash = key->id; +} + +static cairo_int_status_t +_get_jpx_image_info (cairo_surface_t *source, + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned long *mime_data_length) +{ + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, + mime_data, mime_data_length); + if (*mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); +} + +static cairo_int_status_t +_get_jpeg_image_info (cairo_surface_t *source, + cairo_image_info_t *info, + const unsigned char **mime_data, + unsigned long *mime_data_length) +{ + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + mime_data, mime_data_length); + if (*mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); +} + +static cairo_status_t +_get_source_surface_size (cairo_surface_t *source, + int *width, + int *height) +{ + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_image_info_t info; + const unsigned char *mime_data; + unsigned long mime_data_length; + + if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + + *width = sub->extents.width; + *height = sub->extents.height; + + } else { + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; + cairo_box_t bbox; + + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + + *width = extents.width; + *height = extents.height; + } + return CAIRO_STATUS_SUCCESS; + } + + status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *width = info.width; + *height = info.height; + return status; + } + + status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *width = info.width; + *height = info.height; + return status; + } + + if (! _cairo_surface_get_extents (source, &extents)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *width = extents.width; + *height = extents.height; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_filter_t filter, + cairo_pdf_resource_t *surface_res, + int *width, + int *height) +{ + cairo_pdf_source_surface_t src_surface; + cairo_pdf_source_surface_entry_t surface_key; + cairo_pdf_source_surface_entry_t *surface_entry; + cairo_status_t status; + cairo_bool_t interpolate; + + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = TRUE; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = FALSE; + break; + } + + surface_key.id = source->unique_id; + surface_key.interpolate = interpolate; + _cairo_pdf_source_surface_init_key (&surface_key); + surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); + if (surface_entry) { + *surface_res = surface_entry->surface_res; + *width = surface_entry->width; + *height = surface_entry->height; + + return CAIRO_STATUS_SUCCESS; + } + + surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); + if (surface_entry == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + surface_entry->id = surface_key.id; + surface_entry->interpolate = interpolate; + _cairo_pdf_source_surface_init_key (surface_entry); + + src_surface.hash_entry = surface_entry; + src_surface.surface = cairo_surface_reference (source); + surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); + if (surface_entry->surface_res.id == 0) { + cairo_surface_destroy (source); + free (surface_entry); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _get_source_surface_size (source, &surface_entry->width, + &surface_entry->height); + + status = _cairo_array_append (&surface->page_surfaces, &src_surface); + if (unlikely (status)) { + cairo_surface_destroy (source); + free (surface_entry); + return status; + } + + status = _cairo_hash_table_insert (surface->all_surfaces, + &surface_entry->base); + + *surface_res = surface_entry->surface_res; + *width = surface_entry->width; + *height = surface_entry->height; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *pattern_res, + cairo_pdf_resource_t *gstate_res) +{ + cairo_pdf_pattern_t pdf_pattern; + cairo_status_t status; + + /* Solid colors are emitted into the content stream */ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + pattern_res->id = 0; + gstate_res->id = 0; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); + if (unlikely (status)) + return status; + + pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern.pattern_res.id == 0) { + cairo_pattern_destroy (pdf_pattern.pattern); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pdf_pattern.gstate_res.id = 0; + + /* gradient patterns require an smask object to implement transparency */ + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + { + if (_cairo_pattern_is_opaque (pattern, extents) == FALSE) { + pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern.gstate_res.id == 0) { + cairo_pattern_destroy (pdf_pattern.pattern); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + } + + pdf_pattern.width = surface->width; + pdf_pattern.height = surface->height; + if (extents != NULL) { + pdf_pattern.extents = *extents; + } else { + pdf_pattern.extents.x = 0; + pdf_pattern.extents.y = 0; + pdf_pattern.extents.width = surface->width; + pdf_pattern.extents.height = surface->height; + } + + *pattern_res = pdf_pattern.pattern_res; + *gstate_res = pdf_pattern.gstate_res; + + status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); + if (unlikely (status)) { + cairo_pattern_destroy (pdf_pattern.pattern); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *resource, + cairo_bool_t compressed, + const char *fmt, + ...) +{ + va_list ap; + cairo_pdf_resource_t self, length; + cairo_output_stream_t *output = NULL; + + if (resource) { + self = *resource; + _cairo_pdf_surface_update_object (surface, self); + } else { + self = _cairo_pdf_surface_new_object (surface); + if (self.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + length = _cairo_pdf_surface_new_object (surface); + if (length.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (compressed) { + output = _cairo_deflate_stream_create (surface->output); + if (_cairo_output_stream_get_status (output)) + return _cairo_output_stream_destroy (output); + } + + surface->pdf_stream.active = TRUE; + surface->pdf_stream.self = self; + surface->pdf_stream.length = length; + surface->pdf_stream.compressed = compressed; + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Length %d 0 R\n", + surface->pdf_stream.self.id, + surface->pdf_stream.length.id); + if (compressed) + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + + if (fmt != NULL) { + va_start (ap, fmt); + _cairo_output_stream_vprintf (surface->output, fmt, ap); + va_end (ap); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + + surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output); + + if (compressed) { + assert (surface->pdf_stream.old_output == NULL); + surface->pdf_stream.old_output = surface->output; + surface->output = output; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + long length; + + if (! surface->pdf_stream.active) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + if (surface->pdf_stream.compressed) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (surface->output); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = status2; + + surface->output = surface->pdf_stream.old_output; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + surface->pdf_stream.old_output = NULL; + } + + length = _cairo_output_stream_get_position (surface->output) - + surface->pdf_stream.start_offset; + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, + surface->pdf_stream.length); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + " %ld\n" + "endobj\n", + surface->pdf_stream.length.id, + length); + + surface->pdf_stream.active = FALSE; + + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_output_stream_get_status (surface->output); + + return status; +} + +static void +_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, + cairo_output_stream_t *mem_stream, + cairo_pdf_resource_t resource, + cairo_pdf_group_resources_t *resources, + cairo_bool_t is_knockout_group) +{ + _cairo_pdf_surface_update_object (surface, resource); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /XObject\n" + " /Length %d\n", + resource.id, + _cairo_memory_stream_length (mem_stream)); + + if (surface->compress_content) { + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + } + + _cairo_output_stream_printf (surface->output, + " /Subtype /Form\n" + " /BBox [ 0 0 %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /CS /DeviceRGB\n", + surface->width, + surface->height); + + if (is_knockout_group) + _cairo_output_stream_printf (surface->output, + " /K true\n"); + + _cairo_output_stream_printf (surface->output, + " >>\n" + " /Resources\n"); + _cairo_pdf_surface_emit_group_resources (surface, resources); + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + _cairo_memory_stream_copy (mem_stream, surface->output); + _cairo_output_stream_printf (surface->output, + "endstream\n" + "endobj\n"); +} + +static cairo_status_t +_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *resource) +{ + cairo_status_t status; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + + surface->group_stream.active = TRUE; + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + surface->group_stream.mem_stream = _cairo_memory_stream_create (); + + if (surface->compress_content) { + surface->group_stream.stream = + _cairo_deflate_stream_create (surface->group_stream.mem_stream); + } else { + surface->group_stream.stream = surface->group_stream.mem_stream; + } + status = _cairo_output_stream_get_status (surface->group_stream.stream); + + surface->group_stream.old_output = surface->output; + surface->output = surface->group_stream.stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + _cairo_pdf_group_resources_clear (&surface->resources); + + if (resource) { + surface->group_stream.resource = *resource; + } else { + surface->group_stream.resource = _cairo_pdf_surface_new_object (surface); + if (surface->group_stream.resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + surface->group_stream.is_knockout = FALSE; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + + status = _cairo_pdf_surface_open_group (surface, NULL); + if (unlikely (status)) + return status; + + surface->group_stream.is_knockout = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *group) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == TRUE); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (surface->compress_content) { + status = _cairo_output_stream_destroy (surface->group_stream.stream); + surface->group_stream.stream = NULL; + + _cairo_output_stream_printf (surface->group_stream.mem_stream, + "\n"); + } + surface->output = surface->group_stream.old_output; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + surface->group_stream.active = FALSE; + _cairo_pdf_surface_write_memory_stream (surface, + surface->group_stream.mem_stream, + surface->group_stream.resource, + &surface->resources, + surface->group_stream.is_knockout); + if (group) + *group = surface->group_stream.resource; + + status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + surface->group_stream.mem_stream = NULL; + surface->group_stream.stream = NULL; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *resource, + cairo_bool_t is_form) +{ + cairo_status_t status; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + + surface->content_resources = _cairo_pdf_surface_new_object (surface); + if (surface->content_resources.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (is_form) { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ 0 0 %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n", + surface->width, + surface->height, + surface->content_resources.id); + } else { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + NULL); + } + if (unlikely (status)) + return status; + + surface->content = surface->pdf_stream.self; + + _cairo_output_stream_printf (surface->output, "q\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + + assert (surface->pdf_stream.active == TRUE); + assert (surface->group_stream.active == FALSE); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, surface->content_resources); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n", + surface->content_resources.id); + _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); + _cairo_output_stream_printf (surface->output, + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static void +_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) +{ + cairo_pdf_source_surface_entry_t *surface_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry); +} + +static cairo_status_t +_cairo_pdf_surface_finish (void *abstract_surface) +{ + cairo_pdf_surface_t *surface = abstract_surface; + long offset; + cairo_pdf_resource_t info, catalog; + cairo_status_t status, status2; + + status = surface->base.status; + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_pdf_surface_emit_font_subsets (surface); + + _cairo_pdf_surface_write_pages (surface); + + info = _cairo_pdf_surface_write_info (surface); + if (info.id == 0 && status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + + catalog = _cairo_pdf_surface_write_catalog (surface); + if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + + offset = _cairo_pdf_surface_write_xref (surface); + + _cairo_output_stream_printf (surface->output, + "trailer\n" + "<< /Size %d\n" + " /Root %d 0 R\n" + " /Info %d 0 R\n" + ">>\n", + surface->next_available_resource.id, + catalog.id, + info.id); + + _cairo_output_stream_printf (surface->output, + "startxref\n" + "%ld\n" + "%%%%EOF\n", + offset); + + /* pdf_operators has already been flushed when the last stream was + * closed so we should never be writing anything here - however, + * the stream may itself be in an error state. */ + status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + /* close any active streams still open due to fatal errors */ + status2 = _cairo_pdf_surface_close_stream (surface); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + if (surface->group_stream.stream != NULL) { + status2 = _cairo_output_stream_destroy (surface->group_stream.stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + if (surface->group_stream.mem_stream != NULL) { + status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + if (surface->pdf_stream.active) + surface->output = surface->pdf_stream.old_output; + if (surface->group_stream.active) + surface->output = surface->group_stream.old_output; + + /* and finish the pdf surface */ + status2 = _cairo_output_stream_destroy (surface->output); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_pdf_surface_clear (surface); + _cairo_pdf_group_resources_fini (&surface->resources); + + _cairo_array_fini (&surface->objects); + _cairo_array_fini (&surface->pages); + _cairo_array_fini (&surface->rgb_linear_functions); + _cairo_array_fini (&surface->alpha_linear_functions); + _cairo_array_fini (&surface->page_patterns); + _cairo_array_fini (&surface->page_surfaces); + _cairo_hash_table_foreach (surface->all_surfaces, + _cairo_pdf_source_surface_entry_pluck, + surface->all_surfaces); + _cairo_hash_table_destroy (surface->all_surfaces); + _cairo_array_fini (&surface->smask_groups); + _cairo_array_fini (&surface->fonts); + _cairo_array_fini (&surface->knockout_group); + + if (surface->font_subsets) { + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; + } + + _cairo_surface_clipper_reset (&surface->clipper); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_start_page (void *abstract_surface) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + /* Document header */ + if (! surface->header_emitted) { + const char *version; + + switch (surface->pdf_version) { + case CAIRO_PDF_VERSION_1_4: + version = "1.4"; + break; + default: + case CAIRO_PDF_VERSION_1_5: + version = "1.5"; + break; + } + + _cairo_output_stream_printf (surface->output, + "%%PDF-%s\n", version); + _cairo_output_stream_printf (surface->output, + "%%%c%c%c%c\n", 181, 237, 174, 251); + surface->header_emitted = TRUE; + } + + _cairo_pdf_group_resources_clear (&surface->resources); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_has_fallback_images (void *abstract_surface, + cairo_bool_t has_fallbacks) +{ + cairo_status_t status; + cairo_pdf_surface_t *surface = abstract_surface; + + surface->has_fallback_images = has_fallbacks; + status = _cairo_pdf_surface_open_content_stream (surface, NULL, has_fallbacks); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +/* Emit alpha channel from the image into the given data, providing + * an id that can be used to reference the resulting SMask object. + * + * In the case that the alpha channel happens to be all opaque, then + * no SMask object will be emitted and *id_ret will be set to 0. + */ +static cairo_status_t +_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image, + cairo_pdf_resource_t *stream_ret) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + char *alpha; + unsigned long alpha_size; + uint32_t *pixel32; + uint8_t *pixel8; + int i, x, y; + cairo_bool_t opaque; + uint8_t a; + + /* This is the only image format we support, which simplifies things. */ + assert (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_A8 || + image->format == CAIRO_FORMAT_A1 ); + + stream_ret->id = 0; + + if (image->format == CAIRO_FORMAT_A1) { + alpha_size = (image->width + 7) / 8 * image->height; + alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); + } else { + alpha_size = image->height * image->width; + alpha = _cairo_malloc_ab (image->height, image->width); + } + + if (unlikely (alpha == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + opaque = TRUE; + i = 0; + for (y = 0; y < image->height; y++) { + if (image->format == CAIRO_FORMAT_ARGB32) { + pixel32 = (uint32_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel32++) { + a = (*pixel32 & 0xff000000) >> 24; + alpha[i++] = a; + if (a != 0xff) + opaque = FALSE; + } + } else if (image->format == CAIRO_FORMAT_A8){ + pixel8 = (uint8_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel8++) { + a = *pixel8; + alpha[i++] = a; + if (a != 0xff) + opaque = FALSE; + } + } else { /* image->format == CAIRO_FORMAT_A1 */ + pixel8 = (uint8_t *) (image->data + y * image->stride); + + for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { + a = *pixel8; + a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); + alpha[i++] = a; + if (a != 0xff) + opaque = FALSE; + } + } + } + + /* Bail out without emitting smask if it's all opaque. */ + if (opaque) + goto CLEANUP_ALPHA; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerComponent %d\n", + image->width, image->height, + image->format == CAIRO_FORMAT_A1 ? 1 : 8); + if (unlikely (status)) + goto CLEANUP_ALPHA; + + *stream_ret = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, alpha, alpha_size); + status = _cairo_pdf_surface_close_stream (surface); + + CLEANUP_ALPHA: + free (alpha); + CLEANUP: + return status; +} + +/* Emit image data into the given surface, providing a resource that + * can be used to reference the data in image_ret. */ +static cairo_status_t +_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image, + cairo_pdf_resource_t *image_res, + cairo_filter_t filter) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + char *rgb; + unsigned long rgb_size; + uint32_t *pixel; + int i, x, y; + cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ + cairo_bool_t need_smask; + const char *interpolate = "true"; + + /* These are the only image formats we currently support, (which + * makes things a lot simpler here). This is enforced through + * _cairo_pdf_surface_analyze_operation which only accept source surfaces of + * CONTENT_COLOR or CONTENT_COLOR_ALPHA. + */ + assert (image->format == CAIRO_FORMAT_RGB24 || + image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_A8 || + image->format == CAIRO_FORMAT_A1); + + rgb_size = image->height * image->width * 3; + rgb = _cairo_malloc_abc (image->width, image->height, 3); + if (unlikely (rgb == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + i = 0; + for (y = 0; y < image->height; y++) { + pixel = (uint32_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel++) { + /* XXX: We're un-premultiplying alpha here. My reading of the PDF + * specification suggests that we should be able to avoid having + * to do this by filling in the SMask's Matte dictionary + * appropriately, but my attempts to do that so far have + * failed. */ + if (image->format == CAIRO_FORMAT_ARGB32) { + uint8_t a; + a = (*pixel & 0xff000000) >> 24; + if (a == 0) { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } else { + rgb[i++] = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; + rgb[i++] = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; + rgb[i++] = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; + } + } else if (image->format == CAIRO_FORMAT_RGB24) { + rgb[i++] = (*pixel & 0x00ff0000) >> 16; + rgb[i++] = (*pixel & 0x0000ff00) >> 8; + rgb[i++] = (*pixel & 0x000000ff) >> 0; + } else { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + } + } + + need_smask = FALSE; + if (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_A8 || + image->format == CAIRO_FORMAT_A1) { + status = _cairo_pdf_surface_emit_smask (surface, image, &smask); + if (unlikely (status)) + goto CLEANUP_RGB; + + if (smask.id) + need_smask = TRUE; + } + + switch (filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + +#define IMAGE_DICTIONARY " /Type /XObject\n" \ + " /Subtype /Image\n" \ + " /Width %d\n" \ + " /Height %d\n" \ + " /ColorSpace /DeviceRGB\n" \ + " /Interpolate %s\n" \ + " /BitsPerComponent 8\n" + + if (need_smask) + status = _cairo_pdf_surface_open_stream (surface, + image_res, + TRUE, + IMAGE_DICTIONARY + " /SMask %d 0 R\n", + image->width, image->height, + interpolate, + smask.id); + else + status = _cairo_pdf_surface_open_stream (surface, + image_res, + TRUE, + IMAGE_DICTIONARY, + image->width, image->height, + interpolate); + if (unlikely (status)) + goto CLEANUP_RGB; + +#undef IMAGE_DICTIONARY + + _cairo_output_stream_write (surface->output, rgb, rgb_size); + status = _cairo_pdf_surface_close_stream (surface); + +CLEANUP_RGB: + free (rgb); +CLEANUP: + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_resource_t res) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t info; + + if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); + if (status) + return status; + + status = _cairo_pdf_surface_open_stream (surface, + &res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /Filter /JPXDecode\n", + info.width, + info.height); + if (status) + return status; + + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + status = _cairo_pdf_surface_close_stream (surface); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_resource_t res) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t info; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.num_components != 1 && info.num_components != 3) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pdf_surface_open_stream (surface, + &res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /BitsPerComponent %d\n" + " /Filter /DCTDecode\n", + info.width, + info.height, + info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB", + info.bits_per_component); + if (unlikely (status)) + return status; + + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + status = _cairo_pdf_surface_close_stream (surface); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_resource_t resource, + cairo_bool_t interpolate) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_image (surface, image, + &resource, interpolate); + if (unlikely (status)) + goto BAIL; + +BAIL: + _cairo_surface_release_source_image (source, image, image_extra); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern, + cairo_pdf_resource_t *resource, + int *width, + int *height, + int *origin_x, + int *origin_y) +{ + cairo_image_surface_t *image; + cairo_surface_t *pad_image; + void *image_extra; + cairo_status_t status; + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; + int x = 0; + int y = 0; + cairo_bool_t interpolate; + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + if (unlikely (status)) + return status; + + pad_image = &image->base; + if (pattern->base.extend == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, &pdf_pattern->extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + x = -rect.x; + y = -rect.y; + + pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, + rect.width, + rect.height); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } + + _cairo_pattern_init_for_surface (&pad_pattern, &image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL, + pad_image, + 0, 0, + 0, 0, + 0, 0, + rect.width, + rect.height, + NULL); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) + goto BAIL; + } + + switch (pdf_pattern->pattern->filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = TRUE; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = FALSE; + break; + } + + *resource = _cairo_pdf_surface_new_object (surface); + if (resource->id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, + resource, interpolate); + if (unlikely (status)) + goto BAIL; + + *width = ((cairo_image_surface_t *)pad_image)->width; + *height = ((cairo_image_surface_t *)pad_image)->height; + *origin_x = x; + *origin_y = y; + +BAIL: + if (pad_image != &image->base) + cairo_surface_destroy (pad_image); + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + + +static cairo_status_t +_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + cairo_pdf_resource_t resource) +{ + double old_width, old_height; + cairo_paginated_mode_t old_paginated_mode; + cairo_rectangle_int_t recording_extents; + cairo_bool_t is_bounded; + cairo_status_t status; + int alpha = 0; + + is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents); + assert (is_bounded); + + old_width = surface->width; + old_height = surface->height; + old_paginated_mode = surface->paginated_mode; + + _cairo_pdf_surface_set_size_internal (surface, + recording_extents.width, + recording_extents.height); + /* Patterns are emitted after fallback images. The paginated mode + * needs to be set to _RENDER while the recording surface is replayed + * back to this surface. + */ + surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; + _cairo_pdf_group_resources_clear (&surface->resources); + status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); + if (unlikely (status)) + return status; + + if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", + alpha, + surface->width, + surface->height); + } + + status = _cairo_recording_surface_replay_region (recording_surface, + NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_content_stream (surface); + + _cairo_pdf_surface_set_size_internal (surface, + old_width, + old_height); + surface->paginated_mode = old_paginated_mode; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_recording_subsurface (cairo_pdf_surface_t *surface, + cairo_surface_t *recording_surface, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t resource) +{ + double old_width, old_height; + cairo_paginated_mode_t old_paginated_mode; + cairo_status_t status; + int alpha = 0; + + old_width = surface->width; + old_height = surface->height; + old_paginated_mode = surface->paginated_mode; + + _cairo_pdf_surface_set_size_internal (surface, + extents->width, + extents->height); + /* Patterns are emitted after fallback images. The paginated mode + * needs to be set to _RENDER while the recording surface is replayed + * back to this surface. + */ + surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; + _cairo_pdf_group_resources_clear (&surface->resources); + status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); + if (unlikely (status)) + return status; + + if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", + alpha, + surface->width, + surface->height); + } + + status = _cairo_recording_surface_replay_region (recording_surface, + extents, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_content_stream (surface); + + _cairo_pdf_surface_set_size_internal (surface, + old_width, + old_height); + surface->paginated_mode = old_paginated_mode; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, + cairo_pdf_source_surface_t *src_surface) +{ + if (src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (src_surface->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) src_surface->surface; + return _cairo_pdf_surface_emit_recording_subsurface (surface, + sub->target, + &sub->extents, + src_surface->hash_entry->surface_res); + } else { + return _cairo_pdf_surface_emit_recording_surface (surface, + src_surface->surface, + src_surface->hash_entry->surface_res); + } + } else { + return _cairo_pdf_surface_emit_image_surface (surface, + src_surface->surface, + src_surface->hash_entry->surface_res, + src_surface->hash_entry->interpolate); + } +} + +static cairo_status_t +_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; + cairo_status_t status; + cairo_pdf_resource_t pattern_resource = {0}; + cairo_matrix_t cairo_p2d, pdf_p2d; + cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base); + double xstep, ystep; + int pattern_width = 0; /* squelch bogus compiler warning */ + int pattern_height = 0; /* squelch bogus compiler warning */ + int origin_x = 0; /* squelch bogus compiler warning */ + int origin_y = 0; /* squelch bogus compiler warning */ + int bbox_x, bbox_y; + char draw_surface[200]; + + if (pattern->base.extend == CAIRO_EXTEND_PAD && + pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + { + status = _cairo_pdf_surface_emit_padded_image_surface (surface, + pdf_pattern, + &pattern_resource, + &pattern_width, + &pattern_height, + &origin_x, + &origin_y); + } + else + { + status = _cairo_pdf_surface_add_source_surface (surface, + pattern->surface, + pdf_pattern->pattern->filter, + &pattern_resource, + &pattern_width, + &pattern_height); + } + if (unlikely (status)) + return status; + + bbox_x = pattern_width; + bbox_y = pattern_height; + switch (extend) { + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_NONE: + { + /* In PS/PDF, (as far as I can tell), all patterns are + * repeating. So we support cairo's EXTEND_NONE semantics + * by setting the repeat step size to a size large enough + * to guarantee that no more than a single occurrence will + * be visible. + * + * First, map the surface extents into pattern space (since + * xstep and ystep are in pattern space). Then use an upper + * bound on the length of the diagonal of the pattern image + * and the surface as repeat size. This guarantees to never + * repeat visibly. + */ + double x1 = 0.0, y1 = 0.0; + double x2 = surface->width, y2 = surface->height; + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &x1, &y1, &x2, &y2, + NULL); + + /* Rather than computing precise bounds of the union, just + * add the surface extents unconditionally. We only + * required an answer that's large enough, we don't really + * care if it's not as tight as possible.*/ + xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + + pattern_width + pattern_height); + } + break; + case CAIRO_EXTEND_REPEAT: + xstep = pattern_width; + ystep = pattern_height; + break; + case CAIRO_EXTEND_REFLECT: + bbox_x = pattern_width*2; + bbox_y = pattern_height*2; + xstep = bbox_x; + ystep = bbox_y; + break; + /* All the rest (if any) should have been analyzed away, so this + * case should be unreachable. */ + default: + ASSERT_NOT_REACHED; + xstep = 0; + ystep = 0; + } + + /* At this point, (that is, within the surface backend interface), + * the pattern's matrix maps from cairo's device space to cairo's + * pattern space, (both with their origin at the upper-left, and + * cairo's pattern space of size width,height). + * + * Then, we must emit a PDF pattern object that maps from its own + * pattern space, (which has a size that we establish in the BBox + * dictionary entry), to the PDF page's *initial* space, (which + * does not benefit from the Y-axis flipping matrix that we emit + * on each page). So the PDF patterns matrix maps from a + * (width,height) pattern space to a device space with the origin + * in the lower-left corner. + * + * So to handle all of that, we start with an identity matrix for + * the PDF pattern to device matrix. We translate it up by the + * image height then flip it in the Y direction, (moving us from + * the PDF origin to cairo's origin). We then multiply in the + * inverse of the cairo pattern matrix, (since it maps from device + * to pattern, while we're setting up pattern to device). Finally, + * we translate back down by the image height and flip again to + * end up at the lower-left origin that PDF expects. + * + * Additionally, within the stream that paints the pattern itself, + * we are using a PDF image object that has a size of (1,1) so we + * have to scale it up by the image width and height to fill our + * pattern cell. + */ + cairo_p2d = pattern->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); + cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y); + cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + status = _cairo_pdf_surface_open_stream (surface, + &pdf_pattern->pattern_res, + FALSE, + " /PatternType 1\n" + " /BBox [0 0 %d %d]\n" + " /XStep %f\n" + " /YStep %f\n" + " /TilingType 1\n" + " /PaintType 1\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Resources << /XObject << /x%d %d 0 R >> >>\n", + bbox_x, bbox_y, + xstep, ystep, + pdf_p2d.xx, pdf_p2d.yx, + pdf_p2d.xy, pdf_p2d.yy, + pdf_p2d.x0, pdf_p2d.y0, + pattern_resource.id, + pattern_resource.id); + if (unlikely (status)) + return status; + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + snprintf(draw_surface, + sizeof (draw_surface), + "/x%d Do\n", + pattern_resource.id); + } else { + snprintf(draw_surface, + sizeof (draw_surface), + "q %d 0 0 %d 0 0 cm /x%d Do Q", + pattern_width, + pattern_height, + pattern_resource.id); + } + + if (extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->output, + "q 0 0 %d %d re W n %s Q\n" + "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n" + "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n" + "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n", + pattern_width, pattern_height, + draw_surface, + pattern_width*2, pattern_width, pattern_height, + draw_surface, + pattern_height*2, pattern_width, pattern_height, + draw_surface, + pattern_width*2, pattern_height*2, pattern_width, pattern_height, + draw_surface); + } else { + _cairo_output_stream_printf (surface->output, + " %s \n", + draw_surface); + } + + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + return _cairo_output_stream_get_status (surface->output); +} + +typedef struct _cairo_pdf_color_stop { + double offset; + double color[4]; + cairo_pdf_resource_t resource; +} cairo_pdf_color_stop_t; + +static cairo_status_t +cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, + cairo_pdf_color_stop_t *stop1, + cairo_pdf_color_stop_t *stop2, + cairo_pdf_resource_t *function) +{ + int num_elems, i; + cairo_pdf_rgb_linear_function_t elem; + cairo_pdf_resource_t res; + cairo_status_t status; + + num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem); + if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0) + continue; + if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0) + continue; + *function = elem.resource; + return CAIRO_STATUS_SUCCESS; + } + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f %f %f ]\n" + " /C1 [ %f %f %f ]\n" + " /N 1\n" + ">>\n" + "endobj\n", + res.id, + stop1->color[0], + stop1->color[1], + stop1->color[2], + stop2->color[0], + stop2->color[1], + stop2->color[2]); + + elem.resource = res; + memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3); + memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3); + + status = _cairo_array_append (&surface->rgb_linear_functions, &elem); + *function = res; + + return status; +} + +static cairo_status_t +cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, + cairo_pdf_color_stop_t *stop1, + cairo_pdf_color_stop_t *stop2, + cairo_pdf_resource_t *function) +{ + int num_elems, i; + cairo_pdf_alpha_linear_function_t elem; + cairo_pdf_resource_t res; + cairo_status_t status; + + num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem); + if (elem.alpha1 != stop1->color[3]) + continue; + if (elem.alpha2 != stop2->color[3]) + continue; + *function = elem.resource; + return CAIRO_STATUS_SUCCESS; + } + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f ]\n" + " /C1 [ %f ]\n" + " /N 1\n" + ">>\n" + "endobj\n", + res.id, + stop1->color[3], + stop2->color[3]); + + elem.resource = res; + elem.alpha1 = stop1->color[3]; + elem.alpha2 = stop2->color[3]; + + status = _cairo_array_append (&surface->alpha_linear_functions, &elem); + *function = res; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, + unsigned int n_stops, + cairo_pdf_color_stop_t *stops, + cairo_bool_t is_alpha, + cairo_pdf_resource_t *function) +{ + cairo_pdf_resource_t res; + unsigned int i; + cairo_status_t status; + + /* emit linear gradients between pairs of subsequent stops... */ + for (i = 0; i < n_stops-1; i++) { + if (is_alpha) { + status = cairo_pdf_surface_emit_alpha_linear_function (surface, + &stops[i], + &stops[i+1], + &stops[i].resource); + if (unlikely (status)) + return status; + } else { + status = cairo_pdf_surface_emit_rgb_linear_function (surface, + &stops[i], + &stops[i+1], + &stops[i].resource); + if (unlikely (status)) + return status; + } + } + + /* ... and stitch them together */ + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 3\n" + " /Domain [ %f %f ]\n", + res.id, + stops[0].offset, + stops[n_stops - 1].offset); + + _cairo_output_stream_printf (surface->output, + " /Functions [ "); + for (i = 0; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->output, + "%d 0 R ", stops[i].resource.id); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Bounds [ "); + for (i = 1; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->output, + "%f ", stops[i].offset); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Encode [ "); + for (i = 1; i < n_stops; i++) + _cairo_output_stream_printf (surface->output, + "0 1 "); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + *function = res; + + return _cairo_output_stream_get_status (surface->output); +} + + +static void +calc_gradient_color (cairo_pdf_color_stop_t *new_stop, + cairo_pdf_color_stop_t *stop1, + cairo_pdf_color_stop_t *stop2) +{ + int i; + double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); + + for (i = 0; i < 4; i++) + new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); +} + +#define COLOR_STOP_EPSILON 1e-6 + +static cairo_status_t +_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, + cairo_gradient_pattern_t *pattern, + cairo_pdf_resource_t *color_function, + cairo_pdf_resource_t *alpha_function) +{ + cairo_pdf_color_stop_t *allstops, *stops; + unsigned int n_stops; + unsigned int i; + cairo_bool_t emit_alpha = FALSE; + cairo_status_t status; + + color_function->id = 0; + alpha_function->id = 0; + + allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); + if (unlikely (allstops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + stops = &allstops[1]; + n_stops = pattern->n_stops; + + for (i = 0; i < n_stops; i++) { + stops[i].color[0] = pattern->stops[i].color.red; + stops[i].color[1] = pattern->stops[i].color.green; + stops[i].color[2] = pattern->stops[i].color.blue; + stops[i].color[3] = pattern->stops[i].color.alpha; + if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3])) + emit_alpha = TRUE; + stops[i].offset = pattern->stops[i].offset; + } + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) { + if (stops[0].offset > COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) + memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t)); + else + calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); + stops = allstops; + n_stops++; + } + stops[0].offset = 0.0; + + if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + memcpy (&stops[n_stops], + &stops[n_stops - 1], + sizeof (cairo_pdf_color_stop_t)); + } else { + calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); + } + n_stops++; + } + stops[n_stops-1].offset = 1.0; + } + + if (n_stops <= 2) { + /* no need for stitched function */ + status = cairo_pdf_surface_emit_rgb_linear_function (surface, + &stops[0], + &stops[n_stops - 1], + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = cairo_pdf_surface_emit_alpha_linear_function (surface, + &stops[0], + &stops[n_stops - 1], + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } else { + /* multiple stops: stitch. XXX possible optimization: regularly spaced + * stops do not require stitching. XXX */ + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + n_stops, + stops, + FALSE, + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + n_stops, + stops, + TRUE, + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } + +BAIL: + free (allstops); + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, + cairo_gradient_pattern_t *pattern, + cairo_pdf_resource_t *function, + int begin, + int end) +{ + cairo_pdf_resource_t res; + int i; + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 3\n" + " /Domain [ %d %d ]\n", + res.id, + begin, + end); + + _cairo_output_stream_printf (surface->output, + " /Functions [ "); + for (i = begin; i < end; i++) + _cairo_output_stream_printf (surface->output, + "%d 0 R ", function->id); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Bounds [ "); + for (i = begin + 1; i < end; i++) + _cairo_output_stream_printf (surface->output, + "%d ", i); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Encode [ "); + for (i = begin; i < end; i++) { + if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->output, + "1 0 "); + } else { + _cairo_output_stream_printf (surface->output, + "0 1 "); + } + } + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + *function = res; + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t gstate_resource, + cairo_pdf_resource_t gradient_mask) +{ + cairo_pdf_resource_t smask_resource; + cairo_status_t status; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /FormType 1\n" + " /BBox [ 0 0 %f %f ]\n" + " /Resources\n" + " << /ExtGState\n" + " << /a0 << /ca 1 /CA 1 >>" + " >>\n" + " /Pattern\n" + " << /p%d %d 0 R >>\n" + " >>\n" + " /Group\n" + " << /Type /Group\n" + " /S /Transparency\n" + " /CS /DeviceGray\n" + " >>\n", + surface->width, + surface->height, + gradient_mask.id, + gradient_mask.id); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q\n" + "/a0 gs\n" + "/Pattern cs /p%d scn\n" + "0 0 %f %f re\n" + "f\n" + "Q\n", + gradient_mask.id, + surface->width, + surface->height); + + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + smask_resource = _cairo_pdf_surface_new_object (surface); + if (smask_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Mask\n" + " /S /Luminosity\n" + " /G %d 0 R\n" + ">>\n" + "endobj\n", + smask_resource.id, + surface->pdf_stream.self.id); + + /* Create GState which uses the transparency group as an SMask. */ + _cairo_pdf_surface_update_object (surface, gstate_resource); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ExtGState\n" + " /SMask %d 0 R\n" + " /ca 1\n" + " /CA 1\n" + " /AIS false\n" + ">>\n" + "endobj\n", + gstate_resource.id, + smask_resource.id); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern; + cairo_pdf_resource_t color_function, alpha_function; + double x1, y1, x2, y2; + double _x1, _y1, _x2, _y2; + cairo_matrix_t pat_to_pdf; + cairo_extend_t extend; + cairo_status_t status; + cairo_gradient_pattern_t *gradient = &pattern->base; + double first_stop, last_stop; + int repeat_begin = 0, repeat_end = 1; + + assert (pattern->base.n_stops != 0); + + extend = cairo_pattern_get_extend (pdf_pattern->pattern); + + pat_to_pdf = pattern->base.base.matrix; + status = cairo_matrix_invert (&pat_to_pdf); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); + first_stop = gradient->stops[0].offset; + last_stop = gradient->stops[gradient->n_stops - 1].offset; + + if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + double dx, dy; + int x_rep = 0, y_rep = 0; + + x1 = _cairo_fixed_to_double (pattern->p1.x); + y1 = _cairo_fixed_to_double (pattern->p1.y); + cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1); + + x2 = _cairo_fixed_to_double (pattern->p2.x); + y2 = _cairo_fixed_to_double (pattern->p2.y); + cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2); + + dx = fabs (x2 - x1); + dy = fabs (y2 - y1); + if (dx > 1e-6) + x_rep = ceil (surface->width/dx); + if (dy > 1e-6) + y_rep = ceil (surface->height/dy); + + repeat_end = MAX (x_rep, y_rep); + repeat_begin = -repeat_end; + first_stop = repeat_begin; + last_stop = repeat_end; + } + + /* PDF requires the first and last stop to be the same as the line + * coordinates. For repeating patterns this moves the line + * coordinates out to the begin/end of the repeating function. For + * non repeating patterns this may move the line coordinates in if + * there are not stops at offset 0 and 1. */ + x1 = _cairo_fixed_to_double (pattern->p1.x); + y1 = _cairo_fixed_to_double (pattern->p1.y); + x2 = _cairo_fixed_to_double (pattern->p2.x); + y2 = _cairo_fixed_to_double (pattern->p2.y); + + _x1 = x1 + (x2 - x1)*first_stop; + _y1 = y1 + (y2 - y1)*first_stop; + _x2 = x1 + (x2 - x1)*last_stop; + _y2 = y1 + (y2 - y1)*last_stop; + + x1 = _x1; + x2 = _x2; + y1 = _y1; + y2 = _y2; + + /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a + * Type 2 function is used by itself without a stitching + * function. Type 2 functions always have the domain [0 1] */ + if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || + pattern->base.base.extend == CAIRO_EXTEND_PAD) && + gradient->n_stops == 2) { + first_stop = 0.0; + last_stop = 1.0; + } + + status = _cairo_pdf_surface_emit_pattern_stops (surface, + &pattern->base, + &color_function, + &alpha_function); + if (unlikely (status)) + return status; + + if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + status = _cairo_pdf_surface_emit_repeating_function (surface, + &pattern->base, + &color_function, + repeat_begin, + repeat_end); + if (unlikely (status)) + return status; + + if (alpha_function.id != 0) { + status = _cairo_pdf_surface_emit_repeating_function (surface, + &pattern->base, + &alpha_function, + repeat_begin, + repeat_end); + if (unlikely (status)) + return status; + } + } + + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Shading\n" + " << /ShadingType 2\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f ]\n" + " /Domain [ %f %f ]\n" + " /Function %d 0 R\n", + pdf_pattern->pattern_res.id, + pat_to_pdf.xx, pat_to_pdf.yx, + pat_to_pdf.xy, pat_to_pdf.yy, + pat_to_pdf.x0, pat_to_pdf.y0, + x1, y1, x2, y2, + first_stop, last_stop, + color_function.id); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->output, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " >>\n" + ">>\n" + "endobj\n"); + + if (alpha_function.id != 0) { + cairo_pdf_resource_t mask_resource; + + assert (pdf_pattern->gstate_res.id != 0); + + /* Create pattern for SMask. */ + mask_resource = _cairo_pdf_surface_new_object (surface); + if (mask_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Shading\n" + " << /ShadingType 2\n" + " /ColorSpace /DeviceGray\n" + " /Coords [ %f %f %f %f ]\n" + " /Domain [ %f %f ]\n" + " /Function %d 0 R\n", + mask_resource.id, + pat_to_pdf.xx, pat_to_pdf.yx, + pat_to_pdf.xy, pat_to_pdf.yy, + pat_to_pdf.x0, pat_to_pdf.y0, + x1, y1, x2, y2, + first_stop, last_stop, + alpha_function.id); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->output, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " >>\n" + ">>\n" + "endobj\n"); + status = _cairo_pdf_surface_add_pattern (surface, mask_resource); + if (unlikely (status)) + return status; + + status = cairo_pdf_surface_emit_transparency_group (surface, + pdf_pattern->gstate_res, + mask_resource); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_pdf_resource_t color_function, alpha_function; + double x1, y1, x2, y2, r1, r2; + cairo_matrix_t pat_to_pdf; + cairo_extend_t extend; + cairo_status_t status; + cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern; + + assert (pattern->base.n_stops != 0); + + extend = cairo_pattern_get_extend (pdf_pattern->pattern); + + status = _cairo_pdf_surface_emit_pattern_stops (surface, + &pattern->base, + &color_function, + &alpha_function); + if (unlikely (status)) + return status; + + pat_to_pdf = pattern->base.base.matrix; + status = cairo_matrix_invert (&pat_to_pdf); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); + x1 = _cairo_fixed_to_double (pattern->c1.x); + y1 = _cairo_fixed_to_double (pattern->c1.y); + r1 = _cairo_fixed_to_double (pattern->r1); + x2 = _cairo_fixed_to_double (pattern->c2.x); + y2 = _cairo_fixed_to_double (pattern->c2.y); + r2 = _cairo_fixed_to_double (pattern->r2); + + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Shading\n" + " << /ShadingType 3\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f %f %f ]\n" + " /Function %d 0 R\n", + pdf_pattern->pattern_res.id, + pat_to_pdf.xx, pat_to_pdf.yx, + pat_to_pdf.xy, pat_to_pdf.yy, + pat_to_pdf.x0, pat_to_pdf.y0, + x1, y1, r1, x2, y2, r2, + color_function.id); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->output, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " >>\n" + ">>\n" + "endobj\n"); + + if (alpha_function.id != 0) { + cairo_pdf_resource_t mask_resource; + + assert (pdf_pattern->gstate_res.id != 0); + + /* Create pattern for SMask. */ + mask_resource = _cairo_pdf_surface_new_object (surface); + if (mask_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ %f %f %f %f %f %f ]\n" + " /Shading\n" + " << /ShadingType 3\n" + " /ColorSpace /DeviceGray\n" + " /Coords [ %f %f %f %f %f %f ]\n" + " /Function %d 0 R\n", + mask_resource.id, + pat_to_pdf.xx, pat_to_pdf.yx, + pat_to_pdf.xy, pat_to_pdf.yy, + pat_to_pdf.x0, pat_to_pdf.y0, + x1, y1, r1, x2, y2, r2, + alpha_function.id); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->output, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " >>\n" + ">>\n" + "endobj\n"); + + status = cairo_pdf_surface_emit_transparency_group (surface, + pdf_pattern->gstate_res, + mask_resource); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) +{ + double old_width, old_height; + cairo_status_t status; + + old_width = surface->width; + old_height = surface->height; + _cairo_pdf_surface_set_size_internal (surface, + pdf_pattern->width, + pdf_pattern->height); + + switch (pdf_pattern->pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + ASSERT_NOT_REACHED; + status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern); + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern); + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern); + break; + + default: + ASSERT_NOT_REACHED; + status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + break; + } + + _cairo_pdf_surface_set_size_internal (surface, + old_width, + old_height); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, + cairo_surface_pattern_t *source) +{ + cairo_pdf_resource_t surface_res; + int width, height; + cairo_matrix_t cairo_p2d, pdf_p2d; + cairo_status_t status; + int alpha; + + status = _cairo_pdf_surface_add_source_surface (surface, + source->surface, + source->base.filter, + &surface_res, + &width, + &height); + if (unlikely (status)) + return status; + + cairo_p2d = source->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + pdf_p2d = surface->cairo_to_pdf; + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); + cairo_matrix_translate (&pdf_p2d, 0.0, height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + cairo_matrix_scale (&pdf_p2d, width, height); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pdf_p2d)) { + _cairo_output_stream_printf (surface->output, + "%f %f %f %f %f %f cm\n", + pdf_p2d.xx, pdf_p2d.yx, + pdf_p2d.xy, pdf_p2d.yy, + pdf_p2d.x0, pdf_p2d.y0); + } + + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs /x%d Do\n", + alpha, + surface_res.id); + + return _cairo_pdf_surface_add_xobject (surface, surface_res); +} + +static cairo_status_t +_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_status_t status; + + if (op == surface->current_operator) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/b%d gs\n", op); + surface->current_operator = op; + _cairo_pdf_surface_add_operator (surface, op); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_pdf_resource_t pattern_res, + cairo_bool_t is_stroke) +{ + cairo_status_t status; + int alpha; + const cairo_color_t *solid_color = NULL; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; + + solid_color = &solid->color; + } + + if (solid_color != NULL) { + if (surface->current_pattern_is_solid_color == FALSE || + surface->current_color_red != solid_color->red || + surface->current_color_green != solid_color->green || + surface->current_color_blue != solid_color->blue || + surface->current_color_is_stroke != is_stroke) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%f %f %f ", + solid_color->red, + solid_color->green, + solid_color->blue); + + if (is_stroke) + _cairo_output_stream_printf (surface->output, "RG "); + else + _cairo_output_stream_printf (surface->output, "rg "); + + surface->current_color_red = solid_color->red; + surface->current_color_green = solid_color->green; + surface->current_color_blue = solid_color->blue; + surface->current_color_is_stroke = is_stroke; + } + + if (surface->current_pattern_is_solid_color == FALSE || + surface->current_color_alpha != solid_color->alpha) + { + status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs\n", + alpha); + surface->current_color_alpha = solid_color->alpha; + } + + surface->current_pattern_is_solid_color = TRUE; + } else { + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_pattern (surface, pattern_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + /* fill-stroke calls select_pattern twice. Don't save if the + * gstate is already saved. */ + if (!surface->select_pattern_gstate_saved) + _cairo_output_stream_printf (surface->output, "q "); + + if (is_stroke) { + _cairo_output_stream_printf (surface->output, + "/Pattern CS /p%d SCN ", + pattern_res.id); + } else { + _cairo_output_stream_printf (surface->output, + "/Pattern cs /p%d scn ", + pattern_res.id); + } + _cairo_output_stream_printf (surface->output, + "/a%d gs\n", + alpha); + surface->select_pattern_gstate_saved = TRUE; + surface->current_pattern_is_solid_color = FALSE; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + + if (surface->select_pattern_gstate_saved) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + _cairo_pdf_operators_reset (&surface->pdf_operators); + } + surface->select_pattern_gstate_saved = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_show_page (void *abstract_surface) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_pdf_surface_close_content_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_write_page (surface); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_clear (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_pdf_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + /* XXX: The conversion to integers here is pretty bogus, (not to + * mention the arbitrary limitation of width to a short(!). We + * may need to come up with a better interface for get_size. + */ + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); + + return TRUE; +} + +static void +_cairo_pdf_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); +} + +static cairo_pdf_resource_t +_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t info; + + info = _cairo_pdf_surface_new_object (surface); + if (info.id == 0) + return info; + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Creator (cairo %s (http://cairographics.org))\n" + " /Producer (cairo %s (http://cairographics.org))\n" + ">>\n" + "endobj\n", + info.id, + cairo_version_string (), + cairo_version_string ()); + + return info; +} + +static void +_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t page; + int num_pages, i; + + _cairo_pdf_surface_update_object (surface, surface->pages_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pages\n" + " /Kids [ ", + surface->pages_resource.id); + + num_pages = _cairo_array_num_elements (&surface->pages); + for (i = 0; i < num_pages; i++) { + _cairo_array_copy_element (&surface->pages, i, &page); + _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id); + } + + _cairo_output_stream_printf (surface->output, "]\n"); + _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages); + + + /* TODO: Figure out which other defaults to be inherited by /Page + * objects. */ + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); +} + +static cairo_status_t +_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, + const char *utf8) +{ + uint16_t *utf16 = NULL; + int utf16_len = 0; + cairo_status_t status; + int i; + + if (utf8 && *utf8) { + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (surface->output, "<"); + if (utf16 == NULL || utf16_len == 0) { + /* According to the "ToUnicode Mapping File Tutorial" + * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf + * + * Glyphs that do not map to a Unicode code point must be + * mapped to 0xfffd "REPLACEMENT CHARACTER". + */ + _cairo_output_stream_printf (surface->output, + "fffd"); + } else { + for (i = 0; i < utf16_len; i++) + _cairo_output_stream_printf (surface->output, + "%04x", (int) (utf16[i])); + } + _cairo_output_stream_printf (surface->output, ">"); + + if (utf16) + free (utf16); + + return CAIRO_STATUS_SUCCESS; +} + +/* Bob Jenkins hash + * + * Public domain code from: + * http://burtleburtle.net/bob/hash/doobs.html + */ + +#define HASH_MIX(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +static uint32_t +_hash_data (const unsigned char *data, int length, uint32_t initval) +{ + uint32_t a, b, c, len; + + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + while (len >= 12) { + a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); + b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); + c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); + HASH_MIX (a,b,c); + data += 12; + len -= 12; + } + + c += length; + switch(len) { + case 11: c+= ((uint32_t) data[10] << 24); + case 10: c+= ((uint32_t) data[9] << 16); + case 9 : c+= ((uint32_t) data[8] << 8); + case 8 : b+= ((uint32_t) data[7] << 24); + case 7 : b+= ((uint32_t) data[6] << 16); + case 6 : b+= ((uint32_t) data[5] << 8); + case 5 : b+= data[4]; + case 4 : a+= ((uint32_t) data[3] << 24); + case 3 : a+= ((uint32_t) data[2] << 16); + case 2 : a+= ((uint32_t) data[1] << 8); + case 1 : a+= data[0]; + } + HASH_MIX (a,b,c); + + return c; +} + +static void +_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, + const char *font_name, + char *tag) +{ + uint32_t hash; + int i; + long numerator; + ldiv_t d; + + hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); + hash = _hash_data ((unsigned char *) (font_subset->glyphs), + font_subset->num_glyphs * sizeof(unsigned long), hash); + + numerator = abs (hash); + for (i = 0; i < 6; i++) { + d = ldiv (numerator, 26); + numerator = d.quot; + tag[i] = 'A' + d.rem; + } + tag[i] = 0; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset, + cairo_bool_t is_composite, + cairo_pdf_resource_t *stream) +{ + unsigned int i, num_bfchar; + cairo_int_status_t status; + + stream->id = 0; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + surface->compress_content, + NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + " /Ordering (UCS)\n" + " /Supplement 0\n" + ">> def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n"); + + if (is_composite) { + _cairo_output_stream_printf (surface->output, + "<0000> \n"); + } else { + _cairo_output_stream_printf (surface->output, + "<00> \n"); + } + + _cairo_output_stream_printf (surface->output, + "endcodespacerange\n"); + + if (font_subset->is_scaled) { + /* Type 3 fonts include glyph 0 in the subset */ + num_bfchar = font_subset->num_glyphs; + + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + _cairo_output_stream_printf (surface->output, "<%02x> ", i); + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } + } else { + /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ + num_bfchar = font_subset->num_glyphs - 1; + + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + if (is_composite) + _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); + else + _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); + + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i + 1]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } + } + + _cairo_output_stream_printf (surface->output, + "endbfchar\n"); + + _cairo_output_stream_printf (surface->output, + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n"); + + *stream = surface->pdf_stream.self; + return _cairo_pdf_surface_close_stream (surface); +} + +#define PDF_UNITS_PER_EM 1000 + +static cairo_status_t +_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset, + cairo_cff_subset_t *subset) +{ + cairo_pdf_resource_t stream, descriptor, cidfont_dict; + cairo_pdf_resource_t subset_resource, to_unicode_stream; + cairo_pdf_font_t font; + unsigned int i; + cairo_status_t status; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->ps_name, tag); + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Subtype /CIDFontType0C\n"); + if (unlikely (status)) + return status; + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, + subset->data, subset->data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, TRUE, + &to_unicode_stream); + if (_cairo_status_is_error (status)) + return status; + + descriptor = _cairo_pdf_surface_new_object (surface); + if (descriptor.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /FontDescriptor\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset->ps_name); + + if (subset->font_name) { + _cairo_output_stream_printf (surface->output, + " /FontFamily (%s)\n", + subset->font_name); + } + + _cairo_output_stream_printf (surface->output, + " /Flags 4\n" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /FontFile3 %u 0 R\n" + ">>\n" + "endobj\n", + (long)(subset->x_min*PDF_UNITS_PER_EM), + (long)(subset->y_min*PDF_UNITS_PER_EM), + (long)(subset->x_max*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + (long)(subset->ascent*PDF_UNITS_PER_EM), + (long)(subset->descent*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + stream.id); + + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType0\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset->ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset->ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + status = _cairo_array_append (&surface->fonts, &font); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_status_t status; + cairo_cff_subset_t subset; + char name[64]; + + snprintf (name, sizeof name, "CairoFont-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_cff_subset_init (&subset, name, font_subset); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); + + _cairo_cff_subset_fini (&subset); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_status_t status; + cairo_cff_subset_t subset; + char name[64]; + + snprintf (name, sizeof name, "CairoFont-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_cff_fallback_init (&subset, name, font_subset); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); + + _cairo_cff_fallback_fini (&subset); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset, + cairo_type1_subset_t *subset) +{ + cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; + cairo_pdf_font_t font; + cairo_status_t status; + unsigned long length; + unsigned int i; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->base_font, tag); + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + length = subset->header_length + subset->data_length + subset->trailer_length; + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Length1 %lu\n" + " /Length2 %lu\n" + " /Length3 %lu\n", + subset->header_length, + subset->data_length, + subset->trailer_length); + if (unlikely (status)) + return status; + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, subset->data, length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, FALSE, + &to_unicode_stream); + if (_cairo_status_is_error (status)) + return status; + + descriptor = _cairo_pdf_surface_new_object (surface); + if (descriptor.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /FontDescriptor\n" + " /FontName /%s+%s\n" + " /Flags 4\n" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /FontFile %u 0 R\n" + ">>\n" + "endobj\n", + descriptor.id, + tag, + subset->base_font, + (long)(subset->x_min*PDF_UNITS_PER_EM), + (long)(subset->y_min*PDF_UNITS_PER_EM), + (long)(subset->x_max*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + (long)(subset->ascent*PDF_UNITS_PER_EM), + (long)(subset->descent*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + stream.id); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type1\n" + " /BaseFont /%s+%s\n" + " /FirstChar 0\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n" + " /Widths [", + subset_resource.id, + tag, + subset->base_font, + font_subset->num_glyphs - 1, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + + _cairo_output_stream_printf (surface->output, + " ]\n"); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + return _cairo_array_append (&surface->fonts, &font); +} + +#if CAIRO_HAS_FT_FONT +static cairo_status_t +_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_status_t status; + cairo_type1_subset_t subset; + char name[64]; + + snprintf (name, sizeof name, "CairoFont-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); + + _cairo_type1_subset_fini (&subset); + return status; +} +#endif + +static cairo_status_t +_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_status_t status; + cairo_type1_subset_t subset; + char name[64]; + + snprintf (name, sizeof name, "CairoFont-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); + + _cairo_type1_fallback_fini (&subset); + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_pdf_resource_t stream, descriptor, cidfont_dict; + cairo_pdf_resource_t subset_resource, to_unicode_stream; + cairo_status_t status; + cairo_pdf_font_t font; + cairo_truetype_subset_t subset; + unsigned int i; + char tag[10]; + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_truetype_subset_init (&subset, font_subset); + if (unlikely (status)) + return status; + + _create_font_subset_tag (font_subset, subset.ps_name, tag); + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Length1 %lu\n", + subset.data_length); + if (unlikely (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, + subset.data, subset.data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, TRUE, + &to_unicode_stream); + if (_cairo_status_is_error (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + descriptor = _cairo_pdf_surface_new_object (surface); + if (descriptor.id == 0) { + _cairo_truetype_subset_fini (&subset); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /FontDescriptor\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset.ps_name); + + if (subset.font_name) { + _cairo_output_stream_printf (surface->output, + " /FontFamily (%s)\n", + subset.font_name); + } + + _cairo_output_stream_printf (surface->output, + " /Flags 4\n" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /FontFile2 %u 0 R\n" + ">>\n" + "endobj\n", + (long)(subset.x_min*PDF_UNITS_PER_EM), + (long)(subset.y_min*PDF_UNITS_PER_EM), + (long)(subset.x_max*PDF_UNITS_PER_EM), + (long)(subset.y_max*PDF_UNITS_PER_EM), + (long)(subset.ascent*PDF_UNITS_PER_EM), + (long)(subset.descent*PDF_UNITS_PER_EM), + (long)(subset.y_max*PDF_UNITS_PER_EM), + stream.id); + + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) { + _cairo_truetype_subset_fini (&subset); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType2\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset.ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset.widths[i]*PDF_UNITS_PER_EM)); + + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset.ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + status = _cairo_array_append (&surface->fonts, &font); + + _cairo_truetype_subset_fini (&subset); + + return status; +} + +static cairo_status_t +_cairo_pdf_emit_imagemask (cairo_image_surface_t *image, + cairo_output_stream_t *stream) +{ + uint8_t *byte, output_byte; + int row, col, num_cols; + + /* The only image type supported by Type 3 fonts are 1-bit image + * masks */ + assert (image->format == CAIRO_FORMAT_A1); + + _cairo_output_stream_printf (stream, + "BI\n" + "/IM true\n" + "/W %d\n" + "/H %d\n" + "/BPC 1\n" + "/D [1 0]\n", + image->width, + image->height); + + _cairo_output_stream_printf (stream, + "ID "); + + num_cols = (image->width + 7) / 8; + for (row = 0; row < image->height; row++) { + byte = image->data + row * image->stride; + for (col = 0; col < num_cols; col++) { + output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + _cairo_output_stream_write (stream, &output_byte, 1); + byte++; + } + } + + _cairo_output_stream_printf (stream, + "\nEI\n"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_status_t +_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status2; + unsigned int i; + cairo_surface_t *type3_surface; + cairo_output_stream_t *null_stream; + + null_stream = _cairo_null_stream_create (); + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + null_stream, + _cairo_pdf_emit_imagemask, + surface->font_subsets); + if (unlikely (type3_surface->status)) { + status2 = _cairo_output_stream_destroy (null_stream); + return type3_surface->status; + } + + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, + _cairo_pdf_surface_add_font, + surface); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, + font_subset->glyphs[i]); + if (unlikely (status)) + break; + } + + cairo_surface_destroy (type3_surface); + status2 = _cairo_output_stream_destroy (null_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; + cairo_pdf_font_t font; + double *widths; + unsigned int i; + cairo_box_t font_bbox = {{0,0},{0,0}}; + cairo_box_t bbox = {{0,0},{0,0}}; + cairo_surface_t *type3_surface; + + if (font_subset->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); + if (unlikely (glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); + if (unlikely (widths == NULL)) { + free (glyphs); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_pdf_group_resources_clear (&surface->resources); + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + NULL, + _cairo_pdf_emit_imagemask, + surface->font_subsets); + if (unlikely (type3_surface->status)) { + free (glyphs); + free (widths); + return type3_surface->status; + } + + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, + _cairo_pdf_surface_add_font, + surface); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_pdf_surface_open_stream (surface, + NULL, + surface->compress_content, + NULL); + if (unlikely (status)) + break; + + glyphs[i] = surface->pdf_stream.self; + status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, + surface->output, + font_subset->glyphs[i], + &bbox, + &widths[i]); + if (unlikely (status)) + break; + + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + break; + + if (i == 0) { + font_bbox.p1.x = bbox.p1.x; + font_bbox.p1.y = bbox.p1.y; + font_bbox.p2.x = bbox.p2.x; + font_bbox.p2.y = bbox.p2.y; + } else { + if (bbox.p1.x < font_bbox.p1.x) + font_bbox.p1.x = bbox.p1.x; + if (bbox.p1.y < font_bbox.p1.y) + font_bbox.p1.y = bbox.p1.y; + if (bbox.p2.x > font_bbox.p2.x) + font_bbox.p2.x = bbox.p2.x; + if (bbox.p2.y > font_bbox.p2.y) + font_bbox.p2.y = bbox.p2.y; + } + } + cairo_surface_destroy (type3_surface); + if (unlikely (status)) { + free (glyphs); + free (widths); + return status; + } + + encoding = _cairo_pdf_surface_new_object (surface); + if (encoding.id == 0) { + free (glyphs); + free (widths); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Encoding\n" + " /Differences [0", encoding.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d", i); + _cairo_output_stream_printf (surface->output, + "]\n" + ">>\n" + "endobj\n"); + + char_procs = _cairo_pdf_surface_new_object (surface); + if (char_procs.id == 0) { + free (glyphs); + free (widths); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<<\n", char_procs.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d %d 0 R\n", + i, glyphs[i].id); + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + free (glyphs); + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, FALSE, + &to_unicode_stream); + if (_cairo_status_is_error (status)) { + free (widths); + return status; + } + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type3\n" + " /FontBBox [%f %f %f %f]\n" + " /FontMatrix [ 1 0 0 1 0 0 ]\n" + " /Encoding %d 0 R\n" + " /CharProcs %d 0 R\n" + " /FirstChar 0\n" + " /LastChar %d\n", + subset_resource.id, + _cairo_fixed_to_double (font_bbox.p1.x), + - _cairo_fixed_to_double (font_bbox.p2.y), + _cairo_fixed_to_double (font_bbox.p2.x), + - _cairo_fixed_to_double (font_bbox.p1.y), + encoding.id, + char_procs.id, + font_subset->num_glyphs - 1); + + _cairo_output_stream_printf (surface->output, + " /Widths ["); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, " %f", widths[i]); + _cairo_output_stream_printf (surface->output, + "]\n"); + free (widths); + + _cairo_output_stream_printf (surface->output, + " /Resources\n"); + _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + return _cairo_array_append (&surface->fonts, &font); +} + +static cairo_status_t +_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_status_t status; + + if (font_subset->is_composite) { + status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } else { +#if CAIRO_HAS_FT_FONT + status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; +#endif + + status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + } + + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_status_t status; + + status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_pdf_surface_analyze_user_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, + _cairo_pdf_surface_emit_unscaled_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_pdf_surface_emit_scaled_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_pdf_surface_emit_scaled_font_subset, + surface); + +BAIL: + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; + + return status; +} + +static cairo_pdf_resource_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t catalog; + + catalog = _cairo_pdf_surface_new_object (surface); + if (catalog.id == 0) + return catalog; + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Catalog\n" + " /Pages %d 0 R\n" + ">>\n" + "endobj\n", + catalog.id, + surface->pages_resource.id); + + return catalog; +} + +static long +_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) +{ + cairo_pdf_object_t *object; + int num_objects, i; + long offset; + char buffer[11]; + + num_objects = _cairo_array_num_elements (&surface->objects); + + offset = _cairo_output_stream_get_position (surface->output); + _cairo_output_stream_printf (surface->output, + "xref\n" + "%d %d\n", + 0, num_objects + 1); + + _cairo_output_stream_printf (surface->output, + "0000000000 65535 f \n"); + for (i = 0; i < num_objects; i++) { + object = _cairo_array_index (&surface->objects, i); + snprintf (buffer, sizeof buffer, "%010ld", object->offset); + _cairo_output_stream_printf (surface->output, + "%s 00000 n \n", buffer); + } + + return offset; +} + +static cairo_status_t +_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, + cairo_pdf_smask_group_t *group) +{ + cairo_pdf_resource_t mask_group; + cairo_pdf_resource_t smask; + cairo_pdf_smask_group_t *smask_group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_status_t status; + + /* Create mask group */ + status = _cairo_pdf_surface_open_group (surface, NULL); + if (unlikely (status)) + return status; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, + &pattern_res, &gstate_res); + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + smask_group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (smask_group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + smask_group->operation = PDF_PAINT; + smask_group->source = cairo_pattern_reference (group->mask); + smask_group->source_res = pattern_res; + status = _cairo_pdf_surface_add_smask_group (surface, smask_group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (smask_group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + smask_group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "0 0 %f %f re f\n", + surface->width, surface->height); + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + status = _cairo_pdf_surface_close_group (surface, &mask_group); + if (unlikely (status)) + return status; + + /* Create source group */ + status = _cairo_pdf_surface_open_group (surface, &group->source_res); + if (unlikely (status)) + return status; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, + &pattern_res, &gstate_res); + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + smask_group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (smask_group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + smask_group->operation = PDF_PAINT; + smask_group->source = cairo_pattern_reference (group->source); + smask_group->source_res = pattern_res; + status = _cairo_pdf_surface_add_smask_group (surface, smask_group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (smask_group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + smask_group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "0 0 %f %f re f\n", + surface->width, surface->height); + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + status = _cairo_pdf_surface_close_group (surface, NULL); + if (unlikely (status)) + return status; + + /* Create an smask based on the alpha component of mask_group */ + smask = _cairo_pdf_surface_new_object (surface); + if (smask.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Mask\n" + " /S /Alpha\n" + " /G %d 0 R\n" + ">>\n" + "endobj\n", + smask.id, + mask_group.id); + + /* Create a GState that uses the smask */ + _cairo_pdf_surface_update_object (surface, group->group_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ExtGState\n" + " /SMask %d 0 R\n" + " /ca 1\n" + " /CA 1\n" + " /AIS false\n" + ">>\n" + "endobj\n", + group->group_res.id, + smask.id); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_status_t +_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, + cairo_pdf_smask_group_t *group) +{ + double old_width, old_height; + cairo_status_t status; + + old_width = surface->width; + old_height = surface->height; + _cairo_pdf_surface_set_size_internal (surface, + group->width, + group->height); + /* _mask is a special case that requires two groups - source + * and mask as well as a smask and gstate dictionary */ + if (group->operation == PDF_MASK) { + status = _cairo_pdf_surface_write_mask_group (surface, group); + goto RESTORE_SIZE; + } + + status = _cairo_pdf_surface_open_group (surface, &group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_pattern (surface, + group->source, + group->source_res, + group->operation == PDF_STROKE); + if (unlikely (status)) + return status; + + switch (group->operation) { + case PDF_PAINT: + _cairo_output_stream_printf (surface->output, + "0 0 %f %f re f\n", + surface->width, surface->height); + break; + case PDF_MASK: + ASSERT_NOT_REACHED; + break; + case PDF_FILL: + status = _cairo_pdf_operators_fill (&surface->pdf_operators, + &group->path, + group->fill_rule); + break; + case PDF_STROKE: + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + &group->path, + &group->style, + &group->ctm, + &group->ctm_inverse); + break; + case PDF_SHOW_GLYPHS: + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + group->utf8, group->utf8_len, + group->glyphs, group->num_glyphs, + group->clusters, group->num_clusters, + group->cluster_flags, + group->scaled_font); + break; + } + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_group (surface, NULL); + +RESTORE_SIZE: + _cairo_pdf_surface_set_size_internal (surface, + old_width, + old_height); + + return status; +} + +static cairo_status_t +_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface) +{ + cairo_pdf_pattern_t pattern; + cairo_pdf_smask_group_t *group; + cairo_pdf_source_surface_t src_surface; + int pattern_index, group_index, surface_index; + cairo_status_t status; + + /* Writing out PDF_MASK groups will cause additional smask groups + * to be appended to surface->smask_groups. Additional patterns + * may also be appended to surface->patterns. + * + * Writing recording surface patterns will cause additional patterns + * and groups to be appended. + */ + pattern_index = 0; + group_index = 0; + surface_index = 0; + while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || + (group_index < _cairo_array_num_elements (&surface->smask_groups)) || + (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) + { + for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { + _cairo_array_copy_element (&surface->smask_groups, group_index, &group); + status = _cairo_pdf_surface_write_smask_group (surface, group); + if (unlikely (status)) + return status; + } + + for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { + _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); + status = _cairo_pdf_surface_emit_pattern (surface, &pattern); + if (unlikely (status)) + return status; + } + + for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { + _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); + status = _cairo_pdf_surface_emit_surface (surface, &src_surface); + if (unlikely (status)) + return status; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t page, knockout, res; + cairo_status_t status; + int i, len; + + _cairo_pdf_group_resources_clear (&surface->resources); + if (surface->has_fallback_images) { + status = _cairo_pdf_surface_open_knockout_group (surface); + if (unlikely (status)) + return status; + + len = _cairo_array_num_elements (&surface->knockout_group); + for (i = 0; i < len; i++) { + _cairo_array_copy_element (&surface->knockout_group, i, &res); + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + res.id); + status = _cairo_pdf_surface_add_xobject (surface, res); + if (unlikely (status)) + return status; + } + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + surface->content.id); + status = _cairo_pdf_surface_add_xobject (surface, surface->content); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_group (surface, &knockout); + if (unlikely (status)) + return status; + + _cairo_pdf_group_resources_clear (&surface->resources); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + knockout.id); + status = _cairo_pdf_surface_add_xobject (surface, knockout); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_content_stream (surface); + if (unlikely (status)) + return status; + } + + page = _cairo_pdf_surface_new_object (surface); + if (page.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Page\n" + " /Parent %d 0 R\n" + " /MediaBox [ 0 0 %f %f ]\n" + " /Contents %d 0 R\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n" + ">>\n" + "endobj\n", + page.id, + surface->pages_resource.id, + surface->width, + surface->height, + surface->content.id, + surface->content_resources.id); + + status = _cairo_array_append (&surface->pages, &page); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; + cairo_image_transparency_t transparency; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + transparency = _cairo_image_analyze_transparency (image); + if (transparency == CAIRO_IMAGE_IS_OPAQUE) + status = CAIRO_STATUS_SUCCESS; + else + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_bool_t +_surface_pattern_supported (cairo_surface_pattern_t *pattern) +{ + cairo_extend_t extend; + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return TRUE; + + if (pattern->surface->backend->acquire_source_image == NULL) + return FALSE; + + /* Does an ALPHA-only source surface even make sense? Maybe, but I + * don't think it's worth the extra code to support it. */ + +/* XXX: Need to write this function here... + content = cairo_surface_get_content (pattern->surface); + if (content == CAIRO_CONTENT_ALPHA) + return FALSE; +*/ + + extend = cairo_pattern_get_extend (&pattern->base); + switch (extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + case CAIRO_EXTEND_REFLECT: + /* There's no point returning FALSE for EXTEND_PAD, as the image + * surface does not currently implement it either */ + case CAIRO_EXTEND_PAD: + return TRUE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +static cairo_bool_t +_gradient_pattern_supported (const cairo_pattern_t *pattern) +{ + cairo_extend_t extend; + + extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); + + + /* Radial gradients are currently only supported with EXTEND_NONE + * and EXTEND_PAD and when one circle is inside the other. */ + if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { + double x1, y1, x2, y2, r1, r2, d; + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + if (extend == CAIRO_EXTEND_REPEAT || + extend == CAIRO_EXTEND_REFLECT) { + return FALSE; + } + + x1 = _cairo_fixed_to_double (radial->c1.x); + y1 = _cairo_fixed_to_double (radial->c1.y); + r1 = _cairo_fixed_to_double (radial->r1); + x2 = _cairo_fixed_to_double (radial->c2.x); + y2 = _cairo_fixed_to_double (radial->c2.y); + r2 = _cairo_fixed_to_double (radial->r2); + + d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); + if (d > fabs(r2 - r1)) { + return FALSE; + } + } + + return TRUE; +} + +static cairo_bool_t +_pattern_supported (const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + return _gradient_pattern_supported (pattern); + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); + + return FALSE; +} + +static cairo_bool_t +_pdf_operator_supported (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return TRUE; + + default: + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return FALSE; + } +} + +static cairo_int_status_t +_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! _pattern_supported (pattern)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_pdf_operator_supported (op)) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (pattern->extend == CAIRO_EXTEND_PAD) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + } + + return CAIRO_STATUS_SUCCESS; + } + + + /* The SOURCE operator is supported if the pattern is opaque or if + * there is nothing painted underneath. */ + if (op == CAIRO_OPERATOR_SOURCE) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (_cairo_pattern_is_opaque (pattern, extents)) { + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } else { + /* FIXME: The analysis surface does not yet have + * the capability to analyze a non opaque recording + * surface and mark it supported if there is + * nothing underneath. For now recording surfaces of + * type CONTENT_COLOR_ALPHA painted with + * OPERATOR_SOURCE will result in a fallback + * image. */ + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else { + return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface, + surface_pattern); + } + } + + if (_cairo_pattern_is_opaque (pattern, extents)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_bool_t +_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) +{ + cairo_status_t status; + + status = _cairo_pdf_surface_close_content_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_array_append (&surface->knockout_group, &surface->content); + if (unlikely (status)) + return status; + + _cairo_pdf_group_resources_clear (&surface->resources); + return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE); +} + +static cairo_int_status_t +_cairo_pdf_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &rect, + op, source, clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return status; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + status = _cairo_pdf_surface_start_fallback (surface); + if (unlikely (status)) + return status; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) + { + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + group->operation = PDF_PAINT; + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + group->source_res = pattern_res; + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "0 0 %f %f re f\n", + surface->width, surface->height); + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_smask_group_t *group; + cairo_status_t status; + cairo_composite_rectangles_t extents; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &rect, + op, source, mask, clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return status; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + cairo_status_t source_status, mask_status; + + source_status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + if (_cairo_status_is_error (source_status)) + return source_status; + + if (mask->has_component_alpha) { + mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); + if (_cairo_status_is_error (mask_status)) + return mask_status; + } + + return _cairo_analysis_surface_merge_status (source_status, + mask_status); + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + status = _cairo_pdf_surface_start_fallback (surface); + if (unlikely (status)) + return status; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + group->operation = PDF_MASK; + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + status = _cairo_pattern_create_copy (&group->mask, mask); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + group->source_res = _cairo_pdf_surface_new_object (surface); + if (group->source_res.id == 0) { + _cairo_pdf_smask_group_destroy (group); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, group->source_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + group->group_res.id, + group->source_res.id); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &rect, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return status; + } + + /* use the more accurate extents */ + if (extents.is_bounded) { + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &extents.mask); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + return CAIRO_STATUS_SUCCESS; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + group->operation = PDF_STROKE; + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + group->source_res = pattern_res; + status = _cairo_path_fixed_init_copy (&group->path, path); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + group->style = *style; + group->ctm = *ctm; + group->ctm_inverse = *ctm_inverse; + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &rect, + op, source, path, + clip); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return status; + } + + /* use the more accurate extents */ + if (extents.is_bounded) { + _cairo_path_fixed_fill_extents (path, + fill_rule, + tolerance, + &extents.mask); + + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + return CAIRO_STATUS_SUCCESS; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + status = _cairo_pdf_surface_start_fallback (surface); + if (unlikely (status)) + return status; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) source); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + return _cairo_output_stream_get_status (surface->output); + } + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + group->operation = PDF_FILL; + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + group->source_res = pattern_res; + status = _cairo_path_fixed_init_copy (&group->path, path); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + group->fill_rule = fill_rule; + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_fill_stroke (void *abstract_surface, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; + cairo_rectangle_int_t extents; + + /* During analysis we return unsupported and let the _fill and + * _stroke functions that are on the fallback path do the analysis + * for us. During render we may still encounter unsupported + * combinations of fill/stroke patterns. However we can return + * unsupported anytime to let the _fill and _stroke functions take + * over. + */ + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* PDF rendering of fill-stroke is not the same as cairo when + * either the fill or stroke is not opaque. + */ + if ( !_cairo_pattern_is_opaque (fill_source, NULL) || + !_cairo_pattern_is_opaque (stroke_source, NULL)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (fill_op != stroke_op) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, fill_op); + if (unlikely (status)) + return status; + + status = _cairo_surface_fill_extents (&surface->base, + fill_op, fill_source, path, fill_rule, + fill_tolerance, fill_antialias, + clip, &extents); + if (unlikely (status)) + return status; + + + fill_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, + &extents, + &fill_pattern_res, + &gstate_res); + if (unlikely (status)) + return status; + + assert (gstate_res.id == 0); + + status = _cairo_surface_stroke_extents (&surface->base, + stroke_op, stroke_source, path, + stroke_style, stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip, &extents); + if (unlikely (status)) + return status; + + stroke_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, + stroke_source, + &extents, + &stroke_pattern_res, + &gstate_res); + if (unlikely (status)) + return status; + + assert (gstate_res.id == 0); + + /* As PDF has separate graphics state for fill and stroke we can + * select both at the same time */ + status = _cairo_pdf_surface_select_pattern (surface, fill_source, + fill_pattern_res, FALSE); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_pattern (surface, stroke_source, + stroke_pattern_res, TRUE); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, + path, + fill_rule, + stroke_style, + stroke_ctm, + stroke_ctm_inverse); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_bool_t +_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_pdf_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + cairo_bool_t overlap; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &rect, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return status; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + group = _cairo_pdf_surface_create_smask_group (surface); + if (unlikely (group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + group->operation = PDF_SHOW_GLYPHS; + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + group->source_res = pattern_res; + + if (utf8_len) { + group->utf8 = malloc (utf8_len); + if (unlikely (group->utf8 == NULL)) { + _cairo_pdf_smask_group_destroy (group); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (group->utf8, utf8, utf8_len); + } + group->utf8_len = utf8_len; + + if (num_glyphs) { + group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (group->glyphs == NULL)) { + _cairo_pdf_smask_group_destroy (group); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + } + group->num_glyphs = num_glyphs; + + if (num_clusters) { + group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); + if (unlikely (group->clusters == NULL)) { + _cairo_pdf_smask_group_destroy (group); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); + } + group->num_clusters = num_clusters; + + group->scaled_font = cairo_scaled_font_reference (scaled_font); + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + if (unlikely (status)) + return status; + + /* Each call to show_glyphs() with a transclucent pattern must + * be in a separate text object otherwise overlapping text + * from separate calls to show_glyphs will not composite with + * each other. */ + if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + } + + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + + +static void +_cairo_pdf_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + +static const cairo_surface_backend_t cairo_pdf_surface_backend = { + CAIRO_SURFACE_TYPE_PDF, + NULL, /* create similar: handled by wrapper */ + _cairo_pdf_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* _cairo_pdf_surface_copy_page */ + _cairo_pdf_surface_show_page, + _cairo_pdf_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_pdf_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here are the drawing functions */ + + _cairo_pdf_surface_paint, + _cairo_pdf_surface_mask, + _cairo_pdf_surface_stroke, + _cairo_pdf_surface_fill, + NULL, /* show_glyphs */ + NULL, /* snapshot */ + + NULL, /* is_compatible */ + _cairo_pdf_surface_fill_stroke, + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + _cairo_pdf_surface_has_show_text_glyphs, + _cairo_pdf_surface_show_text_glyphs, +}; + +static const cairo_paginated_surface_backend_t +cairo_pdf_surface_paginated_backend = { + _cairo_pdf_surface_start_page, + _cairo_pdf_surface_set_paginated_mode, + NULL, /* set_bounding_box */ + _cairo_pdf_surface_has_fallback_images, + _cairo_pdf_surface_supports_fine_grained_fallbacks, +}; diff --git a/libs/cairo/src/cairo-pdf.h b/libs/cairo/src/cairo-pdf.h new file mode 100644 index 000000000..0e85e9804 --- /dev/null +++ b/libs/cairo/src/cairo-pdf.h @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PDF_H +#define CAIRO_PDF_H + +#include "cairo.h" + +#if CAIRO_HAS_PDF_SURFACE + +CAIRO_BEGIN_DECLS + +/** + * cairo_pdf_version_t: + * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. + * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. + * + * #cairo_pdf_version_t is used to describe the version number of the PDF + * specification that a generated PDF file will conform to. + * + * Since 1.10 + */ +typedef enum _cairo_pdf_version { + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +} cairo_pdf_version_t; + +cairo_public cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface, + cairo_pdf_version_t version); + +cairo_public void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions); + +cairo_public const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version); + +cairo_public void +cairo_pdf_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_PDF_SURFACE */ +# error Cairo was not compiled with support for the pdf backend +#endif /* CAIRO_HAS_PDF_SURFACE */ + +#endif /* CAIRO_PDF_H */ diff --git a/libs/cairo/src/cairo-pen.c b/libs/cairo/src/cairo-pen.c new file mode 100644 index 000000000..751b5dc3a --- /dev/null +++ b/libs/cairo/src/cairo-pen.c @@ -0,0 +1,364 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-slope-private.h" + +static int +_cairo_pen_vertices_needed (double tolerance, + double radius, + const cairo_matrix_t *matrix); + +static void +_cairo_pen_compute_slopes (cairo_pen_t *pen); + +cairo_status_t +_cairo_pen_init (cairo_pen_t *pen, + double radius, + double tolerance, + const cairo_matrix_t *ctm) +{ + int i; + int reflect; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + + pen->radius = radius; + pen->tolerance = tolerance; + + reflect = _cairo_matrix_compute_determinant (ctm) < 0.; + + pen->num_vertices = _cairo_pen_vertices_needed (tolerance, + radius, + ctm); + + if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { + pen->vertices = _cairo_malloc_ab (pen->num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (pen->vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + pen->vertices = pen->vertices_embedded; + } + + /* + * Compute pen coordinates. To generate the right ellipse, compute points around + * a circle in user space and transform them to device space. To get a consistent + * orientation in device space, flip the pen if the transformation matrix + * is reflecting + */ + for (i=0; i < pen->num_vertices; i++) { + double theta = 2 * M_PI * i / (double) pen->num_vertices; + double dx = radius * cos (reflect ? -theta : theta); + double dy = radius * sin (reflect ? -theta : theta); + cairo_pen_vertex_t *v = &pen->vertices[i]; + cairo_matrix_transform_distance (ctm, &dx, &dy); + v->point.x = _cairo_fixed_from_double (dx); + v->point.y = _cairo_fixed_from_double (dy); + } + + _cairo_pen_compute_slopes (pen); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pen_fini (cairo_pen_t *pen) +{ + if (pen->vertices != pen->vertices_embedded) + free (pen->vertices); + + + VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t))); +} + +cairo_status_t +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + + *pen = *other; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pen->vertices = pen->vertices_embedded; + if (pen->num_vertices) { + if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { + pen->vertices = _cairo_malloc_ab (pen->num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (pen->vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (pen->vertices, other->vertices, + pen->num_vertices * sizeof (cairo_pen_vertex_t)); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) +{ + cairo_status_t status; + int num_vertices; + int i; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + num_vertices = pen->num_vertices + num_points; + if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || + pen->vertices != pen->vertices_embedded) + { + cairo_pen_vertex_t *vertices; + + if (pen->vertices == pen->vertices_embedded) { + vertices = _cairo_malloc_ab (num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (vertices, pen->vertices, + pen->num_vertices * sizeof (cairo_pen_vertex_t)); + } else { + vertices = _cairo_realloc_ab (pen->vertices, + num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pen->vertices = vertices; + } + + pen->num_vertices = num_vertices; + + /* initialize new vertices */ + for (i=0; i < num_points; i++) + pen->vertices[pen->num_vertices-num_points+i].point = point[i]; + + status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); + if (unlikely (status)) + return status; + + _cairo_pen_compute_slopes (pen); + + return CAIRO_STATUS_SUCCESS; +} + +/* +The circular pen in user space is transformed into an ellipse in +device space. + +We construct the pen by computing points along the circumference +using equally spaced angles. + +We show that this approximation to the ellipse has maximum error at the +major axis of the ellipse. + +Set + + M = major axis length + m = minor axis length + +Align 'M' along the X axis and 'm' along the Y axis and draw +an ellipse parameterized by angle 't': + + x = M cos t y = m sin t + +Perturb t by ± d and compute two new points (x+,y+), (x-,y-). +The distance from the average of these two points to (x,y) represents +the maximum error in approximating the ellipse with a polygon formed +from vertices 2∆ radians apart. + + x+ = M cos (t+∆) y+ = m sin (t+∆) + x- = M cos (t-∆) y- = m sin (t-∆) + +Now compute the approximation error, E: + + Ex = (x - (x+ + x-) / 2) + Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) + = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + + cos(t)cos(∆) - sin(t)sin(∆))/2) + = M(cos(t) - cos(t)cos(∆)) + = M cos(t) (1 - cos(∆)) + + Ey = y - (y+ - y-) / 2 + = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 + = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + + sin(t)cos(∆) - cos(t)sin(∆))/2) + = m (sin(t) - sin(t)cos(∆)) + = m sin(t) (1 - cos(∆)) + + E² = Ex² + Ey² + = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² + = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) + = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) + = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² + +Find the extremum by differentiation wrt t and setting that to zero + +∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) + + 0 = 2 cos (t) sin (t) + 0 = sin (2t) + t = nπ + +Which is to say that the maximum and minimum errors occur on the +axes of the ellipse at 0 and π radians: + + E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² + = (1-cos(∆))² M² + E²(π) = (1-cos(∆))² m² + +maximum error = M (1-cos(∆)) +minimum error = m (1-cos(∆)) + +We must make maximum error ≤ tolerance, so compute the ∆ needed: + + tolerance = M (1-cos(∆)) + tolerance / M = 1 - cos (∆) + cos(∆) = 1 - tolerance/M + ∆ = acos (1 - tolerance / M); + +Remembering that ∆ is half of our angle between vertices, +the number of vertices is then + + vertices = ceil(2π/2∆). + = ceil(π/∆). + +Note that this also equation works for M == m (a circle) as it +doesn't matter where on the circle the error is computed. +*/ + +static int +_cairo_pen_vertices_needed (double tolerance, + double radius, + const cairo_matrix_t *matrix) +{ + /* + * the pen is a circle that gets transformed to an ellipse by matrix. + * compute major axis length for a pen with the specified radius. + * we don't need the minor axis length. + */ + + double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, + radius); + + /* + * compute number of vertices needed + */ + int num_vertices; + + /* Where tolerance / M is > 1, we use 4 points */ + if (tolerance >= major_axis) { + num_vertices = 4; + } else { + double delta = acos (1 - tolerance / major_axis); + num_vertices = ceil (M_PI / delta); + + /* number of vertices must be even */ + if (num_vertices % 2) + num_vertices++; + + /* And we must always have at least 4 vertices. */ + if (num_vertices < 4) + num_vertices = 4; + } + + return num_vertices; +} + +static void +_cairo_pen_compute_slopes (cairo_pen_t *pen) +{ + int i, i_prev; + cairo_pen_vertex_t *prev, *v, *next; + + for (i=0, i_prev = pen->num_vertices - 1; + i < pen->num_vertices; + i_prev = i++) { + prev = &pen->vertices[i_prev]; + v = &pen->vertices[i]; + next = &pen->vertices[(i + 1) % pen->num_vertices]; + + _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); + _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); + } +} +/* + * Find active pen vertex for clockwise edge of stroke at the given slope. + * + * The strictness of the inequalities here is delicate. The issue is + * that the slope_ccw member of one pen vertex will be equivalent to + * the slope_cw member of the next pen vertex in a counterclockwise + * order. However, for this function, we care strongly about which + * vertex is returned. + * + * [I think the "care strongly" above has to do with ensuring that the + * pen's "extra points" from the spline's initial and final slopes are + * properly found when beginning the spline stroking.] + */ +int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) +{ + int i; + + for (i=0; i < pen->num_vertices; i++) { + if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && + (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) + break; + } + + /* If the desired slope cannot be found between any of the pen + * vertices, then we must have a degenerate pen, (such as a pen + * that's been transformed to a line). In that case, we consider + * the first pen vertex as the appropriate clockwise vertex. + */ + if (i == pen->num_vertices) + i = 0; + + return i; +} + +/* Find active pen vertex for counterclockwise edge of stroke at the given slope. + * + * Note: See the comments for _cairo_pen_find_active_cw_vertex_index + * for some details about the strictness of the inequalities here. + */ +int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) +{ + cairo_slope_t slope_reverse; + int i; + + slope_reverse = *slope; + slope_reverse.dx = -slope_reverse.dx; + slope_reverse.dy = -slope_reverse.dy; + + for (i=pen->num_vertices-1; i >= 0; i--) { + if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && + (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) + break; + } + + /* If the desired slope cannot be found between any of the pen + * vertices, then we must have a degenerate pen, (such as a pen + * that's been transformed to a line). In that case, we consider + * the last pen vertex as the appropriate counterclockwise vertex. + */ + if (i < 0) + i = pen->num_vertices - 1; + + return i; +} diff --git a/libs/cairo/src/cairo-platform.h b/libs/cairo/src/cairo-platform.h new file mode 100644 index 000000000..9d4bc4d1f --- /dev/null +++ b/libs/cairo/src/cairo-platform.h @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PLATFORM_H +#define CAIRO_PLATFORM_H + +#include "prcpucfg.h" + +/* we're replacing any definition from cairoint.h etc */ +#undef cairo_public + +#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE +#define CVISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define CVISIBILITY_HIDDEN __hidden +#else +#define CVISIBILITY_HIDDEN +#endif + +/* In libxul builds we don't ever want to export cairo symbols */ +#define cairo_public extern CVISIBILITY_HIDDEN + +#define CCALLBACK +#define CCALLBACK_DECL +#define CSTATIC_CALLBACK(__x) static __x + +#ifdef MOZILLA_VERSION +#include "cairo-rename.h" +#endif + +#if defined(IS_BIG_ENDIAN) +#define WORDS_BIGENDIAN +#define FLOAT_WORDS_BIGENDIAN +#endif + +#endif /* CAIRO_PLATFORM_H */ diff --git a/libs/cairo/src/cairo-png.c b/libs/cairo/src/cairo-png.c new file mode 100644 index 000000000..bbab2c2ae --- /dev/null +++ b/libs/cairo/src/cairo-png.c @@ -0,0 +1,764 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +#include +#include +#include + +/** + * SECTION:cairo-png + * @Title: PNG Support + * @Short_Description: Reading and writing PNG images + * @See_Also: #cairo_surface_t + * + * The PNG functions allow reading PNG images into image surfaces, and writing + * any surface to a PNG file. + */ + +/** + * CAIRO_HAS_PNG_FUNCTIONS: + * + * Defined if the PNG functions are available. + * This macro can be used to conditionally compile code using the cairo + * PNG functions. + */ + +struct png_read_closure_t { + cairo_read_func_t read_func; + void *closure; + cairo_output_stream_t *png_data; +}; + + +/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ +static void +unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + uint8_t alpha; + + memcpy (&pixel, b, sizeof (uint32_t)); + alpha = (pixel & 0xff000000) >> 24; + if (alpha == 0) { + b[0] = b[1] = b[2] = b[3] = 0; + } else { + b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } + } +} + +/* Converts native endian xRGB => RGBx bytes */ +static void +convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + + memcpy (&pixel, b, sizeof (uint32_t)); + + b[0] = (pixel & 0xff0000) >> 16; + b[1] = (pixel & 0x00ff00) >> 8; + b[2] = (pixel & 0x0000ff) >> 0; + b[3] = 0; + } +} + +/* Use a couple of simple error callbacks that do not print anything to + * stderr and rely on the user to check for errors via the #cairo_status_t + * return. + */ +static void +png_simple_error_callback (png_structp png, + png_const_charp error_msg) +{ + cairo_status_t *error = png_get_error_ptr (png); + + /* default to the most likely error */ + if (*error == CAIRO_STATUS_SUCCESS) + *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); + +#ifdef PNG_SETJMP_SUPPORTED + longjmp (png_jmpbuf (png), 1); +#endif + + /* if we get here, then we have to choice but to abort ... */ +} + +static void +png_simple_warning_callback (png_structp png, + png_const_charp error_msg) +{ + cairo_status_t *error = png_get_error_ptr (png); + + /* default to the most likely error */ + if (*error == CAIRO_STATUS_SUCCESS) + *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* png does not expect to abort and will try to tidy up after a warning */ +} + + +/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn. + * Otherwise, we will segfault if we are writing to a stream. */ +static void +png_simple_output_flush_fn (png_structp png_ptr) +{ +} + +static cairo_status_t +write_png (cairo_surface_t *surface, + png_rw_ptr write_func, + void *closure) +{ + int i; + cairo_status_t status; + cairo_image_surface_t *image; + cairo_image_surface_t * volatile clone; + void *image_extra; + png_struct *png; + png_info *info; + png_byte **volatile rows = NULL; + png_color_16 white; + int png_color_type; + int depth; + + status = _cairo_surface_acquire_source_image (surface, + &image, + &image_extra); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + else if (unlikely (status)) + return status; + + /* PNG complains about "Image width or height is zero in IHDR" */ + if (image->width == 0 || image->height == 0) { + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + goto BAIL1; + } + + /* Handle the various fallback formats (e.g. low bit-depth XServers) + * by coercing them to a simpler format using pixman. + */ + clone = _cairo_image_surface_coerce (image); + status = clone->base.status; + if (unlikely (status)) + goto BAIL1; + + rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*)); + if (unlikely (rows == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL2; + } + + for (i = 0; i < clone->height; i++) + rows[i] = (png_byte *) clone->data + i * clone->stride; + + png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status, + png_simple_error_callback, + png_simple_warning_callback); + if (unlikely (png == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL3; + } + + info = png_create_info_struct (png); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL4; + } + +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) + goto BAIL4; +#endif + + png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn); + + switch (clone->format) { + case CAIRO_FORMAT_ARGB32: + depth = 8; + if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE) + png_color_type = PNG_COLOR_TYPE_RGB; + else + png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case CAIRO_FORMAT_RGB24: + depth = 8; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_A8: + depth = 8; + png_color_type = PNG_COLOR_TYPE_GRAY; + break; + case CAIRO_FORMAT_A1: + depth = 1; + png_color_type = PNG_COLOR_TYPE_GRAY; +#ifndef WORDS_BIGENDIAN + png_set_packswap (png); +#endif + break; + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB16_565: + default: + status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + goto BAIL4; + } + + png_set_IHDR (png, info, + clone->width, + clone->height, depth, + png_color_type, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + white.gray = (1 << depth) - 1; + white.red = white.blue = white.green = white.gray; + png_set_bKGD (png, info, &white); + + if (0) { /* XXX extract meta-data from surface (i.e. creation date) */ + png_time pt; + + png_convert_from_time_t (&pt, time (NULL)); + png_set_tIME (png, info, &pt); + } + + /* We have to call png_write_info() before setting up the write + * transformation, since it stores data internally in 'png' + * that is needed for the write transformation functions to work. + */ + png_write_info (png, info); + + if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + png_set_write_user_transform_fn (png, unpremultiply_data); + } else if (png_color_type == PNG_COLOR_TYPE_RGB) { + png_set_write_user_transform_fn (png, convert_data_to_bytes); + png_set_filler (png, 0, PNG_FILLER_AFTER); + } + + png_write_image (png, rows); + png_write_end (png, info); + +BAIL4: + png_destroy_write_struct (&png, &info); +BAIL3: + free (rows); +BAIL2: + cairo_surface_destroy (&clone->base); +BAIL1: + _cairo_surface_release_source_image (surface, image, image_extra); + + return status; +} + +static void +stdio_write_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp; + + fp = png_get_io_ptr (png); + while (size) { + size_t ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + png_error (png, NULL); + } + } +} + +/** + * cairo_surface_write_to_png: + * @surface: a #cairo_surface_t with pixel contents + * @filename: the name of a file to write to + * + * Writes the contents of @surface to a new file @filename as a PNG + * image. + * + * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not + * be allocated for the operation or + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have + * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs + * while attempting to write the file. + **/ +cairo_status_t +cairo_surface_write_to_png (cairo_surface_t *surface, + const char *filename) +{ + FILE *fp; + cairo_status_t status; + + if (surface->status) + return surface->status; + + if (surface->finished) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + fp = fopen (filename, "wb"); + if (fp == NULL) { + switch (errno) { + case ENOMEM: + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + default: + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + } + } + + status = write_png (surface, stdio_write_func, fp); + + if (fclose (fp) && status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + + return status; +} + +struct png_write_closure_t { + cairo_write_func_t write_func; + void *closure; +}; + +static void +stream_write_func (png_structp png, png_bytep data, png_size_t size) +{ + cairo_status_t status; + struct png_write_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + status = png_closure->write_func (png_closure->closure, data, size); + if (unlikely (status)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = status; + png_error (png, NULL); + } +} + +/** + * cairo_surface_write_to_png_stream: + * @surface: a #cairo_surface_t with pixel contents + * @write_func: a #cairo_write_func_t + * @closure: closure data for the write function + * + * Writes the image surface to the write function. + * + * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if + * memory could not be allocated for the operation, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have + * pixel contents. + **/ +cairo_status_t +cairo_surface_write_to_png_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure) +{ + struct png_write_closure_t png_closure; + + if (surface->status) + return surface->status; + + if (surface->finished) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + png_closure.write_func = write_func; + png_closure.closure = closure; + + return write_png (surface, stream_write_func, &png_closure); +} +slim_hidden_def (cairo_surface_write_to_png_stream); + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +/* Premultiplies data and converts RGBA bytes => native endian */ +static void +premultiply_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t alpha = base[3]; + uint32_t p; + + if (alpha == 0) { + p = 0; + } else { + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + + if (alpha != 0xff) { + red = multiply_alpha (alpha, red); + green = multiply_alpha (alpha, green); + blue = multiply_alpha (alpha, blue); + } + p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + memcpy (base, &p, sizeof (uint32_t)); + } +} + +/* Converts RGBx bytes to native endian xRGB */ +static void +convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + uint32_t pixel; + + pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &pixel, sizeof (uint32_t)); + } +} + +static cairo_status_t +stdio_read_func (void *closure, unsigned char *data, unsigned int size) +{ + FILE *file = closure; + + while (size) { + size_t ret; + + ret = fread (data, 1, size, file); + size -= ret; + data += ret; + + if (size && (feof (file) || ferror (file))) + return _cairo_error (CAIRO_STATUS_READ_ERROR); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +stream_read_func (png_structp png, png_bytep data, png_size_t size) +{ + cairo_status_t status; + struct png_read_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + status = png_closure->read_func (png_closure->closure, data, size); + if (unlikely (status)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = status; + png_error (png, NULL); + } + + _cairo_output_stream_write (png_closure->png_data, data, size); +} + +static cairo_surface_t * +read_png (struct png_read_closure_t *png_closure) +{ + cairo_surface_t *surface; + png_struct *png = NULL; + png_info *info; + png_byte *data = NULL; + png_byte **row_pointers = NULL; + png_uint_32 png_width, png_height; + int depth, color_type, interlace, stride; + unsigned int i; + cairo_format_t format; + cairo_status_t status; + unsigned char *mime_data; + unsigned long mime_data_length; + + png_closure->png_data = _cairo_memory_stream_create (); + + /* XXX: Perhaps we'll want some other error handlers? */ + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, + &status, + png_simple_error_callback, + png_simple_warning_callback); + if (unlikely (png == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + info = png_create_info_struct (png); + if (unlikely (info == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + png_set_read_fn (png, png_closure, stream_read_func); + + status = CAIRO_STATUS_SUCCESS; +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) { + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } +#endif + + png_read_info (png, info); + + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + if (unlikely (status)) { /* catch any early warnings */ + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY) { +#if PNG_LIBPNG_VER >= 10209 + png_set_expand_gray_1_2_4_to_8 (png); +#else + png_set_gray_1_2_4_to_8 (png); +#endif + } + + /* transform transparency to alpha */ + if (png_get_valid (png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 16) + png_set_strip_16 (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb (png); + } + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + /* recheck header after setting EXPAND options */ + png_read_update_info (png, info); + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + if (depth != 8 || + ! (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA)) + { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR)); + goto BAIL; + } + + switch (color_type) { + default: + ASSERT_NOT_REACHED; + /* fall-through just in case ;-) */ + + case PNG_COLOR_TYPE_RGB_ALPHA: + format = CAIRO_FORMAT_ARGB32; + png_set_read_user_transform_fn (png, premultiply_data); + break; + + case PNG_COLOR_TYPE_RGB: + format = CAIRO_FORMAT_RGB24; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + break; + } + + stride = cairo_format_stride_for_width (format, png_width); + if (stride < 0) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + goto BAIL; + } + + data = _cairo_malloc_ab (png_height, stride); + if (unlikely (data == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + row_pointers = _cairo_malloc_ab (png_height, sizeof (char *)); + if (unlikely (row_pointers == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + for (i = 0; i < png_height; i++) + row_pointers[i] = &data[i * stride]; + + png_read_image (png, row_pointers); + png_read_end (png, info); + + if (unlikely (status)) { /* catch any late warnings - probably hit an error already */ + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + surface = cairo_image_surface_create_for_data (data, format, + png_width, png_height, + stride); + if (surface->status) + goto BAIL; + + _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); + data = NULL; + + _cairo_debug_check_image_surface_is_defined (surface); + + status = _cairo_memory_stream_destroy (png_closure->png_data, + &mime_data, + &mime_data_length); + png_closure->png_data = NULL; + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + status = cairo_surface_set_mime_data (surface, + CAIRO_MIME_TYPE_PNG, + mime_data, + mime_data_length, + free, + mime_data); + if (unlikely (status)) { + free (mime_data); + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + BAIL: + if (row_pointers != NULL) + free (row_pointers); + if (data != NULL) + free (data); + if (png != NULL) + png_destroy_read_struct (&png, &info, NULL); + if (png_closure->png_data != NULL) { + cairo_status_t status_ignored; + + status_ignored = _cairo_output_stream_destroy (png_closure->png_data); + } + + return surface; +} + +/** + * cairo_image_surface_create_from_png: + * @filename: name of PNG file to load + * + * Creates a new image surface and initializes the contents to the + * given PNG file. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file, or a "nil" surface if any error occurred. A nil + * surface can be checked for with cairo_surface_status(surface) which + * may return one of the following values: + * + * %CAIRO_STATUS_NO_MEMORY + * %CAIRO_STATUS_FILE_NOT_FOUND + * %CAIRO_STATUS_READ_ERROR + * + * Alternatively, you can allow errors to propagate through the drawing + * operations and check the status on the context upon completion + * using cairo_status(). + **/ +cairo_surface_t * +cairo_image_surface_create_from_png (const char *filename) +{ + struct png_read_closure_t png_closure; + cairo_surface_t *surface; + + png_closure.closure = fopen (filename, "rb"); + if (png_closure.closure == NULL) { + cairo_status_t status; + switch (errno) { + case ENOMEM: + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + case ENOENT: + status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); + break; + default: + status = _cairo_error (CAIRO_STATUS_READ_ERROR); + break; + } + return _cairo_surface_create_in_error (status); + } + + png_closure.read_func = stdio_read_func; + + surface = read_png (&png_closure); + + fclose (png_closure.closure); + + return surface; +} + +/** + * cairo_image_surface_create_from_png_stream: + * @read_func: function called to read the data of the file + * @closure: data to pass to @read_func. + * + * Creates a new image surface from PNG data read incrementally + * via the @read_func function. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file or a "nil" surface if the data read is not a valid PNG image + * or memory could not be allocated for the operation. A nil + * surface can be checked for with cairo_surface_status(surface) which + * may return one of the following values: + * + * %CAIRO_STATUS_NO_MEMORY + * %CAIRO_STATUS_READ_ERROR + * + * Alternatively, you can allow errors to propagate through the drawing + * operations and check the status on the context upon completion + * using cairo_status(). + **/ +cairo_surface_t * +cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, + void *closure) +{ + struct png_read_closure_t png_closure; + + png_closure.read_func = read_func; + png_closure.closure = closure; + + return read_png (&png_closure); +} diff --git a/libs/cairo/src/cairo-polygon.c b/libs/cairo/src/cairo-polygon.c new file mode 100644 index 000000000..fd71319e9 --- /dev/null +++ b/libs/cairo/src/cairo-polygon.c @@ -0,0 +1,460 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-slope-private.h" + +void +_cairo_polygon_init (cairo_polygon_t *polygon) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + + polygon->status = CAIRO_STATUS_SUCCESS; + + polygon->num_edges = 0; + + polygon->edges = polygon->edges_embedded; + polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); + + polygon->has_current_point = FALSE; + polygon->has_current_edge = FALSE; + polygon->num_limits = 0; + + polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; + polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; +} + +void +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *limits, + int num_limits) +{ + int n; + + polygon->limits = limits; + polygon->num_limits = num_limits; + + if (polygon->num_limits) { + polygon->limit = limits[0]; + for (n = 1; n < num_limits; n++) { + if (limits[n].p1.x < polygon->limit.p1.x) + polygon->limit.p1.x = limits[n].p1.x; + + if (limits[n].p1.y < polygon->limit.p1.y) + polygon->limit.p1.y = limits[n].p1.y; + + if (limits[n].p2.x > polygon->limit.p2.x) + polygon->limit.p2.x = limits[n].p2.x; + + if (limits[n].p2.y > polygon->limit.p2.y) + polygon->limit.p2.y = limits[n].p2.y; + } + } +} + +void +_cairo_polygon_fini (cairo_polygon_t *polygon) +{ + if (polygon->edges != polygon->edges_embedded) + free (polygon->edges); + + VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t))); +} + +/* make room for at least one more edge */ +static cairo_bool_t +_cairo_polygon_grow (cairo_polygon_t *polygon) +{ + cairo_edge_t *new_edges; + int old_size = polygon->edges_size; + int new_size = 4 * old_size; + + if (CAIRO_INJECT_FAULT ()) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + if (polygon->edges == polygon->edges_embedded) { + new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); + if (new_edges != NULL) + memcpy (new_edges, polygon->edges, old_size * sizeof (cairo_edge_t)); + } else { + new_edges = _cairo_realloc_ab (polygon->edges, + new_size, sizeof (cairo_edge_t)); + } + + if (unlikely (new_edges == NULL)) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + polygon->edges = new_edges; + polygon->edges_size = new_size; + + return TRUE; +} + +static void +_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_edge_t *edge; + + assert (top < bottom); + + if (unlikely (polygon->num_edges == polygon->edges_size)) { + if (! _cairo_polygon_grow (polygon)) + return; + } + + edge = &polygon->edges[polygon->num_edges++]; + edge->line.p1 = *p1; + edge->line.p2 = *p2; + edge->top = top; + edge->bottom = bottom; + edge->dir = dir; + + if (top < polygon->extents.p1.y) + polygon->extents.p1.y = top; + if (bottom > polygon->extents.p2.y) + polygon->extents.p2.y = bottom; + + if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) { + cairo_fixed_t x = p1->x; + if (top != p1->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; + } + + if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) { + cairo_fixed_t x = p2->x; + if (bottom != p2->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; + } +} + +static void +_add_clipped_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + const int top, const int bottom, + const int dir) +{ + cairo_point_t p[2]; + int top_y, bot_y; + int n; + + for (n = 0; n < polygon->num_limits; n++) { + const cairo_box_t *limits = &polygon->limits[n]; + + if (top >= limits->p2.y) + continue; + if (bottom <= limits->p1.y) + continue; + + if (p1->x >= limits->p1.x && p2->x >= limits->p1.x && + p1->x <= limits->p2.x && p2->x <= limits->p2.x) + { + top_y = top; + if (top_y < limits->p1.y) + top_y = limits->p1.y; + + bot_y = bottom; + if (bot_y > limits->p2.y) + bot_y = limits->p2.y; + + _add_edge (polygon, p1, p2, top_y, bot_y, dir); + } + else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x) + { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = top; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = bottom; + if (bot_y > p[1].y) + bot_y = p[1].y; + + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + } + else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x) + { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = top; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = bottom; + if (bot_y > p[1].y) + bot_y = p[1].y; + + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + } + else + { + int left_y, right_y; + int p1_y, p2_y; + + left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p1.x); + right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p2.x); + + p1_y = top; + p2_y = bottom; + + if (p1->x < p2->x) { + if (p1->x < limits->p1.x && left_y > limits->p1.y) { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = p1_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = left_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p1_y = bot_y; + } + + if (p2->x > limits->p2.x && right_y < limits->p2.y) { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = right_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = p2_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p2_y = top_y; + } + } else { + if (p1->x > limits->p2.x && right_y > limits->p1.y) { + p[0].x = limits->p2.x; + p[0].y = limits->p1.y; + top_y = p1_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p2.x; + p[1].y = limits->p2.y; + bot_y = right_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p1_y = bot_y; + } + + if (p2->x < limits->p1.x && left_y < limits->p2.y) { + p[0].x = limits->p1.x; + p[0].y = limits->p1.y; + top_y = left_y; + if (top_y < p[0].y) + top_y = p[0].y; + + p[1].x = limits->p1.x; + p[1].y = limits->p2.y; + bot_y = p2_y; + if (bot_y > p[1].y) + bot_y = p[1].y; + + if (bot_y > top_y) + _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); + p2_y = top_y; + } + } + + if (p1_y < limits->p1.y) + p1_y = limits->p1.y; + if (p2_y > limits->p2.y) + p2_y = limits->p2.y; + if (p2_y > p1_y) + _add_edge (polygon, p1, p2, p1_y, p2_y, dir); + } + } +} + +static void +_cairo_polygon_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int dir; + + /* drop horizontal edges */ + if (p1->y == p2->y) + return; + + if (p1->y < p2->y) { + dir = 1; + } else { + const cairo_point_t *t; + t = p1, p1 = p2, p2 = t; + dir = -1; + } + + if (polygon->num_limits) { + if (p2->y <= polygon->limit.p1.y) + return; + + if (p1->y >= polygon->limit.p2.y) + return; + + _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir); + } else + _add_edge (polygon, p1, p2, p1->y, p2->y, dir); +} + +cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + _cairo_polygon_add_edge (polygon, p1, p2); + return _cairo_polygon_status (polygon); +} + +cairo_status_t +_cairo_polygon_add_line (cairo_polygon_t *polygon, + const cairo_line_t *line, + int top, int bottom, + int dir) +{ + /* drop horizontal edges */ + if (line->p1.y == line->p2.y) + return CAIRO_STATUS_SUCCESS; + + if (bottom <= top) + return CAIRO_STATUS_SUCCESS; + + if (polygon->num_limits) { + if (line->p2.y <= polygon->limit.p1.y) + return CAIRO_STATUS_SUCCESS; + + if (line->p1.y >= polygon->limit.p2.y) + return CAIRO_STATUS_SUCCESS; + + _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + } else + _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + + return polygon->status; +} + +/* flattened path operations */ + +cairo_status_t +_cairo_polygon_move_to (cairo_polygon_t *polygon, + const cairo_point_t *point) +{ + if (polygon->has_current_edge) { + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + polygon->has_current_edge = FALSE; + } + + if (! polygon->has_current_point) { + polygon->first_point = *point; + polygon->has_current_point = TRUE; + } + + polygon->current_point = *point; + return polygon->status; +} + +cairo_status_t +_cairo_polygon_line_to (cairo_polygon_t *polygon, + const cairo_point_t *point) +{ + /* squash collinear edges */ + if (polygon->has_current_edge) { + if (polygon->current_point.x != point->x || + polygon->current_point.y != point->y) + { + cairo_slope_t this; + + _cairo_slope_init (&this, &polygon->current_point, point); + if (_cairo_slope_equal (&polygon->current_edge, &this)) { + polygon->current_point = *point; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + + polygon->last_point = polygon->current_point; + polygon->current_edge = this; + } + } else if (polygon->has_current_point) { + if (polygon->current_point.x != point->x || + polygon->current_point.y != point->y) + { + polygon->last_point = polygon->current_point; + _cairo_slope_init (&polygon->current_edge, + &polygon->last_point, + point); + polygon->has_current_edge = TRUE; + } + } else { + polygon->first_point = *point; + polygon->has_current_point = TRUE; + } + + polygon->current_point = *point; + return polygon->status; +} + +cairo_status_t +_cairo_polygon_close (cairo_polygon_t *polygon) +{ + cairo_status_t status; + + if (polygon->has_current_point) { + status = _cairo_polygon_line_to (polygon, &polygon->first_point); + polygon->has_current_point = FALSE; + } + + if (polygon->has_current_edge) { + _cairo_polygon_add_edge (polygon, + &polygon->last_point, + &polygon->current_point); + polygon->has_current_edge = FALSE; + } + + return polygon->status; +} diff --git a/libs/cairo/src/cairo-private.h b/libs/cairo/src/cairo-private.h new file mode 100644 index 000000000..33b049678 --- /dev/null +++ b/libs/cairo/src/cairo-private.h @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PRIVATE_H +#define CAIRO_PRIVATE_H + +#include "cairo-reference-count-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" + +struct _cairo { + cairo_reference_count_t ref_count; + + cairo_status_t status; + + cairo_user_data_array_t user_data; + + cairo_gstate_t *gstate; + cairo_gstate_t gstate_tail[2]; + cairo_gstate_t *gstate_freelist; + + cairo_path_fixed_t path[1]; +}; + +#endif /* CAIRO_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-ps-surface-private.h b/libs/cairo/src/cairo-ps-surface-private.h new file mode 100644 index 000000000..96da53554 --- /dev/null +++ b/libs/cairo/src/cairo-ps-surface-private.h @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PS_SURFACE_PRIVATE_H +#define CAIRO_PS_SURFACE_PRIVATE_H + +#include "cairo-ps.h" + +#include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-pdf-operators-private.h" + +#include + +typedef struct cairo_ps_surface { + cairo_surface_t base; + + /* Here final_stream corresponds to the stream/file passed to + * cairo_ps_surface_create surface is built. Meanwhile stream is a + * temporary stream in which the file output is built, (so that + * the header can be built and inserted into the target stream + * before the contents of the temporary stream are copied). */ + cairo_output_stream_t *final_stream; + + FILE *tmpfile; + cairo_output_stream_t *stream; + + cairo_bool_t eps; + cairo_content_t content; + double width; + double height; + cairo_rectangle_int_t page_bbox; + int bbox_x1, bbox_y1, bbox_x2, bbox_y2; + cairo_matrix_t cairo_to_ps; + + /* XXX These 3 are used as temporary storage whilst emitting patterns */ + cairo_image_surface_t *image; + cairo_image_surface_t *acquired_image; + void *image_extra; + + cairo_bool_t use_string_datasource; + + cairo_bool_t current_pattern_is_solid_color; + cairo_color_t current_color; + + int num_pages; + + cairo_paginated_mode_t paginated_mode; + + cairo_bool_t force_fallbacks; + cairo_bool_t has_creation_date; + time_t creation_date; + + cairo_scaled_font_subsets_t *font_subsets; + + cairo_list_t document_media; + cairo_array_t dsc_header_comments; + cairo_array_t dsc_setup_comments; + cairo_array_t dsc_page_setup_comments; + + cairo_array_t *dsc_comment_target; + + cairo_ps_level_t ps_level; + cairo_ps_level_t ps_level_used; + + cairo_surface_clipper_t clipper; + + cairo_pdf_operators_t pdf_operators; + cairo_surface_t *paginated_surface; +} cairo_ps_surface_t; + +#endif /* CAIRO_PS_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-ps-surface.c b/libs/cairo/src/cairo-ps-surface.c new file mode 100644 index 000000000..5696d6cb7 --- /dev/null +++ b/libs/cairo/src/cairo-ps-surface.c @@ -0,0 +1,3890 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Design of the PS output: + * + * The PS output is harmonised with the PDF operations using PS procedures + * to emulate the PDF operators. + * + * This has a number of advantages: + * 1. A large chunk of code is shared between the PDF and PS backends. + * See cairo-pdf-operators. + * 2. Using gs to do PS -> PDF and PDF -> PS will always work well. + */ + +#define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-ps.h" +#include "cairo-ps-surface-private.h" +#include "cairo-pdf-operators-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-type3-glyph-surface-private.h" +#include "cairo-image-info-private.h" + +#include +#include +#include +#include +#include + +#define DEBUG_PS 0 + +#if DEBUG_PS +#define DEBUG_FALLBACK(s) \ + fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s)) +#else +#define DEBUG_FALLBACK(s) +#endif + +#ifndef HAVE_CTIME_R +#define ctime_r(T, BUF) ctime (T) +#endif + +/** + * SECTION:cairo-ps + * @Title: PostScript Surfaces + * @Short_Description: Rendering PostScript documents + * @See_Also: #cairo_surface_t + * + * The PostScript surface is used to render cairo graphics to Adobe + * PostScript files and is a multi-page vector surface backend. + */ + +/** + * CAIRO_HAS_PS_SURFACE: + * + * Defined if the PostScript surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +static const cairo_surface_backend_t cairo_ps_surface_backend; +static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; + +static void +_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern); + +static const cairo_ps_level_t _cairo_ps_levels[] = +{ + CAIRO_PS_LEVEL_2, + CAIRO_PS_LEVEL_3 +}; + +#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels) + +static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = +{ + "PS Level 2", + "PS Level 3" +}; + +typedef struct _cairo_page_standard_media { + const char *name; + int width; + int height; +} cairo_page_standard_media_t; + +static const cairo_page_standard_media_t _cairo_page_standard_media[] = +{ + { "A0", 2384, 3371 }, + { "A1", 1685, 2384 }, + { "A2", 1190, 1684 }, + { "A3", 842, 1190 }, + { "A4", 595, 842 }, + { "A5", 420, 595 }, + { "B4", 729, 1032 }, + { "B5", 516, 729 }, + { "Letter", 612, 792 }, + { "Tabloid", 792, 1224 }, + { "Ledger", 1224, 792 }, + { "Legal", 612, 1008 }, + { "Statement", 396, 612 }, + { "Executive", 540, 720 }, + { "Folio", 612, 936 }, + { "Quarto", 610, 780 }, + { "10x14", 720, 1008 }, +}; + +typedef struct _cairo_page_media { + char *name; + int width; + int height; + cairo_list_t link; +} cairo_page_media_t; + +static void +_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) +{ + char ctime_buf[26]; + time_t now; + char **comments; + int i, num_comments; + int level; + const char *eps_header = ""; + + if (surface->has_creation_date) + now = surface->creation_date; + else + now = time (NULL); + + if (surface->ps_level_used == CAIRO_PS_LEVEL_2) + level = 2; + else + level = 3; + + if (surface->eps) + eps_header = " EPSF-3.0"; + + _cairo_output_stream_printf (surface->final_stream, + "%%!PS-Adobe-3.0%s\n" + "%%%%Creator: cairo %s (http://cairographics.org)\n" + "%%%%CreationDate: %s" + "%%%%Pages: %d\n" + "%%%%BoundingBox: %d %d %d %d\n", + eps_header, + cairo_version_string (), + ctime_r (&now, ctime_buf), + surface->num_pages, + surface->bbox_x1, + surface->bbox_y1, + surface->bbox_x2, + surface->bbox_y2); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentData: Clean7Bit\n" + "%%%%LanguageLevel: %d\n", + level); + + if (!cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + cairo_bool_t first = TRUE; + + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (first) { + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentMedia: "); + first = FALSE; + } else { + _cairo_output_stream_printf (surface->final_stream, + "%%%%+ "); + } + _cairo_output_stream_printf (surface->final_stream, + "%s %d %d 0 () ()\n", + page->name, + page->width, + page->height); + } + } + + num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); + comments = _cairo_array_index (&surface->dsc_header_comments, 0); + for (i = 0; i < num_comments; i++) { + _cairo_output_stream_printf (surface->final_stream, + "%s\n", comments[i]); + free (comments[i]); + comments[i] = NULL; + } + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndComments\n"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginProlog\n"); + + if (surface->eps) { + _cairo_output_stream_printf (surface->final_stream, + "/cairo_eps_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n"); + } else { + _cairo_output_stream_printf (surface->final_stream, + "/languagelevel where\n" + "{ pop languagelevel } { 1 } ifelse\n" + "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n" + " (This print job requires a PostScript Language Level %d printer.) show\n" + " showpage quit } if\n", + level, + level); + } + + _cairo_output_stream_printf (surface->final_stream, + "/q { gsave } bind def\n" + "/Q { grestore } bind def\n" + "/cm { 6 array astore concat } bind def\n" + "/w { setlinewidth } bind def\n" + "/J { setlinecap } bind def\n" + "/j { setlinejoin } bind def\n" + "/M { setmiterlimit } bind def\n" + "/d { setdash } bind def\n" + "/m { moveto } bind def\n" + "/l { lineto } bind def\n" + "/c { curveto } bind def\n" + "/h { closepath } bind def\n" + "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n" + " 0 exch rlineto 0 rlineto closepath } bind def\n" + "/S { stroke } bind def\n" + "/f { fill } bind def\n" + "/f* { eofill } bind def\n" + "/n { newpath } bind def\n" + "/W { clip } bind def\n" + "/W* { eoclip } bind def\n" + "/BT { } bind def\n" + "/ET { } bind def\n" + "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" + " { globaldict begin /?pdfmark /pop load def /pdfmark\n" + " /cleartomark load def end } ifelse\n" + "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" + "/EMC { mark /EMC pdfmark } bind def\n" + "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n" + "/Tj { show currentpoint cairo_store_point } bind def\n" + "/TJ {\n" + " {\n" + " dup\n" + " type /stringtype eq\n" + " { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n" + " } forall\n" + " currentpoint cairo_store_point\n" + "} bind def\n" + "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n" + " cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n" + "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n" + " { pop cairo_selectfont } if } bind def\n" + "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n" + " /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n" + " /cairo_font where { pop cairo_selectfont } if } bind def\n" + "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n" + " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n" + "/g { setgray } bind def\n" + "/rg { setrgbcolor } bind def\n" + "/d1 { setcachedevice } bind def\n"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndProlog\n"); + + num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); + if (num_comments) { + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginSetup\n"); + + comments = _cairo_array_index (&surface->dsc_setup_comments, 0); + for (i = 0; i < num_comments; i++) { + _cairo_output_stream_printf (surface->final_stream, + "%s\n", comments[i]); + free (comments[i]); + comments[i] = NULL; + } + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndSetup\n"); + } +} + +#if CAIRO_HAS_FT_FONT +static cairo_status_t +_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_type1_subset_t subset; + cairo_status_t status; + int length; + char name[64]; + + snprintf (name, sizeof name, "f-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE); + if (unlikely (status)) + return status; + + /* FIXME: Figure out document structure convention for fonts */ + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_type1_font_subset\n"); +#endif + + length = subset.header_length + subset.data_length + subset.trailer_length; + _cairo_output_stream_write (surface->final_stream, subset.data, length); + + _cairo_type1_subset_fini (&subset); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_status_t +_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_type1_subset_t subset; + cairo_status_t status; + int length; + char name[64]; + + snprintf (name, sizeof name, "f-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_fallback_init_hex (&subset, name, font_subset); + if (unlikely (status)) + return status; + + /* FIXME: Figure out document structure convention for fonts */ + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_type1_font_fallback\n"); +#endif + + length = subset.header_length + subset.data_length + subset.trailer_length; + _cairo_output_stream_write (surface->final_stream, subset.data, length); + + _cairo_type1_fallback_fini (&subset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_truetype_subset_t subset; + cairo_status_t status; + unsigned int i, begin, end; + + status = _cairo_truetype_subset_init (&subset, font_subset); + if (unlikely (status)) + return status; + + /* FIXME: Figure out document structure convention for fonts */ + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_truetype_font_subset\n"); +#endif + + _cairo_output_stream_printf (surface->final_stream, + "11 dict begin\n" + "/FontType 42 def\n" + "/FontName /%s def\n" + "/PaintType 0 def\n" + "/FontMatrix [ 1 0 0 1 0 0 ] def\n" + "/FontBBox [ 0 0 0 0 ] def\n" + "/Encoding 256 array def\n" + "0 1 255 { Encoding exch /.notdef put } for\n", + subset.ps_name); + + /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ + + for (i = 1; i < font_subset->num_glyphs; i++) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /%s put\n", + i, font_subset->glyph_names[i]); + } else { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /g%d put\n", i, i); + } + } + + _cairo_output_stream_printf (surface->final_stream, + "/CharStrings %d dict dup begin\n" + "/.notdef 0 def\n", + font_subset->num_glyphs); + + for (i = 1; i < font_subset->num_glyphs; i++) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "/%s %d def\n", + font_subset->glyph_names[i], i); + } else { + _cairo_output_stream_printf (surface->final_stream, + "/g%d %d def\n", i, i); + } + } + + _cairo_output_stream_printf (surface->final_stream, + "end readonly def\n"); + + _cairo_output_stream_printf (surface->final_stream, + "/sfnts [\n"); + begin = 0; + end = 0; + for (i = 0; i < subset.num_string_offsets; i++) { + end = subset.string_offsets[i]; + _cairo_output_stream_printf (surface->final_stream,"<"); + _cairo_output_stream_write_hex_string (surface->final_stream, + subset.data + begin, end - begin); + _cairo_output_stream_printf (surface->final_stream,"00>\n"); + begin = end; + } + if (subset.data_length > end) { + _cairo_output_stream_printf (surface->final_stream,"<"); + _cairo_output_stream_write_hex_string (surface->final_stream, + subset.data + end, subset.data_length - end); + _cairo_output_stream_printf (surface->final_stream,"00>\n"); + } + + _cairo_output_stream_printf (surface->final_stream, + "] def\n" + "/f-%d-%d currentdict end definefont pop\n", + font_subset->font_id, + font_subset->subset_id); + + _cairo_truetype_subset_fini (&subset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_emit_imagemask (cairo_image_surface_t *image, + cairo_output_stream_t *stream) +{ + uint8_t *row, *byte; + int rows, cols; + + /* The only image type supported by Type 3 fonts are 1-bit image + * masks */ + assert (image->format == CAIRO_FORMAT_A1); + + _cairo_output_stream_printf (stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /ImageMatrix [%d 0 0 %d 0 %d]\n" + " /Decode [1 0]\n" + " /BitsPerComponent 1\n", + image->width, + image->height, + image->width, + -image->height, + image->height); + + _cairo_output_stream_printf (stream, + " /DataSource {<\n "); + for (row = image->data, rows = image->height; rows; row += image->stride, rows--) { + for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { + uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + _cairo_output_stream_printf (stream, "%02x ", output_byte); + } + _cairo_output_stream_printf (stream, "\n "); + } + _cairo_output_stream_printf (stream, ">}\n>>\n"); + + _cairo_output_stream_printf (stream, + "imagemask\n"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_status_t +_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_ps_surface_t *surface = closure; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + unsigned int i; + cairo_surface_t *type3_surface; + + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + NULL, + _cairo_ps_emit_imagemask, + surface->font_subsets); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, + font_subset->glyphs[i]); + if (unlikely (status)) + break; + + } + cairo_surface_finish (type3_surface); + cairo_surface_destroy (type3_surface); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_status_t status; + unsigned int i; + cairo_box_t font_bbox = {{0,0},{0,0}}; + cairo_box_t bbox = {{0,0},{0,0}}; + cairo_surface_t *type3_surface; + double width; + + if (font_subset->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_type3_font_subset\n"); +#endif + + _cairo_output_stream_printf (surface->final_stream, + "8 dict begin\n" + "/FontType 3 def\n" + "/FontMatrix [1 0 0 1 0 0] def\n" + "/Encoding 256 array def\n" + "0 1 255 { Encoding exch /.notdef put } for\n"); + + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + NULL, + _cairo_ps_emit_imagemask, + surface->font_subsets); + status = type3_surface->status; + if (unlikely (status)) + return status; + + for (i = 0; i < font_subset->num_glyphs; i++) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /%s put\n", + i, font_subset->glyph_names[i]); + } else { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /g%d put\n", i, i); + } + } + + _cairo_output_stream_printf (surface->final_stream, + "/Glyphs [\n"); + + for (i = 0; i < font_subset->num_glyphs; i++) { + _cairo_output_stream_printf (surface->final_stream, + " { %% %d\n", i); + status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, + surface->final_stream, + font_subset->glyphs[i], + &bbox, + &width); + if (unlikely (status)) + break; + + _cairo_output_stream_printf (surface->final_stream, + " }\n"); + if (i == 0) { + font_bbox.p1.x = bbox.p1.x; + font_bbox.p1.y = bbox.p1.y; + font_bbox.p2.x = bbox.p2.x; + font_bbox.p2.y = bbox.p2.y; + } else { + if (bbox.p1.x < font_bbox.p1.x) + font_bbox.p1.x = bbox.p1.x; + if (bbox.p1.y < font_bbox.p1.y) + font_bbox.p1.y = bbox.p1.y; + if (bbox.p2.x > font_bbox.p2.x) + font_bbox.p2.x = bbox.p2.x; + if (bbox.p2.y > font_bbox.p2.y) + font_bbox.p2.y = bbox.p2.y; + } + } + cairo_surface_finish (type3_surface); + cairo_surface_destroy (type3_surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->final_stream, + "] def\n" + "/FontBBox [%f %f %f %f] def\n" + "/BuildChar {\n" + " exch /Glyphs get\n" + " exch get\n" + " 10 dict begin exec end\n" + "} bind def\n" + "currentdict\n" + "end\n" + "/f-%d-%d exch definefont pop\n", + _cairo_fixed_to_double (font_bbox.p1.x), + - _cairo_fixed_to_double (font_bbox.p2.y), + _cairo_fixed_to_double (font_bbox.p2.x), + - _cairo_fixed_to_double (font_bbox.p1.y), + font_subset->font_id, + font_subset->subset_id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_ps_surface_t *surface = closure; + cairo_status_t status; + + + status = _cairo_scaled_font_subset_create_glyph_names (font_subset); + if (_cairo_status_is_error (status)) + return status; + +#if CAIRO_HAS_FT_FONT + status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; +#endif + + status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_ps_surface_t *surface = closure; + cairo_status_t status; + + status = _cairo_scaled_font_subset_create_glyph_names (font_subset); + if (_cairo_status_is_error (status)) + return status; + + status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) +{ + cairo_status_t status; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_font_subsets\n"); +#endif + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_ps_surface_analyze_user_font_subset, + surface); + if (unlikely (status)) + return status; + + status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, + _cairo_ps_surface_emit_unscaled_font_subset, + surface); + if (unlikely (status)) + return status; + + status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); + if (unlikely (status)) + return status; + + return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); +} + +static cairo_status_t +_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) +{ + char buf[4096]; + int n; + + if (ferror (surface->tmpfile) != 0) + return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + + rewind (surface->tmpfile); + while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0) + _cairo_output_stream_write (surface->final_stream, buf, n); + + if (ferror (surface->tmpfile) != 0) + return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) +{ + _cairo_output_stream_printf (surface->final_stream, + "%%%%Trailer\n"); + + if (surface->eps) { + _cairo_output_stream_printf (surface->final_stream, + "count op_count sub {pop} repeat\n" + "countdictstack dict_count sub {end} repeat\n" + "cairo_eps_state restore\n"); + } + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EOF\n"); +} + +static cairo_bool_t +_path_covers_bbox (cairo_ps_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&box, &rect); + + /* skip trivial whole-page clips */ + if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { + if (rect.x == surface->page_bbox.x && + rect.width == surface->page_bbox.width && + rect.y == surface->page_bbox.y && + rect.height == surface->page_bbox.height) + { + return TRUE; + } + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_ps_surface_t *surface = cairo_container_of (clipper, + cairo_ps_surface_t, + clipper); + cairo_output_stream_t *stream = surface->stream; + cairo_status_t status; + + assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_intersect_clip_path\n"); +#endif + + if (path == NULL) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + +/* PLRM specifies a tolerance of 5 points when matching page sizes */ +static cairo_bool_t +_ps_page_dimension_equal (int a, int b) +{ + return (abs (a - b) < 5); +} + +static const char * +_cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) +{ + int width, height, i; + char buf[50]; + cairo_page_media_t *page; + const char *page_name; + + width = _cairo_lround (surface->width); + height = _cairo_lround (surface->height); + + /* search previously used page sizes */ + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (_ps_page_dimension_equal (width, page->width) && + _ps_page_dimension_equal (height, page->height)) + return page->name; + } + + /* search list of standard page sizes */ + page_name = NULL; + for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) { + if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) && + _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height)) + { + page_name = _cairo_page_standard_media[i].name; + width = _cairo_page_standard_media[i].width; + height = _cairo_page_standard_media[i].height; + break; + } + } + + page = malloc (sizeof (cairo_page_media_t)); + if (unlikely (page == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + if (page_name) { + page->name = strdup (page_name); + } else { + snprintf (buf, sizeof (buf), "%dx%dmm", + (int) _cairo_lround (surface->width * 25.4/72), + (int) _cairo_lround (surface->height * 25.4/72)); + page->name = strdup (buf); + } + + if (unlikely (page->name == NULL)) { + free (page); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + page->width = width; + page->height = height; + cairo_list_add_tail (&page->link, &surface->document_media); + + return page->name; +} + +static cairo_surface_t * +_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height) +{ + cairo_status_t status, status_ignored; + cairo_ps_surface_t *surface; + + surface = malloc (sizeof (cairo_ps_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + _cairo_surface_init (&surface->base, + &cairo_ps_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + surface->final_stream = stream; + + surface->tmpfile = tmpfile (); + if (surface->tmpfile == NULL) { + switch (errno) { + case ENOMEM: + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + default: + status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + break; + } + goto CLEANUP_SURFACE; + } + + surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); + status = _cairo_output_stream_get_status (surface->stream); + if (unlikely (status)) + goto CLEANUP_OUTPUT_STREAM; + + surface->font_subsets = _cairo_scaled_font_subsets_create_simple (); + if (unlikely (surface->font_subsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_OUTPUT_STREAM; + } + + surface->has_creation_date = FALSE; + surface->eps = FALSE; + surface->ps_level = CAIRO_PS_LEVEL_3; + surface->ps_level_used = CAIRO_PS_LEVEL_2; + surface->width = width; + surface->height = height; + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->force_fallbacks = FALSE; + surface->content = CAIRO_CONTENT_COLOR_ALPHA; + surface->use_string_datasource = FALSE; + surface->current_pattern_is_solid_color = FALSE; + + surface->page_bbox.x = 0; + surface->page_bbox.y = 0; + surface->page_bbox.width = width; + surface->page_bbox.height = height; + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_ps_surface_clipper_intersect_clip_path); + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->stream, + &surface->cairo_to_ps, + surface->font_subsets); + surface->num_pages = 0; + + cairo_list_init (&surface->document_media); + _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); + _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); + _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); + + surface->dsc_comment_target = &surface->dsc_header_comments; + + surface->paginated_surface = _cairo_paginated_surface_create ( + &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_ps_surface_paginated_backend); + status = surface->paginated_surface->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return surface->paginated_surface; + } + + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + CLEANUP_OUTPUT_STREAM: + status_ignored = _cairo_output_stream_destroy (surface->stream); + fclose (surface->tmpfile); + CLEANUP_SURFACE: + free (surface); + CLEANUP: + /* destroy stream on behalf of caller */ + status_ignored = _cairo_output_stream_destroy (stream); + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_ps_surface_create: + * @filename: a filename for the PS output (must be writable), %NULL may be + * used to specify no output. This will generate a PS surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written to @filename. See cairo_ps_surface_create_for_stream() for + * a more flexible mechanism for handling the PostScript output than + * simply writing it to a named file. + * + * Note that the size of individual pages of the PostScript output can + * vary. See cairo_ps_surface_set_size(). + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +/** + * cairo_ps_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written incrementally to the stream represented by @write_func and + * @closure. See cairo_ps_surface_create() for a more convenient way + * to simply direct the PostScript output to a named file. + * + * Note that the size of individual pages of the PostScript + * output can vary. See cairo_ps_surface_set_size(). + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + */ +cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +static cairo_bool_t +_cairo_surface_is_ps (cairo_surface_t *surface) +{ + return surface->backend == &cairo_ps_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a ps_surface, then set ps_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_ps_surface (cairo_surface_t *surface, + cairo_bool_t set_error_on_failure, + cairo_ps_surface_t **ps_surface) +{ + cairo_surface_t *target; + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + if (surface->finished) { + if (set_error_on_failure) + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + if (set_error_on_failure) + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + target = _cairo_paginated_surface_get_target (surface); + if (target->status) { + if (set_error_on_failure) + status_ignored = _cairo_surface_set_error (surface, target->status); + return FALSE; + } + if (target->finished) { + if (set_error_on_failure) + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_ps (target)) { + if (set_error_on_failure) + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *ps_surface = (cairo_ps_surface_t *) target; + return TRUE; +} + +/** + * cairo_ps_surface_restrict_to_level: + * @surface: a PostScript #cairo_surface_t + * @level: PostScript level + * + * Restricts the generated PostSript file to @level. See + * cairo_ps_get_levels() for a list of available level values that + * can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.6 + **/ +void +cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, + cairo_ps_level_t level) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (level < CAIRO_PS_LEVEL_LAST) + ps_surface->ps_level = level; +} + +/** + * cairo_ps_get_levels: + * @levels: supported level list + * @num_levels: list length + * + * Used to retrieve the list of supported levels. See + * cairo_ps_surface_restrict_to_level(). + * + * Since: 1.6 + **/ +void +cairo_ps_get_levels (cairo_ps_level_t const **levels, + int *num_levels) +{ + if (levels != NULL) + *levels = _cairo_ps_levels; + + if (num_levels != NULL) + *num_levels = CAIRO_PS_LEVEL_LAST; +} + +/** + * cairo_ps_level_to_string: + * @level: a level id + * + * Get the string representation of the given @level id. This function + * will return %NULL if @level id isn't valid. See cairo_ps_get_levels() + * for a way to get the list of valid level ids. + * + * Return value: the string associated to given level. + * + * Since: 1.6 + **/ +const char * +cairo_ps_level_to_string (cairo_ps_level_t level) +{ + if (level >= CAIRO_PS_LEVEL_LAST) + return NULL; + + return _cairo_ps_level_strings[level]; +} + +/** + * cairo_ps_surface_set_eps: + * @surface: a PostScript #cairo_surface_t + * @eps: %TRUE to output EPS format PostScript + * + * If @eps is %TRUE, the PostScript surface will output Encapsulated + * PostScript. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface. An Encapsulated PostScript file should never contain more + * than one page. + * + * Since: 1.6 + **/ +void +cairo_ps_surface_set_eps (cairo_surface_t *surface, + cairo_bool_t eps) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + ps_surface->eps = eps; +} + +/** + * cairo_ps_surface_get_eps: + * @surface: a PostScript #cairo_surface_t + * + * Check whether the PostScript surface will output Encapsulated PostScript. + * + * Return value: %TRUE if the surface will output Encapsulated PostScript. + * + * Since: 1.6 + **/ +cairo_public cairo_bool_t +cairo_ps_surface_get_eps (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, FALSE, &ps_surface)) + return FALSE; + + return ps_surface->eps; +} + +/** + * cairo_ps_surface_set_size: + * @surface: a PostScript #cairo_surface_t + * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) + * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) + * + * Changes the size of a PostScript surface for the current (and + * subsequent) pages. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface or immediately after completing a page with either + * cairo_show_page() or cairo_copy_page(). + * + * Since: 1.2 + **/ +void +cairo_ps_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + ps_surface->width = width_in_points; + ps_surface->height = height_in_points; + cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, + &ps_surface->cairo_to_ps); +} + +/** + * cairo_ps_surface_dsc_comment: + * @surface: a PostScript #cairo_surface_t + * @comment: a comment string to be emitted into the PostScript output + * + * Emit a comment into the PostScript output for the given surface. + * + * The comment is expected to conform to the PostScript Language + * Document Structuring Conventions (DSC). Please see that manual for + * details on the available comments and their meanings. In + * particular, the %%IncludeFeature comment allows a + * device-independent means of controlling printer device features. So + * the PostScript Printer Description Files Specification will also be + * a useful reference. + * + * The comment string must begin with a percent character (%) and the + * total length of the string (including any initial percent + * characters) must not exceed 255 characters. Violating either of + * these conditions will place @surface into an error state. But + * beyond these two conditions, this function will not enforce + * conformance of the comment with any particular specification. + * + * The comment string should not have a trailing newline. + * + * The DSC specifies different sections in which particular comments + * can appear. This function provides for comments to be emitted + * within three sections: the header, the Setup section, and the + * PageSetup section. Comments appearing in the first two sections + * apply to the entire document while comments in the BeginPageSetup + * section apply only to a single page. + * + * For comments to appear in the header section, this function should + * be called after the surface is created, but before a call to + * cairo_ps_surface_begin_setup(). + * + * For comments to appear in the Setup section, this function should + * be called after a call to cairo_ps_surface_begin_setup() but before + * a call to cairo_ps_surface_begin_page_setup(). + * + * For comments to appear in the PageSetup section, this function + * should be called after a call to cairo_ps_surface_begin_page_setup(). + * + * Note that it is only necessary to call cairo_ps_surface_begin_page_setup() + * for the first page of any surface. After a call to + * cairo_show_page() or cairo_copy_page() comments are unambiguously + * directed to the PageSetup section of the current page. But it + * doesn't hurt to call this function at the beginning of every page + * as that consistency may make the calling code simpler. + * + * As a final note, cairo automatically generates several comments on + * its own. As such, applications must not manually generate any of + * the following comments: + * + * Header section: %!PS-Adobe-3.0, %%Creator, %%CreationDate, %%Pages, + * %%BoundingBox, %%DocumentData, %%LanguageLevel, %%EndComments. + * + * Setup section: %%BeginSetup, %%EndSetup + * + * PageSetup section: %%BeginPageSetup, %%PageBoundingBox, + * %%EndPageSetup. + * + * Other sections: %%BeginProlog, %%EndProlog, %%Page, %%Trailer, %%EOF + * + * Here is an example sequence showing how this function might be used: + * + * + * #cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); + * ... + * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); + * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") + * ... + * cairo_ps_surface_dsc_begin_setup (surface); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White"); + * ... + * cairo_ps_surface_dsc_begin_page_setup (surface); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue"); + * ... draw to first page here .. + * cairo_show_page (cr); + * ... + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5"); + * ... + * + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_comment (cairo_surface_t *surface, + const char *comment) +{ + cairo_ps_surface_t *ps_surface = NULL; + cairo_status_t status; + char *comment_copy; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + /* A couple of sanity checks on the comment value. */ + if (comment == NULL) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (comment[0] != '%' || strlen (comment) > 255) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT); + return; + } + + /* Then, copy the comment and store it in the appropriate array. */ + comment_copy = strdup (comment); + if (unlikely (comment_copy == NULL)) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); + return; + } + + status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); + if (unlikely (status)) { + free (comment_copy); + status = _cairo_surface_set_error (surface, status); + return; + } +} + +/** + * cairo_ps_surface_dsc_begin_setup: + * @surface: a PostScript #cairo_surface_t + * + * This function indicates that subsequent calls to + * cairo_ps_surface_dsc_comment() should direct comments to the Setup + * section of the PostScript output. + * + * This function should be called at most once per surface, and must + * be called before any call to cairo_ps_surface_dsc_begin_page_setup() + * and before any drawing is performed to the surface. + * + * See cairo_ps_surface_dsc_comment() for more details. + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments) + ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments; +} + +/** + * cairo_ps_surface_dsc_begin_page_setup: + * @surface: a PostScript #cairo_surface_t + * + * This function indicates that subsequent calls to + * cairo_ps_surface_dsc_comment() should direct comments to the + * PageSetup section of the PostScript output. + * + * This function call is only needed for the first page of a + * surface. It should be called after any call to + * cairo_ps_surface_dsc_begin_setup() and before any drawing is + * performed to the surface. + * + * See cairo_ps_surface_dsc_comment() for more details. + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments || + ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments) + { + ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments; + } +} + +static cairo_status_t +_cairo_ps_surface_finish (void *abstract_surface) +{ + cairo_status_t status, status2; + cairo_ps_surface_t *surface = abstract_surface; + int i, num_comments; + char **comments; + + status = surface->base.status; + if (unlikely (status)) + goto CLEANUP; + + _cairo_ps_surface_emit_header (surface); + + status = _cairo_ps_surface_emit_font_subsets (surface); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_ps_surface_emit_body (surface); + if (unlikely (status)) + goto CLEANUP; + + _cairo_ps_surface_emit_footer (surface); + +CLEANUP: + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + + status2 = _cairo_output_stream_destroy (surface->stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + fclose (surface->tmpfile); + + status2 = _cairo_output_stream_destroy (surface->final_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + while (! cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + + page = cairo_list_first_entry (&surface->document_media, + cairo_page_media_t, + link); + cairo_list_del (&page->link); + free (page->name); + free (page); + } + + num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); + comments = _cairo_array_index (&surface->dsc_header_comments, 0); + for (i = 0; i < num_comments; i++) + free (comments[i]); + _cairo_array_fini (&surface->dsc_header_comments); + + num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); + comments = _cairo_array_index (&surface->dsc_setup_comments, 0); + for (i = 0; i < num_comments; i++) + free (comments[i]); + _cairo_array_fini (&surface->dsc_setup_comments); + + num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); + comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); + for (i = 0; i < num_comments; i++) + free (comments[i]); + _cairo_array_fini (&surface->dsc_page_setup_comments); + + _cairo_surface_clipper_reset (&surface->clipper); + + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_start_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + /* Increment before print so page numbers start at 1. */ + surface->num_pages++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_ps_surface_end_page (cairo_ps_surface_t *surface) +{ + cairo_int_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (surface->clipper.clip.path != NULL) { + _cairo_output_stream_printf (surface->stream, "Q Q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } else + _cairo_output_stream_printf (surface->stream, "Q\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_ps_surface_show_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_ps_surface_end_page (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, "showpage\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +color_is_gray (double red, double green, double blue) +{ + const double epsilon = 0.00001; + + return (fabs (red - green) < epsilon && + fabs (red - blue) < epsilon); +} + +static cairo_int_status_t +_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; + cairo_image_transparency_t transparency; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + transparency = _cairo_image_analyze_transparency (image); + switch (transparency) { + case CAIRO_IMAGE_IS_OPAQUE: + status = CAIRO_STATUS_SUCCESS; + break; + + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + if (surface->ps_level == CAIRO_PS_LEVEL_2) { + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + } else { + surface->ps_level_used = CAIRO_PS_LEVEL_3; + status = CAIRO_STATUS_SUCCESS; + } + break; + + case CAIRO_IMAGE_HAS_ALPHA: + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + break; + + case CAIRO_IMAGE_UNKNOWN: + ASSERT_NOT_REACHED; + } + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return TRUE; + + if (pattern->surface->backend->acquire_source_image == NULL) + return FALSE; + + /* Does an ALPHA-only source surface even make sense? Maybe, but I + * don't think it's worth the extra code to support it. */ + +/* XXX: Need to write this function here... + content = pattern->surface->content; + if (content == CAIRO_CONTENT_ALPHA) + return FALSE; +*/ + + return TRUE; +} + +static cairo_bool_t +_gradient_pattern_supported (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern) +{ + const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern; + uint16_t alpha; + cairo_extend_t extend; + unsigned int i; + + if (surface->ps_level == CAIRO_PS_LEVEL_2) + return FALSE; + + if (gradient->n_stops == 0) + return TRUE; + + /* Alpha gradients are only supported (by flattening the alpha) + * if there is no variation in the alpha across the gradient. */ + alpha = gradient->stops[0].color.alpha_short; + for (i = 0; i < gradient->n_stops; i++) { + if (gradient->stops[i].color.alpha_short != alpha) + return FALSE; + } + + extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); + + /* Radial gradients are currently only supported when one circle + * is inside the other. */ + if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { + double x1, y1, x2, y2, r1, r2, d; + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + if (extend == CAIRO_EXTEND_REPEAT || + extend == CAIRO_EXTEND_REFLECT) { + return FALSE; + } + + x1 = _cairo_fixed_to_double (radial->c1.x); + y1 = _cairo_fixed_to_double (radial->c1.y); + r1 = _cairo_fixed_to_double (radial->r1); + x2 = _cairo_fixed_to_double (radial->c2.x); + y2 = _cairo_fixed_to_double (radial->c2.y); + r2 = _cairo_fixed_to_double (radial->r2); + + d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); + if (d > fabs(r2 - r1)) { + return FALSE; + } + } + + surface->ps_level_used = CAIRO_PS_LEVEL_3; + + return TRUE; +} + +static cairo_bool_t +pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + return _gradient_pattern_supported (surface, pattern); + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return surface_pattern_supported ((cairo_surface_pattern_t *) pattern); + + return FALSE; +} + +static cairo_int_status_t +_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! pattern_supported (surface, pattern)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (pattern->extend == CAIRO_EXTEND_PAD) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + } + + if (op == CAIRO_OPERATOR_SOURCE) + return CAIRO_STATUS_SUCCESS; + + /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If + * the pattern contains transparency, we return + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transparency into the white + * background to convert the pattern to opaque. + */ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, + surface_pattern); + } + + if (_cairo_pattern_is_opaque (pattern, extents)) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; +} + +static cairo_bool_t +_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + return _cairo_ps_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* The "standard" implementation limit for PostScript string sizes is + * 65535 characters (see PostScript Language Reference, Appendix + * B). We go one short of that because we sometimes need two + * characters in a string to represent a single ASCII85 byte, (for the + * escape sequences "\\", "\(", and "\)") and we must not split these + * across two strings. So we'd be in trouble if we went right to the + * limit and one of these escape sequences just happened to land at + * the end. + */ +#define STRING_ARRAY_MAX_STRING_SIZE (65535-1) +#define STRING_ARRAY_MAX_COLUMN 72 + +typedef struct _string_array_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + int column; + int string_size; + cairo_bool_t use_strings; +} string_array_stream_t; + +static cairo_status_t +_string_array_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + string_array_stream_t *stream = (string_array_stream_t *) base; + unsigned char c; + const unsigned char backslash = '\\'; + + if (length == 0) + return CAIRO_STATUS_SUCCESS; + + while (length--) { + if (stream->string_size == 0 && stream->use_strings) { + _cairo_output_stream_printf (stream->output, "("); + stream->column++; + } + + c = *data++; + if (stream->use_strings) { + switch (c) { + case '\\': + case '(': + case ')': + _cairo_output_stream_write (stream->output, &backslash, 1); + stream->column++; + stream->string_size++; + break; + } + } + /* Have to be careful to never split the final ~> sequence. */ + if (c == '~') { + _cairo_output_stream_write (stream->output, &c, 1); + stream->column++; + stream->string_size++; + + if (length-- == 0) + break; + + c = *data++; + } + _cairo_output_stream_write (stream->output, &c, 1); + stream->column++; + stream->string_size++; + + if (stream->use_strings && + stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) + { + _cairo_output_stream_printf (stream->output, ")\n"); + stream->string_size = 0; + stream->column = 0; + } + if (stream->column >= STRING_ARRAY_MAX_COLUMN) { + _cairo_output_stream_printf (stream->output, "\n "); + stream->string_size += 2; + stream->column = 1; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_string_array_stream_close (cairo_output_stream_t *base) +{ + cairo_status_t status; + string_array_stream_t *stream = (string_array_stream_t *) base; + + if (stream->use_strings) + _cairo_output_stream_printf (stream->output, ")\n"); + + status = _cairo_output_stream_get_status (stream->output); + + return status; +} + +/* A string_array_stream wraps an existing output stream. It takes the + * data provided to it and output one or more consecutive string + * objects, each within the standard PostScript implementation limit + * of 65k characters. + * + * The strings are each separated by a space character for easy + * inclusion within an array object, (but the array delimiters are not + * added by the string_array_stream). + * + * The string array stream is also careful to wrap the output within + * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds + * necessary escaping for special characters within a string, + * (specifically '\', '(', and ')'). + */ +static cairo_output_stream_t * +_string_array_stream_create (cairo_output_stream_t *output) +{ + string_array_stream_t *stream; + + stream = malloc (sizeof (string_array_stream_t)); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + _string_array_stream_write, + NULL, + _string_array_stream_close); + stream->output = output; + stream->column = 0; + stream->string_size = 0; + stream->use_strings = TRUE; + + return &stream->base; +} + +/* A base85_array_stream wraps an existing output stream. It wraps the + * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output + * is not enclosed in strings like string_array_stream. + */ +static cairo_output_stream_t * +_base85_array_stream_create (cairo_output_stream_t *output) +{ + string_array_stream_t *stream; + + stream = malloc (sizeof (string_array_stream_t)); + if (unlikely (stream == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + _string_array_stream_write, + NULL, + _string_array_stream_close); + stream->output = output; + stream->column = 0; + stream->string_size = 0; + stream->use_strings = FALSE; + + return &stream->base; +} + + +/* PS Output - this section handles output of the parts of the recording + * surface we can render natively in PS. */ + +static cairo_status_t +_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, + cairo_image_surface_t *image, + cairo_image_surface_t **opaque_image) +{ + cairo_surface_t *opaque; + cairo_surface_pattern_t pattern; + cairo_status_t status; + + opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + image->width, + image->height); + if (unlikely (opaque->status)) + return opaque->status; + + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + status = _cairo_surface_paint (opaque, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (opaque); + return status; + } + } + + _cairo_pattern_init_for_surface (&pattern, &image->base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (opaque); + return status; + } + + *opaque_image = (cairo_image_surface_t *) opaque; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, + const unsigned char *data, + unsigned long length, + cairo_bool_t use_strings) +{ + cairo_output_stream_t *base85_stream, *string_array_stream; + cairo_status_t status, status2; + + if (use_strings) + string_array_stream = _string_array_stream_create (surface->stream); + else + string_array_stream = _base85_array_stream_create (surface->stream); + + status = _cairo_output_stream_get_status (string_array_stream); + if (unlikely (status)) + return _cairo_output_stream_destroy (string_array_stream); + + base85_stream = _cairo_base85_stream_create (string_array_stream); + status = _cairo_output_stream_get_status (base85_stream); + if (unlikely (status)) { + status2 = _cairo_output_stream_destroy (string_array_stream); + return _cairo_output_stream_destroy (base85_stream); + } + + _cairo_output_stream_write (base85_stream, data, length); + + status = _cairo_output_stream_destroy (base85_stream); + + /* Mark end of base85 data */ + _cairo_output_stream_printf (string_array_stream, "~>"); + status2 = _cairo_output_stream_destroy (string_array_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, + cairo_image_surface_t *image, + cairo_operator_t op, + cairo_filter_t filter) +{ + cairo_status_t status; + unsigned char *data, *data_compressed; + unsigned long data_size, data_compressed_size; + cairo_image_surface_t *opaque_image = NULL; + int x, y, i; + cairo_image_transparency_t transparency; + cairo_bool_t use_mask; + uint32_t *pixel; + int bit; + const char *interpolate; + + if (image->base.status) + return image->base.status; + + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + + transparency = _cairo_image_analyze_transparency (image); + + /* PostScript can not represent the alpha channel, so we blend the + current image over a white (or black for CONTENT_COLOR + surfaces) RGB surface to eliminate it. */ + + if (op == CAIRO_OPERATOR_SOURCE || + transparency == CAIRO_IMAGE_HAS_ALPHA || + (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && + surface->ps_level == CAIRO_PS_LEVEL_2)) + { + status = _cairo_ps_surface_flatten_image_transparency (surface, + image, + &opaque_image); + if (unlikely (status)) + return status; + + use_mask = FALSE; + } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) { + opaque_image = image; + use_mask = FALSE; + } else { + use_mask = TRUE; + } + + if (use_mask) { + /* Type 2 (mask and image interleaved) has the mask and image + * samples interleaved by row. The mask row is first, one bit + * per pixel with (bit 7 first). The row is padded to byte + * boundaries. The image data is 3 bytes per pixel RGB + * format. */ + data_size = image->height * ((image->width + 7)/8 + 3*image->width); + } else { + data_size = image->height * image->width * 3; + } + data = malloc (data_size); + if (unlikely (data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail1; + } + + if (use_mask) { + i = 0; + for (y = 0; y < image->height; y++) { + /* mask row */ + pixel = (uint32_t *) (image->data + y * image->stride); + bit = 7; + for (x = 0; x < image->width; x++, pixel++) { + if (bit == 7) + data[i] = 0; + if (((*pixel & 0xff000000) >> 24) > 0x80) + data[i] |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + i++; + } + } + if (bit != 7) + i++; + + /* image row*/ + pixel = (uint32_t *) (image->data + y * image->stride); + for (x = 0; x < image->width; x++, pixel++) { + data[i++] = (*pixel & 0x00ff0000) >> 16; + data[i++] = (*pixel & 0x0000ff00) >> 8; + data[i++] = (*pixel & 0x000000ff) >> 0; + } + } + } else { + i = 0; + for (y = 0; y < opaque_image->height; y++) { + pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride); + for (x = 0; x < opaque_image->width; x++, pixel++) { + data[i++] = (*pixel & 0x00ff0000) >> 16; + data[i++] = (*pixel & 0x0000ff00) >> 8; + data[i++] = (*pixel & 0x000000ff) >> 0; + } + } + } + + /* XXX: Should fix cairo-lzw to provide a stream-based interface + * instead. */ + data_compressed_size = data_size; + data_compressed = _cairo_lzw_compress (data, &data_compressed_size); + if (unlikely (data_compressed == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail2; + } + + if (surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/CairoImageData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + data_compressed, + data_compressed_size, + TRUE); + if (unlikely (status)) + goto bail3; + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoImageDataIndex 0 def\n"); + } + + if (use_mask) { + _cairo_output_stream_printf (surface->stream, + "/DeviceRGB setcolorspace\n" + "5 dict dup begin\n" + " /ImageType 3 def\n" + " /InterleaveType 2 def\n" + " /DataDict 8 dict def\n" + " DataDict begin\n" + " /ImageType 1 def\n" + " /Width %d def\n" + " /Height %d def\n" + " /Interpolate %s def\n" + " /BitsPerComponent 8 def\n" + " /Decode [ 0 1 0 1 0 1 ] def\n", + image->width, + image->height, + interpolate); + + if (surface->use_string_datasource) { + _cairo_output_stream_printf (surface->stream, + " /DataSource {\n" + " CairoImageData CairoImageDataIndex get\n" + " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" + " CairoImageDataIndex CairoImageData length 1 sub gt\n" + " { /CairoImageDataIndex 0 def } if\n" + " } /ASCII85Decode filter /LZWDecode filter def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); + } + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" + " end\n" + " /MaskDict 8 dict def\n" + " MaskDict begin\n" + " /ImageType 1 def\n" + " /Width %d def\n" + " /Height %d def\n" + " /Interpolate %s def\n" + " /BitsPerComponent 1 def\n" + " /Decode [ 1 0 ] def\n" + " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" + " end\n" + "end\n" + "image\n", + image->height, + image->width, + image->height, + interpolate, + image->height); + } else { + _cairo_output_stream_printf (surface->stream, + "/DeviceRGB setcolorspace\n" + "8 dict dup begin\n" + " /ImageType 1 def\n" + " /Width %d def\n" + " /Height %d def\n" + " /BitsPerComponent 8 def\n" + " /Decode [ 0 1 0 1 0 1 ] def\n", + opaque_image->width, + opaque_image->height); + if (surface->use_string_datasource) { + _cairo_output_stream_printf (surface->stream, + " /DataSource {\n" + " CairoImageData CairoImageDataIndex get\n" + " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" + " CairoImageDataIndex CairoImageData length 1 sub gt\n" + " { /CairoImageDataIndex 0 def } if\n" + " } /ASCII85Decode filter /LZWDecode filter def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); + } + + _cairo_output_stream_printf (surface->stream, + " /Interpolate %s def\n" + " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" + "end\n" + "image\n", + interpolate, + opaque_image->height); + } + + if (!surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + data_compressed, + data_compressed_size, + FALSE); + _cairo_output_stream_printf (surface->stream, "\n"); + } else { + status = CAIRO_STATUS_SUCCESS; + } + +bail3: + free (data_compressed); + +bail2: + free (data); + +bail1: + if (!use_mask && opaque_image != image) + cairo_surface_destroy (&opaque_image->base); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, + cairo_surface_t *source, + int width, + int height) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t info; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.num_components != 1 && info.num_components != 3) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/CairoImageData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + mime_data, + mime_data_length, + TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoImageDataIndex 0 def\n"); + } + + _cairo_output_stream_printf (surface->stream, + "/%s setcolorspace\n" + "8 dict dup begin\n" + " /ImageType 1 def\n" + " /Width %d def\n" + " /Height %d def\n" + " /BitsPerComponent %d def\n" + " /Decode [ 0 1 0 1 0 1 ] def\n", + info.num_components == 1 ? "DeviceGray" : "DeviceRGB", + info.width, + info.height, + info.bits_per_component); + + if (surface->use_string_datasource) { + _cairo_output_stream_printf (surface->stream, + " /DataSource {\n" + " CairoImageData CairoImageDataIndex get\n" + " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" + " CairoImageDataIndex CairoImageData length 1 sub gt\n" + " { /CairoImageDataIndex 0 def } if\n" + " } /ASCII85Decode filter /DCTDecode filter def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); + } + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" + "end\n" + "image\n", + info.height); + + if (!surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + mime_data, + mime_data_length, + FALSE); + } + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, + cairo_surface_t *recording_surface) +{ + double old_width, old_height; + cairo_matrix_t old_cairo_to_ps; + cairo_content_t old_content; + cairo_rectangle_int_t old_page_bbox; + cairo_box_t bbox; + cairo_status_t status; + + old_content = surface->content; + old_width = surface->width; + old_height = surface->height; + old_page_bbox = surface->page_bbox; + old_cairo_to_ps = surface->cairo_to_ps; + + status = + _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + NULL); + if (unlikely (status)) + return status; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", + _cairo_fixed_to_double (bbox.p1.x), + _cairo_fixed_to_double (bbox.p1.y), + _cairo_fixed_to_double (bbox.p2.x), + _cairo_fixed_to_double (bbox.p2.y)); +#endif + + surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); + surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); + _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, + &surface->cairo_to_ps); + _cairo_output_stream_printf (surface->stream, " q\n"); + + if (recording_surface->content == CAIRO_CONTENT_COLOR) { + surface->content = CAIRO_CONTENT_COLOR; + _cairo_output_stream_printf (surface->stream, + " 0 g %d %d %d %d rectfill\n", + surface->page_bbox.x, + surface->page_bbox.y, + surface->page_bbox.width, + surface->page_bbox.height); + } + + status = _cairo_recording_surface_replay_region (recording_surface, + NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, " Q\n"); + surface->content = old_content; + surface->width = old_width; + surface->height = old_height; + surface->page_bbox = old_page_bbox; + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->cairo_to_ps = old_cairo_to_ps; + + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, + &surface->cairo_to_ps); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, + cairo_surface_t *recording_surface, + const cairo_rectangle_int_t *extents) +{ + double old_width, old_height; + cairo_matrix_t old_cairo_to_ps; + cairo_content_t old_content; + cairo_rectangle_int_t old_page_bbox; + cairo_status_t status; + + old_content = surface->content; + old_width = surface->width; + old_height = surface->height; + old_page_bbox = surface->page_bbox; + old_cairo_to_ps = surface->cairo_to_ps; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n", + extents->x, extents->y, + extents->width, extents->height); +#endif + + surface->page_bbox.x = surface->page_bbox.y = 0; + surface->page_bbox.width = surface->width = extents->width; + surface->page_bbox.height = surface->height = extents->height; + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, + &surface->cairo_to_ps); + _cairo_output_stream_printf (surface->stream, " q\n"); + + if (recording_surface->content == CAIRO_CONTENT_COLOR) { + surface->content = CAIRO_CONTENT_COLOR; + _cairo_output_stream_printf (surface->stream, + " 0 g %d %d %d %d rectfill\n", + surface->page_bbox.x, + surface->page_bbox.y, + surface->page_bbox.width, + surface->page_bbox.height); + } + + status = _cairo_recording_surface_replay_region (recording_surface, + extents, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, " Q\n"); + surface->content = old_content; + surface->width = old_width; + surface->height = old_height; + surface->page_bbox = old_page_bbox; + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->cairo_to_ps = old_cairo_to_ps; + + _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, + &surface->cairo_to_ps); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface, + const cairo_color_t *color, + double *red, + double *green, + double *blue) +{ + *red = color->red; + *green = color->green; + *blue = color->blue; + + if (! CAIRO_COLOR_IS_OPAQUE (color)) { + *red *= color->alpha; + *green *= color->alpha; + *blue *= color->alpha; + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + double one_minus_alpha = 1. - color->alpha; + *red += one_minus_alpha; + *green += one_minus_alpha; + *blue += one_minus_alpha; + } + } +} + +static void +_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + double red, green, blue; + + _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue); + + if (color_is_gray (red, green, blue)) + _cairo_output_stream_printf (surface->stream, + "%f g\n", + red); + else + _cairo_output_stream_printf (surface->stream, + "%f %f %f rg\n", + red, green, blue); +} + +static cairo_status_t +_cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, + int *width, + int *height, + int *origin_x, + int *origin_y) +{ + cairo_status_t status; + cairo_surface_t *pad_image; + int x = 0; + int y = 0; + + surface->acquired_image = NULL; + surface->image = NULL; + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (pattern->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) pattern->surface; + + *width = sub->extents.width; + *height = sub->extents.height; + } else { + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; + cairo_box_t bbox; + cairo_rectangle_int_t extents; + + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + *width = extents.width; + *height = extents.height; + } + return CAIRO_STATUS_SUCCESS; + } else { + status = _cairo_surface_acquire_source_image (pattern->surface, + &surface->acquired_image, + &surface->image_extra); + if (unlikely (status)) + return status; + + pad_image = &surface->acquired_image->base; + if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + x = -rect.x; + y = -rect.y; + + pad_image = + _cairo_image_surface_create_with_pixman_format (NULL, + surface->acquired_image->pixman_format, + rect.width, rect.height, + 0); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } + + _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_paint (pad_image, + CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) { + if (pad_image != &surface->acquired_image->base) + cairo_surface_destroy (pad_image); + + goto BAIL; + } + } + + surface->image = (cairo_image_surface_t *) pad_image; + *width = surface->image->width; + *height = surface->image->height; + *origin_x = x; + *origin_y = y; + return CAIRO_STATUS_SUCCESS; + } + +BAIL: + _cairo_ps_surface_release_surface (surface, pattern); + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_operator_t op, + int width, + int height) +{ + cairo_status_t status; + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + cairo_surface_t *source = pattern->surface; + + if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents); + } else { + status = _cairo_ps_surface_emit_recording_surface (surface, source); + } + } else { + if (pattern->base.extend != CAIRO_EXTEND_PAD) { + status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface, + width, height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_ps_surface_emit_image (surface, surface->image, + op, pattern->base.filter); + } + + return status; +} + +static void +_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + if (surface->image != surface->acquired_image) + cairo_surface_destroy (&surface->image->base); + + if (pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) { + _cairo_surface_release_source_image (pattern->surface, + surface->acquired_image, + surface->image_extra); + } + + surface->acquired_image = NULL; + surface->image = NULL; +} + +static void +_path_fixed_init_rectangle (cairo_path_fixed_t *path, + cairo_rectangle_int_t *rect) +{ + cairo_status_t status; + + _cairo_path_fixed_init (path); + + status = _cairo_path_fixed_move_to (path, + _cairo_fixed_from_int (rect->x), + _cairo_fixed_from_int (rect->y)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (rect->height)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (-rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + + status = _cairo_path_fixed_close_path (path); + assert (status == CAIRO_STATUS_SUCCESS); +} + +static cairo_status_t +_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) +{ + cairo_status_t status; + int width, height; + cairo_matrix_t cairo_p2d, ps_p2d; + cairo_path_fixed_t path; + int origin_x = 0; + int origin_y = 0; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_acquire_surface (surface, + pattern, + extents, + &width, &height, + &origin_x, &origin_y); + if (unlikely (status)) + return status; + + _path_fixed_init_rectangle (&path, extents); + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + &path, + CAIRO_FILL_RULE_WINDING); + _cairo_path_fixed_fini (&path); + if (unlikely (status)) + return status; + + cairo_p2d = pattern->base.matrix; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + double scale = cairo_p2d.xx; + + _cairo_output_stream_printf (surface->stream, + "%% Fallback Image: x=%f, y=%f, w=%d, h=%d res=%fdpi size=%ld\n", + -cairo_p2d.x0/scale, + -cairo_p2d.y0/scale, + (int)(width/scale), + (int)(height/scale), + scale*72, + (long)width*height*3); + } else { + if (op == CAIRO_OPERATOR_SOURCE) { + _cairo_output_stream_printf (surface->stream, + "%d g 0 0 %f %f rectfill\n", + surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + surface->width, + surface->height); + } + } + + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + ps_p2d = surface->cairo_to_ps; + cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); + cairo_matrix_translate (&ps_p2d, 0.0, height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + + if (! _cairo_matrix_is_identity (&ps_p2d)) { + _cairo_output_stream_printf (surface->stream, + "[ %f %f %f %f %f %f ] concat\n", + ps_p2d.xx, ps_p2d.yx, + ps_p2d.xy, ps_p2d.yy, + ps_p2d.x0, ps_p2d.y0); + } + + status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height); + _cairo_ps_surface_release_surface (surface, pattern); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) +{ + cairo_status_t status; + int pattern_width = 0; /* squelch bogus compiler warning */ + int pattern_height = 0; /* squelch bogus compiler warning */ + double xstep, ystep; + cairo_matrix_t cairo_p2d, ps_p2d; + cairo_bool_t old_use_string_datasource; + int origin_x = 0; + int origin_y = 0; + + cairo_p2d = pattern->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + status = _cairo_ps_surface_acquire_surface (surface, + pattern, + extents, + &pattern_width, &pattern_height, + &origin_x, &origin_y); + if (unlikely (status)) + return status; + + switch (pattern->base.extend) { + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_NONE: + { + /* In PS/PDF, (as far as I can tell), all patterns are + * repeating. So we support cairo's EXTEND_NONE semantics + * by setting the repeat step size to a size large enough + * to guarantee that no more than a single occurrence will + * be visible. + * + * First, map the surface extents into pattern space (since + * xstep and ystep are in pattern space). Then use an upper + * bound on the length of the diagonal of the pattern image + * and the surface as repeat size. This guarantees to never + * repeat visibly. + */ + double x1 = 0.0, y1 = 0.0; + double x2 = surface->width, y2 = surface->height; + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &x1, &y1, &x2, &y2, + NULL); + + /* Rather than computing precise bounds of the union, just + * add the surface extents unconditionally. We only + * required an answer that's large enough, we don't really + * care if it's not as tight as possible.*/ + xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + + pattern_width + pattern_height); + break; + } + case CAIRO_EXTEND_REPEAT: + xstep = pattern_width; + ystep = pattern_height; + break; + case CAIRO_EXTEND_REFLECT: + xstep = pattern_width*2; + ystep = pattern_height*2; + break; + /* All the rest (if any) should have been analyzed away, so these + * cases should be unreachable. */ + default: + ASSERT_NOT_REACHED; + xstep = 0; + ystep = 0; + } + + _cairo_output_stream_printf (surface->stream, + "/CairoPattern {\n"); + + old_use_string_datasource = surface->use_string_datasource; + surface->use_string_datasource = TRUE; + if (op == CAIRO_OPERATOR_SOURCE) { + _cairo_output_stream_printf (surface->stream, + "%d g 0 0 %f %f rectfill\n", + surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + xstep, ystep); + } + status = _cairo_ps_surface_emit_surface (surface, pattern, op, + pattern_width, pattern_height); + if (unlikely (status)) + return status; + + surface->use_string_datasource = old_use_string_datasource; + _cairo_output_stream_printf (surface->stream, + "} bind def\n"); + + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 1\n" + " /PaintType 1\n" + " /TilingType 1\n"); + _cairo_output_stream_printf (surface->stream, + " /XStep %f /YStep %f\n", + xstep, ystep); + + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->stream, + " /BBox [0 0 %d %d]\n" + " /PaintProc {\n" + " CairoPattern\n" + " [-1 0 0 1 %d 0] concat CairoPattern\n" + " [ 1 0 0 -1 0 %d] concat CairoPattern\n" + " [-1 0 0 1 %d 0] concat CairoPattern\n" + " CairoPattern\n" + " } bind\n", + pattern_width*2, pattern_height*2, + pattern_width*2, + pattern_height*2, + pattern_width*2); + } else { + if (op == CAIRO_OPERATOR_SOURCE) { + _cairo_output_stream_printf (surface->stream, + " /BBox [0 0 %f %f]\n", + xstep, ystep); + } else { + _cairo_output_stream_printf (surface->stream, + " /BBox [0 0 %d %d]\n", + pattern_width, pattern_height); + } + _cairo_output_stream_printf (surface->stream, + " /PaintProc { CairoPattern }\n"); + } + + _cairo_output_stream_printf (surface->stream, + ">>\n"); + + cairo_p2d = pattern->base.matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_init_identity (&ps_p2d); + cairo_matrix_translate (&ps_p2d, 0.0, surface->height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + + _cairo_output_stream_printf (surface->stream, + "[ %f %f %f %f %f %f ]\n", + ps_p2d.xx, ps_p2d.yx, + ps_p2d.xy, ps_p2d.yy, + ps_p2d.x0, ps_p2d.y0); + _cairo_output_stream_printf (surface->stream, + "makepattern setpattern\n"); + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_ps_color_stop { + double offset; + double color[4]; +} cairo_ps_color_stop_t; + +static void +_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface, + cairo_ps_color_stop_t *stop1, + cairo_ps_color_stop_t *stop2) +{ + _cairo_output_stream_printf (surface->stream, + " << /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f %f %f ]\n" + " /C1 [ %f %f %f ]\n" + " /N 1\n" + " >>\n", + stop1->color[0], + stop1->color[1], + stop1->color[2], + stop2->color[0], + stop2->color[1], + stop2->color[2]); +} + +static void +_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface, + unsigned int n_stops, + cairo_ps_color_stop_t stops[]) +{ + unsigned int i; + + _cairo_output_stream_printf (surface->stream, + "<< /FunctionType 3\n" + " /Domain [ 0 1 ]\n" + " /Functions [\n"); + for (i = 0; i < n_stops - 1; i++) + _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]); + + _cairo_output_stream_printf (surface->stream, " ]\n"); + + _cairo_output_stream_printf (surface->stream, " /Bounds [ "); + for (i = 1; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset); + _cairo_output_stream_printf (surface->stream, "]\n"); + + _cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n", + n_stops - 1); + + _cairo_output_stream_printf (surface->stream, ">>\n"); +} + +static void +calc_gradient_color (cairo_ps_color_stop_t *new_stop, + cairo_ps_color_stop_t *stop1, + cairo_ps_color_stop_t *stop2) +{ + int i; + double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); + + for (i = 0; i < 4; i++) + new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); +} + +#define COLOR_STOP_EPSILON 1e-6 + +static cairo_status_t +_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern) +{ + cairo_ps_color_stop_t *allstops, *stops; + unsigned int i, n_stops; + + allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t)); + if (unlikely (allstops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + stops = &allstops[1]; + n_stops = pattern->n_stops; + + for (i = 0; i < n_stops; i++) { + cairo_gradient_stop_t *stop = &pattern->stops[i]; + + stops[i].color[0] = stop->color.red; + stops[i].color[1] = stop->color.green; + stops[i].color[2] = stop->color.blue; + stops[i].color[3] = stop->color.alpha; + stops[i].offset = pattern->stops[i].offset; + } + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) { + if (stops[0].offset > COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) + memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t)); + else + calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); + stops = allstops; + n_stops++; + } + stops[0].offset = 0.0; + + if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + memcpy (&stops[n_stops], + &stops[n_stops - 1], + sizeof (cairo_ps_color_stop_t)); + } else { + calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); + } + n_stops++; + } + stops[n_stops-1].offset = 1.0; + } + + for (i = 0; i < n_stops; i++) { + double red, green, blue; + cairo_color_t color; + + _cairo_color_init_rgba (&color, + stops[i].color[0], + stops[i].color[1], + stops[i].color[2], + stops[i].color[3]); + _cairo_ps_surface_flatten_transparency (surface, &color, + &red, &green, &blue); + stops[i].color[0] = red; + stops[i].color[1] = green; + stops[i].color[2] = blue; + } + + _cairo_output_stream_printf (surface->stream, + "/CairoFunction\n"); + if (n_stops == 1) { + /* work around single stop gradients */ + _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[0]); + } else if (n_stops == 2) { + /* no need for stitched function */ + _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]); + } else { + /* multiple stops: stitch. XXX possible optimization: regulary spaced + * stops do not require stitching. XXX */ + _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops,stops); + } + _cairo_output_stream_printf (surface->stream, + "def\n"); + + free (allstops); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern, + int begin, + int end) +{ + _cairo_output_stream_printf (surface->stream, + "/CairoFunction\n" + "<< /FunctionType 3\n" + " /Domain [ %d %d ]\n" + " /Functions [ %d {CairoFunction} repeat ]\n" + " /Bounds [ %d 1 %d {} for ]\n", + begin, + end, + end - begin, + begin + 1, + end - 1); + + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n", + begin, + end - 1); + } else { + _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n", + begin, + end - 1); + } + + _cairo_output_stream_printf (surface->stream, ">> def\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ + double x1, y1, x2, y2; + double _x1, _y1, _x2, _y2; + cairo_matrix_t pat_to_ps; + cairo_extend_t extend; + cairo_status_t status; + cairo_gradient_pattern_t *gradient = &pattern->base; + double first_stop, last_stop; + int repeat_begin = 0, repeat_end = 1; + + extend = cairo_pattern_get_extend (&pattern->base.base); + + pat_to_ps = pattern->base.base.matrix; + status = cairo_matrix_invert (&pat_to_ps); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); + first_stop = gradient->stops[0].offset; + last_stop = gradient->stops[gradient->n_stops - 1].offset; + + if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + double dx, dy; + int x_rep = 0, y_rep = 0; + + x1 = _cairo_fixed_to_double (pattern->p1.x); + y1 = _cairo_fixed_to_double (pattern->p1.y); + cairo_matrix_transform_point (&pat_to_ps, &x1, &y1); + + x2 = _cairo_fixed_to_double (pattern->p2.x); + y2 = _cairo_fixed_to_double (pattern->p2.y); + cairo_matrix_transform_point (&pat_to_ps, &x2, &y2); + + dx = fabs (x2 - x1); + dy = fabs (y2 - y1); + if (dx > 1e-6) + x_rep = ceil (surface->width/dx); + if (dy > 1e-6) + y_rep = ceil (surface->height/dy); + + repeat_end = MAX (x_rep, y_rep); + repeat_begin = -repeat_end; + first_stop = repeat_begin; + last_stop = repeat_end; + } + + /* PS requires the first and last stop to be the same as the line + * coordinates. For repeating patterns this moves the line + * coordinates out to the begin/end of the repeating function. For + * non repeating patterns this may move the line coordinates in if + * there are not stops at offset 0 and 1. */ + x1 = _cairo_fixed_to_double (pattern->p1.x); + y1 = _cairo_fixed_to_double (pattern->p1.y); + x2 = _cairo_fixed_to_double (pattern->p2.x); + y2 = _cairo_fixed_to_double (pattern->p2.y); + + _x1 = x1 + (x2 - x1)*first_stop; + _y1 = y1 + (y2 - y1)*first_stop; + _x2 = x1 + (x2 - x1)*last_stop; + _y2 = y1 + (y2 - y1)*last_stop; + + x1 = _x1; + x2 = _x2; + y1 = _y1; + y2 = _y2; + + /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a + * Type 2 function is used by itself without a stitching + * function. Type 2 functions always have the domain [0 1] */ + if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || + pattern->base.base.extend == CAIRO_EXTEND_PAD) && + gradient->n_stops == 2) { + first_stop = 0.0; + last_stop = 1.0; + } + + status = _cairo_ps_surface_emit_pattern_stops (surface, + &pattern->base); + if (unlikely (status)) + return status; + + if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + status = _cairo_ps_surface_emit_repeating_function (surface, + &pattern->base, + repeat_begin, + repeat_end); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 2\n" + " /Shading\n" + " << /ShadingType 2\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f ]\n" + " /Domain [ %f %f ]\n" + " /Function CairoFunction\n", + x1, y1, x2, y2, + first_stop, last_stop); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->stream, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->stream, + " >>\n" + ">>\n"); + _cairo_output_stream_printf (surface->stream, + "[ %f %f %f %f %f %f ]\n", + pat_to_ps.xx, pat_to_ps.yx, + pat_to_ps.xy, pat_to_ps.yy, + pat_to_ps.x0, pat_to_ps.y0); + _cairo_output_stream_printf (surface->stream, + "makepattern setpattern\n"); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface, + cairo_radial_pattern_t *pattern) +{ + double x1, y1, x2, y2, r1, r2; + cairo_matrix_t pat_to_ps; + cairo_extend_t extend; + cairo_status_t status; + + extend = cairo_pattern_get_extend (&pattern->base.base); + + pat_to_ps = pattern->base.base.matrix; + status = cairo_matrix_invert (&pat_to_ps); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); + x1 = _cairo_fixed_to_double (pattern->c1.x); + y1 = _cairo_fixed_to_double (pattern->c1.y); + r1 = _cairo_fixed_to_double (pattern->r1); + x2 = _cairo_fixed_to_double (pattern->c2.x); + y2 = _cairo_fixed_to_double (pattern->c2.y); + r2 = _cairo_fixed_to_double (pattern->r2); + + status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 2\n" + " /Shading\n" + " << /ShadingType 3\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f %f %f ]\n" + " /Function CairoFunction\n", + x1, y1, r1, x2, y2, r2); + + if (extend == CAIRO_EXTEND_PAD) { + _cairo_output_stream_printf (surface->stream, + " /Extend [ true true ]\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->stream, + " >>\n" + ">>\n"); + + _cairo_output_stream_printf (surface->stream, + "[ %f %f %f %f %f %f ]\n", + pat_to_ps.xx, pat_to_ps.yx, + pat_to_ps.xy, pat_to_ps.yy, + pat_to_ps.x0, pat_to_ps.y0); + _cairo_output_stream_printf (surface->stream, + "makepattern setpattern\n"); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) +{ + cairo_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + if (surface->current_pattern_is_solid_color == FALSE || + ! _cairo_color_equal (&surface->current_color, &solid->color)) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + + surface->current_pattern_is_solid_color = TRUE; + surface->current_color = solid->color; + } + + return CAIRO_STATUS_SUCCESS; + } + + surface->current_pattern_is_solid_color = FALSE; + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + + _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_ps_surface_emit_surface_pattern (surface, + (cairo_surface_pattern_t *) pattern, + extents, + op); + if (unlikely (status)) + return status; + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_ps_surface_emit_linear_pattern (surface, + (cairo_linear_pattern_t *) pattern); + if (unlikely (status)) + return status; + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_ps_surface_emit_radial_pattern (surface, + (cairo_radial_pattern_t *) pattern); + if (unlikely (status)) + return status; + break; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_ps_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_ps_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + /* XXX: The conversion to integers here is pretty bogus, (not to + * mention the aribitray limitation of width to a short(!). We + * may need to come up with a better interface for get_extents. + */ + rectangle->width = ceil (surface->width); + rectangle->height = ceil (surface->height); + + return TRUE; +} + +static void +_cairo_ps_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); +} + +static cairo_int_status_t +_cairo_ps_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->stream; + cairo_composite_rectangles_t extents; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &rect, + op, source, clip); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_paint\n"); +#endif + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + (source->extend == CAIRO_EXTEND_NONE || + source->extend == CAIRO_EXTEND_PAD)) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "q\n"); + status = _cairo_ps_surface_paint_surface (surface, + (cairo_surface_pattern_t *) source, + &extents.bounded, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "Q\n"); + } else { + status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", + extents.bounded.x, extents.bounded.y, + extents.bounded.width, extents.bounded.height); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_ps_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &rect, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + /* use the more accurate extents */ + if (extents.is_bounded) { + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &extents.mask); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + return CAIRO_STATUS_SUCCESS; + } + + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_stroke\n"); +#endif + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); +} + +static cairo_int_status_t +_cairo_ps_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &rect, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) + return CAIRO_STATUS_SUCCESS; + + /* use the more accurate extents */ + if (extents.is_bounded) { + _cairo_path_fixed_fill_extents (path, + fill_rule, + tolerance, + &extents.mask); + + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + return CAIRO_STATUS_SUCCESS; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_fill\n"); +#endif + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + (source->extend == CAIRO_EXTEND_NONE || + source->extend == CAIRO_EXTEND_PAD)) + { + _cairo_output_stream_printf (surface->stream, "q\n"); + + status = _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_paint_surface (surface, + (cairo_surface_pattern_t *) source, + &extents.bounded, op); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, "Q\n"); + _cairo_pdf_operators_reset (&surface->pdf_operators); + } else { + status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); + } + + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_bool_t overlap; + cairo_status_t status; + + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &rect, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + + assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_show_glyphs\n"); +#endif + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font); +} + +static void +_cairo_ps_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_status_t status; + + surface->paginated_mode = paginated_mode; + + if (surface->clipper.clip.path != NULL) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->stream, "Q q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } +} + +static cairo_int_status_t +_cairo_ps_surface_set_bounding_box (void *abstract_surface, + cairo_box_t *bbox) +{ + cairo_ps_surface_t *surface = abstract_surface; + int i, num_comments; + char **comments; + int x1, y1, x2, y2; + cairo_bool_t has_page_media; + const char *page_media; + + if (surface->eps) { + x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); + y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); + x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); + y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); + } else { + x1 = 0; + y1 = 0; + x2 = ceil (surface->width); + y2 = ceil (surface->height); + } + + surface->page_bbox.x = x1; + surface->page_bbox.y = y1; + surface->page_bbox.width = x2 - x1; + surface->page_bbox.height = y2 - y1; + + _cairo_output_stream_printf (surface->stream, + "%%%%Page: %d %d\n", + surface->num_pages, + surface->num_pages); + + _cairo_output_stream_printf (surface->stream, + "%%%%BeginPageSetup\n"); + + has_page_media = FALSE; + num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); + comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); + for (i = 0; i < num_comments; i++) { + _cairo_output_stream_printf (surface->stream, + "%s\n", comments[i]); + if (strncmp (comments[i], "%%PageMedia:", 11) == 0) + has_page_media = TRUE; + free (comments[i]); + comments[i] = NULL; + } + _cairo_array_truncate (&surface->dsc_page_setup_comments, 0); + + if (!has_page_media && !surface->eps) { + page_media = _cairo_ps_surface_get_page_media (surface); + if (unlikely (page_media == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->stream, + "%%%%PageMedia: %s\n", + page_media); + } + + _cairo_output_stream_printf (surface->stream, + "%%%%PageBoundingBox: %d %d %d %d\n", + x1, y1, x2, y2); + + _cairo_output_stream_printf (surface->stream, + "%%%%EndPageSetup\n" + "q %d %d %d %d rectclip q\n", + surface->page_bbox.x, + surface->page_bbox.y, + surface->page_bbox.width, + surface->page_bbox.height); + + if (surface->num_pages == 1) { + surface->bbox_x1 = x1; + surface->bbox_y1 = y1; + surface->bbox_x2 = x2; + surface->bbox_y2 = y2; + } else { + if (x1 < surface->bbox_x1) + surface->bbox_x1 = x1; + if (y1 < surface->bbox_y1) + surface->bbox_y1 = y1; + if (x2 > surface->bbox_x2) + surface->bbox_x2 = x2; + if (y2 > surface->bbox_y2) + surface->bbox_y2 = y2; + } + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return _cairo_output_stream_get_status (surface->stream); +} + +static cairo_bool_t +_cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +static const cairo_surface_backend_t cairo_ps_surface_backend = { + CAIRO_SURFACE_TYPE_PS, + NULL, /* create similar: handled by wrapper */ + _cairo_ps_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* cairo_ps_surface_copy_page */ + _cairo_ps_surface_show_page, + _cairo_ps_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_ps_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here are the drawing functions */ + + _cairo_ps_surface_paint, /* paint */ + NULL, /* mask */ + _cairo_ps_surface_stroke, + _cairo_ps_surface_fill, + _cairo_ps_surface_show_glyphs, + NULL, /* snapshot */ +}; + +static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = { + _cairo_ps_surface_start_page, + _cairo_ps_surface_set_paginated_mode, + _cairo_ps_surface_set_bounding_box, + NULL, /* _cairo_ps_surface_has_fallback_images, */ + _cairo_ps_surface_supports_fine_grained_fallbacks, +}; diff --git a/libs/cairo/src/cairo-ps.h b/libs/cairo/src/cairo-ps.h new file mode 100644 index 000000000..3d609c9d1 --- /dev/null +++ b/libs/cairo/src/cairo-ps.h @@ -0,0 +1,82 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_PS_H +#define CAIRO_PS_H + +#include "cairo.h" + +#if CAIRO_HAS_PS_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +/* PS-surface functions */ + +/** + * cairo_ps_level_t: + * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. + * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. + * + * #cairo_ps_level_t is used to describe the language level of the + * PostScript Language Reference that a generated PostScript file will + * conform to. + */ +typedef enum _cairo_ps_level { + CAIRO_PS_LEVEL_2, + CAIRO_PS_LEVEL_3 +} cairo_ps_level_t; + +cairo_public cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, + cairo_ps_level_t level); + +cairo_public void +cairo_ps_get_levels (cairo_ps_level_t const **levels, + int *num_levels); + +cairo_public const char * +cairo_ps_level_to_string (cairo_ps_level_t level); + +cairo_public void +cairo_ps_surface_set_eps (cairo_surface_t *surface, + cairo_bool_t eps); + +cairo_public cairo_bool_t +cairo_ps_surface_get_eps (cairo_surface_t *surface); + +cairo_public void +cairo_ps_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_ps_surface_dsc_comment (cairo_surface_t *surface, + const char *comment); + +cairo_public void +cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface); + +cairo_public void +cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_PS_SURFACE */ +# error Cairo was not compiled with support for the ps backend +#endif /* CAIRO_HAS_PS_SURFACE */ + +#endif /* CAIRO_PS_H */ diff --git a/libs/cairo/src/cairo-recording-surface-private.h b/libs/cairo/src/cairo-recording-surface-private.h new file mode 100644 index 000000000..c21a93205 --- /dev/null +++ b/libs/cairo/src/cairo-recording-surface-private.h @@ -0,0 +1,139 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_RECORDING_SURFACE_H +#define CAIRO_RECORDING_SURFACE_H + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" +#include "cairo-clip-private.h" + +typedef enum { + /* The 5 basic drawing operations. */ + CAIRO_COMMAND_PAINT, + CAIRO_COMMAND_MASK, + CAIRO_COMMAND_STROKE, + CAIRO_COMMAND_FILL, + CAIRO_COMMAND_SHOW_TEXT_GLYPHS, +} cairo_command_type_t; + +typedef enum { + CAIRO_RECORDING_REGION_ALL, + CAIRO_RECORDING_REGION_NATIVE, + CAIRO_RECORDING_REGION_IMAGE_FALLBACK +} cairo_recording_region_type_t; + +typedef struct _cairo_command_header { + cairo_command_type_t type; + cairo_recording_region_type_t region; + cairo_operator_t op; + cairo_clip_t clip; +} cairo_command_header_t; + +typedef struct _cairo_command_paint { + cairo_command_header_t header; + cairo_pattern_union_t source; +} cairo_command_paint_t; + +typedef struct _cairo_command_mask { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_pattern_union_t mask; +} cairo_command_mask_t; + +typedef struct _cairo_command_stroke { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_stroke_t; + +typedef struct _cairo_command_fill { + cairo_command_header_t header; + cairo_pattern_union_t source; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; +} cairo_command_fill_t; + +typedef struct _cairo_command_show_text_glyphs { + cairo_command_header_t header; + cairo_pattern_union_t source; + char *utf8; + int utf8_len; + cairo_glyph_t *glyphs; + unsigned int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + cairo_scaled_font_t *scaled_font; +} cairo_command_show_text_glyphs_t; + +typedef union _cairo_command { + cairo_command_header_t header; + + cairo_command_paint_t paint; + cairo_command_mask_t mask; + cairo_command_stroke_t stroke; + cairo_command_fill_t fill; + cairo_command_show_text_glyphs_t show_text_glyphs; +} cairo_command_t; + +typedef struct _cairo_recording_surface { + cairo_surface_t base; + + cairo_content_t content; + + /* A recording-surface is logically unbounded, but when used as a + * source we need to render it to an image, so we need a size at + * which to create that image. */ + cairo_rectangle_t extents_pixels; + cairo_rectangle_int_t extents; + cairo_bool_t unbounded; + + cairo_clip_t clip; + + cairo_array_t commands; + + int replay_start_idx; +} cairo_recording_surface_t; + +slim_hidden_proto (cairo_recording_surface_create); + +cairo_private cairo_int_status_t +_cairo_recording_surface_get_path (cairo_surface_t *surface, + cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_recording_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target); + + +cairo_private cairo_status_t +_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_private cairo_status_t +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target); +cairo_private cairo_status_t +_cairo_recording_surface_replay_region (cairo_surface_t *surface, + const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *target, + cairo_recording_region_type_t region); + +cairo_private cairo_status_t +_cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, + cairo_box_t *bbox, + const cairo_matrix_t *transform); + +cairo_private cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface); + +#endif /* CAIRO_RECORDING_SURFACE_H */ diff --git a/libs/cairo/src/cairo-recording-surface.c b/libs/cairo/src/cairo-recording-surface.c new file mode 100644 index 000000000..8230ac375 --- /dev/null +++ b/libs/cairo/src/cairo-recording-surface.c @@ -0,0 +1,1099 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * SECTION:cairo-recording + * @Title: Recording Surfaces + * @Short_Description: Records all drawing operations + * @See_Also: #cairo_surface_t + * + * A recording surface is a surface that records all drawing operations at + * the highest level of the surface backend interface, (that is, the + * level of paint, mask, stroke, fill, and show_text_glyphs). The recording + * surface can then be "replayed" against any target surface by using it + * as a source surface. + * + * If you want to replay a surface so that the results in target will be + * identical to the results that would have been obtained if the original + * operations applied to the recording surface had instead been applied to the + * target surface, you can use code like this: + * + * cairo_t *cr; + * + * cr = cairo_create (target); + * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); + * cairo_paint (cr); + * cairo_destroy (cr); + * + * + * A recording surface is logically unbounded, i.e. it has no implicit constraint + * on the size of the drawing surface. However, in practice this is rarely + * useful as you wish to replay against a particular target surface with + * known bounds. For this case, it is more efficient to specify the target + * extents to the recording surface upon creation. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. The efficiency of the recording surface could be + * improved by improving the implementation of snapshot for the + * various objects. For example, it would be nice to have a + * copy-on-write implementation for _cairo_surface_snapshot. + */ + +#include "cairoint.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-wrapper-private.h" + +typedef enum { + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_CREATE_REGIONS +} cairo_recording_replay_type_t; + +static const cairo_surface_backend_t cairo_recording_surface_backend; + +/** + * CAIRO_HAS_RECORDING_SURFACE: + * + * Defined if the recording surface backend is available. + * The recording surface backend is always built in. + * This macro was added for completeness in cairo 1.10. + * + * Since: 1.10 + */ + +/* Currently all recording surfaces do have a size which should be passed + * in as the maximum size of any target surface against which the + * recording-surface will ever be replayed. + * + * XXX: The naming of "pixels" in the size here is a misnomer. It's + * actually a size in whatever device-space units are desired (again, + * according to the intended replay target). + */ + +/** + * cairo_recording_surface_create: + * @content: the content of the recording surface + * @extents: the extents to record in pixels, can be %NULL to record + * unbounded operations. + * + * Creates a recording-surface which can be used to record all drawing operations + * at the highest level (that is, the level of paint, mask, stroke, fill + * and show_text_glyphs). The recording surface can then be "replayed" against + * any target surface by using it as a source to drawing operations. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents) +{ + cairo_recording_surface_t *recording_surface; + cairo_status_t status; + + recording_surface = malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (recording_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&recording_surface->base, + &cairo_recording_surface_backend, + NULL, /* device */ + content); + + recording_surface->content = content; + + recording_surface->unbounded = TRUE; + _cairo_clip_init (&recording_surface->clip); + + /* unbounded -> 'infinite' extents */ + if (extents != NULL) { + recording_surface->extents_pixels = *extents; + + /* XXX check for overflow */ + recording_surface->extents.x = floor (extents->x); + recording_surface->extents.y = floor (extents->y); + recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x; + recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y; + + status = _cairo_clip_rectangle (&recording_surface->clip, + &recording_surface->extents); + if (unlikely (status)) { + free (recording_surface); + return _cairo_surface_create_in_error (status); + } + + recording_surface->unbounded = FALSE; + } + + _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); + + recording_surface->replay_start_idx = 0; + recording_surface->base.is_clear = TRUE; + + return &recording_surface->base; +} +slim_hidden_def (cairo_recording_surface_create); + +static cairo_surface_t * +_cairo_recording_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_status_t +_cairo_recording_surface_finish (void *abstract_surface) +{ + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_t **elements; + int i, num_elements; + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + _cairo_pattern_fini (&command->paint.source.base); + break; + + case CAIRO_COMMAND_MASK: + _cairo_pattern_fini (&command->mask.source.base); + _cairo_pattern_fini (&command->mask.mask.base); + break; + + case CAIRO_COMMAND_STROKE: + _cairo_pattern_fini (&command->stroke.source.base); + _cairo_path_fixed_fini (&command->stroke.path); + _cairo_stroke_style_fini (&command->stroke.style); + break; + + case CAIRO_COMMAND_FILL: + _cairo_pattern_fini (&command->fill.source.base); + _cairo_path_fixed_fini (&command->fill.path); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + _cairo_pattern_fini (&command->show_text_glyphs.source.base); + free (command->show_text_glyphs.utf8); + free (command->show_text_glyphs.glyphs); + free (command->show_text_glyphs.clusters); + cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); + break; + + default: + ASSERT_NOT_REACHED; + } + + _cairo_clip_fini (&command->header.clip); + free (command); + } + + _cairo_array_fini (&recording_surface->commands); + _cairo_clip_fini (&recording_surface->clip); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_recording_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_surface_t *image; + + image = _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); + if (image != NULL) { + *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + image = _cairo_image_surface_create_with_content (surface->content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->status)) + return image->status; + + cairo_surface_set_device_offset (image, + -surface->extents.x, + -surface->extents.y); + + status = _cairo_recording_surface_replay (&surface->base, image); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + cairo_surface_attach_snapshot (&surface->base, image, NULL); + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_recording_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_command_init (cairo_recording_surface_t *recording_surface, + cairo_command_header_t *command, + cairo_command_type_t type, + cairo_operator_t op, + cairo_clip_t *clip) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + command->type = type; + command->op = op; + command->region = CAIRO_RECORDING_REGION_ALL; + _cairo_clip_init_copy (&command->clip, clip); + if (recording_surface->clip.path != NULL) + status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip); + + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_paint_t *command; + + command = malloc (sizeof (cairo_command_paint_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_PAINT, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + /* An optimisation that takes care to not replay what was done + * before surface is cleared. We don't erase recorded commands + * since we may have earlier snapshots of this surface. */ + if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) + recording_surface->replay_start_idx = recording_surface->commands.num_elements; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_fini (&command->header.clip); + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_mask_t *command; + + command = malloc (sizeof (cairo_command_mask_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_MASK, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->mask.base, mask); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_MASK; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_MASK: + _cairo_pattern_fini (&command->mask.base); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_fini (&command->header.clip); + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_stroke_t *command; + + command = malloc (sizeof (cairo_command_stroke_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_STROKE, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + status = _cairo_stroke_style_init_copy (&command->style, style); + if (unlikely (status)) + goto CLEANUP_PATH; + + command->ctm = *ctm; + command->ctm_inverse = *ctm_inverse; + command->tolerance = tolerance; + command->antialias = antialias; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_STYLE; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_STYLE: + _cairo_stroke_style_fini (&command->style); + CLEANUP_PATH: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_fini (&command->header.clip); + free (command); + return status; +} + +static cairo_int_status_t +_cairo_recording_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_fill_t *command; + + command = malloc (sizeof (cairo_command_fill_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status =_command_init (recording_surface, + &command->header, CAIRO_COMMAND_FILL, op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + command->fill_rule = fill_rule; + command->tolerance = tolerance; + command->antialias = antialias; + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_PATH; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_PATH: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_fini (&command->header.clip); + free (command); + return status; +} + +static cairo_bool_t +_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_recording_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_command_show_text_glyphs_t *command; + + command = malloc (sizeof (cairo_command_show_text_glyphs_t)); + if (unlikely (command == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _command_init (recording_surface, + &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, + op, clip); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + command->utf8 = NULL; + command->utf8_len = utf8_len; + command->glyphs = NULL; + command->num_glyphs = num_glyphs; + command->clusters = NULL; + command->num_clusters = num_clusters; + + if (utf8_len) { + command->utf8 = malloc (utf8_len); + if (unlikely (command->utf8 == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->utf8, utf8, utf8_len); + } + if (num_glyphs) { + command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); + if (unlikely (command->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); + } + if (num_clusters) { + command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); + if (unlikely (command->clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); + } + + command->cluster_flags = cluster_flags; + + command->scaled_font = cairo_scaled_font_reference (scaled_font); + + status = _cairo_array_append (&recording_surface->commands, &command); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SCALED_FONT: + cairo_scaled_font_destroy (command->scaled_font); + CLEANUP_ARRAYS: + free (command->utf8); + free (command->glyphs); + free (command->clusters); + + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_fini (&command->header.clip); + free (command); + return status; +} + +/** + * _cairo_recording_surface_snapshot + * @surface: a #cairo_surface_t which must be a recording surface + * + * Make an immutable copy of @surface. It is an error to call a + * surface-modifying function on the result of this function. + * + * The caller owns the return value and should call + * cairo_surface_destroy() when finished with it. This function will not + * return %NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. + **/ +static cairo_surface_t * +_cairo_recording_surface_snapshot (void *abstract_other) +{ + cairo_recording_surface_t *other = abstract_other; + cairo_recording_surface_t *recording_surface; + cairo_status_t status; + + recording_surface = malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (recording_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&recording_surface->base, + &cairo_recording_surface_backend, + NULL, /* device */ + other->base.content); + + recording_surface->extents_pixels = other->extents_pixels; + recording_surface->extents = other->extents; + recording_surface->unbounded = other->unbounded; + recording_surface->content = other->content; + + _cairo_clip_init_copy (&recording_surface->clip, &other->clip); + + /* XXX We should in theory be able to reuse the original array, but we + * need to handle reference cycles during subsurface and self-copy. + */ + recording_surface->replay_start_idx = 0; + recording_surface->base.is_clear = TRUE; + + _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); + status = _cairo_recording_surface_replay (&other->base, &recording_surface->base); + if (unlikely (status)) { + cairo_surface_destroy (&recording_surface->base); + return _cairo_surface_create_in_error (status); + } + + return &recording_surface->base; +} + +static cairo_bool_t +_cairo_recording_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_recording_surface_t *surface = abstract_surface; + + if (surface->unbounded) + return FALSE; + + *rectangle = surface->extents; + return TRUE; +} + +/** + * _cairo_surface_is_recording: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_recording_surface_t + * + * Return value: %TRUE if the surface is a recording surface + **/ +cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface) +{ + return surface->backend == &cairo_recording_surface_backend; +} + +static const cairo_surface_backend_t cairo_recording_surface_backend = { + CAIRO_SURFACE_TYPE_RECORDING, + _cairo_recording_surface_create_similar, + _cairo_recording_surface_finish, + _cairo_recording_surface_acquire_source_image, + _cairo_recording_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_recording_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here are the 5 basic drawing operations, (which are in some + * sense the only things that cairo_recording_surface should need to + * implement). However, we implement the more generic show_text_glyphs + * instead of show_glyphs. One or the other is eough. */ + + _cairo_recording_surface_paint, + _cairo_recording_surface_mask, + _cairo_recording_surface_stroke, + _cairo_recording_surface_fill, + NULL, + + _cairo_recording_surface_snapshot, + + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + _cairo_recording_surface_has_show_text_glyphs, + _cairo_recording_surface_show_text_glyphs +}; + +cairo_int_status_t +_cairo_recording_surface_get_path (cairo_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_recording_surface_t *recording_surface; + cairo_command_t **elements; + int i, num_elements; + cairo_int_status_t status; + + if (surface->status) + return surface->status; + + recording_surface = (cairo_recording_surface_t *) surface; + status = CAIRO_STATUS_SUCCESS; + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + case CAIRO_COMMAND_MASK: + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + + case CAIRO_COMMAND_STROKE: + { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + /* XXX call cairo_stroke_to_path() when that is implemented */ + status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + &traps); + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_traps_path (&traps, path); + + _cairo_traps_fini (&traps); + break; + } + case CAIRO_COMMAND_FILL: + { + status = _cairo_path_fixed_append (path, + &command->fill.path, CAIRO_DIRECTION_FORWARD, + 0, 0); + break; + } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + { + status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, + command->show_text_glyphs.glyphs, + command->show_text_glyphs.num_glyphs, + path); + break; + } + + default: + ASSERT_NOT_REACHED; + } + + if (unlikely (status)) + break; + } + + return _cairo_surface_set_error (surface, status); +} + +#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL) +static cairo_status_t +_cairo_recording_surface_replay_internal (cairo_surface_t *surface, + const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *target, + cairo_recording_replay_type_t type, + cairo_recording_region_type_t region) +{ + cairo_recording_surface_t *recording_surface; + cairo_command_t **elements; + int i, num_elements; + cairo_int_status_t status; + cairo_surface_wrapper_t wrapper; + + if (unlikely (surface->status)) + return surface->status; + + if (unlikely (target->status)) + return target->status; + + if (unlikely (surface->finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + if (surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + assert (_cairo_surface_is_recording (surface)); + + _cairo_surface_wrapper_init (&wrapper, target); + _cairo_surface_wrapper_set_extents (&wrapper, surface_extents); + + recording_surface = (cairo_recording_surface_t *) surface; + status = CAIRO_STATUS_SUCCESS; + + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) { + if (command->header.region != region) + continue; + } + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + status = _cairo_surface_wrapper_paint (&wrapper, + command->header.op, + &command->paint.source.base, + _clip (command)); + break; + + case CAIRO_COMMAND_MASK: + status = _cairo_surface_wrapper_mask (&wrapper, + command->header.op, + &command->mask.source.base, + &command->mask.mask.base, + _clip (command)); + break; + + case CAIRO_COMMAND_STROKE: + { + status = _cairo_surface_wrapper_stroke (&wrapper, + command->header.op, + &command->stroke.source.base, + &command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + command->stroke.antialias, + _clip (command)); + break; + } + case CAIRO_COMMAND_FILL: + { + cairo_command_t *stroke_command; + + stroke_command = NULL; + if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) + stroke_command = elements[i + 1]; + + if (stroke_command != NULL && + type == CAIRO_RECORDING_REPLAY && + region != CAIRO_RECORDING_REGION_ALL) + { + if (stroke_command->header.region != region) + stroke_command = NULL; + } + + if (stroke_command != NULL && + stroke_command->header.type == CAIRO_COMMAND_STROKE && + _cairo_path_fixed_is_equal (&command->fill.path, + &stroke_command->stroke.path)) + { + status = _cairo_surface_wrapper_fill_stroke (&wrapper, + command->header.op, + &command->fill.source.base, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + &command->fill.path, + stroke_command->header.op, + &stroke_command->stroke.source.base, + &stroke_command->stroke.style, + &stroke_command->stroke.ctm, + &stroke_command->stroke.ctm_inverse, + stroke_command->stroke.tolerance, + stroke_command->stroke.antialias, + _clip (command)); + i++; + } + else + { + status = _cairo_surface_wrapper_fill (&wrapper, + command->header.op, + &command->fill.source.base, + &command->fill.path, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + _clip (command)); + } + break; + } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + { + cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; + cairo_glyph_t *glyphs_copy; + int num_glyphs = command->show_text_glyphs.num_glyphs; + + /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed + * to modify the glyph array that's passed in. We must always + * copy the array before handing it to the backend. + */ + glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (glyphs_copy == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + } + + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + + status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, + command->header.op, + &command->show_text_glyphs.source.base, + command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, + glyphs_copy, num_glyphs, + command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, + command->show_text_glyphs.cluster_flags, + command->show_text_glyphs.scaled_font, + _clip (command)); + free (glyphs_copy); + break; + } + default: + ASSERT_NOT_REACHED; + } + + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + if (status == CAIRO_STATUS_SUCCESS) { + command->header.region = CAIRO_RECORDING_REGION_NATIVE; + } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { + command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; + status = CAIRO_STATUS_SUCCESS; + } else { + assert (_cairo_status_is_error (status)); + } + } + + if (unlikely (status)) + break; + } + + /* free up any caches */ + for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + + _cairo_clip_drop_cache (&command->header.clip); + } + + _cairo_surface_wrapper_fini (&wrapper); + + return _cairo_surface_set_error (surface, status); +} + +/** + * _cairo_recording_surface_replay: + * @surface: the #cairo_recording_surface_t + * @target: a target #cairo_surface_t onto which to replay the operations + * @width_pixels: width of the surface, in pixels + * @height_pixels: height of the surface, in pixels + * + * A recording surface can be "replayed" against any target surface, + * after which the results in target will be identical to the results + * that would have been obtained if the original operations applied to + * the recording surface had instead been applied to the target surface. + **/ +cairo_status_t +_cairo_recording_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_recording_surface_replay_internal (surface, NULL, + target, + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_REGION_ALL); +} + +/* Replay recording to surface. When the return status of each operation is + * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or + * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation + * will be stored in the recording surface. Any other status will abort the + * replay and return the status. + */ +cairo_status_t +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + cairo_surface_t *target) +{ + return _cairo_recording_surface_replay_internal (surface, NULL, + target, + CAIRO_RECORDING_CREATE_REGIONS, + CAIRO_RECORDING_REGION_ALL); +} + +cairo_status_t +_cairo_recording_surface_replay_region (cairo_surface_t *surface, + const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *target, + cairo_recording_region_type_t region) +{ + return _cairo_recording_surface_replay_internal (surface, surface_extents, + target, + CAIRO_RECORDING_REPLAY, + region); +} + +static cairo_status_t +_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + cairo_surface_t *null_surface; + cairo_surface_t *analysis_surface; + cairo_status_t status; + + null_surface = cairo_null_surface_create (surface->content); + analysis_surface = _cairo_analysis_surface_create (null_surface); + cairo_surface_destroy (null_surface); + + status = analysis_surface->status; + if (unlikely (status)) + return status; + + if (transform != NULL) + _cairo_analysis_surface_set_ctm (analysis_surface, transform); + + status = _cairo_recording_surface_replay (&surface->base, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); + cairo_surface_destroy (analysis_surface); + + return status; +} + +/** + * cairo_recording_surface_ink_extents: + * @surface: a #cairo_recording_surface_t + * @x0: the x-coordinate of the top-left of the ink bounding box + * @y0: the y-coordinate of the top-left of the ink bounding box + * @width: the width of the ink bounding box + * @height: the height of the ink bounding box + * + * Measures the extents of the operations stored within the recording-surface. + * This is useful to compute the required size of an image surface (or + * equivalent) into which to replay the full sequence of drawing operations. + * + * Since: 1.10 + **/ +void +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height) +{ + cairo_status_t status; + cairo_box_t bbox; + + memset (&bbox, 0, sizeof (bbox)); + + if (! _cairo_surface_is_recording (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + goto DONE; + } + + status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface, + &bbox, + NULL); + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + +DONE: + if (x0) + *x0 = _cairo_fixed_to_double (bbox.p1.x); + if (y0) + *y0 = _cairo_fixed_to_double (bbox.p1.y); + if (width) + *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); + if (height) + *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); +} + +cairo_status_t +_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + if (! surface->unbounded) { + _cairo_box_from_rectangle (bbox, &surface->extents); + if (transform != NULL) + _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); + + return CAIRO_STATUS_SUCCESS; + } + + return _recording_surface_get_ink_bbox (surface, bbox, transform); +} diff --git a/libs/cairo/src/cairo-rectangle.c b/libs/cairo/src/cairo-rectangle.c new file mode 100644 index 000000000..ab043c1bc --- /dev/null +++ b/libs/cairo/src/cairo-rectangle.c @@ -0,0 +1,231 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +cairo_private void +_cairo_box_from_doubles (cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2) +{ + box->p1.x = _cairo_fixed_from_double (*x1); + box->p1.y = _cairo_fixed_from_double (*y1); + box->p2.x = _cairo_fixed_from_double (*x2); + box->p2.y = _cairo_fixed_from_double (*y2); +} + +cairo_private void +_cairo_box_to_doubles (const cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2) +{ + *x1 = _cairo_fixed_to_double (box->p1.x); + *y1 = _cairo_fixed_to_double (box->p1.y); + *x2 = _cairo_fixed_to_double (box->p2.x); + *y2 = _cairo_fixed_to_double (box->p2.y); +} + +void +_cairo_box_from_rectangle (cairo_box_t *box, + const cairo_rectangle_int_t *rect) +{ + box->p1.x = _cairo_fixed_from_int (rect->x); + box->p1.y = _cairo_fixed_from_int (rect->y); + box->p2.x = _cairo_fixed_from_int (rect->x + rect->width); + box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); +} + +void +_cairo_boxes_get_extents (const cairo_box_t *boxes, + int num_boxes, + cairo_box_t *extents) +{ + int n; + + assert (num_boxes > 0); + *extents = *boxes; + + for (n = 1; n < num_boxes; n++) { + if (boxes[n].p1.x < extents->p1.x) + extents->p1.x = boxes[n].p1.x; + if (boxes[n].p2.x > extents->p2.x) + extents->p2.x = boxes[n].p2.x; + + if (boxes[n].p1.y < extents->p1.y) + extents->p1.y = boxes[n].p1.y; + if (boxes[n].p2.y > extents->p2.y) + extents->p2.y = boxes[n].p2.y; + } +} + +/* This function will return 'true' if the containing_rectangle contains the + * contained_rectangle, and false otherwise. + */ +cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle) +{ + if (containing_rectangle->x > contained_rectangle->x || + containing_rectangle->y > contained_rectangle->y) + return FALSE; + + if (containing_rectangle->x + containing_rectangle->width < + contained_rectangle->x + contained_rectangle->width || + containing_rectangle->y + containing_rectangle->height < + contained_rectangle->y + contained_rectangle->height) + return FALSE; + + return TRUE; +} + +/* XXX We currently have a confusing mix of boxes and rectangles as + * exemplified by this function. A #cairo_box_t is a rectangular area + * represented by the coordinates of the upper left and lower right + * corners, expressed in fixed point numbers. A #cairo_rectangle_int_t is + * also a rectangular area, but represented by the upper left corner + * and the width and the height, as integer numbers. + * + * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by + * increasing the area to the nearest integer coordinates. We should + * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and + * this function could be renamed to the more reasonable + * _cairo_rectangle_fixed_round. + */ + +void +_cairo_box_round_to_rectangle (const cairo_box_t *box, + cairo_rectangle_int_t *rectangle) +{ + rectangle->x = _cairo_fixed_integer_floor (box->p1.x); + rectangle->y = _cairo_fixed_integer_floor (box->p1.y); + rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; + rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; +} + +cairo_bool_t +_cairo_rectangle_intersect (cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src) +{ + int x1, y1, x2, y2; + + x1 = MAX (dst->x, src->x); + y1 = MAX (dst->y, src->y); + /* Beware the unsigned promotion, fortunately we have bits to spare + * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX + */ + x2 = MIN (dst->x + (int) dst->width, src->x + (int) src->width); + y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height); + + if (x1 >= x2 || y1 >= y2) { + dst->x = 0; + dst->y = 0; + dst->width = 0; + dst->height = 0; + + return FALSE; + } else { + dst->x = x1; + dst->y = y1; + dst->width = x2 - x1; + dst->height = y2 - y1; + + return TRUE; + } +} + +#define P1x (line->p1.x) +#define P1y (line->p1.y) +#define P2x (line->p2.x) +#define P2y (line->p2.y) +#define B1x (box->p1.x) +#define B1y (box->p1.y) +#define B2x (box->p2.x) +#define B2y (box->p2.y) + +/* + * Check whether any part of line intersects box. This function essentially + * computes whether the ray starting at line->p1 in the direction of line->p2 + * intersects the box before it reaches p2. Normally, this is done + * by dividing by the lengths of the line projected onto each axis. Because + * we're in fixed point, this function does a bit more work to avoid having to + * do the division -- we don't care about the actual intersection point, so + * it's of no interest to us. + */ + +cairo_bool_t +_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) +{ + cairo_fixed_t t1=0, t2=0, t3=0, t4=0; + cairo_int64_t t1y, t2y, t3x, t4x; + + cairo_fixed_t xlen, ylen; + + if (_cairo_box_contains_point (box, &line->p1) || + _cairo_box_contains_point (box, &line->p2)) + return TRUE; + + xlen = P2x - P1x; + ylen = P2y - P1y; + + if (xlen) { + if (xlen > 0) { + t1 = B1x - P1x; + t2 = B2x - P1x; + } else { + t1 = P1x - B2x; + t2 = P1x - B1x; + xlen = - xlen; + } + + if (t1 > xlen || t2 < 0) + return FALSE; + } else { + /* Fully vertical line -- check that X is in bounds */ + if (P1x < B1x || P1x > B2x) + return FALSE; + } + + if (ylen) { + if (ylen > 0) { + t3 = B1y - P1y; + t4 = B2y - P1y; + } else { + t3 = P1y - B2y; + t4 = P1y - B1y; + ylen = - ylen; + } + + if (t3 > ylen || t4 < 0) + return FALSE; + } else { + /* Fully horizontal line -- check Y */ + if (P1y < B1y || P1y > B2y) + return FALSE; + } + + /* If we had a horizontal or vertical line, then it's already been checked */ + if (P1x == P2x || P1y == P2y) + return TRUE; + + /* Check overlap. Note that t1 < t2 and t3 < t4 here. */ + t1y = _cairo_int32x32_64_mul (t1, ylen); + t2y = _cairo_int32x32_64_mul (t2, ylen); + t3x = _cairo_int32x32_64_mul (t3, xlen); + t4x = _cairo_int32x32_64_mul (t4, xlen); + + if (_cairo_int64_lt(t1y, t4x) && + _cairo_int64_lt(t3x, t2y)) + return TRUE; + + return FALSE; +} + +cairo_bool_t +_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point) +{ + if (point->x < box->p1.x || point->x > box->p2.x || + point->y < box->p1.y || point->y > box->p2.y) + return FALSE; + return TRUE; +} diff --git a/libs/cairo/src/cairo-rectangular-scan-converter.c b/libs/cairo/src/cairo-rectangular-scan-converter.c new file mode 100644 index 000000000..6c21f5fd5 --- /dev/null +++ b/libs/cairo/src/cairo-rectangular-scan-converter.c @@ -0,0 +1,694 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-combsort-private.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-spans-private.h" + +#include + +typedef struct _rectangle { + struct _rectangle *next, *prev; + cairo_fixed_t left, right; + cairo_fixed_t top, bottom; + int32_t top_y, bottom_y; + int dir; +} rectangle_t; + +#define UNROLL3(x) x x x + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef struct _pqueue { + int size, max_size; + + rectangle_t **elements; + rectangle_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct { + rectangle_t **start; + pqueue_t stop; + rectangle_t head, tail; + rectangle_t *insert_cursor; + int32_t current_y; + int32_t xmin, xmax; + + struct coverage { + struct cell { + struct cell *prev, *next; + int x, covered, uncovered; + } head, tail, *cursor; + unsigned int count; + cairo_freepool_t pool; + } coverage; + + cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; + cairo_half_open_span_t *spans; + unsigned int num_spans; + unsigned int size_spans; + + jmp_buf jmpbuf; +} sweep_line_t; + +static inline int +rectangle_compare_start (const rectangle_t *a, + const rectangle_t *b) +{ + int cmp; + + cmp = a->top_y - b->top_y; + if (cmp) + return cmp; + + return a->left - b->left; +} + +static inline int +rectangle_compare_stop (const rectangle_t *a, + const rectangle_t *b) +{ + return a->bottom_y - b->bottom_y; +} + +static inline void +pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; + pq->elements[PQ_FIRST_ENTRY] = NULL; +} + +static inline void +pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_bool_t +pqueue_grow (pqueue_t *pq) +{ + rectangle_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + } + + pq->elements = new_elements; + return TRUE; +} + +static inline void +pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) +{ + rectangle_t **elements; + int i, parent; + + if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) { + if (unlikely (! pqueue_grow (&sweep->stop))) + longjmp (sweep->jmpbuf, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + elements = sweep->stop.elements; + for (i = ++sweep->stop.size; + i != PQ_FIRST_ENTRY && + rectangle_compare_stop (rectangle, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = rectangle; +} + +static inline void +pqueue_pop (pqueue_t *pq) +{ + rectangle_t **elements = pq->elements; + rectangle_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + rectangle_compare_stop (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (rectangle_compare_stop (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline rectangle_t * +peek_stop (sweep_line_t *sweep) +{ + return sweep->stop.elements[PQ_FIRST_ENTRY]; +} + +CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start) + +static void +sweep_line_init (sweep_line_t *sweep) +{ + sweep->head.left = INT_MIN; + sweep->head.next = &sweep->tail; + sweep->tail.left = INT_MAX; + sweep->tail.prev = &sweep->head; + sweep->insert_cursor = &sweep->tail; + + _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell)); + + sweep->spans = sweep->spans_stack; + sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack); + + sweep->coverage.head.prev = NULL; + sweep->coverage.head.x = INT_MIN; + sweep->coverage.tail.next = NULL; + sweep->coverage.tail.x = INT_MAX; + + pqueue_init (&sweep->stop); +} + +static void +sweep_line_fini (sweep_line_t *sweep) +{ + _cairo_freepool_fini (&sweep->coverage.pool); + pqueue_fini (&sweep->stop); + + if (sweep->spans != sweep->spans_stack) + free (sweep->spans); +} + +static inline void +add_cell (sweep_line_t *sweep, int x, int covered, int uncovered) +{ + struct cell *cell; + + cell = sweep->coverage.cursor; + if (cell->x > x) { + do { + UNROLL3({ + if (cell->prev->x < x) + break; + cell = cell->prev; + }) + } while (TRUE); + } else { + if (cell->x == x) + goto found; + + do { + UNROLL3({ + cell = cell->next; + if (cell->x >= x) + break; + }) + } while (TRUE); + } + + if (x != cell->x) { + struct cell *c; + + sweep->coverage.count++; + + c = _cairo_freepool_alloc (&sweep->coverage.pool); + if (unlikely (c == NULL)) { + longjmp (sweep->jmpbuf, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + cell->prev->next = c; + c->prev = cell->prev; + c->next = cell; + cell->prev = c; + + c->x = x; + c->covered = 0; + c->uncovered = 0; + + cell = c; + } + +found: + cell->covered += covered; + cell->uncovered += uncovered; + sweep->coverage.cursor = cell; +} + +static inline void +_active_edges_to_spans (sweep_line_t *sweep) +{ + int32_t y = sweep->current_y; + rectangle_t *rectangle; + int coverage, prev_coverage; + int prev_x; + struct cell *cell; + + sweep->num_spans = 0; + if (sweep->head.next == &sweep->tail) + return; + + sweep->coverage.head.next = &sweep->coverage.tail; + sweep->coverage.tail.prev = &sweep->coverage.head; + sweep->coverage.cursor = &sweep->coverage.tail; + sweep->coverage.count = 0; + + /* XXX cell coverage only changes when a rectangle appears or + * disappears. Try only modifying coverage at such times. + */ + for (rectangle = sweep->head.next; + rectangle != &sweep->tail; + rectangle = rectangle->next) + { + int height; + int frac, i; + + if (y == rectangle->bottom_y) { + height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK; + if (height == 0) + continue; + } else + height = CAIRO_FIXED_ONE; + if (y == rectangle->top_y) + height -= rectangle->top & CAIRO_FIXED_FRAC_MASK; + height *= rectangle->dir; + + i = _cairo_fixed_integer_part (rectangle->left), + frac = _cairo_fixed_fractional_part (rectangle->left); + add_cell (sweep, i, + (CAIRO_FIXED_ONE-frac) * height, + frac * height); + + i = _cairo_fixed_integer_part (rectangle->right), + frac = _cairo_fixed_fractional_part (rectangle->right); + add_cell (sweep, i, + -(CAIRO_FIXED_ONE-frac) * height, + -frac * height); + } + + if (2*sweep->coverage.count >= sweep->size_spans) { + unsigned size; + + size = sweep->size_spans; + while (size <= 2*sweep->coverage.count) + size <<= 1; + + if (sweep->spans != sweep->spans_stack) + free (sweep->spans); + + sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t)); + if (unlikely (sweep->spans == NULL)) + longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + sweep->size_spans = size; + } + + prev_coverage = coverage = 0; + prev_x = INT_MIN; + for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) { + if (cell->x != prev_x && coverage != prev_coverage) { + int n = sweep->num_spans++; + sweep->spans[n].x = prev_x; + sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); + sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; + prev_coverage = coverage; + } + + coverage += cell->covered; + if (coverage != prev_coverage) { + int n = sweep->num_spans++; + sweep->spans[n].x = cell->x; + sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); + sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; + prev_coverage = coverage; + } + coverage += cell->uncovered; + prev_x = cell->x + 1; + } + _cairo_freepool_reset (&sweep->coverage.pool); + + if (sweep->num_spans) { + if (prev_x <= sweep->xmax) { + int n = sweep->num_spans++; + sweep->spans[n].x = prev_x; + sweep->spans[n].coverage = coverage; + } + + if (coverage && prev_x < sweep->xmax) { + int n = sweep->num_spans++; + sweep->spans[n].x = sweep->xmax; + sweep->spans[n].coverage = 0; + } + } +} + +static inline void +sweep_line_delete (sweep_line_t *sweep, + rectangle_t *rectangle) +{ + if (sweep->insert_cursor == rectangle) + sweep->insert_cursor = rectangle->next; + + rectangle->prev->next = rectangle->next; + rectangle->next->prev = rectangle->prev; + + pqueue_pop (&sweep->stop); +} + +static inline void +sweep_line_insert (sweep_line_t *sweep, + rectangle_t *rectangle) +{ + rectangle_t *pos; + + pos = sweep->insert_cursor; + if (pos->left != rectangle->left) { + if (pos->left > rectangle->left) { + do { + UNROLL3({ + if (pos->prev->left < rectangle->left) + break; + pos = pos->prev; + }) + } while (TRUE); + } else { + do { + UNROLL3({ + pos = pos->next; + if (pos->left >= rectangle->left) + break; + }); + } while (TRUE); + } + } + + pos->prev->next = rectangle; + rectangle->prev = pos->prev; + rectangle->next = pos; + pos->prev = rectangle; + sweep->insert_cursor = rectangle; + + pqueue_push (sweep, rectangle); +} + +static void +render_rows (sweep_line_t *sweep_line, + cairo_span_renderer_t *renderer, + int height) +{ + cairo_status_t status; + + _active_edges_to_spans (sweep_line); + + status = renderer->render_rows (renderer, + sweep_line->current_y, height, + sweep_line->spans, + sweep_line->num_spans); + if (unlikely (status)) + longjmp (sweep_line->jmpbuf, status); +} + +static cairo_status_t +generate (cairo_rectangular_scan_converter_t *self, + cairo_span_renderer_t *renderer, + rectangle_t **rectangles) +{ + sweep_line_t sweep_line; + rectangle_t *start, *stop; + cairo_status_t status; + + sweep_line_init (&sweep_line); + sweep_line.xmin = self->xmin; + sweep_line.xmax = self->xmax; + sweep_line.start = rectangles; + if ((status = setjmp (sweep_line.jmpbuf))) + goto BAIL; + + sweep_line.current_y = self->ymin; + start = *sweep_line.start++; + do { + if (start->top_y != sweep_line.current_y) { + render_rows (&sweep_line, renderer, + start->top_y - sweep_line.current_y); + sweep_line.current_y = start->top_y; + } + + do { + sweep_line_insert (&sweep_line, start); + start = *sweep_line.start++; + if (start == NULL) + goto end; + if (start->top_y != sweep_line.current_y) + break; + } while (TRUE); + + render_rows (&sweep_line, renderer, 1); + + stop = peek_stop (&sweep_line); + while (stop->bottom_y == sweep_line.current_y) { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + break; + } + + sweep_line.current_y++; + + while (stop != NULL && stop->bottom_y < start->top_y) { + if (stop->bottom_y != sweep_line.current_y) { + render_rows (&sweep_line, renderer, + stop->bottom_y - sweep_line.current_y); + sweep_line.current_y = stop->bottom_y; + } + + render_rows (&sweep_line, renderer, 1); + + do { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + } while (stop != NULL && stop->bottom_y == sweep_line.current_y); + + sweep_line.current_y++; + } + } while (TRUE); + + end: + render_rows (&sweep_line, renderer, 1); + + stop = peek_stop (&sweep_line); + while (stop->bottom_y == sweep_line.current_y) { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + goto out; + } + + sweep_line.current_y++; + + do { + if (stop->bottom_y != sweep_line.current_y) { + render_rows (&sweep_line, renderer, + stop->bottom_y - sweep_line.current_y); + sweep_line.current_y = stop->bottom_y; + } + + render_rows (&sweep_line, renderer, 1); + + do { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + goto out; + } while (stop->bottom_y == sweep_line.current_y); + + sweep_line.current_y++; + } while (TRUE); + + out: + status = renderer->render_rows (renderer, + sweep_line.current_y, + self->ymax - sweep_line.current_y, + NULL, 0); + + BAIL: + sweep_line_fini (&sweep_line); + + return status; +} + +static cairo_status_t +_cairo_rectangular_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_rectangular_scan_converter_t *self = converter; + rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)]; + rectangle_t **rectangles; + struct _cairo_rectangular_scan_converter_chunk *chunk; + cairo_status_t status; + int i, j; + + if (unlikely (self->num_rectangles == 0)) { + return renderer->render_rows (renderer, + self->ymin, self->ymax - self->ymin, + NULL, 0); + } + + rectangles = rectangles_stack; + if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) { + rectangles = _cairo_malloc_ab (self->num_rectangles + 1, + sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { + rectangle_t *rectangle; + + rectangle = chunk->base; + for (i = 0; i < chunk->count; i++) + rectangles[j++] = &rectangle[i]; + } + rectangle_sort (rectangles, j); + rectangles[j] = NULL; + + status = generate (self, renderer, rectangles); + + if (rectangles != rectangles_stack) + free (rectangles); + + return status; +} + +static rectangle_t * +_allocate_rectangle (cairo_rectangular_scan_converter_t *self) +{ + rectangle_t *rectangle; + struct _cairo_rectangular_scan_converter_chunk *chunk; + + chunk = self->tail; + if (chunk->count == chunk->size) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (rectangle_t), + sizeof (struct _cairo_rectangular_scan_converter_chunk)); + + if (unlikely (chunk->next == NULL)) + return NULL; + + chunk = chunk->next; + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = chunk + 1; + self->tail = chunk; + } + + rectangle = chunk->base; + return rectangle + chunk->count++; +} + +cairo_status_t +_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, + const cairo_box_t *box, + int dir) +{ + rectangle_t *rectangle; + + rectangle = _allocate_rectangle (self); + if (unlikely (rectangle == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangle->left = box->p1.x; + rectangle->right = box->p2.x; + rectangle->dir = dir; + + rectangle->top = box->p1.y; + rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y); + rectangle->bottom = box->p2.y; + rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y); + assert (rectangle->bottom_y >= rectangle->top_y); + + self->num_rectangles++; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_rectangular_scan_converter_destroy (void *converter) +{ + cairo_rectangular_scan_converter_t *self = converter; + struct _cairo_rectangular_scan_converter_chunk *chunk, *next; + + for (chunk = self->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } +} + +void +_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, + const cairo_rectangle_int_t *extents) +{ + self->base.destroy = _cairo_rectangular_scan_converter_destroy; + self->base.add_edge = NULL; + self->base.add_polygon = NULL; + self->base.generate = _cairo_rectangular_scan_converter_generate; + + self->xmin = extents->x; + self->xmax = extents->x + extents->width; + self->ymin = extents->y; + self->ymax = extents->y + extents->height; + + self->chunks.base = self->buf; + self->chunks.next = NULL; + self->chunks.count = 0; + self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t); + self->tail = &self->chunks; + + self->num_rectangles = 0; +} diff --git a/libs/cairo/src/cairo-reference-count-private.h b/libs/cairo/src/cairo-reference-count-private.h new file mode 100644 index 000000000..c05d4c910 --- /dev/null +++ b/libs/cairo/src/cairo-reference-count-private.h @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H +#define CAIRO_REFRENCE_COUNT_PRIVATE_H + +#include "cairo-atomic-private.h" + +/* Encapsulate operations on the object's reference count */ +typedef struct { + cairo_atomic_int_t ref_count; +} cairo_reference_count_t; + +#define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count) +#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count) + +#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE)) + +#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count) + +#define CAIRO_REFERENCE_COUNT_INVALID_VALUE (-1) +#define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE} + +#define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE) + +#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0) + +#endif diff --git a/libs/cairo/src/cairo-region-private.h b/libs/cairo/src/cairo-region-private.h new file mode 100644 index 000000000..32c1e82ab --- /dev/null +++ b/libs/cairo/src/cairo-region-private.h @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_REGION_PRIVATE_H +#define CAIRO_REGION_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-reference-count-private.h" + +#include + +CAIRO_BEGIN_DECLS + +struct _cairo_region { + cairo_reference_count_t ref_count; + cairo_status_t status; + + pixman_region32_t rgn; +}; + +cairo_private cairo_region_t * +_cairo_region_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_region_init (cairo_region_t *region); + +cairo_private void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_region_fini (cairo_region_t *region); + +CAIRO_END_DECLS + +#endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-region.c b/libs/cairo/src/cairo-region.c new file mode 100644 index 000000000..3e8ac1248 --- /dev/null +++ b/libs/cairo/src/cairo-region.c @@ -0,0 +1,871 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-region-private.h" + +/* XXX need to update pixman headers to be const as appropriate */ +#define CONST_CAST (pixman_region32_t *) + +/** + * SECTION:cairo-region + * @Title: Regions + * @Short_Description: Representing a pixel-aligned area + * + * Regions are a simple graphical data type representing an area of + * integer-aligned rectangles. They are often used on raster surfaces + * to track areas of interest, such as change or clip areas. + */ + +static const cairo_region_t _cairo_region_nil = { + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NO_MEMORY, /* status */ +}; + +cairo_region_t * +_cairo_region_create_in_error (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_region_t *) &_cairo_region_nil; + + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_STATUS: + case CAIRO_STATUS_INVALID_CONTENT: + case CAIRO_STATUS_INVALID_FORMAT: + case CAIRO_STATUS_INVALID_VISUAL: + case CAIRO_STATUS_READ_ERROR: + case CAIRO_STATUS_WRITE_ERROR: + case CAIRO_STATUS_FILE_NOT_FOUND: + case CAIRO_STATUS_TEMP_FILE_ERROR: + case CAIRO_STATUS_INVALID_STRIDE: + case CAIRO_STATUS_INVALID_SIZE: + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + case CAIRO_STATUS_DEVICE_ERROR: + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + case CAIRO_STATUS_INVALID_SLANT: + case CAIRO_STATUS_INVALID_WEIGHT: + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + default: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_region_t *) &_cairo_region_nil; + } +} + +/** + * _cairo_region_set_error: + * @region: a region + * @status: a status value indicating an error + * + * Atomically sets region->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to region->status should happen + * through _cairo_region_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +static cairo_status_t +_cairo_region_set_error (cairo_region_t *region, + cairo_status_t status) +{ + if (! _cairo_status_is_error (status)) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (®ion->status, status); + + return _cairo_error (status); +} + +void +_cairo_region_init (cairo_region_t *region) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); + pixman_region32_init (®ion->rgn); +} + +void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); +} + +void +_cairo_region_fini (cairo_region_t *region) +{ + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + pixman_region32_fini (®ion->rgn); + VG (VALGRIND_MAKE_MEM_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 (®ion->ref_count, 1); + + pixman_region32_init (®ion->rgn); + + return region; +} +slim_hidden_def (cairo_region_create); + +/** + * cairo_region_create_rectangles: + * @rects: an array of @count rectangles + * @count: number of rectangles + * + * Allocates a new region object containing the union of all given @rects. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, + int count) +{ + pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; + pixman_box32_t *pboxes = stack_pboxes; + cairo_region_t *region; + int i; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (unlikely (region == NULL)) + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + if (count > ARRAY_LENGTH (stack_pboxes)) { + pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); + if (unlikely (pboxes == NULL)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + for (i = 0; i < count; i++) { + pboxes[i].x1 = rects[i].x; + pboxes[i].y1 = rects[i].y; + pboxes[i].x2 = rects[i].x + rects[i].width; + pboxes[i].y2 = rects[i].y + rects[i].height; + } + + i = pixman_region32_init_rects (®ion->rgn, pboxes, count); + + if (pboxes != stack_pboxes) + free (pboxes); + + if (unlikely (i == 0)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + region->status = CAIRO_STATUS_SUCCESS; + return region; +} +slim_hidden_def (cairo_region_create_rectangles); + +/** + * cairo_region_create_rectangle: + * @rectangle: a #cairo_rectangle_int_t + * + * Allocates a new region object containing @rectangle. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) +{ + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (unlikely (region == NULL)) + return (cairo_region_t *) &_cairo_region_nil; + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + return region; +} +slim_hidden_def (cairo_region_create_rectangle); + +/** + * cairo_region_copy: + * @original: a #cairo_region_t + * + * Allocates a new region object copying the area from @original. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_copy (const cairo_region_t *original) +{ + cairo_region_t *copy; + + if (original != NULL && original->status) + return (cairo_region_t *) &_cairo_region_nil; + + copy = cairo_region_create (); + if (unlikely (copy->status)) + return copy; + + if (original != NULL && + ! pixman_region32_copy (©->rgn, CONST_CAST &original->rgn)) + { + cairo_region_destroy (copy); + return (cairo_region_t *) &_cairo_region_nil; + } + + return copy; +} +slim_hidden_def (cairo_region_copy); + +/** + * cairo_region_reference: + * @region: a #cairo_region_t + * + * Increases the reference count on @region by one. This prevents + * @region from being destroyed until a matching call to + * cairo_region_destroy() is made. + * + * Return value: the referenced #cairo_region_t. + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_reference (cairo_region_t *region) +{ + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) + return NULL; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + _cairo_reference_count_inc (®ion->ref_count); + return region; +} +slim_hidden_def (cairo_region_reference); + +/** + * cairo_region_destroy: + * @region: a #cairo_region_t + * + * Destroys a #cairo_region_t object created with + * cairo_region_create(), cairo_region_copy(), or + * or cairo_region_create_rectangle(). + * + * Since: 1.10 + **/ +void +cairo_region_destroy (cairo_region_t *region) +{ + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + if (! _cairo_reference_count_dec_and_test (®ion->ref_count)) + return; + + _cairo_region_fini (region); + free (region); +} +slim_hidden_def (cairo_region_destroy); + +/** + * cairo_region_num_rectangles: + * @region: a #cairo_region_t + * + * Returns the number of rectangles contained in @region. + * + * Return value: The number of rectangles contained in @region. + * + * Since: 1.10 + **/ +int +cairo_region_num_rectangles (const cairo_region_t *region) +{ + if (region->status) + return 0; + + return pixman_region32_n_rects (CONST_CAST ®ion->rgn); +} +slim_hidden_def (cairo_region_num_rectangles); + +/** + * cairo_region_get_rectangle: + * @region: a #cairo_region_t + * @nth: a number indicating which rectangle should be returned + * @rectangle: return location for a #cairo_rectangle_int_t + * + * Stores the @nth rectangle from the region in @rectangle. + * + * Since: 1.10 + **/ +void +cairo_region_get_rectangle (const cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle) +{ + pixman_box32_t *pbox; + + if (region->status) { + rectangle->x = rectangle->y = 0; + rectangle->width = rectangle->height = 0; + return; + } + + pbox = pixman_region32_rectangles (CONST_CAST ®ion->rgn, NULL) + nth; + + rectangle->x = pbox->x1; + rectangle->y = pbox->y1; + rectangle->width = pbox->x2 - pbox->x1; + rectangle->height = pbox->y2 - pbox->y1; +} +slim_hidden_def (cairo_region_get_rectangle); + +/** + * cairo_region_get_extents: + * @region: a #cairo_region_t + * @extents: rectangle into which to store the extents + * + * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t + * + * Since: 1.10 + **/ +void +cairo_region_get_extents (const cairo_region_t *region, + cairo_rectangle_int_t *extents) +{ + pixman_box32_t *pextents; + + if (region->status) { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; + } + + pextents = pixman_region32_extents (CONST_CAST ®ion->rgn); + + extents->x = pextents->x1; + extents->y = pextents->y1; + extents->width = pextents->x2 - pextents->x1; + extents->height = pextents->y2 - pextents->y1; +} +slim_hidden_def (cairo_region_get_extents); + +/** + * cairo_region_status: + * @region: a #cairo_region_t + * + * Checks whether an error has previous occured for this + * region object. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_status (const cairo_region_t *region) +{ + return region->status; +} +slim_hidden_def (cairo_region_status); + +/** + * cairo_region_subtract: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Subtracts @other from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_subtract (&dst->rgn, + &dst->rgn, + CONST_CAST &other->rgn)) + { + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_subtract); + +/** + * cairo_region_subtract_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Subtracts @rectangle from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_subtract_rectangle); + +/** + * cairo_region_intersect: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the intersection of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_intersect); + +/** + * cairo_region_intersect_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the intersection of @dst with @rectangle and places the + * result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_intersect_rectangle); + +/** + * cairo_region_union: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the union of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union (cairo_region_t *dst, + const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_union); + +/** + * cairo_region_union_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the union of @dst with @rectangle and places the result in @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_union_rectangle); + +/** + * cairo_region_xor: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the exclusive difference of @dst with @other and places the + * result in @dst. That is, @dst will be set to contain all areas that + * are either in @dst or in @other, but not in both. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t tmp; + + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + pixman_region32_init (&tmp); + + /* XXX: get an xor function into pixman */ + if (! pixman_region32_subtract (&tmp, CONST_CAST &other->rgn, &dst->rgn) || + ! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn) || + ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (&tmp); + + return status; +} +slim_hidden_def (cairo_region_xor); + +/** + * cairo_region_xor_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the exclusive difference of @dst with @rectangle and places the + * result in @dst. That is, @dst will be set to contain all areas that are + * either in @dst or in @rectangle, but not in both. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_xor_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region, tmp; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + pixman_region32_init (&tmp); + + /* XXX: get an xor function into pixman */ + if (! pixman_region32_subtract (&tmp, ®ion, &dst->rgn) || + ! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion) || + ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (&tmp); + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_xor_rectangle); + +/** + * cairo_region_is_empty: + * @region: a #cairo_region_t + * + * Checks whether @region is empty. + * + * Return value: %TRUE if @region is empty, %FALSE if it isn't. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_is_empty (const cairo_region_t *region) +{ + if (region->status) + return TRUE; + + return ! pixman_region32_not_empty (CONST_CAST ®ion->rgn); +} +slim_hidden_def (cairo_region_is_empty); + +/** + * cairo_region_translate: + * @region: a #cairo_region_t + * @dx: Amount to translate in the x direction + * @dy: Amount to translate in the y direction + * + * Translates @region by (@dx, @dy). + * + * Since: 1.10 + **/ +void +cairo_region_translate (cairo_region_t *region, + int dx, int dy) +{ + if (region->status) + return; + + pixman_region32_translate (®ion->rgn, dx, dy); +} +slim_hidden_def (cairo_region_translate); + +/** + * cairo_region_overlap_t: + * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region + * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region + * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and + * partially outside the region. + * + * Used as the return value for cairo_region_contains_rectangle(). + */ + +/** + * cairo_region_contains_rectangle: + * @region: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Checks whether @rectangle is inside, outside or partially contained + * in @region + * + * Return value: + * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, + * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or + * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. + * + * Since: 1.10 + **/ +cairo_region_overlap_t +cairo_region_contains_rectangle (const cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) +{ + pixman_box32_t pbox; + pixman_region_overlap_t poverlap; + + if (region->status) + return CAIRO_REGION_OVERLAP_OUT; + + pbox.x1 = rectangle->x; + pbox.y1 = rectangle->y; + pbox.x2 = rectangle->x + rectangle->width; + pbox.y2 = rectangle->y + rectangle->height; + + poverlap = pixman_region32_contains_rectangle (CONST_CAST ®ion->rgn, + &pbox); + switch (poverlap) { + default: + case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; + case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; + case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; + } +} +slim_hidden_def (cairo_region_contains_rectangle); + +/** + * cairo_region_contains_point: + * @region: a #cairo_region_t + * @x: the x coordinate of a point + * @y: the y coordinate of a point + * + * Checks whether (@x, @y) is contained in @region. + * + * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_contains_point (const cairo_region_t *region, + int x, int y) +{ + pixman_box32_t box; + + if (region->status) + return FALSE; + + return pixman_region32_contains_point (CONST_CAST ®ion->rgn, x, y, &box); +} +slim_hidden_def (cairo_region_contains_point); + +/** + * cairo_region_equal: + * @a: a #cairo_region_t or %NULL + * @b: a #cairo_region_t or %NULL + * + * Compares whether region_a is equivalent to region_b. %NULL as an argument + * is equal to itself, but not to any non-%NULL region. + * + * Return value: %TRUE if both regions contained the same coverage, + * %FALSE if it is not or any region is in an error status. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_equal (const cairo_region_t *a, + const cairo_region_t *b) +{ + /* error objects are never equal */ + if ((a != NULL && a->status) || (b != NULL && b->status)) + return FALSE; + + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn); +} +slim_hidden_def (cairo_region_equal); diff --git a/libs/cairo/src/cairo-rename.h b/libs/cairo/src/cairo-rename.h new file mode 100644 index 000000000..db20352ea --- /dev/null +++ b/libs/cairo/src/cairo-rename.h @@ -0,0 +1,411 @@ +#define cairo_append_path _moz_cairo_append_path +#define cairo_arc _moz_cairo_arc +#define cairo_arc_negative _moz_cairo_arc_negative +#define cairo_arc_to _moz_cairo_arc_to +#define cairo_beos_surface_create _moz_cairo_beos_surface_create +#define cairo_beos_surface_create_for_bitmap _moz_cairo_beos_surface_create_for_bitmap +#define cairo_clip _moz_cairo_clip +#define cairo_clip_extents _moz_cairo_clip_extents +#define cairo_clip_preserve _moz_cairo_clip_preserve +#define cairo_close_path _moz_cairo_close_path +#define cairo_copy_clip_rectangle_list _moz_cairo_copy_clip_rectangle_list +#define cairo_copy_page _moz_cairo_copy_page +#define cairo_copy_path _moz_cairo_copy_path +#define cairo_copy_path_flat _moz_cairo_copy_path_flat +#define cairo_create _moz_cairo_create +#define cairo_curve_to _moz_cairo_curve_to +#define cairo_d2d_create_device _moz_cairo_d2d_create_device +#define cairo_d2d_create_device_from_d3d10device _moz_cairo_d2d_create_device_from_d3d10device +#define cairo_d2d_device_get_device _moz_cairo_d2d_device_get_device +#define cairo_d2d_get_dc _moz_cairo_d2d_get_dc +#define cairo_d2d_get_image_surface_cache_usage _moz_cairo_d2d_get_image_surface_cache_usage +#define cairo_d2d_get_surface_vram_usage _moz_cairo_d2d_get_surface_vram_usage +#define cairo_d2d_present_backbuffer _moz_cairo_d2d_present_backbuffer +#define cairo_d2d_release_dc _moz_cairo_d2d_release_dc +#define cairo_d2d_scroll _moz_cairo_d2d_scroll +#define cairo_d2d_surface_create _moz_cairo_d2d_surface_create +#define cairo_d2d_surface_create_for_handle _moz_cairo_d2d_surface_create_for_handle +#define cairo_d2d_surface_create_for_hwnd _moz_cairo_d2d_surface_create_for_hwnd +#define cairo_d2d_surface_create_for_texture _moz_cairo_d2d_surface_create_for_texture +#define cairo_d2d_surface_get_height _moz_cairo_d2d_surface_get_height +#define cairo_d2d_surface_get_texture _moz_cairo_d2d_surface_get_texture +#define cairo_d2d_surface_get_width _moz_cairo_d2d_surface_get_width +#define cairo_debug_reset_static_data _moz_cairo_debug_reset_static_data +#define cairo_destroy _moz_cairo_destroy +#define cairo_device_acquire _moz_cairo_device_acquire +#define cairo_device_destroy _moz_cairo_device_destroy +#define cairo_device_finish _moz_cairo_device_finish +#define cairo_device_flush _moz_cairo_device_flush +#define cairo_device_get_reference_count _moz_cairo_device_get_reference_count +#define cairo_device_get_type _moz_cairo_device_get_type +#define cairo_device_get_user_data _moz_cairo_device_get_user_data +#define cairo_device_release _moz_cairo_device_release +#define cairo_device_set_user_data _moz_cairo_device_set_user_data +#define cairo_device_status _moz_cairo_device_status +#define cairo_device_reference _moz_cairo_device_reference +#define cairo_device_to_user _moz_cairo_device_to_user +#define cairo_device_to_user_distance _moz_cairo_device_to_user_distance +#define cairo_directfb_surface_create _moz_cairo_directfb_surface_create +#define cairo_dwrite_font_face_create_for_dwrite_fontface _moz_cairo_dwrite_font_face_create_for_dwrite_fontface +#define cairo_dwrite_get_cleartype_rendering_mode _moz_cairo_dwrite_get_cleartype_rendering_mode +#define cairo_dwrite_scaled_font_allow_manual_show_glyphs _moz_cairo_dwrite_scaled_font_allow_manual_show_glyphs +#define cairo_dwrite_scaled_font_get_force_GDI_classic _moz_cairo_dwrite_scaled_font_get_force_GDI_classic +#define cairo_dwrite_scaled_font_set_force_GDI_classic _moz_cairo_dwrite_scaled_font_set_force_GDI_classic +#define cairo_dwrite_set_cleartype_params _moz_cairo_dwrite_set_cleartype_params +#define cairo_fill _moz_cairo_fill +#define cairo_fill_extents _moz_cairo_fill_extents +#define cairo_fill_preserve _moz_cairo_fill_preserve +#define cairo_font_extents _moz_cairo_font_extents +#define cairo_font_face_destroy _moz_cairo_font_face_destroy +#define cairo_font_face_get_reference_count _moz_cairo_font_face_get_reference_count +#define cairo_font_face_get_type _moz_cairo_font_face_get_type +#define cairo_font_face_get_user_data _moz_cairo_font_face_get_user_data +#define cairo_font_face_reference _moz_cairo_font_face_reference +#define cairo_font_face_set_user_data _moz_cairo_font_face_set_user_data +#define cairo_font_face_status _moz_cairo_font_face_status +#define cairo_font_options_copy _moz_cairo_font_options_copy +#define cairo_font_options_create _moz_cairo_font_options_create +#define cairo_font_options_destroy _moz_cairo_font_options_destroy +#define cairo_font_options_equal _moz_cairo_font_options_equal +#define cairo_font_options_get_antialias _moz_cairo_font_options_get_antialias +#define cairo_font_options_get_hint_metrics _moz_cairo_font_options_get_hint_metrics +#define cairo_font_options_get_hint_style _moz_cairo_font_options_get_hint_style +#define cairo_font_options_get_lcd_filter _moz_cairo_font_options_get_lcd_filter +#define cairo_font_options_get_subpixel_order _moz_cairo_font_options_get_subpixel_order +#define cairo_font_options_hash _moz_cairo_font_options_hash +#define cairo_font_options_merge _moz_cairo_font_options_merge +#define cairo_font_options_set_antialias _moz_cairo_font_options_set_antialias +#define cairo_font_options_set_hint_metrics _moz_cairo_font_options_set_hint_metrics +#define cairo_font_options_set_hint_style _moz_cairo_font_options_set_hint_style +#define cairo_font_options_set_lcd_filter _moz_cairo_font_options_set_lcd_filter +#define cairo_font_options_set_subpixel_order _moz_cairo_font_options_set_subpixel_order +#define cairo_font_options_status _moz_cairo_font_options_status +#define cairo_format_stride_for_width _moz_cairo_format_stride_for_width +#define cairo_ft_font_face_create_for_ft_face _moz_cairo_ft_font_face_create_for_ft_face +#define cairo_ft_font_face_create_for_pattern _moz_cairo_ft_font_face_create_for_pattern +#define cairo_ft_font_options_substitute _moz_cairo_ft_font_options_substitute +#define cairo_ft_scaled_font_lock_face _moz_cairo_ft_scaled_font_lock_face +#define cairo_ft_scaled_font_unlock_face _moz_cairo_ft_scaled_font_unlock_face +#define cairo_get_antialias _moz_cairo_get_antialias +#define cairo_get_current_point _moz_cairo_get_current_point +#define cairo_get_dash _moz_cairo_get_dash +#define cairo_get_dash_count _moz_cairo_get_dash_count +#define cairo_get_fill_rule _moz_cairo_get_fill_rule +#define cairo_get_font_face _moz_cairo_get_font_face +#define cairo_get_font_matrix _moz_cairo_get_font_matrix +#define cairo_get_font_options _moz_cairo_get_font_options +#define cairo_get_group_target _moz_cairo_get_group_target +#define cairo_get_line_cap _moz_cairo_get_line_cap +#define cairo_get_line_join _moz_cairo_get_line_join +#define cairo_get_line_width _moz_cairo_get_line_width +#define cairo_get_matrix _moz_cairo_get_matrix +#define cairo_get_miter_limit _moz_cairo_get_miter_limit +#define cairo_get_operator _moz_cairo_get_operator +#define cairo_get_reference_count _moz_cairo_get_reference_count +#define cairo_get_scaled_font _moz_cairo_get_scaled_font +#define cairo_get_source _moz_cairo_get_source +#define cairo_get_target _moz_cairo_get_target +#define cairo_get_tolerance _moz_cairo_get_tolerance +#define cairo_get_user_data _moz_cairo_get_user_data +#define cairo_glitz_surface_create _moz_cairo_glitz_surface_create +#define cairo_glyph_allocate _moz_cairo_glyph_allocate +#define cairo_glyph_extents _moz_cairo_glyph_extents +#define cairo_glyph_free _moz_cairo_glyph_free +#define cairo_glyph_path _moz_cairo_glyph_path +#define cairo_has_current_point _moz_cairo_has_current_point +#define cairo_has_show_text_glyphs _moz_cairo_has_show_text_glyphs +#define cairo_identity_matrix _moz_cairo_identity_matrix +#define cairo_image_surface_create _moz_cairo_image_surface_create +#define cairo_image_surface_create_for_data _moz_cairo_image_surface_create_for_data +#define cairo_image_surface_create_from_png _moz_cairo_image_surface_create_from_png +#define cairo_image_surface_create_from_png_stream _moz_cairo_image_surface_create_from_png_stream +#define cairo_image_surface_get_data _moz_cairo_image_surface_get_data +#define cairo_image_surface_get_format _moz_cairo_image_surface_get_format +#define cairo_image_surface_get_height _moz_cairo_image_surface_get_height +#define cairo_image_surface_get_stride _moz_cairo_image_surface_get_stride +#define cairo_image_surface_get_width _moz_cairo_image_surface_get_width +#define cairo_in_clip _moz_cairo_in_clip +#define cairo_in_fill _moz_cairo_in_fill +#define cairo_in_stroke _moz_cairo_in_stroke +#define cairo_line_to _moz_cairo_line_to +#define cairo_mask _moz_cairo_mask +#define cairo_mask_surface _moz_cairo_mask_surface +#define cairo_matrix_init _moz_cairo_matrix_init +#define cairo_matrix_init_identity _moz_cairo_matrix_init_identity +#define cairo_matrix_init_rotate _moz_cairo_matrix_init_rotate +#define cairo_matrix_init_scale _moz_cairo_matrix_init_scale +#define cairo_matrix_init_translate _moz_cairo_matrix_init_translate +#define cairo_matrix_invert _moz_cairo_matrix_invert +#define cairo_matrix_multiply _moz_cairo_matrix_multiply +#define cairo_matrix_rotate _moz_cairo_matrix_rotate +#define cairo_matrix_scale _moz_cairo_matrix_scale +#define cairo_matrix_transform_distance _moz_cairo_matrix_transform_distance +#define cairo_matrix_transform_point _moz_cairo_matrix_transform_point +#define cairo_matrix_translate _moz_cairo_matrix_translate +#define cairo_move_to _moz_cairo_move_to +#define cairo_new_path _moz_cairo_new_path +#define cairo_new_sub_path _moz_cairo_new_sub_path +#define cairo_null_surface_create _moz_cairo_null_surface_create +#define cairo_os2_fini _moz_cairo_os2_fini +#define cairo_os2_init _moz_cairo_os2_init +#define cairo_os2_surface_create _moz_cairo_os2_surface_create +#define cairo_os2_surface_create_for_window _moz_cairo_os2_surface_create_for_window +#define cairo_os2_surface_get_hps _moz_cairo_os2_surface_get_hps +#define cairo_os2_surface_get_manual_window_refresh _moz_cairo_os2_surface_get_manual_window_refresh +#define cairo_os2_surface_refresh_window _moz_cairo_os2_surface_refresh_window +#define cairo_os2_surface_set_hps _moz_cairo_os2_surface_set_hps +#define cairo_os2_surface_set_hwnd _moz_cairo_os2_surface_set_hwnd +#define cairo_os2_surface_set_manual_window_refresh _moz_cairo_os2_surface_set_manual_window_refresh +#define cairo_os2_surface_set_size _moz_cairo_os2_surface_set_size +#define cairo_paint _moz_cairo_paint +#define cairo_paint_with_alpha _moz_cairo_paint_with_alpha +#define cairo_path_destroy _moz_cairo_path_destroy +#define cairo_path_extents _moz_cairo_path_extents +#define cairo_pattern_add_color_stop_rgb _moz_cairo_pattern_add_color_stop_rgb +#define cairo_pattern_add_color_stop_rgba _moz_cairo_pattern_add_color_stop_rgba +#define cairo_pattern_create_for_surface _moz_cairo_pattern_create_for_surface +#define cairo_pattern_create_linear _moz_cairo_pattern_create_linear +#define cairo_pattern_create_radial _moz_cairo_pattern_create_radial +#define cairo_pattern_create_rgb _moz_cairo_pattern_create_rgb +#define cairo_pattern_create_rgba _moz_cairo_pattern_create_rgba +#define cairo_pattern_destroy _moz_cairo_pattern_destroy +#define cairo_pattern_get_color_stop_count _moz_cairo_pattern_get_color_stop_count +#define cairo_pattern_get_color_stop_rgba _moz_cairo_pattern_get_color_stop_rgba +#define cairo_pattern_get_extend _moz_cairo_pattern_get_extend +#define cairo_pattern_get_filter _moz_cairo_pattern_get_filter +#define cairo_pattern_get_linear_points _moz_cairo_pattern_get_linear_points +#define cairo_pattern_get_matrix _moz_cairo_pattern_get_matrix +#define cairo_pattern_get_radial_circles _moz_cairo_pattern_get_radial_circles +#define cairo_pattern_get_reference_count _moz_cairo_pattern_get_reference_count +#define cairo_pattern_get_rgba _moz_cairo_pattern_get_rgba +#define cairo_pattern_get_surface _moz_cairo_pattern_get_surface +#define cairo_pattern_get_type _moz_cairo_pattern_get_type +#define cairo_pattern_get_user_data _moz_cairo_pattern_get_user_data +#define cairo_pattern_reference _moz_cairo_pattern_reference +#define cairo_pattern_set_extend _moz_cairo_pattern_set_extend +#define cairo_pattern_set_filter _moz_cairo_pattern_set_filter +#define cairo_pattern_set_matrix _moz_cairo_pattern_set_matrix +#define cairo_pattern_set_user_data _moz_cairo_pattern_set_user_data +#define cairo_pattern_status _moz_cairo_pattern_status +#define cairo_pdf_get_versions _moz_cairo_pdf_get_versions +#define cairo_pdf_surface_create _moz_cairo_pdf_surface_create +#define cairo_pdf_surface_create_for_stream _moz_cairo_pdf_surface_create_for_stream +#define cairo_pdf_surface_restrict_to_version _moz_cairo_pdf_surface_restrict_to_version +#define cairo_pdf_surface_set_size _moz_cairo_pdf_surface_set_size +#define cairo_pdf_version_to_string _moz_cairo_pdf_version_to_string +#define cairo_pop_group _moz_cairo_pop_group +#define cairo_pop_group_to_source _moz_cairo_pop_group_to_source +#define cairo_ps_get_levels _moz_cairo_ps_get_levels +#define cairo_ps_level_to_string _moz_cairo_ps_level_to_string +#define cairo_ps_surface_create _moz_cairo_ps_surface_create +#define cairo_ps_surface_create_for_stream _moz_cairo_ps_surface_create_for_stream +#define cairo_ps_surface_dsc_begin_page_setup _moz_cairo_ps_surface_dsc_begin_page_setup +#define cairo_ps_surface_dsc_begin_setup _moz_cairo_ps_surface_dsc_begin_setup +#define cairo_ps_surface_dsc_comment _moz_cairo_ps_surface_dsc_comment +#define cairo_ps_surface_get_eps _moz_cairo_ps_surface_get_eps +#define cairo_ps_surface_restrict_to_level _moz_cairo_ps_surface_restrict_to_level +#define cairo_ps_surface_set_eps _moz_cairo_ps_surface_set_eps +#define cairo_ps_surface_set_size _moz_cairo_ps_surface_set_size +#define cairo_push_group _moz_cairo_push_group +#define cairo_push_group_with_content _moz_cairo_push_group_with_content +#define cairo_qpainter_surface_create _moz_cairo_qpainter_surface_create +#define cairo_qpainter_surface_create_with_qimage _moz_cairo_qpainter_surface_create_with_qimage +#define cairo_qpainter_surface_create_with_qpixmap _moz_cairo_qpainter_surface_create_with_qpixmap +#define cairo_qpainter_surface_get_image _moz_cairo_qpainter_surface_get_image +#define cairo_qpainter_surface_get_qimage _moz_cairo_qpainter_surface_get_qimage +#define cairo_qpainter_surface_get_qpainter _moz_cairo_qpainter_surface_get_qpainter +#define cairo_quartz_font_face_create_for_atsu_font_id _moz_cairo_quartz_font_face_create_for_atsu_font_id +#define cairo_quartz_font_face_create_for_cgfont _moz_cairo_quartz_font_face_create_for_cgfont +#define cairo_quartz_image_surface_create _moz_cairo_quartz_image_surface_create +#define cairo_quartz_image_surface_get_image _moz_cairo_quartz_image_surface_get_image +#define cairo_quartz_surface_create _moz_cairo_quartz_surface_create +#define cairo_quartz_surface_create_for_cg_context _moz_cairo_quartz_surface_create_for_cg_context +#define cairo_quartz_surface_create_for_data _moz_cairo_quartz_surface_create_for_data +#define cairo_quartz_surface_get_cg_context _moz_cairo_quartz_surface_get_cg_context +#define cairo_quartz_surface_get_image _moz_cairo_quartz_surface_get_image +#define cairo_recording_surface_create _moz_cairo_recording_surface_create +#define cairo_recording_surface_ink_extents _moz_cairo_recording_surface_ink_extents +#define cairo_rectangle _moz_cairo_rectangle +#define cairo_rectangle_list_destroy _moz_cairo_rectangle_list_destroy +#define cairo_reference _moz_cairo_reference +#define cairo_region_contains_point _moz_cairo_region_contains_point +#define cairo_region_contains_rectangle _moz_cairo_region_contains_rectangle +#define cairo_region_copy _moz_cairo_region_copy +#define cairo_region_create _moz_cairo_region_create +#define cairo_region_create_rectangle _moz_cairo_region_create_rectangle +#define cairo_region_create_rectangles _moz_cairo_region_create_rectangles +#define cairo_region_destroy _moz_cairo_region_destroy +#define cairo_region_equal _moz_cairo_region_equal +#define cairo_region_get_extents _moz_cairo_region_get_extents +#define cairo_region_get_rectangle _moz_cairo_region_get_rectangle +#define cairo_region_intersect _moz_cairo_region_intersect +#define cairo_region_intersect_rectangle _moz_cairo_region_intersect_rectangle +#define cairo_region_is_empty _moz_cairo_region_is_empty +#define cairo_region_num_rectangles _moz_cairo_region_num_rectangles +#define cairo_region_reference _moz_cairo_region_reference +#define cairo_region_status _moz_cairo_region_status +#define cairo_region_subtract _moz_cairo_region_subtract +#define cairo_region_subtract_rectangle _moz_cairo_region_subtract_rectangle +#define cairo_region_translate _moz_cairo_region_translate +#define cairo_region_union _moz_cairo_region_union +#define cairo_region_union_rectangle _moz_cairo_region_union_rectangle +#define cairo_region_xor _moz_cairo_region_xor +#define cairo_region_xor_rectangle _moz_cairo_region_xor_rectangle +#define cairo_rel_curve_to _moz_cairo_rel_curve_to +#define cairo_rel_line_to _moz_cairo_rel_line_to +#define cairo_rel_move_to _moz_cairo_rel_move_to +#define cairo_release_device _moz_cairo_release_device +#define cairo_reset_clip _moz_cairo_reset_clip +#define cairo_restore _moz_cairo_restore +#define cairo_rotate _moz_cairo_rotate +#define cairo_save _moz_cairo_save +#define cairo_scale _moz_cairo_scale +#define cairo_scaled_font_create _moz_cairo_scaled_font_create +#define cairo_scaled_font_destroy _moz_cairo_scaled_font_destroy +#define cairo_scaled_font_extents _moz_cairo_scaled_font_extents +#define cairo_scaled_font_get_ctm _moz_cairo_scaled_font_get_ctm +#define cairo_scaled_font_get_font_face _moz_cairo_scaled_font_get_font_face +#define cairo_scaled_font_get_font_matrix _moz_cairo_scaled_font_get_font_matrix +#define cairo_scaled_font_get_font_options _moz_cairo_scaled_font_get_font_options +#define cairo_scaled_font_get_reference_count _moz_cairo_scaled_font_get_reference_count +#define cairo_scaled_font_get_scale_matrix _moz_cairo_scaled_font_get_scale_matrix +#define cairo_scaled_font_get_type _moz_cairo_scaled_font_get_type +#define cairo_scaled_font_get_user_data _moz_cairo_scaled_font_get_user_data +#define cairo_scaled_font_glyph_extents _moz_cairo_scaled_font_glyph_extents +#define cairo_scaled_font_reference _moz_cairo_scaled_font_reference +#define cairo_scaled_font_set_user_data _moz_cairo_scaled_font_set_user_data +#define cairo_scaled_font_status _moz_cairo_scaled_font_status +#define cairo_scaled_font_text_extents _moz_cairo_scaled_font_text_extents +#define cairo_scaled_font_text_to_glyphs _moz_cairo_scaled_font_text_to_glyphs +#define cairo_select_font_face _moz_cairo_select_font_face +#define cairo_set_antialias _moz_cairo_set_antialias +#define cairo_set_dash _moz_cairo_set_dash +#define cairo_set_fill_rule _moz_cairo_set_fill_rule +#define cairo_set_font_face _moz_cairo_set_font_face +#define cairo_set_font_matrix _moz_cairo_set_font_matrix +#define cairo_set_font_options _moz_cairo_set_font_options +#define cairo_set_font_size _moz_cairo_set_font_size +#define cairo_set_line_cap _moz_cairo_set_line_cap +#define cairo_set_line_join _moz_cairo_set_line_join +#define cairo_set_line_width _moz_cairo_set_line_width +#define cairo_set_matrix _moz_cairo_set_matrix +#define cairo_set_miter_limit _moz_cairo_set_miter_limit +#define cairo_set_operator _moz_cairo_set_operator +#define cairo_set_scaled_font _moz_cairo_set_scaled_font +#define cairo_set_source _moz_cairo_set_source +#define cairo_set_source_rgb _moz_cairo_set_source_rgb +#define cairo_set_source_rgba _moz_cairo_set_source_rgba +#define cairo_set_source_surface _moz_cairo_set_source_surface +#define cairo_set_tolerance _moz_cairo_set_tolerance +#define cairo_set_user_data _moz_cairo_set_user_data +#define cairo_show_glyphs _moz_cairo_show_glyphs +#define cairo_show_page _moz_cairo_show_page +#define cairo_show_text _moz_cairo_show_text +#define cairo_show_text_glyphs _moz_cairo_show_text_glyphs +#define cairo_status _moz_cairo_status +#define cairo_status_to_string _moz_cairo_status_to_string +#define cairo_stroke _moz_cairo_stroke +#define cairo_stroke_extents _moz_cairo_stroke_extents +#define cairo_stroke_preserve _moz_cairo_stroke_preserve +#define cairo_stroke_to_path _moz_cairo_stroke_to_path +#define cairo_surface_attach_snapshot _moz_cairo_surface_attach_snapshot +#define cairo_surface_copy_page _moz_cairo_surface_copy_page +#define cairo_surface_create_for_rectangle _moz_cairo_surface_create_for_rectangle +#define cairo_surface_create_similar _moz_cairo_surface_create_similar +#define cairo_surface_detach_snapshot _moz_cairo_surface_detach_snapshot +#define cairo_surface_destroy _moz_cairo_surface_destroy +#define cairo_surface_finish _moz_cairo_surface_finish +#define cairo_surface_flush _moz_cairo_surface_flush +#define cairo_surface_get_content _moz_cairo_surface_get_content +#define cairo_surface_get_device _moz_cairo_surface_get_device +#define cairo_surface_get_device_offset _moz_cairo_surface_get_device_offset +#define cairo_surface_get_fallback_resolution _moz_cairo_surface_get_fallback_resolution +#define cairo_surface_get_font_options _moz_cairo_surface_get_font_options +#define cairo_surface_get_mime_data _moz_cairo_surface_get_mime_data +#define cairo_surface_get_reference_count _moz_cairo_surface_get_reference_count +#define cairo_surface_get_subpixel_antialiasing _moz_cairo_surface_get_subpixel_antialiasing +#define cairo_surface_get_type _moz_cairo_surface_get_type +#define cairo_surface_get_user_data _moz_cairo_surface_get_user_data +#define cairo_surface_has_show_text_glyphs _moz_cairo_surface_has_show_text_glyphs +#define cairo_surface_mark_dirty _moz_cairo_surface_mark_dirty +#define cairo_surface_mark_dirty_rectangle _moz_cairo_surface_mark_dirty_rectangle +#define cairo_surface_reference _moz_cairo_surface_reference +#define cairo_surface_set_device_offset _moz_cairo_surface_set_device_offset +#define cairo_surface_set_fallback_resolution _moz_cairo_surface_set_fallback_resolution +#define cairo_surface_set_mime_data _moz_cairo_surface_set_mime_data +#define cairo_surface_set_subpixel_antialiasing _moz_cairo_surface_set_subpixel_antialiasing +#define cairo_surface_set_user_data _moz_cairo_surface_set_user_data +#define cairo_surface_show_page _moz_cairo_surface_show_page +#define cairo_surface_status _moz_cairo_surface_status +#define cairo_surface_write_to_png _moz_cairo_surface_write_to_png +#define cairo_surface_write_to_png_stream _moz_cairo_surface_write_to_png_stream +#define cairo_svg_get_versions _moz_cairo_svg_get_versions +#define cairo_svg_surface_create _moz_cairo_svg_surface_create +#define cairo_svg_surface_create_for_stream _moz_cairo_svg_surface_create_for_stream +#define cairo_svg_surface_restrict_to_version _moz_cairo_svg_surface_restrict_to_version +#define cairo_svg_version_to_string _moz_cairo_svg_version_to_string +#define cairo_tee_surface_add _moz_cairo_tee_surface_add +#define cairo_tee_surface_create _moz_cairo_tee_surface_create +#define cairo_tee_surface_index _moz_cairo_tee_surface_index +#define cairo_tee_surface_remove _moz_cairo_tee_surface_remove +#define cairo_text_cluster_allocate _moz_cairo_text_cluster_allocate +#define cairo_text_cluster_free _moz_cairo_text_cluster_free +#define cairo_text_extents _moz_cairo_text_extents +#define cairo_text_path _moz_cairo_text_path +#define cairo_toy_font_face_create _moz_cairo_toy_font_face_create +#define cairo_toy_font_face_get_family _moz_cairo_toy_font_face_get_family +#define cairo_toy_font_face_get_slant _moz_cairo_toy_font_face_get_slant +#define cairo_toy_font_face_get_weight _moz_cairo_toy_font_face_get_weight +#define cairo_transform _moz_cairo_transform +#define cairo_translate _moz_cairo_translate +#define cairo_user_font_face_create _moz_cairo_user_font_face_create +#define cairo_user_font_face_get_init_func _moz_cairo_user_font_face_get_init_func +#define cairo_user_font_face_get_render_glyph_func _moz_cairo_user_font_face_get_render_glyph_func +#define cairo_user_font_face_get_text_to_glyphs_func _moz_cairo_user_font_face_get_text_to_glyphs_func +#define cairo_user_font_face_get_unicode_to_glyph_func _moz_cairo_user_font_face_get_unicode_to_glyph_func +#define cairo_user_font_face_set_init_func _moz_cairo_user_font_face_set_init_func +#define cairo_user_font_face_set_render_glyph_func _moz_cairo_user_font_face_set_render_glyph_func +#define cairo_user_font_face_set_text_to_glyphs_func _moz_cairo_user_font_face_set_text_to_glyphs_func +#define cairo_user_font_face_set_unicode_to_glyph_func _moz_cairo_user_font_face_set_unicode_to_glyph_func +#define cairo_user_to_device _moz_cairo_user_to_device +#define cairo_user_to_device_distance _moz_cairo_user_to_device_distance +#define cairo_version _moz_cairo_version +#define cairo_version_string _moz_cairo_version_string +#define cairo_win32_get_dc_with_clip _moz_cairo_win32_get_dc_with_clip +#define cairo_win32_get_system_text_quality _moz_cairo_win32_get_system_text_quality +#define cairo_win32_font_face_create_for_hfont _moz_cairo_win32_font_face_create_for_hfont +#define cairo_win32_font_face_create_for_logfontw _moz_cairo_win32_font_face_create_for_logfontw +#define cairo_win32_font_face_create_for_logfontw_hfont _moz_cairo_win32_font_face_create_for_logfontw_hfont +#define cairo_win32_printing_surface_create _moz_cairo_win32_printing_surface_create +#define cairo_win32_scaled_font_done_font _moz_cairo_win32_scaled_font_done_font +#define cairo_win32_scaled_font_get_device_to_logical _moz_cairo_win32_scaled_font_get_device_to_logical +#define cairo_win32_scaled_font_get_logical_to_device _moz_cairo_win32_scaled_font_get_logical_to_device +#define cairo_win32_scaled_font_get_metrics_factor _moz_cairo_win32_scaled_font_get_metrics_factor +#define cairo_win32_scaled_font_select_font _moz_cairo_win32_scaled_font_select_font +#define cairo_win32_surface_create _moz_cairo_win32_surface_create +#define cairo_win32_surface_create_with_alpha _moz_cairo_win32_surface_create_with_alpha +#define cairo_win32_surface_create_with_d3dsurface9 _moz_cairo_win32_surface_create_with_d3dsurface9 +#define cairo_win32_surface_create_with_ddb _moz_cairo_win32_surface_create_with_ddb +#define cairo_win32_surface_create_with_dib _moz_cairo_win32_surface_create_with_dib +#define cairo_win32_surface_get_dc _moz_cairo_win32_surface_get_dc +#define cairo_win32_surface_get_height _moz_cairo_win32_surface_get_height +#define cairo_win32_surface_get_image _moz_cairo_win32_surface_get_image +#define cairo_win32_surface_get_width _moz_cairo_win32_surface_get_width +#define cairo_win32_surface_set_can_convert_to_dib _moz_cairo_win32_surface_set_can_convert_to_dib +#define cairo_xcb_surface_create _moz_cairo_xcb_surface_create +#define cairo_xcb_surface_create_for_bitmap _moz_cairo_xcb_surface_create_for_bitmap +#define cairo_xcb_surface_create_with_xrender_format _moz_cairo_xcb_surface_create_with_xrender_format +#define cairo_xcb_surface_set_size _moz_cairo_xcb_surface_set_size +#define cairo_xlib_surface_create _moz_cairo_xlib_surface_create +#define cairo_xlib_surface_create_for_bitmap _moz_cairo_xlib_surface_create_for_bitmap +#define cairo_xlib_surface_create_with_xrender_format _moz_cairo_xlib_surface_create_with_xrender_format +#define cairo_xlib_surface_get_depth _moz_cairo_xlib_surface_get_depth +#define cairo_xlib_surface_get_display _moz_cairo_xlib_surface_get_display +#define cairo_xlib_surface_get_drawable _moz_cairo_xlib_surface_get_drawable +#define cairo_xlib_surface_get_height _moz_cairo_xlib_surface_get_height +#define cairo_xlib_surface_get_screen _moz_cairo_xlib_surface_get_screen +#define cairo_xlib_surface_get_visual _moz_cairo_xlib_surface_get_visual +#define cairo_xlib_surface_get_width _moz_cairo_xlib_surface_get_width +#define cairo_xlib_surface_get_xrender_format _moz_cairo_xlib_surface_get_xrender_format +#define cairo_xlib_surface_set_drawable _moz_cairo_xlib_surface_set_drawable +#define cairo_xlib_surface_set_size _moz_cairo_xlib_surface_set_size diff --git a/libs/cairo/src/cairo-rtree-private.h b/libs/cairo/src/cairo-rtree-private.h new file mode 100644 index 000000000..295f48ced --- /dev/null +++ b/libs/cairo/src/cairo-rtree-private.h @@ -0,0 +1,102 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_RTREE_PRIVATE_H +#define CAIRO_RTREE_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" + +enum { + CAIRO_RTREE_NODE_AVAILABLE, + CAIRO_RTREE_NODE_DIVIDED, + CAIRO_RTREE_NODE_OCCUPIED, +}; + +typedef struct _cairo_rtree_node { + struct _cairo_rtree_node *children[4], *parent; + void **owner; + cairo_list_t link; + uint16_t pinned; + uint16_t state; + uint16_t x, y; + uint16_t width, height; +} cairo_rtree_node_t; + +typedef struct _cairo_rtree { + cairo_rtree_node_t root; + int min_size; + cairo_list_t pinned; + cairo_list_t available; + cairo_list_t evictable; + cairo_freepool_t node_freepool; +} cairo_rtree_t; + +cairo_private cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height); + +cairo_private cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_size, + int node_size); + +cairo_private cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out); + +static inline void * +_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + if (! node->pinned) { + cairo_list_move (&node->link, &rtree->pinned); + node->pinned = 1; + } + + return node; +} + +cairo_private void +_cairo_rtree_unpin (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_reset (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_fini (cairo_rtree_t *rtree); + +#endif /* CAIRO_RTREE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-rtree.c b/libs/cairo/src/cairo-rtree.c new file mode 100644 index 000000000..059edff75 --- /dev/null +++ b/libs/cairo/src/cairo-rtree.c @@ -0,0 +1,353 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" + +cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height) +{ + cairo_rtree_node_t *node; + + node = _cairo_freepool_alloc (&rtree->node_freepool); + if (node == NULL) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + node->children[0] = NULL; + node->parent = parent; + node->owner = NULL; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + node->pinned = FALSE; + node->x = x; + node->y = y; + node->width = width; + node->height = height; + + cairo_list_add (&node->link, &rtree->available); + + return node; +} + +void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + cairo_list_del (&node->link); + + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + if (node->owner != NULL) + *node->owner = NULL; + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + } + + _cairo_freepool_free (&rtree->node_freepool, node); +} + +void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + do { + assert (node->state == CAIRO_RTREE_NODE_DIVIDED); + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE) + return; + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + + node->children[0] = NULL; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + } while ((node = node->parent) != NULL); +} + +cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out) +{ + int w, h, i; + + assert (node->state == CAIRO_RTREE_NODE_AVAILABLE); + assert (node->pinned == FALSE); + + if (node->width - width > rtree->min_size || + node->height - height > rtree->min_size) + { + w = node->width - width; + h = node->height - height; + + i = 0; + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, node->y, + width, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y, + w, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + + if (h > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, + node->y + height, + width, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y + height, + w, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + } + + if (i < 4) + node->children[i] = NULL; + + node->state = CAIRO_RTREE_NODE_DIVIDED; + cairo_list_move (&node->link, &rtree->evictable); + node = node->children[0]; + } + + node->state = CAIRO_RTREE_NODE_OCCUPIED; + cairo_list_move (&node->link, &rtree->evictable); + *out = node; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); + assert (node->pinned == FALSE); + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + _cairo_rtree_node_collapse (rtree, node->parent); +} + +cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out) +{ + cairo_rtree_node_t *node; + + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->available, link) + { + if (node->width >= width && node->height >= height) + return _cairo_rtree_node_insert (rtree, node, width, height, out); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out) +{ + cairo_rtree_node_t *node, *next; + int i, cnt; + + /* propagate pinned from children to root */ + cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t, + &rtree->pinned, link) + { + if (node->parent != NULL) + _cairo_rtree_pin (rtree, node->parent); + } + + cnt = 0; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height) + cnt++; + } + + if (cnt == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cnt = hars_petruska_f54_1_random () % cnt; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height && cnt-- == 0) { + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + if (node->owner != NULL) + *node->owner = NULL; + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + node->children[0] = NULL; + } + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + *out = node; + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +void +_cairo_rtree_unpin (cairo_rtree_t *rtree) +{ + cairo_rtree_node_t *node, *next; + cairo_list_t can_collapse; + + if (cairo_list_is_empty (&rtree->pinned)) + return; + + cairo_list_init (&can_collapse); + + cairo_list_foreach_entry_safe (node, next, + cairo_rtree_node_t, + &rtree->pinned, + link) + { + node->pinned = FALSE; + if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) { + cairo_bool_t all_available; + int i; + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + all_available = TRUE; + node = node->parent; + for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++) + all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE; + + if (all_available) { + cairo_list_move (&node->link, &can_collapse); + for (i = 0; i < 4 && node->children[i] != NULL; i++) + cairo_list_del (&node->children[i]->link); + } + } + else + { + cairo_list_move (&node->link, &rtree->evictable); + } + } + + cairo_list_foreach_entry_safe (node, next, + cairo_rtree_node_t, + &can_collapse, + link) + { + _cairo_rtree_node_collapse (rtree, node); + } +} + +void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_size, + int node_size) +{ + assert (node_size >= (int) sizeof (cairo_rtree_node_t)); + _cairo_freepool_init (&rtree->node_freepool, node_size); + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->pinned); + cairo_list_init (&rtree->evictable); + + rtree->min_size = min_size; + + memset (&rtree->root, 0, sizeof (rtree->root)); + rtree->root.width = width; + rtree->root.height = height; + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_add (&rtree->root.link, &rtree->available); +} + +void +_cairo_rtree_reset (cairo_rtree_t *rtree) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + if (rtree->root.owner != NULL) + *rtree->root.owner = NULL; + } else { + for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); + rtree->root.children[0] = NULL; + } + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->evictable); + cairo_list_init (&rtree->pinned); + + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + rtree->root.pinned = FALSE; + cairo_list_add (&rtree->root.link, &rtree->available); +} + +void +_cairo_rtree_fini (cairo_rtree_t *rtree) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + if (rtree->root.owner != NULL) + *rtree->root.owner = NULL; + } else { + for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); + } + + _cairo_freepool_fini (&rtree->node_freepool); +} diff --git a/libs/cairo/src/cairo-scaled-font-private.h b/libs/cairo/src/cairo-scaled-font-private.h new file mode 100644 index 000000000..5d426494a --- /dev/null +++ b/libs/cairo/src/cairo-scaled-font-private.h @@ -0,0 +1,98 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SCALED_FONT_PRIVATE_H +#define CAIRO_SCALED_FONT_PRIVATE_H + +#include "cairo.h" + +#include "cairo-types-private.h" +#include "cairo-list-private.h" +#include "cairo-mutex-type-private.h" +#include "cairo-reference-count-private.h" + +typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t; + +struct _cairo_scaled_font { + /* For most cairo objects, the rule for multiple threads is that + * the user is responsible for any locking if the same object is + * manipulated from multiple threads simultaneously. + * + * However, with the caching that cairo does for scaled fonts, a + * user can easily end up with the same cairo_scaled_font object + * being manipulated from multiple threads without the user ever + * being aware of this, (and in fact, unable to control it). + * + * So, as a special exception, the cairo implementation takes care + * of all locking needed for cairo_scaled_font_t. Most of what is + * in the scaled font is immutable, (which is what allows for the + * sharing in the first place). The things that are modified and + * the locks protecting them are as follows: + * + * 1. The reference count (scaled_font->ref_count) + * + * Modifications to the reference count are protected by the + * _cairo_scaled_font_map_mutex. This is because the reference + * count of a scaled font is intimately related with the font + * map itself, (and the magic holdovers array). + * + * 2. The cache of glyphs (scaled_font->glyphs) + * 3. The backend private data (scaled_font->surface_backend, + * scaled_font->surface_private) + * + * Modifications to these fields are protected with locks on + * scaled_font->mutex in the generic scaled_font code. + */ + + cairo_hash_entry_t hash_entry; + + /* useful bits for _cairo_scaled_font_nil */ + cairo_status_t status; + cairo_reference_count_t ref_count; + cairo_user_data_array_t user_data; + + cairo_font_face_t *original_font_face; /* may be NULL */ + + /* hash key members */ + cairo_font_face_t *font_face; /* may be NULL */ + cairo_matrix_t font_matrix; /* font space => user space */ + cairo_matrix_t ctm; /* user space => device space */ + cairo_font_options_t options; + + unsigned int placeholder : 1; /* protected by fontmap mutex */ + unsigned int holdover : 1; + unsigned int finished : 1; + + /* "live" scaled_font members */ + cairo_matrix_t scale; /* font space => device space */ + cairo_matrix_t scale_inverse; /* device space => font space */ + double max_scale; /* maximum x/y expansion of scale */ + cairo_font_extents_t extents; /* user space */ + cairo_font_extents_t fs_extents; /* font space */ + + /* The mutex protects modification to all subsequent fields. */ + cairo_mutex_t mutex; + + cairo_hash_table_t *glyphs; + cairo_list_t glyph_pages; + cairo_bool_t cache_frozen; + cairo_bool_t global_cache_frozen; + + /* + * One surface backend may store data in each glyph. + * Whichever surface manages to store its pointer here + * first gets to store data in each glyph + */ + const cairo_surface_backend_t *surface_backend; + void *surface_private; + + /* font backend managing this scaled font */ + const cairo_scaled_font_backend_t *backend; + cairo_list_t link; +}; + +cairo_private void +_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font); + +#endif /* CAIRO_SCALED_FONT_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-scaled-font-subsets-private.h b/libs/cairo/src/cairo-scaled-font-subsets-private.h new file mode 100644 index 000000000..246dbdcfd --- /dev/null +++ b/libs/cairo/src/cairo-scaled-font-subsets-private.h @@ -0,0 +1,636 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H +#define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +typedef struct _cairo_scaled_font_subsets_glyph { + unsigned int font_id; + unsigned int subset_id; + unsigned int subset_glyph_index; + cairo_bool_t is_scaled; + cairo_bool_t is_composite; + double x_advance; + double y_advance; + cairo_bool_t utf8_is_mapped; + uint32_t unicode; +} cairo_scaled_font_subsets_glyph_t; + +/** + * _cairo_scaled_font_subsets_create_scaled: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create subsets of any number of #cairo_scaled_font_t + * objects. This allows the (arbitrarily large and sparse) glyph + * indices of a #cairo_scaled_font_t to be mapped to one or more font + * subsets with glyph indices packed into the range + * [0 .. max_glyphs_per_subset). + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_scaled (void); + +/** + * _cairo_scaled_font_subsets_create_simple: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create font subsets suitable for embedding as Postscript or PDF + * simple fonts. + * + * Glyphs with an outline path available will be mapped to one font + * subset for each font face. Glyphs from bitmap fonts will mapped to + * separate font subsets for each #cairo_scaled_font_t object. + * + * The maximum number of glyphs per subset is 256. Each subset + * reserves the first glyph for the .notdef glyph. + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_simple (void); + +/** + * _cairo_scaled_font_subsets_create_composite: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create font subsets suitable for embedding as Postscript or PDF + * composite fonts. + * + * Glyphs with an outline path available will be mapped to one font + * subset for each font face. Each unscaled subset has a maximum of + * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs. + * + * Glyphs from bitmap fonts will mapped to separate font subsets for + * each #cairo_scaled_font_t object. Each unscaled subset has a maximum + * of 256 glyphs. + * + * Each subset reserves the first glyph for the .notdef glyph. + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_composite (void); + +/** + * _cairo_scaled_font_subsets_destroy: + * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed + * + * Destroys @font_subsets and all resources associated with it. + **/ +cairo_private void +_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets); + +/** + * _cairo_scaled_font_subsets_map_glyph: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @scaled_font: the font of the glyph to be mapped + * @scaled_font_glyph_index: the index of the glyph to be mapped + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes + * @subset_glyph_ret: return structure containing subset font and glyph id + * + * Map a glyph from a #cairo_scaled_font to a new index within a + * subset of that font. The mapping performed is from the tuple: + * + * (scaled_font, scaled_font_glyph_index) + * + * to the tuple: + * + * (font_id, subset_id, subset_glyph_index) + * + * This mapping is 1:1. If the input tuple has previously mapped, the + * the output tuple previously returned will be returned again. + * + * Otherwise, the return tuple will be constructed as follows: + * + * 1) There is a 1:1 correspondence between the input scaled_font + * value and the output font_id value. If no mapping has been + * previously performed with the scaled_font value then the + * smallest unused font_id value will be returned. + * + * 2) Within the set of output tuples of the same font_id value the + * smallest value of subset_id will be returned such that + * subset_glyph_index does not exceed max_glyphs_per_subset (as + * passed to _cairo_scaled_font_subsets_create()) and that the + * resulting tuple is unique. + * + * 3) The smallest value of subset_glyph_index is returned such that + * the resulting tuple is unique. + * + * The net result is that any #cairo_scaled_font_t will be represented + * by one or more font subsets. Each subset is effectively a tuple of + * (scaled_font, font_id, subset_id) and within each subset there + * exists a mapping of scaled_glyph_font_index to subset_glyph_index. + * + * This final description of a font subset is the same representation + * used by #cairo_scaled_font_subset_t as provided by + * _cairo_scaled_font_subsets_foreach. + * + * @utf8 and @utf8_len specify a string of unicode characters that the + * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in + * @subset_glyph_ret is %TRUE, the font subsetting will (where index to + * unicode mapping is supported) ensure that @scaled_font_glyph_index + * maps to @utf8. If @utf8_is_mapped is %FALSE, + * @scaled_font_glyph_index has already been mapped to a different + * unicode string. + * + * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are: + * + * @font_id: The font ID of the mapped glyph + * @subset_id : The subset ID of the mapped glyph within the @font_id + * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset + * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font + * subset is created for each font scale used. If false, the outline of the mapped glyph + * is available. One font subset for each font face is created. + * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain + * the x and y advance for the mapped glyph in device space. + * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for + * the the mapped glyph from an unhinted 1 point font. + * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph() + * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already + * mapped to a different utf8 string. + * @unicode: the unicode character mapped to this glyph by the font backend. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + const char * utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph_ret); + +typedef cairo_status_t +(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset, + void *closure); + +/** + * _cairo_scaled_font_subsets_foreach: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @font_subset_callback: a function to be called for each font subset + * @closure: closure data for the callback function + * + * Iterate over each unique scaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subsets_foreach_unscaled: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @font_subset_callback: a function to be called for each font subset + * @closure: closure data for the callback function + * + * Iterate over each unique unscaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subsets_foreach_user: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @font_subset_callback: a function to be called for each font subset + * @closure: closure data for the callback function + * + * Iterate over each unique scaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subset_create_glyph_names: + * @font_subsets: a #cairo_scaled_font_subsets_t + * + * Create an array of strings containing the glyph name for each glyph + * in @font_subsets. The array as store in font_subsets->glyph_names. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support + * mapping the glyph indices to unicode characters. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset); + +typedef struct _cairo_cff_subset { + char *font_name; + char *ps_name; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + char *data; + unsigned long data_length; +} cairo_cff_subset_t; + +/** + * _cairo_cff_subset_init: + * @cff_subset: a #cairo_cff_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a + * cff file corresponding to @font_subset and initialize + * @cff_subset with information about the subset and the cff + * data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * cff file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_cff_subset_fini: + * @cff_subset: a #cairo_cff_subset_t + * + * Free all resources associated with @cff_subset. After this + * call, @cff_subset should not be used again without a + * subsequent call to _cairo_cff_subset_init() again first. + **/ +cairo_private void +_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset); + +/** + * _cairo_cff_fallback_init: + * @cff_subset: a #cairo_cff_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a cff + * file corresponding to @font_subset and initialize @cff_subset + * with information about the subset and the cff data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * cff file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_cff_fallback_fini: + * @cff_subset: a #cairo_cff_subset_t + * + * Free all resources associated with @cff_subset. After this + * call, @cff_subset should not be used again without a + * subsequent call to _cairo_cff_subset_init() again first. + **/ +cairo_private void +_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); + +typedef struct _cairo_truetype_subset { + char *font_name; + char *ps_name; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + unsigned char *data; + unsigned long data_length; + unsigned long *string_offsets; + unsigned long num_string_offsets; +} cairo_truetype_subset_t; + +/** + * _cairo_truetype_subset_init: + * @truetype_subset: a #cairo_truetype_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a + * truetype file corresponding to @font_subset and initialize + * @truetype_subset with information about the subset and the truetype + * data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * truetype file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_truetype_subset_fini: + * @truetype_subset: a #cairo_truetype_subset_t + * + * Free all resources associated with @truetype_subset. After this + * call, @truetype_subset should not be used again without a + * subsequent call to _cairo_truetype_subset_init() again first. + **/ +cairo_private void +_cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset); + + + +typedef struct _cairo_type1_subset { + char *base_font; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + char *data; + unsigned long header_length; + unsigned long data_length; + unsigned long trailer_length; +} cairo_type1_subset_t; + + +#if CAIRO_HAS_FT_FONT + +/** + * _cairo_type1_subset_init: + * @type1_subset: a #cairo_type1_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * @hex_encode: if true the encrypted portion of the font is hex encoded + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_subset_init (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset, + cairo_bool_t hex_encode); + +/** + * _cairo_type1_subset_fini: + * @type1_subset: a #cairo_type1_subset_t + * + * Free all resources associated with @type1_subset. After this call, + * @type1_subset should not be used again without a subsequent call to + * _cairo_truetype_type1_init() again first. + **/ +cairo_private void +_cairo_type1_subset_fini (cairo_type1_subset_t *subset); + +#endif /* CAIRO_HAS_FT_FONT */ + + +/** + * _cairo_type1_scaled_font_is_type1: + * @scaled_font: a #cairo_scaled_font_t + * + * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE. + **/ +cairo_private cairo_bool_t +_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); + +/** + * _cairo_type1_fallback_init_binary: + * @type1_subset: a #cairo_type1_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. The encrypted + * part of the font is binary encoded. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_type1_fallback_init_hexencode: + * @type1_subset: a #cairo_type1_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. The encrypted + * part of the font is hex encoded. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_type1_fallback_fini: + * @type1_subset: a #cairo_type1_subset_t + * + * Free all resources associated with @type1_subset. After this call, + * @type1_subset should not be used again without a subsequent call to + * _cairo_truetype_type1_init() again first. + **/ +cairo_private void +_cairo_type1_fallback_fini (cairo_type1_subset_t *subset); + +typedef struct _cairo_type2_charstrings { + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; + cairo_array_t charstrings; +} cairo_type2_charstrings_t; + +/** + * _cairo_type2_charstrings_init: + * @type2_subset: a #cairo_type2_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate type2 + * charstrings to @font_subset and initialize @type2_subset + * with information about the subset. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2 + * charstrings, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_type2_charstrings_fini: + * @subset: a #cairo_type2_charstrings_t + * + * Free all resources associated with @type2_charstring. After this call, + * @type2_charstring should not be used again without a subsequent call to + * _cairo_type2_charstring_init() again first. + **/ +cairo_private void +_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings); + +/** + * _cairo_truetype_create_glyph_to_unicode_map: + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) assign + * the unicode character of each glyph in font_subset to + * fontsubset->to_unicode. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the unicode encoding of + * the glyphs is not available. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_create_glyph_to_unicode_map (cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_truetype_index_to_ucs4: + * @scaled_font: the #cairo_scaled_font_t + * @index: the glyph index + * @ucs4: return value for the unicode value of the glyph + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) assign + * the unicode character of the glyph to @ucs4. + * + * If mapping glyph indices to unicode is supported but the unicode + * value of the specified glyph is not available, @ucs4 is set to -1. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode + * is not supported. Possible errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, + unsigned long index, + uint32_t *ucs4); + +/** + * _cairo_truetype_read_font_name: + * @scaled_font: the #cairo_scaled_font_t + * @ps_name: returns the PostScript name of the font + * or %NULL if the name could not be found. + * @font_name: returns the font name or %NULL if the name could not be found. + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) read the + * PostScript and Font names from a TrueType/OpenType font. + * + * The font name is the full name of the font eg "DejaVu Sans Bold". + * The PostScript name is a shortened name with spaces removed + * suitable for use as the font name in a PS or PDF file eg + * "DejaVuSans-Bold". + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType + * or the name table is not present. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, + char **ps_name, + char **font_name); + +#endif /* CAIRO_HAS_FONT_SUBSET */ + +#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-scaled-font-subsets.c b/libs/cairo/src/cairo-scaled-font-subsets.c new file mode 100644 index 000000000..ba769d509 --- /dev/null +++ b/libs/cairo/src/cairo-scaled-font-subsets.c @@ -0,0 +1,1053 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-user-font-private.h" + +#define MAX_GLYPHS_PER_SIMPLE_FONT 256 +#define MAX_GLYPHS_PER_COMPOSITE_FONT 65536 + +typedef enum { + CAIRO_SUBSETS_SCALED, + CAIRO_SUBSETS_SIMPLE, + CAIRO_SUBSETS_COMPOSITE +} cairo_subsets_type_t; + +typedef enum { + CAIRO_SUBSETS_FOREACH_UNSCALED, + CAIRO_SUBSETS_FOREACH_SCALED, + CAIRO_SUBSETS_FOREACH_USER +} cairo_subsets_foreach_type_t; + +typedef struct _cairo_sub_font { + cairo_hash_entry_t base; + + cairo_bool_t is_scaled; + cairo_bool_t is_composite; + cairo_bool_t is_user; + cairo_scaled_font_subsets_t *parent; + cairo_scaled_font_t *scaled_font; + unsigned int font_id; + + int current_subset; + int num_glyphs_in_current_subset; + int max_glyphs_per_subset; + + cairo_hash_table_t *sub_font_glyphs; + struct _cairo_sub_font *next; +} cairo_sub_font_t; + +struct _cairo_scaled_font_subsets { + cairo_subsets_type_t type; + + int max_glyphs_per_unscaled_subset_used; + cairo_hash_table_t *unscaled_sub_fonts; + cairo_sub_font_t *unscaled_sub_fonts_list; + cairo_sub_font_t *unscaled_sub_fonts_list_end; + + int max_glyphs_per_scaled_subset_used; + cairo_hash_table_t *scaled_sub_fonts; + cairo_sub_font_t *scaled_sub_fonts_list; + cairo_sub_font_t *scaled_sub_fonts_list_end; + + int num_sub_fonts; +}; + +typedef struct _cairo_sub_font_glyph { + cairo_hash_entry_t base; + + unsigned int subset_id; + unsigned int subset_glyph_index; + double x_advance; + double y_advance; + + cairo_bool_t is_mapped; + uint32_t unicode; + char *utf8; + int utf8_len; +} cairo_sub_font_glyph_t; + +typedef struct _cairo_sub_font_collection { + unsigned long *glyphs; /* scaled_font_glyph_index */ + char **utf8; + unsigned int glyphs_size; + unsigned int max_glyph; + unsigned int num_glyphs; + + unsigned int subset_id; + + cairo_status_t status; + cairo_scaled_font_subset_callback_func_t font_subset_callback; + void *font_subset_callback_closure; +} cairo_sub_font_collection_t; + +typedef struct _cairo_string_entry { + cairo_hash_entry_t base; + char *string; +} cairo_string_entry_t; + +static cairo_status_t +_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + const char * utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph); + +static void +_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, + unsigned long scaled_font_glyph_index) +{ + sub_font_glyph->base.hash = scaled_font_glyph_index; +} + +static cairo_bool_t +_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b) +{ + const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a; + const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b; + + return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash; +} + +static cairo_sub_font_glyph_t * +_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, + unsigned int subset_id, + unsigned int subset_glyph_index, + double x_advance, + double y_advance) +{ + cairo_sub_font_glyph_t *sub_font_glyph; + + sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); + if (unlikely (sub_font_glyph == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index); + sub_font_glyph->subset_id = subset_id; + sub_font_glyph->subset_glyph_index = subset_glyph_index; + sub_font_glyph->x_advance = x_advance; + sub_font_glyph->y_advance = y_advance; + sub_font_glyph->is_mapped = FALSE; + sub_font_glyph->unicode = -1; + sub_font_glyph->utf8 = NULL; + sub_font_glyph->utf8_len = 0; + + return sub_font_glyph; +} + +static void +_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) +{ + if (sub_font_glyph->utf8 != NULL) + free (sub_font_glyph->utf8); + + free (sub_font_glyph); +} + +static void +_cairo_sub_font_glyph_pluck (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_hash_table_t *sub_font_glyphs = closure; + + _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base); + _cairo_sub_font_glyph_destroy (sub_font_glyph); +} + +static void +_cairo_sub_font_glyph_collect (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_sub_font_collection_t *collection = closure; + unsigned long scaled_font_glyph_index; + unsigned int subset_glyph_index; + + if (sub_font_glyph->subset_id != collection->subset_id) + return; + + scaled_font_glyph_index = sub_font_glyph->base.hash; + subset_glyph_index = sub_font_glyph->subset_glyph_index; + + /* Ensure we don't exceed the allocated bounds. */ + assert (subset_glyph_index < collection->glyphs_size); + + collection->glyphs[subset_glyph_index] = scaled_font_glyph_index; + collection->utf8[subset_glyph_index] = sub_font_glyph->utf8; + if (subset_glyph_index > collection->max_glyph) + collection->max_glyph = subset_glyph_index; + + collection->num_glyphs++; +} + +static cairo_bool_t +_cairo_sub_fonts_equal (const void *key_a, const void *key_b) +{ + const cairo_sub_font_t *sub_font_a = key_a; + const cairo_sub_font_t *sub_font_b = key_b; + cairo_scaled_font_t *a = sub_font_a->scaled_font; + cairo_scaled_font_t *b = sub_font_b->scaled_font; + + if (sub_font_a->is_scaled) + return a == b; + else + return a->font_face == b->font_face || a->original_font_face == b->original_font_face; +} + +static void +_cairo_sub_font_init_key (cairo_sub_font_t *sub_font, + cairo_scaled_font_t *scaled_font) +{ + if (sub_font->is_scaled) + { + sub_font->base.hash = (unsigned long) scaled_font; + sub_font->scaled_font = scaled_font; + } + else + { + sub_font->base.hash = (unsigned long) scaled_font->font_face; + sub_font->scaled_font = scaled_font; + } +} + +static cairo_status_t +_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, + cairo_scaled_font_t *scaled_font, + unsigned int font_id, + int max_glyphs_per_subset, + cairo_bool_t is_scaled, + cairo_bool_t is_composite, + cairo_sub_font_t **sub_font_out) +{ + cairo_sub_font_t *sub_font; + cairo_status_t status; + cairo_scaled_font_subsets_glyph_t subset_glyph; + + sub_font = malloc (sizeof (cairo_sub_font_t)); + if (unlikely (sub_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + sub_font->is_scaled = is_scaled; + sub_font->is_composite = is_composite; + sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face); + _cairo_sub_font_init_key (sub_font, scaled_font); + + sub_font->parent = parent; + sub_font->scaled_font = scaled_font; + sub_font->font_id = font_id; + + sub_font->current_subset = 0; + sub_font->num_glyphs_in_current_subset = 0; + sub_font->max_glyphs_per_subset = max_glyphs_per_subset; + + sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal); + if (unlikely (sub_font->sub_font_glyphs == NULL)) { + free (sub_font); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + sub_font->next = NULL; + + /* Reserve first glyph in subset for the .notdef glyph except for + * Type 3 fonts */ + if (! is_scaled) { + status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph); + if (unlikely (status)) { + _cairo_hash_table_destroy (sub_font->sub_font_glyphs); + free (sub_font); + return status; + } + } + + *sub_font_out = sub_font; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_sub_font_destroy (cairo_sub_font_t *sub_font) +{ + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, + _cairo_sub_font_glyph_pluck, + sub_font->sub_font_glyphs); + _cairo_hash_table_destroy (sub_font->sub_font_glyphs); + cairo_scaled_font_destroy (sub_font->scaled_font); + free (sub_font); +} + +static void +_cairo_sub_font_pluck (void *entry, void *closure) +{ + cairo_sub_font_t *sub_font = entry; + cairo_hash_table_t *sub_fonts = closure; + + _cairo_hash_table_remove (sub_fonts, &sub_font->base); + _cairo_sub_font_destroy (sub_font); +} + +static cairo_status_t +_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index) +{ + uint32_t unicode; + char buf[8]; + int len; + cairo_status_t status; + + /* Do a reverse lookup on the glyph index. unicode is -1 if the + * index could not be mapped to a unicode character. */ + unicode = -1; + status = _cairo_truetype_index_to_ucs4 (scaled_font, + scaled_font_glyph_index, + &unicode); + if (_cairo_status_is_error (status)) + return status; + + if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) { + status = scaled_font->backend->index_to_ucs4 (scaled_font, + scaled_font_glyph_index, + &unicode); + if (unlikely (status)) + return status; + } + + sub_font_glyph->unicode = unicode; + sub_font_glyph->utf8 = NULL; + sub_font_glyph->utf8_len = 0; + if (unicode != (uint32_t) -1) { + len = _cairo_ucs4_to_utf8 (unicode, buf); + if (len > 0) { + sub_font_glyph->utf8 = malloc (len + 1); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (sub_font_glyph->utf8, buf, len); + sub_font_glyph->utf8[len] = 0; + sub_font_glyph->utf8_len = len; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, + const char *utf8, + int utf8_len, + cairo_bool_t *is_mapped) +{ + *is_mapped = FALSE; + + if (utf8_len < 0) + return CAIRO_STATUS_SUCCESS; + + if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') + utf8_len--; + + if (utf8 != NULL && utf8_len != 0) { + if (sub_font_glyph->utf8 != NULL) { + if (utf8_len == sub_font_glyph->utf8_len && + memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) + { + /* Requested utf8 mapping matches the existing mapping */ + *is_mapped = TRUE; + } + } else { + /* No existing mapping. Use the requested mapping */ + sub_font_glyph->utf8 = malloc (utf8_len + 1); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (sub_font_glyph->utf8, utf8, utf8_len); + sub_font_glyph->utf8[utf8_len] = 0; + sub_font_glyph->utf8_len = utf8_len; + *is_mapped = TRUE; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + const char *utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_int_status_t status; + + _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph != NULL) { + subset_glyph->font_id = sub_font->font_id; + subset_glyph->subset_id = sub_font_glyph->subset_id; + subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + subset_glyph->is_scaled = sub_font->is_scaled; + subset_glyph->is_composite = sub_font->is_composite; + subset_glyph->x_advance = sub_font_glyph->x_advance; + subset_glyph->y_advance = sub_font_glyph->y_advance; + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); + subset_glyph->unicode = sub_font_glyph->unicode; + + return status; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + const char *utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_status_t status; + + _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph == NULL) { + cairo_scaled_glyph_t *scaled_glyph; + + if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) + { + cairo_scaled_font_subsets_glyph_t tmp_subset_glyph; + + sub_font->current_subset++; + sub_font->num_glyphs_in_current_subset = 0; + + /* Reserve first glyph in subset for the .notdef glyph + * except for Type 3 fonts */ + if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) { + status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph); + if (unlikely (status)) + return status; + } + } + + _cairo_scaled_font_freeze_cache (sub_font->scaled_font); + status = _cairo_scaled_glyph_lookup (sub_font->scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (sub_font->scaled_font); + return status; + } + + sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, + sub_font->current_subset, + sub_font->num_glyphs_in_current_subset, + scaled_glyph->metrics.x_advance, + scaled_glyph->metrics.y_advance); + _cairo_scaled_font_thaw_cache (sub_font->scaled_font); + + if (unlikely (sub_font_glyph == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph, + sub_font->scaled_font, + scaled_font_glyph_index); + if (unlikely (status)) { + _cairo_sub_font_glyph_destroy (sub_font_glyph); + return status; + } + + status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); + if (unlikely (status)) { + _cairo_sub_font_glyph_destroy (sub_font_glyph); + return status; + } + + sub_font->num_glyphs_in_current_subset++; + + if (sub_font->is_scaled) { + if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used) + sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset; + } else { + if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used) + sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset; + } + } + + subset_glyph->font_id = sub_font->font_id; + subset_glyph->subset_id = sub_font_glyph->subset_id; + subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + subset_glyph->is_scaled = sub_font->is_scaled; + subset_glyph->is_composite = sub_font->is_composite; + subset_glyph->x_advance = sub_font_glyph->x_advance; + subset_glyph->y_advance = sub_font_glyph->y_advance; + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); + subset_glyph->unicode = sub_font_glyph->unicode; + + return status; +} + +static void +_cairo_sub_font_collect (void *entry, void *closure) +{ + cairo_sub_font_t *sub_font = entry; + cairo_sub_font_collection_t *collection = closure; + cairo_scaled_font_subset_t subset; + int i; + unsigned int j; + + if (collection->status) + return; + + collection->status = sub_font->scaled_font->status; + if (collection->status) + return; + + for (i = 0; i <= sub_font->current_subset; i++) { + collection->subset_id = i; + collection->num_glyphs = 0; + collection->max_glyph = 0; + + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, + _cairo_sub_font_glyph_collect, collection); + if (collection->status) + break; + if (collection->num_glyphs == 0) + continue; + + /* Ensure the resulting array has no uninitialized holes */ + assert (collection->num_glyphs == collection->max_glyph + 1); + + subset.scaled_font = sub_font->scaled_font; + subset.is_composite = sub_font->is_composite; + subset.is_scaled = sub_font->is_scaled; + subset.font_id = sub_font->font_id; + subset.subset_id = i; + subset.glyphs = collection->glyphs; + subset.utf8 = collection->utf8; + subset.num_glyphs = collection->num_glyphs; + subset.glyph_names = NULL; + /* No need to check for out of memory here. If to_unicode is NULL, the PDF + * surface does not emit an ToUnicode stream */ + subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long)); + if (subset.to_unicode) { + for (j = 0; j < collection->num_glyphs; j++) { + /* default unicode character required when mapping fails */ + subset.to_unicode[j] = 0xfffd; + } + } + collection->status = (collection->font_subset_callback) (&subset, + collection->font_subset_callback_closure); + + if (subset.to_unicode != NULL) + free (subset.to_unicode); + + if (subset.glyph_names != NULL) { + for (j = 0; j < collection->num_glyphs; j++) + free (subset.glyph_names[j]); + free (subset.glyph_names); + } + + if (collection->status) + break; + } +} + +static cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) +{ + cairo_scaled_font_subsets_t *subsets; + + subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); + if (unlikely (subsets == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + subsets->type = type; + subsets->max_glyphs_per_unscaled_subset_used = 0; + subsets->max_glyphs_per_scaled_subset_used = 0; + subsets->num_sub_fonts = 0; + + subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); + if (! subsets->unscaled_sub_fonts) { + free (subsets); + return NULL; + } + subsets->unscaled_sub_fonts_list = NULL; + subsets->unscaled_sub_fonts_list_end = NULL; + + subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); + if (! subsets->scaled_sub_fonts) { + _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); + free (subsets); + return NULL; + } + subsets->scaled_sub_fonts_list = NULL; + subsets->scaled_sub_fonts_list_end = NULL; + + return subsets; +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_scaled (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED); +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_simple (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE); +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_composite (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE); +} + +void +_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) +{ + _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts); + _cairo_hash_table_destroy (subsets->scaled_sub_fonts); + + _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts); + _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); + + free (subsets); +} + +cairo_status_t +_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + const char * utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_t key, *sub_font; + cairo_scaled_glyph_t *scaled_glyph; + cairo_font_face_t *font_face; + cairo_matrix_t identity; + cairo_font_options_t font_options; + cairo_scaled_font_t *unscaled_font; + cairo_status_t status; + int max_glyphs; + cairo_bool_t type1_font; + + /* Lookup glyph in unscaled subsets */ + if (subsets->type != CAIRO_SUBSETS_SCALED) { + key.is_scaled = FALSE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + } + + /* Lookup glyph in scaled subsets */ + key.is_scaled = TRUE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Glyph not found. Determine whether the glyph is outline or + * bitmap and add to the appropriate subset. + * + * glyph_index 0 (the .notdef glyph) is a special case. Some fonts + * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a + * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates + * empty glyphs in this case so we can put the glyph in a unscaled + * subset. */ + if (scaled_font_glyph_index == 0 || + _cairo_font_face_is_user (scaled_font->font_face)) { + status = CAIRO_STATUS_SUCCESS; + } else { + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + _cairo_scaled_font_thaw_cache (scaled_font); + } + if (_cairo_status_is_error (status)) + return status; + + if (status == CAIRO_STATUS_SUCCESS && + subsets->type != CAIRO_SUBSETS_SCALED && + ! _cairo_font_face_is_user (scaled_font->font_face)) + { + /* Path available. Add to unscaled subset. */ + key.is_scaled = FALSE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font == NULL) { + font_face = cairo_scaled_font_get_font_face (scaled_font); + cairo_matrix_init_identity (&identity); + _cairo_font_options_init_default (&font_options); + cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); + unscaled_font = cairo_scaled_font_create (font_face, + &identity, + &identity, + &font_options); + if (unlikely (unscaled_font->status)) + return unscaled_font->status; + + subset_glyph->is_scaled = FALSE; + type1_font = FALSE; +#if CAIRO_HAS_FT_FONT + type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font); +#endif + if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) { + max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT; + subset_glyph->is_composite = TRUE; + } else { + max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; + subset_glyph->is_composite = FALSE; + } + + status = _cairo_sub_font_create (subsets, + unscaled_font, + subsets->num_sub_fonts, + max_glyphs, + subset_glyph->is_scaled, + subset_glyph->is_composite, + &sub_font); + + if (unlikely (status)) { + cairo_scaled_font_destroy (unscaled_font); + return status; + } + + status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts, + &sub_font->base); + + if (unlikely (status)) { + _cairo_sub_font_destroy (sub_font); + return status; + } + if (!subsets->unscaled_sub_fonts_list) + subsets->unscaled_sub_fonts_list = sub_font; + else + subsets->unscaled_sub_fonts_list_end->next = sub_font; + subsets->unscaled_sub_fonts_list_end = sub_font; + subsets->num_sub_fonts++; + } + } else { + /* No path available. Add to scaled subset. */ + key.is_scaled = TRUE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font == NULL) { + subset_glyph->is_scaled = TRUE; + subset_glyph->is_composite = FALSE; + if (subsets->type == CAIRO_SUBSETS_SCALED) + max_glyphs = INT_MAX; + else + max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; + + status = _cairo_sub_font_create (subsets, + cairo_scaled_font_reference (scaled_font), + subsets->num_sub_fonts, + max_glyphs, + subset_glyph->is_scaled, + subset_glyph->is_composite, + &sub_font); + if (unlikely (status)) { + cairo_scaled_font_destroy (scaled_font); + return status; + } + + status = _cairo_hash_table_insert (subsets->scaled_sub_fonts, + &sub_font->base); + if (unlikely (status)) { + _cairo_sub_font_destroy (sub_font); + return status; + } + if (!subsets->scaled_sub_fonts_list) + subsets->scaled_sub_fonts_list = sub_font; + else + subsets->scaled_sub_fonts_list_end->next = sub_font; + subsets->scaled_sub_fonts_list_end = sub_font; + subsets->num_sub_fonts++; + } + } + + return _cairo_sub_font_map_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); +} + +static cairo_status_t +_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure, + cairo_subsets_foreach_type_t type) +{ + cairo_sub_font_collection_t collection; + cairo_sub_font_t *sub_font; + cairo_bool_t is_scaled, is_user; + + is_scaled = FALSE; + is_user = FALSE; + + if (type == CAIRO_SUBSETS_FOREACH_USER) + is_user = TRUE; + + if (type == CAIRO_SUBSETS_FOREACH_SCALED || + type == CAIRO_SUBSETS_FOREACH_USER) + { + is_scaled = TRUE; + } + + if (is_scaled) + collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; + else + collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used; + + if (! collection.glyphs_size) + return CAIRO_STATUS_SUCCESS; + + collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); + collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); + if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) { + if (collection.glyphs != NULL) + free (collection.glyphs); + if (collection.utf8 != NULL) + free (collection.utf8); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + collection.font_subset_callback = font_subset_callback; + collection.font_subset_callback_closure = closure; + collection.status = CAIRO_STATUS_SUCCESS; + + if (is_scaled) + sub_font = font_subsets->scaled_sub_fonts_list; + else + sub_font = font_subsets->unscaled_sub_fonts_list; + + while (sub_font) { + if (sub_font->is_user == is_user) + _cairo_sub_font_collect (sub_font, &collection); + + sub_font = sub_font->next; + } + free (collection.utf8); + free (collection.glyphs); + + return collection.status; +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_SCALED); +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_UNSCALED); +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_USER); +} + +static cairo_bool_t +_cairo_string_equal (const void *key_a, const void *key_b) +{ + const cairo_string_entry_t *a = key_a; + const cairo_string_entry_t *b = key_b; + + if (strcmp (a->string, b->string) == 0) + return TRUE; + else + return FALSE; +} + +static void +_cairo_string_init_key (cairo_string_entry_t *key, char *s) +{ + unsigned long sum = 0; + unsigned int i; + + for (i = 0; i < strlen(s); i++) + sum += s[i]; + key->base.hash = sum; + key->string = s; +} + +static cairo_status_t +create_string_entry (char *s, cairo_string_entry_t **entry) +{ + *entry = malloc (sizeof (cairo_string_entry_t)); + if (unlikely (*entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_string_init_key (*entry, s); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_pluck_entry (void *entry, void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +cairo_int_status_t +_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset) +{ + unsigned int i; + cairo_hash_table_t *names; + cairo_string_entry_t key, *entry; + char buf[30]; + char *utf8; + uint16_t *utf16; + int utf16_len; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + names = _cairo_hash_table_create (_cairo_string_equal); + if (unlikely (names == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *)); + if (unlikely (subset->glyph_names == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + i = 0; + if (! subset->is_scaled) { + subset->glyph_names[0] = strdup (".notdef"); + if (unlikely (subset->glyph_names[0] == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + status = create_string_entry (subset->glyph_names[0], &entry); + if (unlikely (status)) + goto CLEANUP_HASH; + + status = _cairo_hash_table_insert (names, &entry->base); + if (unlikely (status)) { + free (entry); + goto CLEANUP_HASH; + } + i++; + } + + for (; i < subset->num_glyphs; i++) { + utf8 = subset->utf8[i]; + utf16 = NULL; + utf16_len = 0; + if (utf8 && *utf8) { + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); + if (unlikely (status)) + goto CLEANUP_HASH; + } + + if (utf16_len == 1) { + snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); + _cairo_string_init_key (&key, buf); + entry = _cairo_hash_table_lookup (names, &key.base); + if (entry != NULL) + snprintf (buf, sizeof (buf), "g%d", i); + } else { + snprintf (buf, sizeof (buf), "g%d", i); + } + if (utf16) + free (utf16); + + subset->glyph_names[i] = strdup (buf); + if (unlikely (subset->glyph_names[i] == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + status = create_string_entry (subset->glyph_names[i], &entry); + if (unlikely (status)) + goto CLEANUP_HASH; + + status = _cairo_hash_table_insert (names, &entry->base); + if (unlikely (status)) { + free (entry); + goto CLEANUP_HASH; + } + } + +CLEANUP_HASH: + _cairo_hash_table_foreach (names, _pluck_entry, names); + _cairo_hash_table_destroy (names); + + if (likely (status == CAIRO_STATUS_SUCCESS)) + return CAIRO_STATUS_SUCCESS; + + if (subset->glyph_names != NULL) { + for (i = 0; i < subset->num_glyphs; i++) { + if (subset->glyph_names[i] != NULL) + free (subset->glyph_names[i]); + } + + free (subset->glyph_names); + subset->glyph_names = NULL; + } + + return status; +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-scaled-font.c b/libs/cairo/src/cairo-scaled-font.c new file mode 100644 index 000000000..37806bc63 --- /dev/null +++ b/libs/cairo/src/cairo-scaled-font.c @@ -0,0 +1,2954 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-scaled-font-private.h" + +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + +/** + * SECTION:cairo-scaled-font + * @Title: cairo_scaled_font_t + * @Short_Description: Font face at particular size and options + * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t + * + * #cairo_scaled_font_t represents a realization of a font face at a particular + * size and transformation and a certain set of font options. + */ + +/* Global Glyph Cache + * + * We maintain a global pool of glyphs split between all active fonts. This + * allows a heavily used individual font to cache more glyphs than we could + * manage if we used per-font glyph caches, but at the same time maintains + * fairness across all fonts and provides a cap on the maximum number of + * global glyphs. + * + * The glyphs are allocated in pages, which are capped in the global pool. + * Using pages means we can reduce the frequency at which we have to probe the + * global pool and ameliorates the memory allocation pressure. + */ + +/* XXX: This number is arbitrary---we've never done any measurement of this. */ +#define MAX_GLYPH_PAGES_CACHED 256 +static cairo_cache_t cairo_scaled_glyph_page_cache; + +#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 +struct _cairo_scaled_glyph_page { + cairo_cache_entry_t cache_entry; + + cairo_list_t link; + + unsigned int num_glyphs; + cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE]; +}; + +/* + * Notes: + * + * To store rasterizations of glyphs, we use an image surface and the + * device offset to represent the glyph origin. + * + * A device_transform converts from device space (a conceptual space) to + * surface space. For simple cases of translation only, it's called a + * device_offset and is public API (cairo_surface_[gs]et_device_offset()). + * A possibly better name for those functions could have been + * cairo_surface_[gs]et_origin(). So, that's what they do: they set where + * the device-space origin (0,0) is in the surface. If the origin is inside + * the surface, device_offset values are positive. It may look like this: + * + * Device space: + * (-x,-y) <-- negative numbers + * +----------------+ + * | . | + * | . | + * |......(0,0) <---|-- device-space origin + * | | + * | | + * +----------------+ + * (width-x,height-y) + * + * Surface space: + * (0,0) <-- surface-space origin + * +---------------+ + * | . | + * | . | + * |......(x,y) <--|-- device_offset + * | | + * | | + * +---------------+ + * (width,height) + * + * In other words: device_offset is the coordinates of the device-space + * origin relative to the top-left of the surface. + * + * We use device offsets in a couple of places: + * + * - Public API: To let toolkits like Gtk+ give user a surface that + * only represents part of the final destination (say, the expose + * area), but has the same device space as the destination. In these + * cases device_offset is typically negative. Example: + * + * application window + * +---------------+ + * | . | + * | (x,y). | + * |......+---+ | + * | | | <--|-- expose area + * | +---+ | + * +---------------+ + * + * In this case, the user of cairo API can set the device_space on + * the expose area to (-x,-y) to move the device space origin to that + * of the application window, such that drawing in the expose area + * surface and painting it in the application window has the same + * effect as drawing in the application window directly. Gtk+ has + * been using this feature. + * + * - Glyph surfaces: In most font rendering systems, glyph surfaces + * have an origin at (0,0) and a bounding box that is typically + * represented as (x_bearing,y_bearing,width,height). Depending on + * which way y progresses in the system, y_bearing may typically be + * negative (for systems similar to cairo, with origin at top left), + * or be positive (in systems like PDF with origin at bottom left). + * No matter which is the case, it is important to note that + * (x_bearing,y_bearing) is the coordinates of top-left of the glyph + * relative to the glyph origin. That is, for example: + * + * Scaled-glyph space: + * + * (x_bearing,y_bearing) <-- negative numbers + * +----------------+ + * | . | + * | . | + * |......(0,0) <---|-- glyph origin + * | | + * | | + * +----------------+ + * (width+x_bearing,height+y_bearing) + * + * Note the similarity of the origin to the device space. That is + * exactly how we use the device_offset to represent scaled glyphs: + * to use the device-space origin as the glyph origin. + * + * Now compare the scaled-glyph space to device-space and surface-space + * and convince yourself that: + * + * (x_bearing,y_bearing) = (-x,-y) = - device_offset + * + * That's right. If you are not convinced yet, contrast the definition + * of the two: + * + * "(x_bearing,y_bearing) is the coordinates of top-left of the + * glyph relative to the glyph origin." + * + * "In other words: device_offset is the coordinates of the + * device-space origin relative to the top-left of the surface." + * + * and note that glyph origin = device-space origin. + */ + +static void +_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font); + +static void +_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend; + + if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) + surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); + + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); + + if (scaled_glyph->recording_surface != NULL) { + cairo_surface_finish (scaled_glyph->recording_surface); + cairo_surface_destroy (scaled_glyph->recording_surface); + } +} + +#define ZOMBIE 0 +static const cairo_scaled_font_t _cairo_scaled_font_nil = { + { ZOMBIE }, /* hash_entry */ + CAIRO_STATUS_NO_MEMORY, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL, /* original_font_face */ + NULL, /* font_face */ + { 1., 0., 0., 1., 0, 0}, /* font_matrix */ + { 1., 0., 0., 1., 0, 0}, /* ctm */ + { CAIRO_ANTIALIAS_DEFAULT, /* options */ + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_METRICS_DEFAULT} , + FALSE, /* placeholder */ + FALSE, /* holdover */ + TRUE, /* finished */ + { 1., 0., 0., 1., 0, 0}, /* scale */ + { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ + 1., /* max_scale */ + { 0., 0., 0., 0., 0. }, /* extents */ + { 0., 0., 0., 0., 0. }, /* fs_extents */ + CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ + NULL, /* glyphs */ + { NULL, NULL }, /* pages */ + FALSE, /* cache_frozen */ + FALSE, /* global_cache_frozen */ + NULL, /* surface_backend */ + NULL, /* surface_private */ + NULL /* backend */ +}; + +/** + * _cairo_scaled_font_set_error: + * @scaled_font: a scaled_font + * @status: a status value indicating an error + * + * Atomically sets scaled_font->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to scaled_font->status should happen + * through _cairo_scaled_font_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the nil + * objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_status_t +_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&scaled_font->status, status); + + return _cairo_error (status); +} + +/** + * cairo_scaled_font_get_type: + * @scaled_font: a #cairo_scaled_font_t + * + * This function returns the type of the backend used to create + * a scaled font. See #cairo_font_type_t for available types. + * However, this function never returns %CAIRO_FONT_TYPE_TOY. + * + * Return value: The type of @scaled_font. + * + * Since: 1.2 + **/ +cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return CAIRO_FONT_TYPE_TOY; + + return scaled_font->backend->type; +} + +/** + * cairo_scaled_font_status: + * @scaled_font: a #cairo_scaled_font_t + * + * Checks whether an error has previously occurred for this + * scaled_font. + * + * Return value: %CAIRO_STATUS_SUCCESS or another error such as + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->status; +} +slim_hidden_def (cairo_scaled_font_status); + +/* Here we keep a unique mapping from + * font_face/matrix/ctm/font_options => #cairo_scaled_font_t. + * + * Here are the things that we want to map: + * + * a) All otherwise referenced #cairo_scaled_font_t's + * b) Some number of not otherwise referenced #cairo_scaled_font_t's + * + * The implementation uses a hash table which covers (a) + * completely. Then, for (b) we have an array of otherwise + * unreferenced fonts (holdovers) which are expired in + * least-recently-used order. + * + * The cairo_scaled_font_create() code gets to treat this like a regular + * hash table. All of the magic for the little holdover cache is in + * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). + */ + +/* This defines the size of the holdover array ... that is, the number + * of scaled fonts we keep around even when not otherwise referenced + */ +#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256 + +typedef struct _cairo_scaled_font_map { + cairo_scaled_font_t *mru_scaled_font; + cairo_hash_table_t *hash_table; + cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; + int num_holdovers; +} cairo_scaled_font_map_t; + +static cairo_scaled_font_map_t *cairo_scaled_font_map; + +static int +_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); + +static cairo_scaled_font_map_t * +_cairo_scaled_font_map_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + + if (cairo_scaled_font_map == NULL) { + cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); + if (unlikely (cairo_scaled_font_map == NULL)) + goto CLEANUP_MUTEX_LOCK; + + cairo_scaled_font_map->mru_scaled_font = NULL; + cairo_scaled_font_map->hash_table = + _cairo_hash_table_create (_cairo_scaled_font_keys_equal); + + if (unlikely (cairo_scaled_font_map->hash_table == NULL)) + goto CLEANUP_SCALED_FONT_MAP; + + cairo_scaled_font_map->num_holdovers = 0; + } + + return cairo_scaled_font_map; + + CLEANUP_SCALED_FONT_MAP: + free (cairo_scaled_font_map); + cairo_scaled_font_map = NULL; + CLEANUP_MUTEX_LOCK: + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; +} + +static void +_cairo_scaled_font_map_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); +} + +void +_cairo_scaled_font_map_destroy (void) +{ + cairo_scaled_font_map_t *font_map; + cairo_scaled_font_t *scaled_font; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + + font_map = cairo_scaled_font_map; + if (unlikely (font_map == NULL)) { + goto CLEANUP_MUTEX_LOCK; + } + + scaled_font = font_map->mru_scaled_font; + if (scaled_font != NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + cairo_scaled_font_destroy (scaled_font); + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + } + + /* remove scaled_fonts starting from the end so that font_map->holdovers + * is always in a consistent state when we release the mutex. */ + while (font_map->num_holdovers) { + scaled_font = font_map->holdovers[font_map->num_holdovers-1]; + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + + font_map->num_holdovers--; + + /* This releases the font_map lock to avoid the possibility of a + * recursive deadlock when the scaled font destroy closure gets + * called + */ + _cairo_scaled_font_fini (scaled_font); + + free (scaled_font); + } + + _cairo_hash_table_destroy (font_map->hash_table); + + free (cairo_scaled_font_map); + cairo_scaled_font_map = NULL; + + CLEANUP_MUTEX_LOCK: + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); +} +static void +_cairo_scaled_glyph_page_destroy (void *closure) +{ + cairo_scaled_glyph_page_t *page = closure; + cairo_scaled_font_t *scaled_font; + unsigned int n; + + scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + for (n = 0; n < page->num_glyphs; n++) { + _cairo_hash_table_remove (scaled_font->glyphs, + &page->glyphs[n].hash_entry); + _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]); + } + + cairo_list_del (&page->link); + + free (page); +} + +/* If a scaled font wants to unlock the font map while still being + * created (needed for user-fonts), we need to take extra care not + * ending up with multiple identical scaled fonts being created. + * + * What we do is, we create a fake identical scaled font, and mark + * it as placeholder, lock its mutex, and insert that in the fontmap + * hash table. This makes other code trying to create an identical + * scaled font to just wait and retry. + * + * The reason we have to create a fake scaled font instead of just using + * scaled_font is for lifecycle management: we need to (or rather, + * other code needs to) reference the scaled_font in the hash table. + * We can't do that on the input scaled_font as it may be freed by + * font backend upon error. + */ + +cairo_status_t +_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_scaled_font_t *placeholder_scaled_font; + + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex)); + + status = scaled_font->status; + if (unlikely (status)) + return status; + + placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t)); + if (unlikely (placeholder_scaled_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* full initialization is wasteful, but who cares... */ + status = _cairo_scaled_font_init (placeholder_scaled_font, + scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, + &scaled_font->options, + NULL); + if (unlikely (status)) + goto FREE_PLACEHOLDER; + + placeholder_scaled_font->placeholder = TRUE; + + status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table, + &placeholder_scaled_font->hash_entry); + if (unlikely (status)) + goto FINI_PLACEHOLDER; + + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); + + return CAIRO_STATUS_SUCCESS; + + FINI_PLACEHOLDER: + _cairo_scaled_font_fini_internal (placeholder_scaled_font); + FREE_PLACEHOLDER: + free (placeholder_scaled_font); + + return _cairo_scaled_font_set_error (scaled_font, status); +} + +void +_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_t *placeholder_scaled_font; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + + placeholder_scaled_font = + _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, + &scaled_font->hash_entry); + assert (placeholder_scaled_font != NULL); + assert (placeholder_scaled_font->placeholder); + assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex)); + + _cairo_hash_table_remove (cairo_scaled_font_map->hash_table, + &placeholder_scaled_font->hash_entry); + + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + + CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); + cairo_scaled_font_destroy (placeholder_scaled_font); + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); +} + +static void +_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font) +{ + /* reference the place holder so it doesn't go away */ + cairo_scaled_font_reference (placeholder_scaled_font); + + /* now unlock the fontmap mutex so creation has a chance to finish */ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + + /* wait on placeholder mutex until we are awaken */ + CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); + + /* ok, creation done. just clean up and back out */ + CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); + cairo_scaled_font_destroy (placeholder_scaled_font); + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); +} + +/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) + * + * Not necessarily better than a lot of other hashes, but should be OK, and + * well tested with binary data. + */ + +#define FNV_32_PRIME ((uint32_t)0x01000193) +#define FNV1_32_INIT ((uint32_t)0x811c9dc5) + +static uint32_t +_hash_matrix_fnv (const cairo_matrix_t *matrix, + uint32_t hval) +{ + const uint8_t *buffer = (const uint8_t *) matrix; + int len = sizeof (cairo_matrix_t); + do { + hval *= FNV_32_PRIME; + hval ^= *buffer++; + } while (--len); + + return hval; +} + +static uint32_t +_hash_mix_bits (uint32_t hash) +{ + hash += hash << 12; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; +} + +static void +_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + uint32_t hash = FNV1_32_INIT; + + scaled_font->status = CAIRO_STATUS_SUCCESS; + scaled_font->placeholder = FALSE; + scaled_font->font_face = font_face; + scaled_font->font_matrix = *font_matrix; + scaled_font->ctm = *ctm; + /* ignore translation values in the ctm */ + scaled_font->ctm.x0 = 0.; + scaled_font->ctm.y0 = 0.; + _cairo_font_options_init_copy (&scaled_font->options, options); + + /* We do a bytewise hash on the font matrices */ + hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); + hash = _hash_matrix_fnv (&scaled_font->ctm, hash); + hash = _hash_mix_bits (hash); + + hash ^= (uintptr_t) scaled_font->font_face; + hash ^= cairo_font_options_hash (&scaled_font->options); + + /* final mixing of bits */ + hash = _hash_mix_bits (hash); + + assert (hash != ZOMBIE); + scaled_font->hash_entry.hash = hash; +} + +static cairo_bool_t +_cairo_scaled_font_keys_equal (const void *abstract_key_a, + const void *abstract_key_b) +{ + const cairo_scaled_font_t *key_a = abstract_key_a; + const cairo_scaled_font_t *key_b = abstract_key_b; + + if (key_a->hash_entry.hash != key_b->hash_entry.hash) + return FALSE; + + return key_a->font_face == key_b->font_face && + memcmp ((unsigned char *)(&key_a->font_matrix.xx), + (unsigned char *)(&key_b->font_matrix.xx), + sizeof(cairo_matrix_t)) == 0 && + memcmp ((unsigned char *)(&key_a->ctm.xx), + (unsigned char *)(&key_b->ctm.xx), + sizeof(cairo_matrix_t)) == 0 && + cairo_font_options_equal (&key_a->options, &key_b->options); +} + +static cairo_bool_t +_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, + const cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + return scaled_font->original_font_face == font_face && + memcmp ((unsigned char *)(&scaled_font->font_matrix.xx), + (unsigned char *)(&font_matrix->xx), + sizeof(cairo_matrix_t)) == 0 && + memcmp ((unsigned char *)(&scaled_font->ctm.xx), + (unsigned char *)(&ctm->xx), + sizeof(cairo_matrix_t)) == 0 && + cairo_font_options_equal (&scaled_font->options, options); +} + +static cairo_bool_t +_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b) +{ + const cairo_scaled_glyph_t *a = abstract_a; + const cairo_scaled_glyph_t *b = abstract_b; + + return a->hash_entry.hash == b->hash_entry.hash; +} + +/* + * Basic #cairo_scaled_font_t object management + */ + +cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend) +{ + cairo_status_t status; + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) + return status; + + _cairo_scaled_font_init_key (scaled_font, font_face, + font_matrix, ctm, options); + + cairo_matrix_multiply (&scaled_font->scale, + &scaled_font->font_matrix, + &scaled_font->ctm); + + scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy), + fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy)); + scaled_font->scale_inverse = scaled_font->scale; + status = cairo_matrix_invert (&scaled_font->scale_inverse); + if (unlikely (status)) { + /* If the font scale matrix is rank 0, just using an all-zero inverse matrix + * makes everything work correctly. This make font size 0 work without + * producing an error. + * + * FIXME: If the scale is rank 1, we still go into error mode. But then + * again, that's what we do everywhere in cairo. + * + * Also, the check for == 0. below may be too harsh... + */ + if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) { + cairo_matrix_init (&scaled_font->scale_inverse, + 0, 0, 0, 0, + -scaled_font->scale.x0, + -scaled_font->scale.y0); + } else + return status; + } + + scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal); + if (unlikely (scaled_font->glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&scaled_font->glyph_pages); + scaled_font->cache_frozen = FALSE; + scaled_font->global_cache_frozen = FALSE; + + scaled_font->holdover = FALSE; + scaled_font->finished = FALSE; + + CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1); + + _cairo_user_data_array_init (&scaled_font->user_data); + + cairo_font_face_reference (font_face); + scaled_font->original_font_face = NULL; + + CAIRO_MUTEX_INIT (scaled_font->mutex); + + scaled_font->surface_backend = NULL; + scaled_font->surface_private = NULL; + + scaled_font->backend = backend; + cairo_list_init (&scaled_font->link); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) +{ + /* ensure we do not modify an error object */ + assert (scaled_font->status == CAIRO_STATUS_SUCCESS); + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + scaled_font->cache_frozen = TRUE; +} + +void +_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) +{ + scaled_font->cache_frozen = FALSE; + + if (scaled_font->global_cache_frozen) { + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + _cairo_cache_thaw (&cairo_scaled_glyph_page_cache); + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + + scaled_font->global_cache_frozen = FALSE; + } + + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); +} + +void +_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) +{ + assert (! scaled_font->cache_frozen); + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { + _cairo_cache_remove (&cairo_scaled_glyph_page_cache, + &cairo_list_first_entry (&scaled_font->glyph_pages, + cairo_scaled_glyph_page_t, + link)->cache_entry); + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); +} + +cairo_status_t +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics) +{ + cairo_status_t status; + double font_scale_x, font_scale_y; + + scaled_font->fs_extents = *fs_metrics; + + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, + &font_scale_x, &font_scale_y, + 1); + if (unlikely (status)) + return status; + + /* + * The font responded in unscaled units, scale by the font + * matrix scale factors to get to user space + */ + + scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y; + scaled_font->extents.descent = fs_metrics->descent * font_scale_y; + scaled_font->extents.height = fs_metrics->height * font_scale_y; + scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x; + scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) +{ + scaled_font->finished = TRUE; + + _cairo_scaled_font_reset_cache (scaled_font); + _cairo_hash_table_destroy (scaled_font->glyphs); + + cairo_font_face_destroy (scaled_font->font_face); + cairo_font_face_destroy (scaled_font->original_font_face); + + CAIRO_MUTEX_FINI (scaled_font->mutex); + + if (scaled_font->surface_backend != NULL && + scaled_font->surface_backend->scaled_font_fini != NULL) + scaled_font->surface_backend->scaled_font_fini (scaled_font); + + if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL) + scaled_font->backend->fini (scaled_font); + + _cairo_user_data_array_fini (&scaled_font->user_data); +} + +/* XXX: allow multiple backends to share the font */ +void +_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->surface_backend == NULL) + return; + + _cairo_scaled_font_reset_cache (scaled_font); + + if (scaled_font->surface_backend->scaled_font_fini != NULL) + scaled_font->surface_backend->scaled_font_fini (scaled_font); + + scaled_font->surface_backend = NULL; + scaled_font->surface_private = NULL; +} + +void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + /* Release the lock to avoid the possibility of a recursive + * deadlock when the scaled font destroy closure gets called. */ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + _cairo_scaled_font_fini_internal (scaled_font); + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); +} + +/** + * cairo_scaled_font_create: + * @font_face: a #cairo_font_face_t + * @font_matrix: font space to user space transformation matrix for the + * font. In the simplest case of a N point font, this matrix is + * just a scale by N, but it can also be used to shear the font + * or stretch it unequally along the two axes. See + * cairo_set_font_matrix(). + * @ctm: user to device transformation matrix with which the font will + * be used. + * @options: options to use when getting metrics for the font and + * rendering with it. + * + * Creates a #cairo_scaled_font_t object from a font face and matrices that + * describe the size of the font and the environment in which it will + * be used. + * + * Return value: a newly created #cairo_scaled_font_t. Destroy with + * cairo_scaled_font_destroy() + **/ +cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_status_t status; + cairo_scaled_font_map_t *font_map; + cairo_font_face_t *original_font_face = font_face; + cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL; + double det; + + status = font_face->status; + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + det = _cairo_matrix_compute_determinant (font_matrix); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); + + det = _cairo_matrix_compute_determinant (ctm); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + /* Note that degenerate ctm or font_matrix *are* allowed. + * We want to support a font size of 0. */ + + font_map = _cairo_scaled_font_map_lock (); + if (unlikely (font_map == NULL)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + scaled_font = font_map->mru_scaled_font; + if (scaled_font != NULL && + _cairo_scaled_font_matches (scaled_font, + font_face, font_matrix, ctm, options)) + { + assert (scaled_font->hash_entry.hash != ZOMBIE); + assert (! scaled_font->placeholder); + + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { + /* We increment the reference count manually here, (rather + * than calling into cairo_scaled_font_reference), since we + * must modify the reference count while our lock is still + * held. */ + _cairo_reference_count_inc (&scaled_font->ref_count); + _cairo_scaled_font_map_unlock (); + return scaled_font; + } + + /* the font has been put into an error status - abandon the cache */ + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + scaled_font->hash_entry.hash = ZOMBIE; + dead = scaled_font; + font_map->mru_scaled_font = NULL; + + if (font_face->backend->get_implementation != NULL) { + font_face = font_face->backend->get_implementation (font_face, + font_matrix, + ctm, + options); + if (unlikely (font_face->status)) { + _cairo_scaled_font_map_unlock (); + cairo_scaled_font_destroy (scaled_font); + return _cairo_scaled_font_create_in_error (font_face->status); + } + } + + _cairo_scaled_font_init_key (&key, font_face, + font_matrix, ctm, options); + } + else + { + if (font_face->backend->get_implementation != NULL) { + font_face = font_face->backend->get_implementation (font_face, + font_matrix, + ctm, + options); + if (unlikely (font_face->status)) { + _cairo_scaled_font_map_unlock (); + return _cairo_scaled_font_create_in_error (font_face->status); + } + } + + _cairo_scaled_font_init_key (&key, font_face, + font_matrix, ctm, options); + + while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, + &key.hash_entry))) + { + if (! scaled_font->placeholder) + break; + + /* If the scaled font is being created (happens for user-font), + * just wait until it's done, then retry */ + _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font); + } + + /* Return existing scaled_font if it exists in the hash table. */ + if (scaled_font != NULL) { + /* If the original reference count is 0, then this font must have + * been found in font_map->holdovers, (which means this caching is + * actually working). So now we remove it from the holdovers + * array, unless we caught the font in the middle of destruction. + */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { + if (scaled_font->holdover) { + int i; + + for (i = 0; i < font_map->num_holdovers; i++) { + if (font_map->holdovers[i] == scaled_font) { + font_map->num_holdovers--; + memmove (&font_map->holdovers[i], + &font_map->holdovers[i+1], + (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); + break; + } + } + + scaled_font->holdover = FALSE; + } + + /* reset any error status */ + scaled_font->status = CAIRO_STATUS_SUCCESS; + } + + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { + /* We increment the reference count manually here, (rather + * than calling into cairo_scaled_font_reference), since we + * must modify the reference count while our lock is still + * held. */ + + old = font_map->mru_scaled_font; + font_map->mru_scaled_font = scaled_font; + /* increment reference count for the mru cache */ + _cairo_reference_count_inc (&scaled_font->ref_count); + /* and increment for the returned reference */ + _cairo_reference_count_inc (&scaled_font->ref_count); + _cairo_scaled_font_map_unlock (); + + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + return scaled_font; + } + + /* the font has been put into an error status - abandon the cache */ + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + scaled_font->hash_entry.hash = ZOMBIE; + } + } + + /* Otherwise create it and insert it into the hash table. */ + status = font_face->backend->scaled_font_create (font_face, font_matrix, + ctm, options, &scaled_font); + /* Did we leave the backend in an error state? */ + if (unlikely (status)) { + _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + status = _cairo_font_face_set_error (font_face, status); + return _cairo_scaled_font_create_in_error (status); + } + /* Or did we encounter an error whilst constructing the scaled font? */ + if (unlikely (scaled_font->status)) { + _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + return scaled_font; + } + + /* Our caching above is defeated if the backend switches fonts on us - + * e.g. old incarnations of toy-font-face and lazily resolved + * ft-font-faces + */ + assert (scaled_font->font_face == font_face); + + scaled_font->original_font_face = + cairo_font_face_reference (original_font_face); + + status = _cairo_hash_table_insert (font_map->hash_table, + &scaled_font->hash_entry); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + old = font_map->mru_scaled_font; + font_map->mru_scaled_font = scaled_font; + _cairo_reference_count_inc (&scaled_font->ref_count); + } + + _cairo_scaled_font_map_unlock (); + + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + if (unlikely (status)) { + /* We can't call _cairo_scaled_font_destroy here since it expects + * that the font has already been successfully inserted into the + * hash table. */ + _cairo_scaled_font_fini_internal (scaled_font); + free (scaled_font); + return _cairo_scaled_font_create_in_error (status); + } + + return scaled_font; +} +slim_hidden_def (cairo_scaled_font_create); + +static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1]; + +/* XXX This should disappear in favour of a common pool of error objects. */ +cairo_scaled_font_t * +_cairo_scaled_font_create_in_error (cairo_status_t status) +{ + cairo_scaled_font_t *scaled_font; + + assert (status != CAIRO_STATUS_SUCCESS); + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); + scaled_font = _cairo_scaled_font_nil_objects[status]; + if (unlikely (scaled_font == NULL)) { + scaled_font = malloc (sizeof (cairo_scaled_font_t)); + if (unlikely (scaled_font == NULL)) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; + } + + *scaled_font = _cairo_scaled_font_nil; + scaled_font->status = status; + _cairo_scaled_font_nil_objects[status] = scaled_font; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + + return scaled_font; +} + +void +_cairo_scaled_font_reset_static_data (void) +{ + int status; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); + for (status = CAIRO_STATUS_SUCCESS; + status <= CAIRO_STATUS_LAST_STATUS; + status++) + { + if (_cairo_scaled_font_nil_objects[status] != NULL) { + free (_cairo_scaled_font_nil_objects[status]); + _cairo_scaled_font_nil_objects[status] = NULL; + } + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (cairo_scaled_glyph_page_cache.hash_table != NULL) { + _cairo_cache_fini (&cairo_scaled_glyph_page_cache); + cairo_scaled_glyph_page_cache.hash_table = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); +} + +/** + * cairo_scaled_font_reference: + * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case + * this function does nothing) + * + * Increases the reference count on @scaled_font by one. This prevents + * @scaled_font from being destroyed until a matching call to + * cairo_scaled_font_destroy() is made. + * + * The number of references to a #cairo_scaled_font_t can be get using + * cairo_scaled_font_get_reference_count(). + * + * Returns: the referenced #cairo_scaled_font_t + **/ +cairo_scaled_font_t * +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return scaled_font; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + + _cairo_reference_count_inc (&scaled_font->ref_count); + + return scaled_font; +} +slim_hidden_def (cairo_scaled_font_reference); + +/** + * cairo_scaled_font_destroy: + * @scaled_font: a #cairo_scaled_font_t + * + * Decreases the reference count on @font by one. If the result + * is zero, then @font and all associated resources are freed. + * See cairo_scaled_font_reference(). + **/ +void +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_t *lru = NULL; + cairo_scaled_font_map_t *font_map; + + assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex)); + + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) + return; + + font_map = _cairo_scaled_font_map_lock (); + assert (font_map != NULL); + + /* Another thread may have resurrected the font whilst we waited */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { + if (! scaled_font->placeholder && + scaled_font->hash_entry.hash != ZOMBIE) + { + /* Another thread may have already inserted us into the holdovers */ + if (scaled_font->holdover) + goto unlock; + + /* Rather than immediately destroying this object, we put it into + * the font_map->holdovers array in case it will get used again + * soon (and is why we must hold the lock over the atomic op on + * the reference count). To make room for it, we do actually + * destroy the least-recently-used holdover. + */ + + if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { + lru = font_map->holdovers[0]; + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count)); + + _cairo_hash_table_remove (font_map->hash_table, + &lru->hash_entry); + + font_map->num_holdovers--; + memmove (&font_map->holdovers[0], + &font_map->holdovers[1], + font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); + } + + font_map->holdovers[font_map->num_holdovers++] = scaled_font; + scaled_font->holdover = TRUE; + } else + lru = scaled_font; + } + + unlock: + _cairo_scaled_font_map_unlock (); + + /* If we pulled an item from the holdovers array, (while the font + * map lock was held, of course), then there is no way that anyone + * else could have acquired a reference to it. So we can now + * safely call fini on it without any lock held. This is desirable + * as we never want to call into any backend function with a lock + * held. */ + if (lru != NULL) { + _cairo_scaled_font_fini_internal (lru); + free (lru); + } +} +slim_hidden_def (cairo_scaled_font_destroy); + +/** + * cairo_scaled_font_get_reference_count: + * @scaled_font: a #cairo_scaled_font_t + * + * Returns the current reference count of @scaled_font. + * + * Return value: the current reference count of @scaled_font. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count); +} + +/** + * cairo_scaled_font_get_user_data: + * @scaled_font: a #cairo_scaled_font_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @scaled_font using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +void * +cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&scaled_font->user_data, + key); +} +slim_hidden_def (cairo_scaled_font_get_user_data); + +/** + * cairo_scaled_font_set_user_data: + * @scaled_font: a #cairo_scaled_font_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_scaled_font_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @scaled_font. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return scaled_font->status; + + return _cairo_user_data_array_set_data (&scaled_font->user_data, + key, user_data, destroy); +} +slim_hidden_def (cairo_scaled_font_set_user_data); + +/* Public font API follows. */ + +/** + * cairo_scaled_font_extents: + * @scaled_font: a #cairo_scaled_font_t + * @extents: a #cairo_font_extents_t which to store the retrieved extents. + * + * Gets the metrics for a #cairo_scaled_font_t. + **/ +void +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents) +{ + if (scaled_font->status) { + extents->ascent = 0.0; + extents->descent = 0.0; + extents->height = 0.0; + extents->max_x_advance = 0.0; + extents->max_y_advance = 0.0; + return; + } + + *extents = scaled_font->extents; +} +slim_hidden_def (cairo_scaled_font_extents); + +/** + * cairo_scaled_font_text_extents: + * @scaled_font: a #cairo_scaled_font_t + * @utf8: a NUL-terminated string of text, encoded in UTF-8 + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * Gets the extents for a string of text. The extents describe a + * user-space rectangle that encloses the "inked" portion of the text + * drawn at the origin (0,0) (as it would be drawn by cairo_show_text() + * if the cairo graphics state were set to the same font_face, + * font_matrix, ctm, and font_options as @scaled_font). Additionally, + * the x_advance and y_advance values indicate the amount by which the + * current point would be advanced by cairo_show_text(). + * + * Note that whitespace characters do not directly contribute to the + * size of the rectangle (extents.width and extents.height). They do + * contribute indirectly by changing the position of non-whitespace + * characters. In particular, trailing whitespace characters are + * likely to not affect the size of the rectangle, though they will + * affect the x_advance and y_advance values. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_glyph_t *glyphs = NULL; + int num_glyphs; + + if (scaled_font->status) + goto ZERO_EXTENTS; + + if (utf8 == NULL) + goto ZERO_EXTENTS; + + status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., + utf8, -1, + &glyphs, &num_glyphs, + NULL, NULL, + NULL); + if (unlikely (status)) { + status = _cairo_scaled_font_set_error (scaled_font, status); + goto ZERO_EXTENTS; + } + + cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents); + free (glyphs); + + return; + +ZERO_EXTENTS: + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; +} + +/** + * cairo_scaled_font_glyph_extents: + * @scaled_font: a #cairo_scaled_font_t + * @glyphs: an array of glyph IDs with X and Y offsets. + * @num_glyphs: the number of glyphs in the @glyphs array + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * Gets the extents for an array of glyphs. The extents describe a + * user-space rectangle that encloses the "inked" portion of the + * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo + * graphics state were set to the same font_face, font_matrix, ctm, + * and font_options as @scaled_font). Additionally, the x_advance and + * y_advance values indicate the amount by which the current point + * would be advanced by cairo_show_glyphs(). + * + * Note that whitespace glyphs do not contribute to the size of the + * rectangle (extents.width and extents.height). + **/ +void +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + int i; + double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; + cairo_bool_t visible = FALSE; + cairo_scaled_glyph_t *scaled_glyph = NULL; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (scaled_font->status)) + goto ZERO_EXTENTS; + + if (num_glyphs == 0) + goto ZERO_EXTENTS; + + if (unlikely (num_glyphs < 0)) { + _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT); + /* XXX Can't propagate error */ + goto ZERO_EXTENTS; + } + + if (unlikely (glyphs == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); + /* XXX Can't propagate error */ + goto ZERO_EXTENTS; + } + + _cairo_scaled_font_freeze_cache (scaled_font); + + for (i = 0; i < num_glyphs; i++) { + double left, top, right, bottom; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_scaled_font_set_error (scaled_font, status); + } + goto UNLOCK; + } + + /* "Ink" extents should skip "invisible" glyphs */ + if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0) + continue; + + left = scaled_glyph->metrics.x_bearing + glyphs[i].x; + right = left + scaled_glyph->metrics.width; + top = scaled_glyph->metrics.y_bearing + glyphs[i].y; + bottom = top + scaled_glyph->metrics.height; + + if (!visible) { + visible = TRUE; + min_x = left; + max_x = right; + min_y = top; + max_y = bottom; + } else { + if (left < min_x) min_x = left; + if (right > max_x) max_x = right; + if (top < min_y) min_y = top; + if (bottom > max_y) max_y = bottom; + } + } + + if (visible) { + extents->x_bearing = min_x - glyphs[0].x; + extents->y_bearing = min_y - glyphs[0].y; + extents->width = max_x - min_x; + extents->height = max_y - min_y; + } else { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + } + + if (num_glyphs) { + double x0, y0, x1, y1; + + x0 = glyphs[0].x; + y0 = glyphs[0].y; + + /* scaled_glyph contains the glyph for num_glyphs - 1 already. */ + x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance; + y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance; + + extents->x_advance = x1 - x0; + extents->y_advance = y1 - y0; + } else { + extents->x_advance = 0.0; + extents->y_advance = 0.0; + } + + UNLOCK: + _cairo_scaled_font_thaw_cache (scaled_font); + return; + +ZERO_EXTENTS: + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; +} +slim_hidden_def (cairo_scaled_font_glyph_extents); + +#define GLYPH_LUT_SIZE 64 +static cairo_status_t +cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t *glyphs, + cairo_text_cluster_t **clusters, + int num_chars) +{ + struct glyph_lut_elt { + unsigned long index; + double x_advance; + double y_advance; + } glyph_lut[GLYPH_LUT_SIZE]; + uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE]; + cairo_status_t status; + const char *p; + int i; + + for (i = 0; i < GLYPH_LUT_SIZE; i++) + glyph_lut_unicode[i] = ~0U; + + p = utf8; + for (i = 0; i < num_chars; i++) { + int idx, num_bytes; + uint32_t unicode; + cairo_scaled_glyph_t *scaled_glyph; + struct glyph_lut_elt *glyph_slot; + + num_bytes = _cairo_utf8_get_char_validated (p, &unicode); + p += num_bytes; + + glyphs[i].x = x; + glyphs[i].y = y; + + idx = unicode % ARRAY_LENGTH (glyph_lut); + glyph_slot = &glyph_lut[idx]; + if (glyph_lut_unicode[idx] == unicode) { + glyphs[i].index = glyph_slot->index; + x += glyph_slot->x_advance; + y += glyph_slot->y_advance; + } else { + unsigned long g; + + g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); + status = _cairo_scaled_glyph_lookup (scaled_font, + g, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + + glyph_lut_unicode[idx] = unicode; + glyph_slot->index = g; + glyph_slot->x_advance = scaled_glyph->metrics.x_advance; + glyph_slot->y_advance = scaled_glyph->metrics.y_advance; + + glyphs[i].index = g; + } + + if (clusters) { + (*clusters)[i].num_bytes = num_bytes; + (*clusters)[i].num_glyphs = 1; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t *glyphs, + cairo_text_cluster_t **clusters, + int num_chars) +{ + const char *p; + int i; + + p = utf8; + for (i = 0; i < num_chars; i++) { + unsigned long g; + int num_bytes; + uint32_t unicode; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + num_bytes = _cairo_utf8_get_char_validated (p, &unicode); + p += num_bytes; + + glyphs[i].x = x; + glyphs[i].y = y; + + g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); + + /* + * No advance needed for a single character string. So, let's speed up + * one-character strings by skipping glyph lookup. + */ + if (num_chars > 1) { + status = _cairo_scaled_glyph_lookup (scaled_font, + g, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + } + + glyphs[i].index = g; + + if (clusters) { + (*clusters)[i].num_bytes = num_bytes; + (*clusters)[i].num_glyphs = 1; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_scaled_font_text_to_glyphs: + * @x: X position to place first glyph + * @y: Y position to place first glyph + * @scaled_font: a #cairo_scaled_font_t + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated + * @glyphs: pointer to array of glyphs to fill + * @num_glyphs: pointer to number of glyphs + * @clusters: pointer to array of cluster mapping information to fill, or %NULL + * @num_clusters: pointer to number of clusters, or %NULL + * @cluster_flags: pointer to location to store cluster flags corresponding to the + * output @clusters, or %NULL + * + * Converts UTF-8 text to an array of glyphs, optionally with cluster + * mapping, that can be used to render later using @scaled_font. + * + * If @glyphs initially points to a non-%NULL value, that array is used + * as a glyph buffer, and @num_glyphs should point to the number of glyph + * entries available there. If the provided glyph array is too short for + * the conversion, a new glyph array is allocated using cairo_glyph_allocate() + * and placed in @glyphs. Upon return, @num_glyphs always contains the + * number of generated glyphs. If the value @glyphs points to has changed + * after the call, the user is responsible for freeing the allocated glyph + * array using cairo_glyph_free(). This may happen even if the provided + * array was large enough. + * + * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL, + * and cluster mapping will be computed. + * The semantics of how cluster array allocation works is similar to the glyph + * array. That is, + * if @clusters initially points to a non-%NULL value, that array is used + * as a cluster buffer, and @num_clusters should point to the number of cluster + * entries available there. If the provided cluster array is too short for + * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate() + * and placed in @clusters. Upon return, @num_clusters always contains the + * number of generated clusters. If the value @clusters points at has changed + * after the call, the user is responsible for freeing the allocated cluster + * array using cairo_text_cluster_free(). This may happen even if the provided + * array was large enough. + * + * In the simplest case, @glyphs and @clusters can point to %NULL initially + * and a suitable array will be allocated. In code: + * + * cairo_status_t status; + * + * cairo_glyph_t *glyphs = NULL; + * int num_glyphs; + * cairo_text_cluster_t *clusters = NULL; + * int num_clusters; + * cairo_text_cluster_flags_t cluster_flags; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * &clusters, &num_clusters, &cluster_flags); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_text_glyphs (cr, + * utf8, utf8_len, + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); + * + * cairo_glyph_free (glyphs); + * cairo_text_cluster_free (clusters); + * } + * + * + * If no cluster mapping is needed: + * + * cairo_status_t status; + * + * cairo_glyph_t *glyphs = NULL; + * int num_glyphs; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * NULL, NULL, + * NULL); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_glyphs (cr, glyphs, num_glyphs); + * cairo_glyph_free (glyphs); + * } + * + * + * If stack-based glyph and cluster arrays are to be used for small + * arrays: + * + * cairo_status_t status; + * + * cairo_glyph_t stack_glyphs[40]; + * cairo_glyph_t *glyphs = stack_glyphs; + * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]); + * cairo_text_cluster_t stack_clusters[40]; + * cairo_text_cluster_t *clusters = stack_clusters; + * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]); + * cairo_text_cluster_flags_t cluster_flags; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * &clusters, &num_clusters, &cluster_flags); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_text_glyphs (cr, + * utf8, utf8_len, + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); + * + * if (glyphs != stack_glyphs) + * cairo_glyph_free (glyphs); + * if (clusters != stack_clusters) + * cairo_text_cluster_free (clusters); + * } + * + * + * For details of how @clusters, @num_clusters, and @cluster_flags map input + * UTF-8 text to the output glyphs see cairo_show_text_glyphs(). + * + * The output values can be readily passed to cairo_show_text_glyphs() + * cairo_show_glyphs(), or related functions, assuming that the exact + * same @scaled_font is used for the operation. + * + * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status + * if the input values are wrong or if conversion failed. If the input + * values are correct but the conversion failed, the error status is also + * set on @scaled_font. + * + * Since: 1.8 + **/ +#define CACHING_THRESHOLD 16 +cairo_status_t +cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + int num_chars = 0; + cairo_status_t status; + cairo_glyph_t *orig_glyphs; + cairo_text_cluster_t *orig_clusters; + + status = scaled_font->status; + if (unlikely (status)) + return status; + + /* A slew of sanity checks */ + + /* glyphs and num_glyphs can't be NULL */ + if (glyphs == NULL || + num_glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto BAIL; + } + + /* Special case for NULL and -1 */ + if (utf8 == NULL && utf8_len == -1) + utf8_len = 0; + + /* No NULLs for non-NULLs! */ + if ((utf8_len && utf8 == NULL) || + (clusters && num_clusters == NULL) || + (clusters && cluster_flags == NULL)) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto BAIL; + } + + /* A -1 for utf8_len means NUL-terminated */ + if (utf8_len == -1) + utf8_len = strlen (utf8); + + /* A NULL *glyphs means no prealloced glyphs array */ + if (glyphs && *glyphs == NULL) + *num_glyphs = 0; + + /* A NULL *clusters means no prealloced clusters array */ + if (clusters && *clusters == NULL) + *num_clusters = 0; + + if (!clusters && num_clusters) { + num_clusters = NULL; + } + + if (cluster_flags) { + *cluster_flags = FALSE; + } + + if (!clusters && cluster_flags) { + cluster_flags = NULL; + } + + /* Apart from that, no negatives */ + if (utf8_len < 0 || + *num_glyphs < 0 || + (num_clusters && *num_clusters < 0)) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto BAIL; + } + + if (utf8_len == 0) { + status = CAIRO_STATUS_SUCCESS; + goto BAIL; + } + + /* validate input so backend does not have to */ + status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars); + if (unlikely (status)) + goto BAIL; + + _cairo_scaled_font_freeze_cache (scaled_font); + + orig_glyphs = *glyphs; + orig_clusters = clusters ? *clusters : NULL; + + if (scaled_font->backend->text_to_glyphs) { + status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + if (status == CAIRO_STATUS_SUCCESS) { + /* The checks here are crude; we only should do them in + * user-font backend, but they don't hurt here. This stuff + * can be hard to get right. */ + + if (*num_glyphs < 0) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto DONE; + } + if (num_glyphs && *glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto DONE; + } + + if (clusters) { + if (*num_clusters < 0) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto DONE; + } + if (num_clusters && *clusters == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto DONE; + } + + /* Don't trust the backend, validate clusters! */ + status = + _cairo_validate_text_clusters (utf8, utf8_len, + *glyphs, *num_glyphs, + *clusters, *num_clusters, + *cluster_flags); + } + } + + goto DONE; + } + } + + if (*num_glyphs < num_chars) { + *glyphs = cairo_glyph_allocate (num_chars); + if (unlikely (*glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + } + *num_glyphs = num_chars; + + if (clusters) { + if (*num_clusters < num_chars) { + *clusters = cairo_text_cluster_allocate (num_chars); + if (unlikely (*clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + } + *num_clusters = num_chars; + } + + if (num_chars > CACHING_THRESHOLD) + status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font, + x, y, + utf8, + *glyphs, + clusters, + num_chars); + else + status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font, + x, y, + utf8, + *glyphs, + clusters, + num_chars); + + DONE: /* error that should be logged on scaled_font happened */ + _cairo_scaled_font_thaw_cache (scaled_font); + + if (unlikely (status)) { + *num_glyphs = 0; + if (*glyphs != orig_glyphs) { + cairo_glyph_free (*glyphs); + *glyphs = orig_glyphs; + } + + if (clusters) { + *num_clusters = 0; + if (*clusters != orig_clusters) { + cairo_text_cluster_free (*clusters); + *clusters = orig_clusters; + } + } + } + + return _cairo_scaled_font_set_error (scaled_font, status); + + BAIL: /* error with input arguments */ + + if (num_glyphs) + *num_glyphs = 0; + + if (num_clusters) + *num_clusters = 0; + + return status; +} +slim_hidden_def (cairo_scaled_font_text_to_glyphs); + +static inline cairo_bool_t +_range_contains_glyph (const cairo_box_t *extents, + cairo_fixed_t left, + cairo_fixed_t top, + cairo_fixed_t right, + cairo_fixed_t bottom) +{ + return right > extents->p1.x && + left < extents->p2.x && + bottom > extents->p1.y && + top < extents->p2.y; +} + +/* + * Compute a device-space bounding box for the glyphs. + */ +cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap_out) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }}; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_bool_t overlap = overlap_out ? FALSE : TRUE; + cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options); + int i; + + if (unlikely (scaled_font->status)) + return scaled_font->status; + + _cairo_scaled_font_freeze_cache (scaled_font); + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_fixed_t x, y, x1, y1, x2, y2; + int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } + + if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) + x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x)); + else + x = _cairo_fixed_from_double (glyphs[i].x); + x1 = x + scaled_glyph->bbox.p1.x; + x2 = x + scaled_glyph->bbox.p2.x; + + if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) + y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y)); + else + y = _cairo_fixed_from_double (glyphs[i].y); + y1 = y + scaled_glyph->bbox.p1.y; + y2 = y + scaled_glyph->bbox.p2.y; + + if (overlap == FALSE) + overlap = _range_contains_glyph (&box, x1, y1, x2, y2); + + if (x1 < box.p1.x) box.p1.x = x1; + if (x2 > box.p2.x) box.p2.x = x2; + if (y1 < box.p1.y) box.p1.y = y1; + if (y2 > box.p2.y) box.p2.y = y2; + } + + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_set_error (scaled_font, status); + + if (box.p1.x < box.p2.x) { + _cairo_box_round_to_rectangle (&box, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + + if (overlap_out != NULL) + *overlap_out = overlap; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents) +{ + double x0 = HUGE_VAL, x1 = -HUGE_VAL; + double y0 = HUGE_VAL, y1 = -HUGE_VAL; + int i; + + for (i = 0; i < num_glyphs; i++) { + double g; + + g = glyphs[i].x; + if (g < x0) x0 = g; + if (g > x1) x1 = g; + + g = glyphs[i].y; + if (g < y0) y0 = g; + if (g > y1) y1 = g; + } + + if (x0 <= x1 && y0 <= y1) { + extents->x = floor (x0 - scaled_font->extents.max_x_advance); + extents->width = ceil (x1 + scaled_font->extents.max_x_advance); + extents->width -= extents->x; + + extents->y = floor (y0 - scaled_font->extents.ascent); + extents->height = ceil (y1 + scaled_font->extents.descent); + extents->height -= extents->y; + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +cairo_status_t +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region) +{ + cairo_status_t status; + cairo_surface_t *mask = NULL; + cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */ + cairo_surface_pattern_t mask_pattern; + int i; + + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); + + if (scaled_font->status) + return scaled_font->status; + + if (!num_glyphs) + return CAIRO_STATUS_SUCCESS; + + if (scaled_font->backend->show_glyphs != NULL) { + int remaining_glyphs = num_glyphs; + status = scaled_font->backend->show_glyphs (scaled_font, + op, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs, + clip_region, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (remaining_glyphs == 0) + status = CAIRO_STATUS_SUCCESS; + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_scaled_font_set_error (scaled_font, status); + } + + /* Font display routine either does not exist or failed. */ + + _cairo_scaled_font_freeze_cache (scaled_font); + + for (i = 0; i < num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + goto CLEANUP_MASK; + + glyph_surface = scaled_glyph->surface; + + /* To start, create the mask using the format from the first + * glyph. Later we'll deal with different formats. */ + if (mask == NULL) { + mask_format = glyph_surface->format; + mask = cairo_image_surface_create (mask_format, width, height); + status = mask->status; + if (unlikely (status)) + goto CLEANUP_MASK; + } + + /* If we have glyphs of different formats, we "upgrade" the mask + * to the wider of the formats. */ + if (glyph_surface->format != mask_format && + _cairo_format_bits_per_pixel (mask_format) < + _cairo_format_bits_per_pixel (glyph_surface->format) ) + { + cairo_surface_t *new_mask; + + switch (glyph_surface->format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + mask_format = glyph_surface->format; + break; + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + mask_format = CAIRO_FORMAT_ARGB32; + break; + } + + new_mask = cairo_image_surface_create (mask_format, width, height); + status = new_mask->status; + if (unlikely (status)) { + cairo_surface_destroy (new_mask); + goto CLEANUP_MASK; + } + + _cairo_pattern_init_for_surface (&mask_pattern, mask); + /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is + * never any component alpha here. + */ + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &mask_pattern.base, + new_mask, + 0, 0, + 0, 0, + 0, 0, + width, height, + NULL); + + _cairo_pattern_fini (&mask_pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (new_mask); + goto CLEANUP_MASK; + } + + cairo_surface_destroy (mask); + mask = new_mask; + } + + if (glyph_surface->width && glyph_surface->height) { + cairo_surface_pattern_t glyph_pattern; + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (glyphs[i].y - + glyph_surface->base.device_transform.y0); + + _cairo_pattern_init_for_surface (&glyph_pattern, + &glyph_surface->base); + if (mask_format == CAIRO_FORMAT_ARGB32) + glyph_pattern.base.has_component_alpha = TRUE; + + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &glyph_pattern.base, + mask, + 0, 0, + 0, 0, + x - dest_x, y - dest_y, + glyph_surface->width, + glyph_surface->height, + NULL); + + _cairo_pattern_fini (&glyph_pattern.base); + + if (unlikely (status)) + goto CLEANUP_MASK; + } + } + + _cairo_pattern_init_for_surface (&mask_pattern, mask); + if (mask_format == CAIRO_FORMAT_ARGB32) + mask_pattern.base.has_component_alpha = TRUE; + + status = _cairo_surface_composite (op, pattern, &mask_pattern.base, + surface, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height, + clip_region); + + _cairo_pattern_fini (&mask_pattern.base); + +CLEANUP_MASK: + _cairo_scaled_font_thaw_cache (scaled_font); + + if (mask != NULL) + cairo_surface_destroy (mask); + return _cairo_scaled_font_set_error (scaled_font, status); +} + +/* Add a single-device-unit rectangle to a path. */ +static cairo_status_t +_add_unit_rectangle_to_path (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + cairo_status_t status; + + status = _cairo_path_fixed_move_to (path, x, y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (1), + _cairo_fixed_from_int (0)); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (1)); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (-1), + _cairo_fixed_from_int (0)); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +/** + * _trace_mask_to_path: + * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8) + * @path: An initialized path to hold the result + * + * Given a mask surface, (an alpha image), fill out the provided path + * so that when filled it would result in something that approximates + * the mask. + * + * Note: The current tracing code here is extremely primitive. It + * operates only on an A1 surface, (converting an A8 surface to A1 if + * necessary), and performs the tracing by drawing a little square + * around each pixel that is on in the mask. We do not pretend that + * this is a high-quality result. But we are leaving it up to someone + * who cares enough about getting a better result to implement + * something more sophisticated. + **/ +static cairo_status_t +_trace_mask_to_path (cairo_image_surface_t *mask, + cairo_path_fixed_t *path, + double tx, double ty) +{ + const uint8_t *row; + int rows, cols, bytes_per_row; + int x, y, bit; + double xoff, yoff; + cairo_fixed_t x0, y0; + cairo_fixed_t px, py; + cairo_status_t status; + + mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1); + status = mask->base.status; + if (unlikely (status)) + return status; + + cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); + x0 = _cairo_fixed_from_double (tx - xoff); + y0 = _cairo_fixed_from_double (ty - yoff); + + bytes_per_row = (mask->width + 7) / 8; + row = mask->data; + for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { + const uint8_t *byte_ptr = row; + x = 0; + py = _cairo_fixed_from_int (y); + for (cols = bytes_per_row; cols--; ) { + uint8_t byte = *byte_ptr++; + if (byte == 0) { + x += 8; + continue; + } + + byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); + for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { + if (byte & bit) { + px = _cairo_fixed_from_int (x); + status = _add_unit_rectangle_to_path (path, + px + x0, + py + y0); + if (unlikely (status)) + goto BAIL; + } + } + } + } + +BAIL: + cairo_surface_destroy (&mask->base); + + return status; +} + +cairo_status_t +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) +{ + cairo_status_t status; + int i; + + status = scaled_font->status; + if (unlikely (status)) + return status; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_path_fixed_append (path, + scaled_glyph->path, CAIRO_DIRECTION_FORWARD, + _cairo_fixed_from_double (glyphs[i].x), + _cairo_fixed_from_double (glyphs[i].y)); + + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* If the font is incapable of providing a path, then we'll + * have to trace our own from a surface. + */ + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto BAIL; + + status = _trace_mask_to_path (scaled_glyph->surface, path, + glyphs[i].x, glyphs[i].y); + } + + if (unlikely (status)) + goto BAIL; + } + BAIL: + _cairo_scaled_font_thaw_cache (scaled_font); + + return _cairo_scaled_font_set_error (scaled_font, status); +} + +/** + * _cairo_scaled_glyph_set_metrics: + * @scaled_glyph: a #cairo_scaled_glyph_t + * @scaled_font: a #cairo_scaled_font_t + * @fs_metrics: a #cairo_text_extents_t in font space + * + * _cairo_scaled_glyph_set_metrics() stores user space metrics + * for the specified glyph given font space metrics. It is + * called by the font backend when initializing a glyph with + * %CAIRO_SCALED_GLYPH_INFO_METRICS. + **/ +void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics) +{ + cairo_bool_t first = TRUE; + double hm, wm; + double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0; + double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; + double device_x_advance, device_y_advance; + + scaled_glyph->fs_metrics = *fs_metrics; + + for (hm = 0.0; hm <= 1.0; hm += 1.0) + for (wm = 0.0; wm <= 1.0; wm += 1.0) { + double x, y; + + /* Transform this corner to user space */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_point (&scaled_font->font_matrix, + &x, &y); + if (first) { + min_user_x = max_user_x = x; + min_user_y = max_user_y = y; + } else { + if (x < min_user_x) min_user_x = x; + if (x > max_user_x) max_user_x = x; + if (y < min_user_y) min_user_y = y; + if (y > max_user_y) max_user_y = y; + } + + /* Transform this corner to device space from glyph origin */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_distance (&scaled_font->scale, + &x, &y); + + if (first) { + min_device_x = max_device_x = x; + min_device_y = max_device_y = y; + } else { + if (x < min_device_x) min_device_x = x; + if (x > max_device_x) max_device_x = x; + if (y < min_device_y) min_device_y = y; + if (y > max_device_y) max_device_y = y; + } + first = FALSE; + } + scaled_glyph->metrics.x_bearing = min_user_x; + scaled_glyph->metrics.y_bearing = min_user_y; + scaled_glyph->metrics.width = max_user_x - min_user_x; + scaled_glyph->metrics.height = max_user_y - min_user_y; + + scaled_glyph->metrics.x_advance = fs_metrics->x_advance; + scaled_glyph->metrics.y_advance = fs_metrics->y_advance; + cairo_matrix_transform_distance (&scaled_font->font_matrix, + &scaled_glyph->metrics.x_advance, + &scaled_glyph->metrics.y_advance); + + device_x_advance = fs_metrics->x_advance; + device_y_advance = fs_metrics->y_advance; + cairo_matrix_transform_distance (&scaled_font->scale, + &device_x_advance, + &device_y_advance); + + scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x); + scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y); + scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x); + scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y); + + scaled_glyph->x_advance = _cairo_lround (device_x_advance); + scaled_glyph->y_advance = _cairo_lround (device_y_advance); + + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS; +} + +void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface) +{ + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + + /* sanity check the backend glyph contents */ + _cairo_debug_check_image_surface_is_defined (&surface->base); + scaled_glyph->surface = surface; + + if (surface != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE; +} + +void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path) +{ + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); + + scaled_glyph->path = path; + + if (path != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH; +} + +void +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface) +{ + if (scaled_glyph->recording_surface != NULL) { + cairo_surface_finish (scaled_glyph->recording_surface); + cairo_surface_destroy (scaled_glyph->recording_surface); + } + + scaled_glyph->recording_surface = recording_surface; + + if (recording_surface != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; +} + +static cairo_bool_t +_cairo_scaled_glyph_page_can_remove (const void *closure) +{ + const cairo_scaled_glyph_page_t *page = closure; + const cairo_scaled_font_t *scaled_font; + + scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + return scaled_font->cache_frozen == 0; +} + +static cairo_status_t +_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + cairo_status_t status; + + /* only the first page in the list may contain available slots */ + if (! cairo_list_is_empty (&scaled_font->glyph_pages)) { + page = cairo_list_last_entry (&scaled_font->glyph_pages, + cairo_scaled_glyph_page_t, + link); + if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) { + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; + } + } + + page = malloc (sizeof (cairo_scaled_glyph_page_t)); + if (unlikely (page == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + page->cache_entry.hash = (uintptr_t) scaled_font; + page->cache_entry.size = 1; /* XXX occupancy weighting? */ + page->num_glyphs = 0; + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (scaled_font->global_cache_frozen == FALSE) { + if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) { + status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, + NULL, + _cairo_scaled_glyph_page_can_remove, + _cairo_scaled_glyph_page_destroy, + MAX_GLYPH_PAGES_CACHED); + if (unlikely (status)) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + free (page); + return status; + } + } + + _cairo_cache_freeze (&cairo_scaled_glyph_page_cache); + scaled_font->global_cache_frozen = TRUE; + } + + status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache, + &page->cache_entry); + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + if (unlikely (status)) { + free (page); + return status; + } + + cairo_list_add_tail (&page->link, &scaled_font->glyph_pages); + + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + + assert (! cairo_list_is_empty (&scaled_font->glyph_pages)); + page = cairo_list_last_entry (&scaled_font->glyph_pages, + cairo_scaled_glyph_page_t, + link); + assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]); + + _cairo_scaled_glyph_fini (scaled_font, scaled_glyph); + + if (--page->num_glyphs == 0) { + _cairo_cache_remove (&cairo_scaled_glyph_page_cache, + &page->cache_entry); + } +} + +/** + * _cairo_scaled_glyph_lookup: + * @scaled_font: a #cairo_scaled_font_t + * @index: the glyph to create + * @info: a #cairo_scaled_glyph_info_t marking which portions of + * the glyph should be filled in. + * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph + * is returned. + * + * If the desired info is not available, (for example, when trying to + * get INFO_PATH with a bitmapped font), this function will return + * %CAIRO_INT_STATUS_UNSUPPORTED. + * + * Note: This function must be called with the scaled font frozen, and it must + * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled + * font was not frozen, then there is no guarantee that the glyph would not be + * evicted before you tried to access it.) See + * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache(). + * + * Returns: a glyph with the requested portions filled in. Glyph + * lookup is cached and glyph will be automatically freed along + * with the scaled_font so no explicit free is required. + * @info can be one or more of: + * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box + * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image + * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space + **/ +cairo_int_status_t +_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + cairo_scaled_glyph_info_t need_info; + + *scaled_glyph_ret = NULL; + + if (unlikely (scaled_font->status)) + return scaled_font->status; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* + * Check cache for glyph + */ + scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs, + (cairo_hash_entry_t *) &index); + if (scaled_glyph == NULL) { + status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph); + if (unlikely (status)) + goto err; + + memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t)); + _cairo_scaled_glyph_set_index (scaled_glyph, index); + + /* ask backend to initialize metrics and shape fields */ + status = + scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + info | CAIRO_SCALED_GLYPH_INFO_METRICS); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); + goto err; + } + + status = _cairo_hash_table_insert (scaled_font->glyphs, + &scaled_glyph->hash_entry); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); + goto err; + } + } + + /* + * Check and see if the glyph, as provided, + * already has the requested data and amend it if not + */ + need_info = info & ~scaled_glyph->has_info; + if (need_info) { + status = scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + need_info); + if (unlikely (status)) + goto err; + + /* Don't trust the scaled_glyph_init() return value, the font + * backend may not even know about some of the info. For example, + * no backend other than the user-fonts knows about recording-surface + * glyph info. */ + if (info & ~scaled_glyph->has_info) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *scaled_glyph_ret = scaled_glyph; + return CAIRO_STATUS_SUCCESS; + +err: + /* It's not an error for the backend to not support the info we want. */ + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_scaled_font_set_error (scaled_font, status); + return status; +} + +double +_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->max_scale; +} + + +/** + * cairo_scaled_font_get_font_face: + * @scaled_font: a #cairo_scaled_font_t + * + * Gets the font face that this scaled font uses. This is the + * font face passed to cairo_scaled_font_create(). + * + * Return value: The #cairo_font_face_t with which @scaled_font was + * created. + * + * Since: 1.2 + **/ +cairo_font_face_t * +cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->status) + return (cairo_font_face_t*) &_cairo_font_face_nil; + + if (scaled_font->original_font_face != NULL) + return scaled_font->original_font_face; + + return scaled_font->font_face; +} +slim_hidden_def (cairo_scaled_font_get_font_face); + +/** + * cairo_scaled_font_get_font_matrix: + * @scaled_font: a #cairo_scaled_font_t + * @font_matrix: return value for the matrix + * + * Stores the font matrix with which @scaled_font was created into + * @matrix. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *font_matrix) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (font_matrix); + return; + } + + *font_matrix = scaled_font->font_matrix; +} +slim_hidden_def (cairo_scaled_font_get_font_matrix); + +/** + * cairo_scaled_font_get_ctm: + * @scaled_font: a #cairo_scaled_font_t + * @ctm: return value for the CTM + * + * Stores the CTM with which @scaled_font was created into @ctm. + * Note that the translation offsets (x0, y0) of the CTM are ignored + * by cairo_scaled_font_create(). So, the matrix this + * function returns always has 0,0 as x0,y0. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *ctm) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (ctm); + return; + } + + *ctm = scaled_font->ctm; +} +slim_hidden_def (cairo_scaled_font_get_ctm); + +/** + * cairo_scaled_font_get_scale_matrix: + * @scaled_font: a #cairo_scaled_font_t + * @scale_matrix: return value for the matrix + * + * Stores the scale matrix of @scaled_font into @matrix. + * The scale matrix is product of the font matrix and the ctm + * associated with the scaled font, and hence is the matrix mapping from + * font space to device space. + * + * Since: 1.8 + **/ +void +cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *scale_matrix) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (scale_matrix); + return; + } + + *scale_matrix = scaled_font->scale; +} + +/** + * cairo_scaled_font_get_font_options: + * @scaled_font: a #cairo_scaled_font_t + * @options: return value for the font options + * + * Stores the font options with which @scaled_font was created into + * @options. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, + cairo_font_options_t *options) +{ + if (cairo_font_options_status (options)) + return; + + if (scaled_font->status) { + _cairo_font_options_init_default (options); + return; + } + + _cairo_font_options_init_copy (options, &scaled_font->options); +} +slim_hidden_def (cairo_scaled_font_get_font_options); diff --git a/libs/cairo/src/cairo-script-surface.c b/libs/cairo/src/cairo-script-surface.c new file mode 100644 index 000000000..50214aa19 --- /dev/null +++ b/libs/cairo/src/cairo-script-surface.c @@ -0,0 +1,3590 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* The script surface is one that records all operations performed on + * it in the form of a procedural script, similar in fashion to + * PostScript but using Cairo's imaging model. In essence, this is + * equivalent to the recording-surface, but as there is no impedance mismatch + * between Cairo and CairoScript, we can generate output immediately + * without having to copy and hold the data in memory. + */ + +#include "cairoint.h" + +#include "cairo-script.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-list-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-wrapper-private.h" + +#if CAIRO_HAS_FT_FONT +#include "cairo-ft-private.h" +#endif + +#include + +#ifdef WORDS_BIGENDIAN +#define to_be32(x) x +#else +#define to_be32(x) bswap_32(x) +#endif + +#define _cairo_output_stream_puts(S, STR) \ + _cairo_output_stream_write ((S), (STR), strlen (STR)) + +#define static cairo_warn static + +typedef struct _cairo_script_context cairo_script_context_t; +typedef struct _cairo_script_surface cairo_script_surface_t; +typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; +typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; + +typedef struct _operand { + enum { + SURFACE, + DEFERRED, + } type; + cairo_list_t link; +} operand_t; + + +struct deferred_finish { + cairo_list_t link; + operand_t operand; +}; + +struct _cairo_script_context { + cairo_device_t base; + + int active; + + cairo_output_stream_t *stream; + cairo_script_mode_t mode; + + struct _bitmap { + unsigned long min; + unsigned long count; + unsigned int map[64]; + struct _bitmap *next; + } surface_id, font_id; + + cairo_list_t operands; + cairo_list_t deferred; + + cairo_list_t fonts; + cairo_list_t defines; +}; + +struct _cairo_script_surface_font_private { + cairo_script_context_t *ctx; + cairo_bool_t has_sfnt; + unsigned long id; + unsigned long subset_glyph_index; + cairo_list_t link; + cairo_scaled_font_t *parent; +}; + +struct _cairo_script_implicit_context { + cairo_operator_t current_operator; + cairo_fill_rule_t current_fill_rule; + double current_tolerance; + cairo_antialias_t current_antialias; + cairo_stroke_style_t current_style; + cairo_pattern_union_t current_source; + cairo_matrix_t current_ctm; + cairo_matrix_t current_stroke_matrix; + cairo_matrix_t current_font_matrix; + cairo_font_options_t current_font_options; + cairo_scaled_font_t *current_scaled_font; + cairo_path_fixed_t current_path; + cairo_bool_t has_clip; +}; + +struct _cairo_script_surface { + cairo_surface_t base; + + cairo_surface_wrapper_t wrapper; + + cairo_surface_clipper_t clipper; + + operand_t operand; + cairo_bool_t emitted; + cairo_bool_t defined; + cairo_bool_t active; + + double width, height; + + /* implicit flattened context */ + cairo_script_implicit_context_t cr; +}; + +static const cairo_surface_backend_t _cairo_script_surface_backend; + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + double width, + double height, + cairo_surface_t *passthrough); + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); + +static void +_bitmap_release_id (struct _bitmap *b, unsigned long token) +{ + struct _bitmap **prev = NULL; + + do { + if (token < b->min + sizeof (b->map) * CHAR_BIT) { + unsigned int bit, elem; + + token -= b->min; + elem = token / (sizeof (b->map[0]) * CHAR_BIT); + bit = token % (sizeof (b->map[0]) * CHAR_BIT); + b->map[elem] &= ~(1 << bit); + if (! --b->count && prev) { + *prev = b->next; + free (b); + } + return; + } + prev = &b->next; + b = b->next; + } while (b != NULL); +} + +static cairo_status_t +_bitmap_next_id (struct _bitmap *b, + unsigned long *id) +{ + struct _bitmap *bb, **prev = NULL; + unsigned long min = 0; + + do { + if (b->min != min) + break; + + if (b->count < sizeof (b->map) * CHAR_BIT) { + unsigned int n, m, bit; + for (n = 0; n < ARRAY_LENGTH (b->map); n++) { + if (b->map[n] == (unsigned int) -1) + continue; + + for (m=0, bit=1; mmap[0])*CHAR_BIT; m++, bit<<=1) { + if ((b->map[n] & bit) == 0) { + b->map[n] |= bit; + b->count++; + *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; + return CAIRO_STATUS_SUCCESS; + } + } + } + } + min += sizeof (b->map) * CHAR_BIT; + + prev = &b->next; + b = b->next; + } while (b != NULL); + + bb = malloc (sizeof (struct _bitmap)); + if (unlikely (bb == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *prev = bb; + bb->next = b; + bb->min = min; + bb->count = 1; + bb->map[0] = 0x1; + memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); + *id = min; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_bitmap_fini (struct _bitmap *b) +{ + while (b != NULL) { + struct _bitmap *next = b->next; + free (b); + b = next; + } +} + +static const char * +_direction_to_string (cairo_bool_t backward) +{ + static const char *names[] = { + "FORWARD", + "BACKWARD" + }; + assert (backward < ARRAY_LENGTH (names)); + return names[backward]; +} + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ + "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static inline cairo_script_context_t * +to_context (cairo_script_surface_t *surface) +{ + return (cairo_script_context_t *) surface->base.device; +} + +static cairo_bool_t +target_is_active (cairo_script_surface_t *surface) +{ + return cairo_list_is_first (&surface->operand.link, + &to_context (surface)->operands); +} + +static void +target_push (cairo_script_surface_t *surface) +{ + cairo_list_move (&surface->operand.link, &to_context (surface)->operands); +} + +static int +target_depth (cairo_script_surface_t *surface) +{ + cairo_list_t *link; + int depth = 0; + + cairo_list_foreach (link, &to_context (surface)->operands) { + if (link == &surface->operand.link) + break; + depth++; + } + + return depth; +} + +static void +_get_target (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + + if (surface->defined) { + _cairo_output_stream_printf (ctx->stream, "s%u ", + surface->base.unique_id); + } else { + assert (! cairo_list_is_empty (&surface->operand.link)); + if (! target_is_active (surface)) { + int depth = target_depth (surface); + if (ctx->active) { + _cairo_output_stream_printf (ctx->stream, "%d index ", depth); + _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); + } else { + if (depth == 1) { + _cairo_output_stream_puts (ctx->stream, + "exch\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll\n", + depth); + } + _cairo_output_stream_puts (ctx->stream, "/target get "); + } + } else { + _cairo_output_stream_puts (ctx->stream, "/target get "); + } + } +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static cairo_status_t +_emit_surface (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + + _cairo_output_stream_printf (ctx->stream, + "<< /content //%s", + _content_to_string (surface->base.content)); + if (surface->width != -1 && surface->height != -1) { + _cairo_output_stream_printf (ctx->stream, + " /width %f /height %f", + surface->width, + surface->height); + } + + if (surface->base.x_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || + surface->base.y_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) + { + _cairo_output_stream_printf (ctx->stream, + " /fallback-resolution [%f %f]", + surface->base.x_fallback_resolution, + surface->base.y_fallback_resolution); + } + + if (surface->base.device_transform.x0 != 0. || + surface->base.device_transform.y0 != 0.) + { + /* XXX device offset is encoded into the pattern matrices etc. */ + if (0) { + _cairo_output_stream_printf (ctx->stream, + " /device-offset [%f %f]", + surface->base.device_transform.x0, + surface->base.device_transform.y0); + } + } + + _cairo_output_stream_puts (ctx->stream, " >> surface context\n"); + surface->emitted = TRUE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_context (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + while (! cairo_list_is_empty (&ctx->operands)) { + operand_t *op; + cairo_script_surface_t *old; + + op = cairo_list_first_entry (&ctx->operands, + operand_t, + link); + if (op->type == DEFERRED) + break; + + old = cairo_container_of (op, cairo_script_surface_t, operand); + if (old == surface) + break; + if (old->active) + break; + + if (! old->defined) { + assert (old->emitted); + _cairo_output_stream_printf (ctx->stream, + "/target get /s%u exch def pop\n", + old->base.unique_id); + old->defined = TRUE; + } else { + _cairo_output_stream_puts (ctx->stream, "pop\n"); + } + + cairo_list_del (&old->operand.link); + } + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + if (! surface->emitted) { + cairo_status_t status; + + status = _emit_surface (surface); + if (unlikely (status)) + return status; + } else if (cairo_list_is_empty (&surface->operand.link)) { + assert (surface->defined); + _cairo_output_stream_printf (ctx->stream, + "s%u context\n", + surface->base.unique_id); + _cairo_script_implicit_context_reset (&surface->cr); + _cairo_surface_clipper_reset (&surface->clipper); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_puts (ctx->stream, "exch\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll\n", + depth); + } + } + target_push (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_operator (cairo_script_surface_t *surface, + cairo_operator_t op) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_operator == op) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_operator = op; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-operator\n", + _operator_to_string (op)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_fill_rule (cairo_script_surface_t *surface, + cairo_fill_rule_t fill_rule) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_fill_rule == fill_rule) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_fill_rule = fill_rule; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-fill-rule\n", + _fill_rule_to_string (fill_rule)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_tolerance (cairo_script_surface_t *surface, + double tolerance, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && + surface->cr.current_tolerance == tolerance) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_tolerance = tolerance; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-tolerance\n", + tolerance); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_antialias (cairo_script_surface_t *surface, + cairo_antialias_t antialias) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_antialias == antialias) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_antialias = antialias; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-antialias\n", + _antialias_to_string (antialias)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_width (cairo_script_surface_t *surface, + double line_width, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && + surface->cr.current_style.line_width == line_width) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.line_width = line_width; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-line-width\n", + line_width); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_cap (cairo_script_surface_t *surface, + cairo_line_cap_t line_cap) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_cap == line_cap) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_cap = line_cap; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-line-cap\n", + _line_cap_to_string (line_cap)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_join (cairo_script_surface_t *surface, + cairo_line_join_t line_join) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_join == line_join) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_join = line_join; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-line-join\n", + _line_join_to_string (line_join)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_miter_limit (cairo_script_surface_t *surface, + double miter_limit, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && + surface->cr.current_style.miter_limit == miter_limit) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.miter_limit = miter_limit; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-miter-limit\n", + miter_limit); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_dashes_equal (const double *a, const double *b, int num_dashes) +{ + while (num_dashes--) { + if (fabs (*a - *b) > 1e-5) + return FALSE; + a++, b++; + } + + return TRUE; +} + +static cairo_status_t +_emit_dash (cairo_script_surface_t *surface, + const double *dash, + unsigned int num_dashes, + double offset, + cairo_bool_t force) +{ + unsigned int n; + + assert (target_is_active (surface)); + + if (force && + num_dashes == 0 && + surface->cr.current_style.num_dashes == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + if (! force && + (surface->cr.current_style.num_dashes == num_dashes && + (num_dashes == 0 || + (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && + _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) + { + return CAIRO_STATUS_SUCCESS; + } + + + if (num_dashes) { + surface->cr.current_style.dash = _cairo_realloc_ab + (surface->cr.current_style.dash, num_dashes, sizeof (double)); + if (unlikely (surface->cr.current_style.dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes); + } else { + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + } + + surface->cr.current_style.num_dashes = num_dashes; + surface->cr.current_style.dash_offset = offset; + + _cairo_output_stream_puts (to_context (surface)->stream, "["); + for (n = 0; n < num_dashes; n++) { + _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]); + if (n < num_dashes-1) + _cairo_output_stream_puts (to_context (surface)->stream, " "); + } + _cairo_output_stream_printf (to_context (surface)->stream, + "] %f set-dash\n", + offset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_stroke_style (cairo_script_surface_t *surface, + const cairo_stroke_style_t *style, + cairo_bool_t force) +{ + cairo_status_t status; + + assert (target_is_active (surface)); + + status = _emit_line_width (surface, style->line_width, force); + if (unlikely (status)) + return status; + + status = _emit_line_cap (surface, style->line_cap); + if (unlikely (status)) + return status; + + status = _emit_line_join (surface, style->line_join); + if (unlikely (status)) + return status; + + status = _emit_miter_limit (surface, style->miter_limit, force); + if (unlikely (status)) + return status; + + status = _emit_dash (surface, + style->dash, style->num_dashes, style->dash_offset, + force); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_format_to_string (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: return "ARGB32"; + case CAIRO_FORMAT_RGB24: return "RGB24"; + case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; + case CAIRO_FORMAT_A8: return "A8"; + case CAIRO_FORMAT_A1: return "A1"; + case CAIRO_FORMAT_INVALID: return "INVALID"; + } + ASSERT_NOT_REACHED; + return "INVALID"; +} + +static cairo_status_t +_emit_solid_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + cairo_script_context_t *ctx = to_context (surface); + + if (! CAIRO_COLOR_IS_OPAQUE (&solid->color)) + { + if (! (surface->base.content & CAIRO_CONTENT_COLOR) || + ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && + (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && + (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) + { + _cairo_output_stream_printf (ctx->stream, + "%f a", + solid->color.alpha); + } + else + { + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f rgba", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + } + } + else + { + if (solid->color.red_short == solid->color.green_short && + solid->color.red_short == solid->color.blue_short) + { + _cairo_output_stream_printf (ctx->stream, + "%f g", + solid->color.red); + } + else + { + _cairo_output_stream_printf (ctx->stream, + "%f %f %f rgb", + solid->color.red, + solid->color.green, + solid->color.blue); + } + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, + cairo_output_stream_t *output) +{ + unsigned int n; + + for (n = 0; n < gradient->n_stops; n++) { + _cairo_output_stream_printf (output, + "\n %f %f %f %f %f add-color-stop", + gradient->stops[n].offset, + gradient->stops[n].color.red, + gradient->stops[n].color.green, + gradient->stops[n].color.blue, + gradient->stops[n].color.alpha); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_linear_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_linear_pattern_t *linear; + + linear = (cairo_linear_pattern_t *) pattern; + + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f linear", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + return _emit_gradient_color_stops (&linear->base, ctx->stream); +} + +static cairo_status_t +_emit_radial_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_radial_pattern_t *radial; + + radial = (cairo_radial_pattern_t *) pattern; + + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f %f %f radial", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + return _emit_gradient_color_stops (&radial->base, ctx->stream); +} + +static cairo_status_t +_emit_recording_surface_pattern (cairo_script_surface_t *surface, + cairo_recording_surface_t *source) +{ + cairo_script_implicit_context_t old_cr; + cairo_script_surface_t *similar; + cairo_status_t status; + cairo_box_t bbox; + cairo_rectangle_int_t rect; + + /* first measure the extents */ + status = _cairo_recording_surface_get_bbox (source, &bbox, NULL); + if (unlikely (status)) + return status; + + /* convert to extents so that it matches the public api */ + _cairo_box_round_to_rectangle (&bbox, &rect); + + similar = _cairo_script_surface_create_internal (to_context (surface), + source->content, + rect.width, + rect.height, + NULL); + if (unlikely (similar->base.status)) + return similar->base.status; + + cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y); + similar->base.is_clear = TRUE; + + _get_target (surface); + _cairo_output_stream_printf (to_context (surface)->stream, + "%d %d //%s similar dup context\n", + rect.width, rect.height, + _content_to_string (source->content)); + target_push (similar); + similar->emitted = TRUE; + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_recording_surface_replay (&source->base, &similar->base); + surface->cr = old_cr; + + if (unlikely (status)) { + cairo_surface_destroy (&similar->base); + return status; + } + + cairo_list_del (&similar->operand.link); + assert (target_is_active (surface)); + + _cairo_output_stream_puts (to_context (surface)->stream, "pop "); + cairo_surface_destroy (&similar->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_script_surface_pattern (cairo_script_surface_t *surface, + cairo_script_surface_t *source) +{ + _get_target (source); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_image_surface (cairo_output_stream_t *output, + const cairo_image_surface_t *image) +{ + int stride, row, width; + uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *rowdata; + uint8_t *data; + + stride = image->stride; + width = image->width; + data = image->data; +#if WORDS_BIGENDIAN + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB16_565: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 2*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + int col; + rowdata = data; + for (col = width; col--; ) { + _cairo_output_stream_write (output, rowdata, 3); + rowdata+=4; + } + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 4*width); + data += stride; + } + break; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + break; + } +#else + if (stride > ARRAY_LENGTH (row_stack)) { + rowdata = malloc (stride); + if (unlikely (rowdata == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + rowdata = row_stack; + + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + int col; + for (col = 0; col < (width + 7)/8; col++) + rowdata[col] = CAIRO_BITSWAP8 (data[col]); + _cairo_output_stream_write (output, rowdata, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB16_565: + for (row = image->height; row--; ) { + uint16_t *src = (uint16_t *) data; + uint16_t *dst = (uint16_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_16 (src[col]); + _cairo_output_stream_write (output, rowdata, 2*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + uint8_t *src = data; + int col; + for (col = 0; col < width; col++) { + rowdata[3*col+2] = *src++; + rowdata[3*col+1] = *src++; + rowdata[3*col+0] = *src++; + src++; + } + _cairo_output_stream_write (output, rowdata, 3*width); + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + uint32_t *src = (uint32_t *) data; + uint32_t *dst = (uint32_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_32 (src[col]); + _cairo_output_stream_write (output, rowdata, 4*width); + data += stride; + } + break; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + break; + } + if (rowdata != row_stack) + free (rowdata); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_emit_png_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_output_stream_t *base85_stream; + cairo_status_t status; + const uint8_t *mime_data; + unsigned long mime_data_length; + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/mime-type (image/png) " + "/source <~", + image->width, image->height, + _format_to_string (image->format)); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> >> image "); + return CAIRO_STATUS_SUCCESS; +} + +struct def { + cairo_script_context_t *ctx; + cairo_user_data_array_t *user_data; + unsigned int tag; + cairo_list_t link; +}; + +static void +_undef (void *data) +{ + struct def *def = data; + + cairo_list_del (&def->link); + _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag); + free (def); +} + +static cairo_status_t +_emit_image_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + const uint8_t *mime_data; + unsigned long mime_data_length; + struct def *tag; + + if (_cairo_user_data_array_get_data (&image->base.user_data, + (cairo_user_data_key_t *) ctx)) + { + _cairo_output_stream_printf (ctx->stream, + "s%u ", + image->base.unique_id); + return CAIRO_STATUS_SUCCESS; + } + + status = _emit_png_surface (surface, image); + if (_cairo_status_is_error (status)) { + return status; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_image_surface_t *clone; + uint32_t len; + + if (image->format == CAIRO_FORMAT_INVALID) { + clone = _cairo_image_surface_coerce (image); + } else { + clone = (cairo_image_surface_t *) + cairo_surface_reference (&image->base); + } + + _cairo_output_stream_printf (ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/source ", + clone->width, clone->height, + _format_to_string (clone->format)); + + switch (clone->format) { + case CAIRO_FORMAT_A1: + len = (clone->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + len = clone->width; + break; + case CAIRO_FORMAT_RGB16_565: + len = clone->width * 2; + break; + case CAIRO_FORMAT_RGB24: + len = clone->width * 3; + break; + case CAIRO_FORMAT_ARGB32: + len = clone->width * 4; + break; + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + break; + } + len *= clone->height; + + if (len > 24) { + _cairo_output_stream_puts (ctx->stream, "<|"); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + + len = to_be32 (len); + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + status = _write_image_surface (zlib_stream, clone); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } else { + _cairo_output_stream_puts (ctx->stream, "<~"); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + status = _write_image_surface (base85_stream, clone); + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } + _cairo_output_stream_puts (ctx->stream, "~> >> image "); + + cairo_surface_destroy (&clone->base); + } + + tag = malloc (sizeof (*tag)); + if (unlikely (tag == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + tag->ctx = ctx; + tag->tag = image->base.unique_id; + tag->user_data = &image->base.user_data; + cairo_list_add (&tag->link, &ctx->defines); + status = _cairo_user_data_array_set_data (&image->base.user_data, + (cairo_user_data_key_t *) ctx, + tag, _undef); + if (unlikely (status)) { + free (tag); + return status; + } + + _cairo_output_stream_printf (ctx->stream, + "dup /s%u exch def ", + image->base.unique_id); + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JPEG); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JP2); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_image_surface_pattern (cairo_script_surface_t *surface, + cairo_surface_t *source) +{ + cairo_surface_t *snapshot; + cairo_image_surface_t *image; + cairo_status_t status; + void *extra; + + /* XXX keeping a copy is nasty, but we want to hook into the surface's + * lifetime. Using a snapshot is a convenient method. + */ + snapshot = _cairo_surface_snapshot (source); + status = _cairo_surface_acquire_source_image (snapshot, &image, &extra); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _emit_image_surface (surface, image); + _cairo_surface_release_source_image (snapshot, image, extra); + } + cairo_surface_destroy (snapshot); + + return status; +} + +static cairo_status_t +_emit_subsurface_pattern (cairo_script_surface_t *surface, + cairo_surface_subsurface_t *sub) +{ + cairo_surface_t *source = sub->target; + cairo_status_t status; + + switch ((int) source->backend->type) { + case CAIRO_SURFACE_TYPE_RECORDING: + status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SCRIPT: + status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); + break; + default: + status = _emit_image_surface_pattern (surface, source); + break; + } + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%d %d %d %d subsurface ", + sub->extents.x, + sub->extents.y, + sub->extents.width, + sub->extents.height); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + cairo_status_t status; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = ((cairo_surface_snapshot_t *) source)->target; + + switch ((int) source->backend->type) { + case CAIRO_SURFACE_TYPE_RECORDING: + status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SCRIPT: + status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SUBSURFACE: + status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source); + break; + default: + status = _emit_image_surface_pattern (surface, source); + break; + } + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (to_context (surface)->stream, "pattern"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_status_t status; + cairo_bool_t is_default_extend; + cairo_bool_t need_newline = TRUE; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + /* solid colors do not need filter/extend/matrix */ + return _emit_solid_pattern (surface, pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _emit_linear_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _emit_radial_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _emit_surface_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; + break; + + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pattern->matrix)) { + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " [%f %f %f %f %f %f] set-matrix\n ", + pattern->matrix.xx, pattern->matrix.yx, + pattern->matrix.xy, pattern->matrix.yy, + pattern->matrix.x0, pattern->matrix.y0); + } + + /* XXX need to discriminate the user explicitly setting the default */ + if (pattern->filter != CAIRO_FILTER_DEFAULT) { + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " //%s set-filter\n ", + _filter_to_string (pattern->filter)); + } + if (! is_default_extend ){ + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " //%s set-extend\n ", + _extend_to_string (pattern->extend)); + } + + if (need_newline) + _cairo_output_stream_puts (ctx->stream, "\n "); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_identity (cairo_script_surface_t *surface, + cairo_bool_t *matrix_updated) +{ + assert (target_is_active (surface)); + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_puts (to_context (surface)->stream, + "identity set-matrix\n"); + + *matrix_updated = TRUE; + cairo_matrix_init_identity (&surface->cr.current_ctm); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_source (cairo_script_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + assert (target_is_active (surface)); + + if (op == CAIRO_OPERATOR_CLEAR) { + /* the source is ignored, so don't change it */ + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) + return CAIRO_STATUS_SUCCESS; + + _cairo_pattern_fini (&surface->cr.current_source.base); + status = _cairo_pattern_init_copy (&surface->cr.current_source.base, + source); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_pattern (surface, source); + if (unlikely (status)) + return status; + + assert (target_is_active (surface)); + _cairo_output_stream_puts (to_context (surface)->stream, + " set-source\n"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_move_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_line_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_output_stream_printf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_close (void *closure) +{ + _cairo_output_stream_printf (closure, + " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_path (cairo_script_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_box_t box; + cairo_status_t status; + + assert (target_is_active (surface)); + assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); + + if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_fini (&surface->cr.current_path); + + _cairo_output_stream_puts (ctx->stream, "n"); + + if (path == NULL) { + _cairo_path_fixed_init (&surface->cr.current_path); + } else if (_cairo_path_fixed_is_box (path, &box)) { + double x1 = _cairo_fixed_to_double (box.p1.x); + double y1 = _cairo_fixed_to_double (box.p1.y); + double x2 = _cairo_fixed_to_double (box.p2.x); + double y2 = _cairo_fixed_to_double (box.p2.y); + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (ctx->stream, + " %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _path_move_to, + _path_line_to, + _path_curve_to, + _path_close, + ctx->stream); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_puts (ctx->stream, "\n"); + + return CAIRO_STATUS_SUCCESS; +} +static cairo_bool_t +_scaling_matrix_equal (const cairo_matrix_t *a, + const cairo_matrix_t *b) +{ + return fabs (a->xx - b->xx) < 1e-5 && + fabs (a->xy - b->xy) < 1e-5 && + fabs (a->yx - b->yx) < 1e-5 && + fabs (a->yy - b->yy) < 1e-5; +} + +static cairo_status_t +_emit_scaling_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *ctm, + cairo_bool_t *matrix_updated) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_bool_t was_identity; + assert (target_is_active (surface)); + + if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) + return CAIRO_STATUS_SUCCESS; + + was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); + + *matrix_updated = TRUE; + surface->cr.current_ctm = *ctm; + surface->cr.current_ctm.x0 = 0.; + surface->cr.current_ctm.y0 = 0.; + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { + _cairo_output_stream_puts (ctx->stream, + "identity set-matrix\n"); + } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { + _cairo_output_stream_printf (ctx->stream, + "%f %f scale\n", + ctm->xx, ctm->yy); + } else { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f 0 0] set-matrix\n", + ctm->xx, ctm->yx, + ctm->xy, ctm->yy); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_font_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *font_matrix) +{ + cairo_script_context_t *ctx = to_context (surface); + assert (target_is_active (surface)); + + if (memcmp (&surface->cr.current_font_matrix, + font_matrix, + sizeof (cairo_matrix_t)) == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_font_matrix = *font_matrix; + + if (_cairo_matrix_is_identity (font_matrix)) { + _cairo_output_stream_puts (ctx->stream, + "identity set-font-matrix\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f %f %f] set-font-matrix\n", + font_matrix->xx, font_matrix->yx, + font_matrix->xy, font_matrix->yy, + font_matrix->x0, font_matrix->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_script_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_script_surface_t *surface, *other = abstract_surface; + cairo_surface_t *passthrough = NULL; + cairo_script_context_t *ctx; + cairo_status_t status; + + ctx = to_context (other); + + status = cairo_device_acquire (&ctx->base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (! other->emitted) { + status = _emit_surface (other); + if (unlikely (status)) { + cairo_device_release (&ctx->base); + return _cairo_surface_create_in_error (status); + } + + target_push (other); + } + + if (_cairo_surface_wrapper_is_active (&other->wrapper)) { + passthrough = + _cairo_surface_wrapper_create_similar (&other->wrapper, + content, width, height); + if (unlikely (passthrough->status)) { + cairo_device_release (&ctx->base); + return passthrough; + } + } + + surface = _cairo_script_surface_create_internal (ctx, + content, + width, height, + passthrough); + cairo_surface_destroy (passthrough); + + if (unlikely (surface->base.status)) { + cairo_device_release (&ctx->base); + return &surface->base; + } + + _get_target (other); + _cairo_output_stream_printf (ctx->stream, + "%u %u //%s similar dup /s%u exch def context\n", + width, height, + _content_to_string (content), + surface->base.unique_id); + surface->emitted = TRUE; + surface->defined = TRUE; + surface->base.is_clear = TRUE; + target_push (surface); + + cairo_device_release (&ctx->base); + return &surface->base; +} + +static void +_device_flush (void *abstract_device) +{ + cairo_script_context_t *ctx = abstract_device; + cairo_status_t status; + + status = _cairo_output_stream_flush (ctx->stream); +} + +static void +_device_destroy (void *abstract_device) +{ + cairo_script_context_t *ctx = abstract_device; + cairo_status_t status; + + while (! cairo_list_is_empty (&ctx->fonts)) { + cairo_script_surface_font_private_t *font; + + font = cairo_list_first_entry (&ctx->fonts, + cairo_script_surface_font_private_t, + link); + cairo_list_del (&font->link); + if (font->parent->surface_private == font) + font->parent->surface_private = NULL; + free (font); + } + + while (! cairo_list_is_empty (&ctx->defines)) { + struct def *def = cairo_list_first_entry (&ctx->defines, + struct def, link); + + status = _cairo_user_data_array_set_data (def->user_data, + (cairo_user_data_key_t *) ctx, + NULL, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + } + + _bitmap_fini (ctx->surface_id.next); + _bitmap_fini (ctx->font_id.next); + + status = _cairo_output_stream_destroy (ctx->stream); + + free (ctx); +} + +static cairo_status_t +_cairo_script_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, + image_out, + image_extra); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_script_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); + _cairo_surface_wrapper_release_source_image (&surface->wrapper, + image, + image_extra); +} + +static cairo_status_t +_cairo_script_surface_finish (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + + _cairo_surface_wrapper_fini (&surface->wrapper); + + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + _cairo_pattern_fini (&surface->cr.current_source.base); + _cairo_path_fixed_fini (&surface->cr.current_path); + _cairo_surface_clipper_reset (&surface->clipper); + + status = cairo_device_acquire (&ctx->base); + if (unlikely (status)) + return status; + + if (surface->emitted) { + assert (! surface->active); + + if (! cairo_list_is_empty (&surface->operand.link)) { + if (! ctx->active) { + if (target_is_active (surface)) { + _cairo_output_stream_printf (ctx->stream, + "pop\n"); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_printf (ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll pop\n", + depth); + } + } + cairo_list_del (&surface->operand.link); + } else { + struct deferred_finish *link = malloc (sizeof (*link)); + if (link == NULL) { + status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + cairo_list_del (&surface->operand.link); + } else { + link->operand.type = DEFERRED; + cairo_list_swap (&link->operand.link, + &surface->operand.link); + cairo_list_add (&link->link, &ctx->deferred); + } + } + } + + if (surface->defined) { + _cairo_output_stream_printf (ctx->stream, + "/s%u undef\n", + surface->base.unique_id); + } + } + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_output_stream_flush (to_context (surface)->stream); + + cairo_device_release (&ctx->base); + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_copy_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n"); + +BAIL: + cairo_device_release (surface->base.device); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_show_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n"); + +BAIL: + cairo_device_release (surface->base.device); + return status; +} + +static cairo_status_t +_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_script_surface_t *surface = cairo_container_of (clipper, + cairo_script_surface_t, + clipper); + cairo_script_context_t *ctx = to_context (surface); + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + if (path == NULL) { + if (surface->cr.has_clip) { + _cairo_output_stream_puts (ctx->stream, "reset-clip\n"); + surface->cr.has_clip = FALSE; + } + return CAIRO_STATUS_SUCCESS; + } + + /* skip the trivial clip covering the surface extents */ + if (surface->width >=0 && surface->height >= 0 && + _cairo_path_fixed_is_box (path, &box)) + { + if (box.p1.x <= 0 && box.p1.y <= 0 && + box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + } + + if (! path->maybe_fill_region) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + } + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "clip+\n"); + surface->cr.has_clip = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +active (cairo_script_surface_t *surface) +{ + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + if (surface->active++ == 0) + to_context (surface)->active++; + + return CAIRO_STATUS_SUCCESS; +} + +static void +inactive (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_list_t sorted; + + assert (surface->active > 0); + if (--surface->active) + goto DONE; + + assert (ctx->active > 0); + if (--ctx->active) + goto DONE; + + cairo_list_init (&sorted); + while (! cairo_list_is_empty (&ctx->deferred)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&ctx->deferred, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + df->operand.type = depth; + + if (cairo_list_is_empty (&sorted)) { + cairo_list_move (&df->link, &sorted); + } else { + struct deferred_finish *pos; + + cairo_list_foreach_entry (pos, struct deferred_finish, + &sorted, + link) + { + if (df->operand.type < pos->operand.type) + break; + } + cairo_list_move_tail (&df->link, &pos->link); + } + } + + while (! cairo_list_is_empty (&sorted)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&sorted, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + if (depth == 0) { + _cairo_output_stream_printf (ctx->stream, + "pop\n"); + } else if (depth == 1) { + _cairo_output_stream_printf (ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll pop\n", + depth); + } + + cairo_list_del (&df->operand.link); + cairo_list_del (&df->link); + free (df); + } + +DONE: + cairo_device_release (surface->base.device); +} + +static cairo_int_status_t +_cairo_script_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, + "paint\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_paint (&surface->wrapper, + op, source, clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + if (_cairo_pattern_equal (source, mask)) { + _cairo_output_stream_puts (to_context (surface)->stream, "/source get"); + } else { + status = _emit_pattern (surface, mask); + if (unlikely (status)) + goto BAIL; + } + + assert (surface->cr.current_operator == op); + + _cairo_output_stream_puts (to_context (surface)->stream, + " mask\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_mask (&surface->wrapper, + op, source, mask, clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_path (surface, path); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaling_matrix (surface, ctm, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + if (_scaling_matrix_equal (&surface->cr.current_ctm, + &surface->cr.current_stroke_matrix)) + { + matrix_updated = FALSE; + } + else + { + matrix_updated = TRUE; + surface->cr.current_stroke_matrix = surface->cr.current_ctm; + } + + status = _emit_stroke_style (surface, style, matrix_updated); + if (unlikely (status)) + goto BAIL; + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + goto BAIL; + } + + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_stroke (&surface->wrapper, + op, source, path, + style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + if (! _cairo_path_fixed_is_box (path, &box)) { + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + goto BAIL; + } + + if (! path->is_rectilinear) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + goto BAIL; + } + + if (! path->maybe_fill_region) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + goto BAIL; + } + + status = _emit_path (surface, path); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_fill (&surface->wrapper, + op, source, path, + fill_rule, + tolerance, + antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_surface_t * +_cairo_script_surface_snapshot (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) + return _cairo_surface_wrapper_snapshot (&surface->wrapper); + + return NULL; +} + +static cairo_bool_t +_cairo_script_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static const char * +_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) +{ + static const char *names[] = { + "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ + "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ + "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ + "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ + "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ + }; + return names[subpixel_order]; +} +static const char * +_hint_style_to_string (cairo_hint_style_t hint_style) +{ + static const char *names[] = { + "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ + "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ + "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ + "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ + "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ + }; + return names[hint_style]; +} +static const char * +_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) +{ + static const char *names[] = { + "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ + "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ + "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ + }; + return names[hint_metrics]; +} + +static cairo_status_t +_emit_font_options (cairo_script_surface_t *surface, + cairo_font_options_t *font_options) +{ + cairo_script_context_t *ctx = to_context (surface); + + if (cairo_font_options_equal (&surface->cr.current_font_options, + font_options)) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (ctx->stream, "<<"); + + if (font_options->antialias != surface->cr.current_font_options.antialias) { + _cairo_output_stream_printf (ctx->stream, + " /antialias //%s", + _antialias_to_string (font_options->antialias)); + } + + if (font_options->subpixel_order != + surface->cr.current_font_options.subpixel_order) + { + _cairo_output_stream_printf (ctx->stream, + " /subpixel-order //%s", + _subpixel_order_to_string (font_options->subpixel_order)); + } + + if (font_options->hint_style != + surface->cr.current_font_options.hint_style) + { + _cairo_output_stream_printf (ctx->stream, + " /hint-style //%s", + _hint_style_to_string (font_options->hint_style)); + } + + if (font_options->hint_metrics != + surface->cr.current_font_options.hint_metrics) + { + _cairo_output_stream_printf (ctx->stream, + " /hint-metrics //%s", + _hint_metrics_to_string (font_options->hint_metrics)); + } + + _cairo_output_stream_printf (ctx->stream, + " >> set-font-options\n"); + + surface->cr.current_font_options = *font_options; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if (font_private != NULL) { + cairo_status_t status; + cairo_device_t *device; + + status = cairo_device_acquire (device = &font_private->ctx->base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + _cairo_output_stream_printf (font_private->ctx->stream, + "/f%lu undef /sf%lu undef\n", + font_private->id, + font_private->id); + + _bitmap_release_id (&font_private->ctx->font_id, font_private->id); + cairo_list_del (&font_private->link); + free (font_private); + + cairo_device_release (device); + } + + scaled_font->surface_private = NULL; + } +} + +static cairo_status_t +_emit_type42_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + const cairo_scaled_font_backend_t *backend; + cairo_script_surface_font_private_t *font_private; + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + unsigned int load_flags; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); + if (unlikely (status)) { + free (buf); + return status; + } + +#if CAIRO_HAS_FT_FONT + load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); +#else + load_flags = 0; +#endif + _cairo_output_stream_printf (ctx->stream, + "<< " + "/type 42 " + "/index 0 " + "/flags %d " + "/source <|", + load_flags); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + len = to_be32 (size); + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + font_private = scaled_font->surface_private; + _cairo_output_stream_printf (ctx->stream, + "~> >> font dup /f%lu exch def set-font-face", + font_private->id); + + return status; +} + +static cairo_status_t +_emit_scaled_font_init (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + + font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->ctx = ctx; + font_private->parent = scaled_font; + font_private->subset_glyph_index = 0; + font_private->has_sfnt = TRUE; + + cairo_list_add (&font_private->link, &ctx->fonts); + + status = _bitmap_next_id (&ctx->font_id, + &font_private->id); + if (unlikely (status)) { + free (font_private); + return status; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &_cairo_script_surface_backend; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_type42_font (surface, scaled_font); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + font_private->has_sfnt = FALSE; + _cairo_output_stream_printf (ctx->stream, + "dict\n" + " /type 3 set\n" + " /metrics [%f %f %f %f %f] set\n" + " /glyphs array set\n" + " font dup /f%lu exch def set-font-face", + scaled_font->fs_extents.ascent, + scaled_font->fs_extents.descent, + scaled_font->fs_extents.height, + scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.max_y_advance, + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_matrix_t matrix; + cairo_font_options_t options; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_script_surface_font_private_t *font_private; + + cairo_scaled_font_get_ctm (scaled_font, &matrix); + status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); + if (unlikely (status)) + return status; + + if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_scaled_font = scaled_font; + + if (! (scaled_font->surface_backend == NULL || + scaled_font->surface_backend == &_cairo_script_surface_backend)) + { + _cairo_scaled_font_revoke_ownership (scaled_font); + } + + font_private = scaled_font->surface_private; + if (font_private == NULL) { + cairo_scaled_font_get_font_matrix (scaled_font, &matrix); + status = _emit_font_matrix (surface, &matrix); + if (unlikely (status)) + return status; + + cairo_scaled_font_get_font_options (scaled_font, &options); + status = _emit_font_options (surface, &options); + if (unlikely (status)) + return status; + + status = _emit_scaled_font_init (surface, scaled_font); + if (unlikely (status)) + return status; + + font_private = scaled_font->surface_private; + assert (font_private != NULL); + + assert (target_is_active (surface)); + _cairo_output_stream_printf (ctx->stream, + " /scaled-font get /sf%lu exch def\n", + font_private->id); + } else { + _cairo_output_stream_printf (ctx->stream, + "sf%lu set-scaled-font\n", + font_private->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_vector (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_script_surface_font_private_t *font_private; + cairo_script_implicit_context_t old_cr; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance); + + if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f %f %f] transform\n", + scaled_font->scale_inverse.xx, + scaled_font->scale_inverse.yx, + scaled_font->scale_inverse.xy, + scaled_font->scale_inverse.yy, + scaled_font->scale_inverse.x0, + scaled_font->scale_inverse.y0); + } + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + surface->cr = old_cr; + + _cairo_output_stream_puts (ctx->stream, "} >> set\n"); + + return status; +} + +static cairo_status_t +_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n" + "%f %f translate\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing); + + status = _emit_image_surface (surface, scaled_glyph->surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "pattern "); + + if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { + _cairo_output_stream_printf (ctx->stream, + "\n [%f %f %f %f %f %f] set-matrix\n", + scaled_font->font_matrix.xx, + scaled_font->font_matrix.yx, + scaled_font->font_matrix.xy, + scaled_font->font_matrix.yy, + scaled_font->font_matrix.x0, + scaled_font->font_matrix.y0); + } + _cairo_output_stream_puts (ctx->stream, + "mask\n} >> set\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + assert (scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (to_context (surface)->stream, + "f%lu /glyphs get\n", + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyphs (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + unsigned int num_glyphs) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned int n; + cairo_bool_t have_glyph_prologue = FALSE; + + if (num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + font_private = scaled_font->surface_private; + if (font_private->has_sfnt) + return CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (n = 0; n < num_glyphs; n++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (scaled_glyph->surface_private != NULL) + continue; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_vector (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_bitmap (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (have_glyph_prologue) { + _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n"); + } + + return status; +} + +static void +to_octal (int value, char *buf, size_t size) +{ + do { + buf[--size] = '0' + (value & 7); + value >>= 3; + } while (size); +} + +static void +_emit_string_literal (cairo_script_surface_t *surface, + const char *utf8, int len) +{ + cairo_script_context_t *ctx = to_context (surface); + char c; + const char *end; + + _cairo_output_stream_puts (ctx->stream, "("); + + if (utf8 == NULL) { + end = utf8; + } else { + if (len < 0) + len = strlen (utf8); + end = utf8 + len; + } + + while (utf8 < end) { + switch ((c = *utf8++)) { + case '\n': + c = 'n'; + goto ESCAPED_CHAR; + case '\r': + c = 'r'; + goto ESCAPED_CHAR; + case '\t': + c = 't'; + goto ESCAPED_CHAR; + case '\b': + c = 'b'; + goto ESCAPED_CHAR; + case '\f': + c = 'f'; + goto ESCAPED_CHAR; + case '\\': + case '(': + case ')': +ESCAPED_CHAR: + _cairo_output_stream_printf (ctx->stream, "\\%c", c); + break; + default: + if (isprint (c) || isspace (c)) { + _cairo_output_stream_printf (ctx->stream, "%c", c); + } else { + char buf[4] = { '\\' }; + + to_octal (c, buf+1, 3); + _cairo_output_stream_write (ctx->stream, buf, 4); + } + break; + } + } + _cairo_output_stream_puts (ctx->stream, ")"); +} + +static cairo_int_status_t +_cairo_script_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t backward, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + cairo_script_surface_font_private_t *font_private; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t matrix; + cairo_status_t status; + double x, y, ix, iy; + int n; + cairo_output_stream_t *base85_stream = NULL; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaled_font (surface, scaled_font); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + goto BAIL; + + /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ + /* [cx cy [glyphs]] show_glyphs */ + + if (utf8 != NULL && clusters != NULL) { + _emit_string_literal (surface, utf8, utf8_len); + _cairo_output_stream_puts (ctx->stream, " "); + } + + matrix = surface->cr.current_ctm; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + ix = x = glyphs[0].x; + iy = y = glyphs[0].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + + _cairo_scaled_font_freeze_cache (scaled_font); + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (ctx->stream, + "[%f %f ", + ix, iy); + + for (n = 0; n < num_glyphs; n++) { + if (font_private->has_sfnt) { + if (glyphs[n].index > 256) + break; + } else { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (scaled_font); + goto BAIL; + } + + if ((long unsigned) scaled_glyph->surface_private > 256) + break; + } + } + + if (n == num_glyphs) { + _cairo_output_stream_puts (ctx->stream, "<~"); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else + _cairo_output_stream_puts (ctx->stream, "["); + + for (n = 0; n < num_glyphs; n++) { + double dx, dy; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + goto BAIL; + + if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { + if (fabs (glyphs[n].y - y) < 1e-5) { + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (ctx->stream, + "~> %f <~", glyphs[n].x - x); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else { + _cairo_output_stream_printf (ctx->stream, + " ] %f [ ", glyphs[n].x - x); + } + + x = glyphs[n].x; + } else { + ix = x = glyphs[n].x; + iy = y = glyphs[n].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (ctx->stream, + "~> %f %f <~", + ix, iy); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else { + _cairo_output_stream_printf (ctx->stream, + " ] %f %f [ ", + ix, iy); + } + } + } + if (base85_stream != NULL) { + uint8_t c; + + if (font_private->has_sfnt) + c = glyphs[n].index; + else + c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + + _cairo_output_stream_write (base85_stream, &c, 1); + } else { + if (font_private->has_sfnt) + _cairo_output_stream_printf (ctx->stream, " %lu", + glyphs[n].index); + else + _cairo_output_stream_printf (ctx->stream, " %lu", + (long unsigned) scaled_glyph->surface_private); + } + + dx = scaled_glyph->metrics.x_advance; + dy = scaled_glyph->metrics.y_advance; + cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); + x += dx; + y += dy; + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (base85_stream != NULL) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_printf (ctx->stream, "~>"); + } else { + _cairo_output_stream_puts (ctx->stream, " ]"); + } + if (unlikely (status)) + return status; + + if (utf8 != NULL && clusters != NULL) { + for (n = 0; n < num_clusters; n++) { + if (clusters[n].num_bytes > UCHAR_MAX || + clusters[n].num_glyphs > UCHAR_MAX) + { + break; + } + } + + if (n < num_clusters) { + _cairo_output_stream_puts (ctx->stream, "] [ "); + for (n = 0; n < num_clusters; n++) { + _cairo_output_stream_printf (ctx->stream, + "%d %d ", + clusters[n].num_bytes, + clusters[n].num_glyphs); + } + _cairo_output_stream_puts (ctx->stream, "]"); + } + else + { + _cairo_output_stream_puts (ctx->stream, "] <~"); + base85_stream = _cairo_base85_stream_create (ctx->stream); + for (n = 0; n < num_clusters; n++) { + uint8_t c[2]; + c[0] = clusters[n].num_bytes; + c[1] = clusters[n].num_glyphs; + _cairo_output_stream_write (base85_stream, c, 2); + } + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (ctx->stream, "~>"); + } + + _cairo_output_stream_printf (ctx->stream, + " //%s show-text-glyphs\n", + _direction_to_string (backward)); + } else { + _cairo_output_stream_puts (ctx->stream, + "] show-glyphs\n"); + } + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ + return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, + op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + backward, + scaled_font, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_bool_t +_cairo_script_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_get_extents (&surface->wrapper, + rectangle); + } + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static const cairo_surface_backend_t +_cairo_script_surface_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + _cairo_script_surface_create_similar, + _cairo_script_surface_finish, + _cairo_script_surface_acquire_source_image, + _cairo_script_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + _cairo_script_surface_copy_page, + _cairo_script_surface_show_page, + _cairo_script_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_script_surface_scaled_font_fini, + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_script_surface_paint, + _cairo_script_surface_mask, + _cairo_script_surface_stroke, + _cairo_script_surface_fill, + NULL, + + _cairo_script_surface_snapshot, + + NULL, /* is_similar */ + /* XXX need fill-stroke for passthrough */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + /* The alternate high-level text operation */ + _cairo_script_surface_has_show_text_glyphs, + _cairo_script_surface_show_text_glyphs +}; + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) +{ + cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; + cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; + _cairo_stroke_style_init (&cr->current_style); + _cairo_pattern_init_solid (&cr->current_source.solid, + CAIRO_COLOR_BLACK); + _cairo_path_fixed_init (&cr->current_path); + cairo_matrix_init_identity (&cr->current_ctm); + cairo_matrix_init_identity (&cr->current_stroke_matrix); + cairo_matrix_init_identity (&cr->current_font_matrix); + _cairo_font_options_init_default (&cr->current_font_options); + cr->current_scaled_font = NULL; + cr->has_clip = FALSE; +} + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) +{ + if (cr->current_style.dash != NULL) { + free (cr->current_style.dash); + cr->current_style.dash = NULL; + } + _cairo_pattern_fini (&cr->current_source.base); + _cairo_path_fixed_fini (&cr->current_path); + + _cairo_script_implicit_context_init (cr); +} + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + double width, + double height, + cairo_surface_t *passthrough) +{ + cairo_script_surface_t *surface; + + if (unlikely (ctx == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + surface = malloc (sizeof (cairo_script_surface_t)); + if (unlikely (surface == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_script_surface_backend, + &ctx->base, + content); + + _cairo_surface_wrapper_init (&surface->wrapper, passthrough); + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_script_surface_clipper_intersect_clip_path); + + surface->width = width; + surface->height = height; + + surface->emitted = FALSE; + surface->defined = FALSE; + surface->active = FALSE; + surface->operand.type = SURFACE; + cairo_list_init (&surface->operand.link); + + _cairo_script_implicit_context_init (&surface->cr); + + return surface; +} + +static const cairo_device_backend_t _cairo_script_device_backend = { + CAIRO_DEVICE_TYPE_SCRIPT, + + NULL, NULL, /* lock, unlock */ + + _device_flush, /* flush */ + NULL, /* finish */ + _device_destroy +}; + +static cairo_device_t * +_cairo_script_context_create_internal (cairo_output_stream_t *stream) +{ + cairo_script_context_t *ctx; + + ctx = malloc (sizeof (cairo_script_context_t)); + if (unlikely (ctx == NULL)) + return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + memset (ctx, 0, sizeof (cairo_script_context_t)); + + _cairo_device_init (&ctx->base, &_cairo_script_device_backend); + + cairo_list_init (&ctx->operands); + cairo_list_init (&ctx->deferred); + ctx->stream = stream; + ctx->mode = CAIRO_SCRIPT_MODE_ASCII; + + cairo_list_init (&ctx->fonts); + cairo_list_init (&ctx->defines); + + _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); + + return &ctx->base; +} + +cairo_device_t * +cairo_script_create (const char *filename) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_filename (filename); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_script_context_create_internal (stream); +} + +cairo_device_t * +cairo_script_create_for_stream (cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_script_context_create_internal (stream); +} + +void +cairo_script_write_comment (cairo_device_t *device, + const char *comment, + int len) +{ + cairo_script_context_t *context = (cairo_script_context_t *) device; + + if (len < 0) + len = strlen (comment); + + _cairo_output_stream_puts (context->stream, "% "); + _cairo_output_stream_write (context->stream, comment, len); + _cairo_output_stream_puts (context->stream, "\n"); +} + +void +cairo_script_set_mode (cairo_device_t *device, + cairo_script_mode_t mode) +{ + cairo_script_context_t *context = (cairo_script_context_t *) device; + + context->mode = mode; +} + +cairo_script_mode_t +cairo_script_get_mode (cairo_device_t *device) +{ + cairo_script_context_t *context = (cairo_script_context_t *) device; + + return context->mode; +} + +cairo_surface_t * +cairo_script_surface_create (cairo_device_t *device, + cairo_content_t content, + double width, + double height) +{ + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, + content, + width, height, + NULL)->base; +} + +cairo_surface_t * +cairo_script_surface_create_for_target (cairo_device_t *device, + cairo_surface_t *target) +{ + cairo_rectangle_int_t extents; + + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + + if (! _cairo_surface_get_extents (target, &extents)) + extents.width = extents.height = -1; + + return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, + target->content, + extents.width, + extents.height, + target)->base; +} + +cairo_status_t +cairo_script_from_recording_surface (cairo_device_t *device, + cairo_surface_t *recording_surface) +{ + cairo_box_t bbox; + cairo_rectangle_int_t extents; + cairo_surface_t *surface; + cairo_status_t status; + + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (device->status)) + return _cairo_error (device->status); + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + + surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, + recording_surface->content, + extents.width, + extents.height, + NULL)->base; + if (unlikely (surface->status)) + return surface->status; + + cairo_surface_set_device_offset (surface, -extents.x, -extents.y); + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + return status; +} diff --git a/libs/cairo/src/cairo-script.h b/libs/cairo/src/cairo-script.h new file mode 100644 index 000000000..a9d1540cf --- /dev/null +++ b/libs/cairo/src/cairo-script.h @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SCRIPT_H +#define CAIRO_SCRIPT_H + +#include "cairo.h" + +#if CAIRO_HAS_SCRIPT_SURFACE + +CAIRO_BEGIN_DECLS + +typedef enum { + CAIRO_SCRIPT_MODE_BINARY, + CAIRO_SCRIPT_MODE_ASCII +} cairo_script_mode_t; + +cairo_public cairo_device_t * +cairo_script_create (const char *filename); + +cairo_public cairo_device_t * +cairo_script_create_for_stream (cairo_write_func_t write_func, + void *closure); + +cairo_public void +cairo_script_write_comment (cairo_device_t *script, + const char *comment, + int len); + +cairo_public void +cairo_script_set_mode (cairo_device_t *script, + cairo_script_mode_t mode); + +cairo_public cairo_script_mode_t +cairo_script_get_mode (cairo_device_t *script); + +cairo_public cairo_surface_t * +cairo_script_surface_create (cairo_device_t *script, + cairo_content_t content, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_script_surface_create_for_target (cairo_device_t *script, + cairo_surface_t *target); + +cairo_public cairo_status_t +cairo_script_from_recording_surface (cairo_device_t *script, + cairo_surface_t *recording_surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_SCRIPT_SURFACE*/ +# error Cairo was not compiled with support for the CairoScript backend +#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ + +#endif /*CAIRO_SCRIPT_H*/ diff --git a/libs/cairo/src/cairo-skia.h b/libs/cairo/src/cairo-skia.h new file mode 100644 index 000000000..89bd2713e --- /dev/null +++ b/libs/cairo/src/cairo-skia.h @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SKIA_H +#define CAIRO_SKIA_H + +#include "cairo.h" + +#if CAIRO_HAS_SKIA_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_skia_surface_create (cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_skia_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride); + +cairo_public unsigned char * +cairo_skia_surface_get_data (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_skia_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_skia_surface_get_stride (cairo_surface_t *surface); + +cairo_public cairo_surface_t * +cairo_skia_surface_get_image (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else + +# error Cairo was not compiled with support for the Skia backend + +#endif + +#endif diff --git a/libs/cairo/src/cairo-slope-private.h b/libs/cairo/src/cairo-slope-private.h new file mode 100644 index 000000000..bccc955d3 --- /dev/null +++ b/libs/cairo/src/cairo-slope-private.h @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-slope.c b/libs/cairo/src/cairo-slope.c new file mode 100644 index 000000000..fe93d66f6 --- /dev/null +++ b/libs/cairo/src/cairo-slope.c @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-spans-private.h b/libs/cairo/src/cairo-spans-private.h new file mode 100644 index 000000000..aecc9b976 --- /dev/null +++ b/libs/cairo/src/cairo-spans-private.h @@ -0,0 +1,188 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef CAIRO_SPANS_PRIVATE_H +#define CAIRO_SPANS_PRIVATE_H +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +/* Number of bits of precision used for alpha. */ +#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8 +#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1) + +/* A structure representing an open-ended horizontal span of constant + * pixel coverage. */ +typedef struct _cairo_half_open_span { + /* The inclusive x-coordinate of the start of the span. */ + int x; + + /* The pixel coverage for the pixels to the right. */ + int coverage; +} cairo_half_open_span_t; + +/* Span renderer interface. Instances of renderers are provided by + * surfaces if they want to composite spans instead of trapezoids. */ +typedef struct _cairo_span_renderer cairo_span_renderer_t; +struct _cairo_span_renderer { + /* Private status variable. */ + cairo_status_t status; + + /* Called to destroy the renderer. */ + cairo_destroy_func_t destroy; + + /* Render the spans on row y of the destination by whatever compositing + * method is required. */ + cairo_warn cairo_status_t + (*render_rows) (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages); + + /* Called after all rows have been rendered to perform whatever + * final rendering step is required. This function is called just + * once before the renderer is destroyed. */ + cairo_status_t (*finish) (void *abstract_renderer); +}; + +/* Scan converter interface. */ +typedef struct _cairo_scan_converter cairo_scan_converter_t; +struct _cairo_scan_converter { + /* Destroy this scan converter. */ + cairo_destroy_func_t destroy; + + /* Add a single edge to the converter. */ + cairo_status_t (*add_edge) (void *abstract_converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir); + + /* Add a polygon (set of edges) to the converter. */ + cairo_status_t (*add_polygon) (void *abstract_converter, + const cairo_polygon_t *polygon); + + /* Generates coverage spans for rows for the added edges and calls + * the renderer function for each row. After generating spans the + * only valid thing to do with the converter is to destroy it. */ + cairo_status_t (*generate) (void *abstract_converter, + cairo_span_renderer_t *renderer); + + /* Private status. Read with _cairo_scan_converter_status(). */ + cairo_status_t status; +}; + +/* Scan converter constructors. */ + +cairo_private cairo_scan_converter_t * +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule); + +typedef struct _cairo_rectangular_scan_converter { + cairo_scan_converter_t base; + + int xmin, xmax; + int ymin, ymax; + + struct _cairo_rectangular_scan_converter_chunk { + struct _cairo_rectangular_scan_converter_chunk *next; + void *base; + int count; + int size; + } chunks, *tail; + char buf[CAIRO_STACK_BUFFER_SIZE]; + int num_rectangles; +} cairo_rectangular_scan_converter_t; + +cairo_private void +_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, + const cairo_box_t *box, + int dir); + +typedef struct _cairo_botor_scan_converter { + cairo_scan_converter_t base; + + cairo_box_t extents; + cairo_fill_rule_t fill_rule; + + int xmin, xmax; + + struct _cairo_botor_scan_converter_chunk { + struct _cairo_botor_scan_converter_chunk *next; + void *base; + int count; + int size; + } chunks, *tail; + char buf[CAIRO_STACK_BUFFER_SIZE]; + int num_edges; +} cairo_botor_scan_converter_t; + +cairo_private void +_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, + const cairo_box_t *extents, + cairo_fill_rule_t fill_rule); + +/* cairo-spans.c: */ + +cairo_private cairo_scan_converter_t * +_cairo_scan_converter_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_scan_converter_status (void *abstract_converter); + +cairo_private cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error); + +cairo_private cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer); + +/* Set the renderer into an error state. This sets all the method + * pointers except ->destroy() of the renderer to no-op + * implementations that just return the error status. */ +cairo_private cairo_status_t +_cairo_span_renderer_set_error (void *abstract_renderer, + cairo_status_t error); + +cairo_private cairo_status_t +_cairo_surface_composite_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_polygon_t *polygon, + cairo_region_t *clip_region); + +#endif /* CAIRO_SPANS_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-spans.c b/libs/cairo/src/cairo-spans.c new file mode 100644 index 000000000..f556d6b2a --- /dev/null +++ b/libs/cairo/src/cairo-spans.c @@ -0,0 +1,322 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-fixed-private.h" + +static cairo_scan_converter_t * +_create_scan_converter (cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + if (antialias == CAIRO_ANTIALIAS_NONE) { + ASSERT_NOT_REACHED; + return NULL; + } + + return _cairo_tor_scan_converter_create (rects->bounded.x, + rects->bounded.y, + rects->bounded.x + rects->bounded.width, + rects->bounded.y + rects->bounded.height, + fill_rule); +} + +/* XXX Add me to the compositor interface. Ok, first create the compositor + * interface, and then add this with associated fallback! + */ +cairo_status_t +_cairo_surface_composite_polygon (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_polygon_t *polygon, + cairo_region_t *clip_region) +{ + cairo_span_renderer_t *renderer; + cairo_scan_converter_t *converter; + cairo_status_t status; + + converter = _create_scan_converter (fill_rule, antialias, rects); + status = converter->add_polygon (converter, polygon); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + renderer = _cairo_surface_create_span_renderer (op, pattern, surface, + antialias, rects, + clip_region); + status = converter->generate (converter, renderer); + if (unlikely (status)) + goto CLEANUP_RENDERER; + + status = renderer->finish (renderer); + + CLEANUP_RENDERER: + renderer->destroy (renderer); + CLEANUP_CONVERTER: + converter->destroy (converter); + return status; +} + +static void +_cairo_nil_destroy (void *abstract) +{ + (void) abstract; +} + +static cairo_status_t +_cairo_nil_scan_converter_add_polygon (void *abstract_converter, + const cairo_polygon_t *polygon) +{ + (void) abstract_converter; + (void) polygon; + return _cairo_scan_converter_status (abstract_converter); +} + +static cairo_status_t +_cairo_nil_scan_converter_add_edge (void *abstract_converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + (void) abstract_converter; + (void) p1; + (void) p2; + (void) top; + (void) bottom; + (void) dir; + return _cairo_scan_converter_status (abstract_converter); +} + +static cairo_status_t +_cairo_nil_scan_converter_generate (void *abstract_converter, + cairo_span_renderer_t *renderer) +{ + (void) abstract_converter; + (void) renderer; + return _cairo_scan_converter_status (abstract_converter); +} + +cairo_status_t +_cairo_scan_converter_status (void *abstract_converter) +{ + cairo_scan_converter_t *converter = abstract_converter; + return converter->status; +} + +cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error) +{ + cairo_scan_converter_t *converter = abstract_converter; + if (error == CAIRO_STATUS_SUCCESS) + ASSERT_NOT_REACHED; + if (converter->status == CAIRO_STATUS_SUCCESS) { + converter->add_polygon = _cairo_nil_scan_converter_add_polygon; + converter->add_edge = _cairo_nil_scan_converter_add_edge; + converter->generate = _cairo_nil_scan_converter_generate; + converter->status = error; + } + return converter->status; +} + +static void +_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter, + cairo_status_t status) +{ + converter->destroy = _cairo_nil_destroy; + converter->status = CAIRO_STATUS_SUCCESS; + status = _cairo_scan_converter_set_error (converter, status); +} + +cairo_scan_converter_t * +_cairo_scan_converter_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_scan_converter_t nil;\ + _cairo_nil_scan_converter_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; + default: + break; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} + +static cairo_status_t +_cairo_nil_span_renderer_render_rows ( + void *abstract_renderer, + int y, + int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages) +{ + (void) y; + (void) height; + (void) coverages; + (void) num_coverages; + return _cairo_span_renderer_status (abstract_renderer); +} + +static cairo_status_t +_cairo_nil_span_renderer_finish (void *abstract_renderer) +{ + return _cairo_span_renderer_status (abstract_renderer); +} + +cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + return renderer->status; +} + +cairo_status_t +_cairo_span_renderer_set_error ( + void *abstract_renderer, + cairo_status_t error) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + if (error == CAIRO_STATUS_SUCCESS) { + ASSERT_NOT_REACHED; + } + if (renderer->status == CAIRO_STATUS_SUCCESS) { + renderer->render_rows = _cairo_nil_span_renderer_render_rows; + renderer->finish = _cairo_nil_span_renderer_finish; + renderer->status = error; + } + return renderer->status; +} + +static void +_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer, + cairo_status_t status) +{ + renderer->destroy = _cairo_nil_destroy; + renderer->status = CAIRO_STATUS_SUCCESS; + status = _cairo_span_renderer_set_error (renderer, status); +} + +cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_span_renderer_t nil;\ + _cairo_nil_span_renderer_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; + default: + break; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} diff --git a/libs/cairo/src/cairo-spline.c b/libs/cairo/src/cairo-spline.c new file mode 100644 index 000000000..2fc99dfcb --- /dev/null +++ b/libs/cairo/src/cairo-spline.c @@ -0,0 +1,339 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-slope-private.h" + +cairo_bool_t +_cairo_spline_init (cairo_spline_t *spline, + cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *a, const cairo_point_t *b, + const cairo_point_t *c, const cairo_point_t *d) +{ + spline->add_point_func = add_point_func; + spline->closure = closure; + + spline->knots.a = *a; + spline->knots.b = *b; + spline->knots.c = *c; + spline->knots.d = *d; + + if (a->x != b->x || a->y != b->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); + else if (a->x != c->x || a->y != c->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); + else if (a->x != d->x || a->y != d->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); + else + return FALSE; + + if (c->x != d->x || c->y != d->y) + _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); + else if (b->x != d->x || b->y != d->y) + _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); + else + _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d); + + return TRUE; +} + +static cairo_status_t +_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point) +{ + cairo_point_t *prev; + + prev = &spline->last_point; + if (prev->x == point->x && prev->y == point->y) + return CAIRO_STATUS_SUCCESS; + + spline->last_point = *point; + return spline->add_point_func (spline->closure, point); +} + +static void +_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result) +{ + result->x = a->x + ((b->x - a->x) >> 1); + result->y = a->y + ((b->y - a->y) >> 1); +} + +static void +_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2) +{ + cairo_point_t ab, bc, cd; + cairo_point_t abbc, bccd; + cairo_point_t final; + + _lerp_half (&s1->a, &s1->b, &ab); + _lerp_half (&s1->b, &s1->c, &bc); + _lerp_half (&s1->c, &s1->d, &cd); + _lerp_half (&ab, &bc, &abbc); + _lerp_half (&bc, &cd, &bccd); + _lerp_half (&abbc, &bccd, &final); + + s2->a = final; + s2->b = bccd; + s2->c = cd; + s2->d = s1->d; + + s1->b = ab; + s1->c = abbc; + s1->d = final; +} + +/* Return an upper bound on the error (squared) that could result from + * approximating a spline as a line segment connecting the two endpoints. */ +static double +_cairo_spline_error_squared (const cairo_spline_knots_t *knots) +{ + double bdx, bdy, berr; + double cdx, cdy, cerr; + + /* We are going to compute the distance (squared) between each of the the b + * and c control points and the segment a-b. The maximum of these two + * distances will be our approximation error. */ + + bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); + bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); + + cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x); + cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); + + if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { + /* Intersection point (px): + * px = p1 + u(p2 - p1) + * (p - px) ∙ (p2 - p1) = 0 + * Thus: + * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; + */ + + double dx, dy, u, v; + + dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); + dy = _cairo_fixed_to_double (knots->d.y - knots->a.y); + v = dx * dx + dy * dy; + + u = bdx * dx + bdy * dy; + if (u <= 0) { + /* bdx -= 0; + * bdy -= 0; + */ + } else if (u >= v) { + bdx -= dx; + bdy -= dy; + } else { + bdx -= u/v * dx; + bdy -= u/v * dy; + } + + u = cdx * dx + cdy * dy; + if (u <= 0) { + /* cdx -= 0; + * cdy -= 0; + */ + } else if (u >= v) { + cdx -= dx; + cdy -= dy; + } else { + cdx -= u/v * dx; + cdy -= u/v * dy; + } + } + + berr = bdx * bdx + bdy * bdy; + cerr = cdx * cdx + cdy * cdy; + if (berr > cerr) + return berr; + else + return cerr; +} + +static cairo_status_t +_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result) +{ + cairo_spline_knots_t s2; + cairo_status_t status; + + if (_cairo_spline_error_squared (s1) < tolerance_squared) { + return _cairo_spline_add_point (result, &s1->a); + } + + _de_casteljau (s1, &s2); + + status = _cairo_spline_decompose_into (s1, tolerance_squared, result); + if (unlikely (status)) { + return status; + } + + status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); + return status; +} + +cairo_status_t +_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) +{ + cairo_spline_knots_t s1; + cairo_status_t status; + + s1 = spline->knots; + spline->last_point = s1.a; + status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); + if (unlikely (status)) + return status; + + return _cairo_spline_add_point (spline, &spline->knots.d); +} + +/* Note: this function is only good for computing bounds in device space. */ +cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3) +{ + double x0, x1, x2, x3; + double y0, y1, y2, y3; + double a, b, c; + double t[4]; + int t_num = 0, i; + cairo_status_t status; + + x0 = _cairo_fixed_to_double (p0->x); + y0 = _cairo_fixed_to_double (p0->y); + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + + /* The spline can be written as a polynomial of the four points: + * + * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 + * + * for 0≤t≤1. Now, the X and Y components of the spline follow the + * same polynomial but with x and y replaced for p. To find the + * bounds of the spline, we just need to find the X and Y bounds. + * To find the bound, we take the derivative and equal it to zero, + * and solve to find the t's that give the extreme points. + * + * Here is the derivative of the curve, sorted on t: + * + * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 + * + * Let: + * + * a = -p0+3p1-3p2+p3 + * b = p0-2p1+p2 + * c = -p0+p1 + * + * Gives: + * + * a.t² + 2b.t + c = 0 + * + * With: + * + * delta = b*b - a*c + * + * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if + * delta is positive, and at -b/a if delta is zero. + */ + +#define ADD(t0) \ + { \ + double _t0 = (t0); \ + if (0 < _t0 && _t0 < 1) \ + t[t_num++] = _t0; \ + } + +#define FIND_EXTREMES(a,b,c) \ + { \ + if (a == 0) { \ + if (b != 0) \ + ADD (-c / (2*b)); \ + } else { \ + double b2 = b * b; \ + double delta = b2 - a * c; \ + if (delta > 0) { \ + cairo_bool_t feasible; \ + double _2ab = 2 * a * b; \ + /* We are only interested in solutions t that satisfy 0= 0) \ + feasible = delta > b2 && delta < a*a + b2 + _2ab; \ + else if (-b / a >= 1) \ + feasible = delta < b2 && delta > a*a + b2 + _2ab; \ + else \ + feasible = delta < b2 || delta < a*a + b2 + _2ab; \ + \ + if (unlikely (feasible)) { \ + double sqrt_delta = sqrt (delta); \ + ADD ((-b - sqrt_delta) / a); \ + ADD ((-b + sqrt_delta) / a); \ + } \ + } else if (delta == 0) { \ + ADD (-b / a); \ + } \ + } \ + } + + /* Find X extremes */ + a = -x0 + 3*x1 - 3*x2 + x3; + b = x0 - 2*x1 + x2; + c = -x0 + x1; + FIND_EXTREMES (a, b, c); + + /* Find Y extremes */ + a = -y0 + 3*y1 - 3*y2 + y3; + b = y0 - 2*y1 + y2; + c = -y0 + y1; + FIND_EXTREMES (a, b, c); + + status = add_point_func (closure, p0); + if (unlikely (status)) + return status; + + for (i = 0; i < t_num; i++) { + cairo_point_t p; + double x, y; + double t_1_0, t_0_1; + double t_2_0, t_0_2; + double t_3_0, t_2_1_3, t_1_2_3, t_0_3; + + t_1_0 = t[i]; /* t */ + t_0_1 = 1 - t_1_0; /* (1 - t) */ + + t_2_0 = t_1_0 * t_1_0; /* t * t */ + t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ + + t_3_0 = t_2_0 * t_1_0; /* t * t * t */ + t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ + t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ + t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ + + /* Bezier polynomial */ + x = x0 * t_0_3 + + x1 * t_1_2_3 + + x2 * t_2_1_3 + + x3 * t_3_0; + y = y0 * t_0_3 + + y1 * t_1_2_3 + + y2 * t_2_1_3 + + y3 * t_3_0; + + p.x = _cairo_fixed_from_double (x); + p.y = _cairo_fixed_from_double (y); + status = add_point_func (closure, &p); + if (unlikely (status)) + return status; + } + + return add_point_func (closure, p3); +} diff --git a/libs/cairo/src/cairo-stroke-style.c b/libs/cairo/src/cairo-stroke-style.c new file mode 100644 index 000000000..e068e9dec --- /dev/null +++ b/libs/cairo/src/cairo-stroke-style.c @@ -0,0 +1,279 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +void +_cairo_stroke_style_init (cairo_stroke_style_t *style) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + + style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; + style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; + style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; + style->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; + + style->dash = NULL; + style->num_dashes = 0; + style->dash_offset = 0.0; +} + +cairo_status_t +_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, + const cairo_stroke_style_t *other) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + + style->line_width = other->line_width; + style->line_cap = other->line_cap; + style->line_join = other->line_join; + style->miter_limit = other->miter_limit; + + style->num_dashes = other->num_dashes; + + if (other->dash == NULL) { + style->dash = NULL; + } else { + style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double)); + if (unlikely (style->dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (style->dash, other->dash, + style->num_dashes * sizeof (double)); + } + + style->dash_offset = other->dash_offset; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_stroke_style_fini (cairo_stroke_style_t *style) +{ + if (style->dash) { + free (style->dash); + style->dash = NULL; + } + style->num_dashes = 0; + + VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t))); +} + +/* + * For a stroke in the given style, compute the maximum distance + * from the path that vertices could be generated. In the case + * of rotation in the ctm, the distance will not be exact. + */ +void +_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double *dx, double *dy) +{ + double style_expansion = 0.5; + + if (style->line_cap == CAIRO_LINE_CAP_SQUARE) + style_expansion = M_SQRT1_2; + + if (style->line_join == CAIRO_LINE_JOIN_MITER && + style_expansion < M_SQRT2 * style->miter_limit) + { + style_expansion = M_SQRT2 * style->miter_limit; + } + + style_expansion *= style->line_width; + + *dx = style_expansion * hypot (ctm->xx, ctm->xy); + *dy = style_expansion * hypot (ctm->yy, ctm->yx); +} + +/* + * Computes the period of a dashed stroke style. + * Returns 0 for non-dashed styles. + */ +double +_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style) +{ + double period; + unsigned int i; + + period = 0.0; + for (i = 0; i < style->num_dashes; i++) + period += style->dash[i]; + + if (style->num_dashes & 1) + period *= 2.0; + + return period; +} + +/* + * Coefficient of the linear approximation (minimizing square difference) + * of the surface covered by round caps + * + * This can be computed in the following way: + * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is: + * f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2) + * The square difference to a generic linear approximation (c*d) in the range (0,w) would be: + * integrate ((f(w,d) - c*d)^2, d, 0, w) + * To minimize this difference it is sufficient to find a solution of the differential with + * respect to c: + * solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c) + * Which leads to c = 9/32*pi*w + * Since we're not interested in the true area, but just in a coverage extimate, + * we always divide the real area by the line width (w). + * The same computation for square caps would be + * f(w,d) = 2 * integrate(w/2, x, -d/2, d/2) + * c = 1*w + * but in this case it would not be an approximation, since f is already linear in d. + */ +#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32) + +/* + * Computes the length of the "on" part of a dashed stroke style, + * taking into account also line caps. + * Returns 0 for non-dashed styles. + */ +double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) +{ + double stroked, cap_scale; + unsigned int i; + + switch (style->line_cap) { + default: ASSERT_NOT_REACHED; + case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; + case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; + case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; + } + + stroked = 0.0; + if (style->num_dashes & 1) { + /* Each dash element is used both as on and as off. The order in which they are summed is + * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */ + for (i = 0; i < style->num_dashes; i++) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width); + } else { + /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus + * their coverage is approximated based on the area covered by the caps of adjacent on dases. */ + for (i = 0; i < style->num_dashes; i+=2) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); + } + + return stroked; +} + +/* + * Verifies if _cairo_stroke_style_dash_approximate should be used to generate + * an approximation of the dash pattern in the specified style, when used for + * stroking a path with the given CTM and tolerance. + * Always %FALSE for non-dashed styles. + */ +cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance) +{ + double period; + + if (! style->num_dashes) + return FALSE; + + period = _cairo_stroke_style_dash_period (style); + return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance; +} + +/* + * Create a 2-dashes approximation of a dashed style, by making the "on" and "off" + * parts respect the original ratio. + */ +void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes) +{ + double coverage, scale, offset; + cairo_bool_t on = TRUE; + unsigned int i = 0; + + coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style); + coverage = MIN (coverage, 1.0); + scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0); + + /* We stop searching for a starting point as soon as the + * offset reaches zero. Otherwise when an initial dash + * segment shrinks to zero it will be skipped over. */ + offset = style->dash_offset; + while (offset > 0.0 && offset >= style->dash[i]) { + offset -= style->dash[i]; + on = !on; + if (++i == style->num_dashes) + i = 0; + } + + *num_dashes = 2; + + /* + * We want to create a new dash pattern with the same relative coverage, + * but composed of just 2 elements with total length equal to scale. + * Based on the formula in _cairo_stroke_style_dash_stroked: + * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width) + * = MIN (dashes[0] + cap_scale * (scale - dashes[0]), + * dashes[0] + cap_scale * line_width) = + * = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale, + * dashes[0] + cap_scale * line_width) + * + * Solving both cases we get: + * dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale) + * when scale - dashes[0] <= line_width + * dashes[0] = scale * coverage - cap_scale * line_width + * when scale - dashes[0] > line_width. + * + * Comparing the two cases we get: + * second > first + * second > scale * (coverage - cap_scale) / (1 - cap_scale) + * second - cap_scale * second - scale * coverage + scale * cap_scale > 0 + * (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0 + * - line_width - second + scale > 0 + * scale - second > line_width + * which is the condition for the second solution to be the valid one. + * So when second > first, the second solution is the correct one (i.e. + * the solution is always MAX (first, second). + */ + switch (style->line_cap) { + default: + ASSERT_NOT_REACHED; + dashes[0] = 0.0; + break; + + case CAIRO_LINE_CAP_BUTT: + /* Simplified formula (substituting 0 for cap_scale): */ + dashes[0] = scale * coverage; + break; + + case CAIRO_LINE_CAP_ROUND: + dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION), + scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width); + break; + + case CAIRO_LINE_CAP_SQUARE: + /* + * Special attention is needed to handle the case cap_scale == 1 (since the first solution + * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using + * 0 as first solution always leads to the correct solution. + */ + dashes[0] = MAX(0.0, scale * coverage - style->line_width); + break; + } + + dashes[1] = scale - dashes[0]; + + *dash_offset = on ? 0.0 : dashes[0]; +} diff --git a/libs/cairo/src/cairo-supported-features.h b/libs/cairo/src/cairo-supported-features.h new file mode 100644 index 000000000..aacbab781 --- /dev/null +++ b/libs/cairo/src/cairo-supported-features.h @@ -0,0 +1,25 @@ +/* Generated by configure. Do not edit. */ +#ifndef CAIRO_SUPPORTED_FEATURES_H +#define CAIRO_SUPPORTED_FEATURES_H + +/* This is a dummy header, to trick gtk-doc only */ + +#define CAIRO_HAS_XLIB_SURFACE 1 +#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 +#define CAIRO_HAS_QUARTZ_SURFACE 1 +#define CAIRO_HAS_QUARTZ_FONT 1 +#define CAIRO_HAS_WIN32_SURFACE 1 +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_PNG_FUNCTIONS 1 +#define CAIRO_HAS_EGL_FUNCTIONS 1 +#define CAIRO_HAS_GLX_FUNCTIONS 1 +#define CAIRO_HAS_FT_FONT 1 +#define CAIRO_HAS_FC_FONT 1 +#define CAIRO_HAS_PS_SURFACE 1 +#define CAIRO_HAS_PDF_SURFACE 1 +#define CAIRO_HAS_SVG_SURFACE 1 +#define CAIRO_HAS_IMAGE_SURFACE 1 +#define CAIRO_HAS_META_SURFACE 1 +#define CAIRO_HAS_USER_FONT 1 + +#endif diff --git a/libs/cairo/src/cairo-surface-clipper-private.h b/libs/cairo/src/cairo-surface-clipper-private.h new file mode 100644 index 000000000..d8750e642 --- /dev/null +++ b/libs/cairo/src/cairo-surface-clipper-private.h @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H +#define CAIRO_SURFACE_CLIPPER_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-clip-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_surface_clipper cairo_surface_clipper_t; + +typedef cairo_status_t +(*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *, + cairo_path_fixed_t *, + cairo_fill_rule_t, + double, + cairo_antialias_t); +struct _cairo_surface_clipper { + cairo_clip_t clip; + cairo_bool_t is_clipped; + cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path; +}; + +cairo_private cairo_status_t +_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + cairo_clip_t *clip); + +cairo_private void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t intersect); + +cairo_private void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper); + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-clipper.c b/libs/cairo/src/cairo-surface-clipper.c new file mode 100644 index 000000000..21c6fb1d5 --- /dev/null +++ b/libs/cairo/src/cairo-surface-clipper.c @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-surface-clipper-private.h" + +/* A collection of routines to facilitate vector surface clipping */ + +static cairo_status_t +_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, + cairo_clip_path_t *clip_path) +{ + cairo_status_t status; + + if (clip_path->prev != NULL) { + status = + _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip_path->prev); + if (unlikely (status)) + return status; + } + + return clipper->intersect_clip_path (clipper, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias); +} + +cairo_status_t +_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_bool_t clear; + + /* XXX as we cache a reference to the path, and compare every time, + * we may in future need to install a notification if the clip->path + * is every modified (e.g. cairo_clip_translate). + */ + + if (clip == NULL && clipper->clip.path == NULL) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL && clipper->clip.path != NULL && + _cairo_clip_equal (clip, &clipper->clip)) + { + return CAIRO_STATUS_SUCCESS; + } + + /* all clipped out state should never propagate this far */ + assert (clip == NULL || clip->path != NULL); + + /* Check whether this clip is a continuation of the previous. + * If not, we have to remove the current clip and rebuild. + */ + clear = clip == NULL || clip->path->prev != clipper->clip.path; + + _cairo_clip_reset (&clipper->clip); + _cairo_clip_init_copy (&clipper->clip, clip); + + if (clear) { + clipper->is_clipped = FALSE; + status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); + if (unlikely (status)) + return status; + + if (clip != NULL && clip->path != NULL) { + status = + _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip->path); + clipper->is_clipped = TRUE; + } + } else { + cairo_clip_path_t *path = clip->path; + + clipper->is_clipped = TRUE; + status = clipper->intersect_clip_path (clipper, + &path->path, + path->fill_rule, + path->tolerance, + path->antialias); + } + + return status; +} + +void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t func) +{ + _cairo_clip_init (&clipper->clip); + clipper->is_clipped = FALSE; + clipper->intersect_clip_path = func; +} + +void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) +{ + _cairo_clip_reset (&clipper->clip); + clipper->is_clipped = FALSE; +} diff --git a/libs/cairo/src/cairo-surface-fallback-private.h b/libs/cairo/src/cairo-surface-fallback-private.h new file mode 100644 index 000000000..3c6324815 --- /dev/null +++ b/libs/cairo/src/cairo-surface-fallback-private.h @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H +#define CAIRO_SURFACE_FALLBACK_PRIVATE_H + +#include "cairoint.h" + +cairo_private cairo_status_t +_cairo_surface_fallback_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fallback_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fallback_stroke (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fallback_fill (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_surface_fallback_snapshot (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_surface_fallback_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects); + +cairo_private cairo_status_t +_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_fallback_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out); + +#endif diff --git a/libs/cairo/src/cairo-surface-fallback.c b/libs/cairo/src/cairo-surface-fallback.c new file mode 100644 index 000000000..09ab644a7 --- /dev/null +++ b/libs/cairo/src/cairo-surface-fallback.c @@ -0,0 +1,1602 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-spans-private.h" +#include "cairo-surface-fallback-private.h" + +typedef struct { + cairo_surface_t *dst; + cairo_rectangle_int_t extents; + cairo_image_surface_t *image; + cairo_rectangle_int_t image_rect; + void *image_extra; +} fallback_state_t; + +/** + * _fallback_init: + * + * Acquire destination image surface needed for an image-based + * fallback. + * + * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not + * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all + * went well, or some error status otherwise. + **/ +static cairo_int_status_t +_fallback_init (fallback_state_t *state, + cairo_surface_t *dst, + int x, + int y, + int width, + int height) +{ + cairo_status_t status; + + state->extents.x = x; + state->extents.y = y; + state->extents.width = width; + state->extents.height = height; + + state->dst = dst; + + status = _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, + &state->image_extra); + if (unlikely (status)) + return status; + + + /* XXX: This NULL value tucked away in state->image is a rather + * ugly interface. Cleaner would be to push the + * CAIRO_INT_STATUS_NOTHING_TO_DO value down into + * _cairo_surface_acquire_dest_image and its backend + * counterparts. */ + assert (state->image != NULL); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_fallback_fini (fallback_state_t *state) +{ + _cairo_surface_release_dest_image (state->dst, &state->extents, + state->image, &state->image_rect, + state->image_extra); +} + +typedef cairo_status_t +(*cairo_draw_func_t) (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); + +static cairo_status_t +_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, + cairo_clip_t *clip, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *mask; + cairo_region_t *clip_region = NULL, *fallback_region = NULL; + cairo_status_t status; + cairo_bool_t clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with + * a mask (as called via _cairo_surface_mask) triggers assertion failures. + */ + mask = _cairo_surface_create_similar_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + if (unlikely (mask->status)) + return mask->status; + + if (clip_region && (extents->x || extents->y)) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto CLEANUP_SURFACE; + + cairo_region_translate (fallback_region, + -extents->x, + -extents->y); + clip_region = fallback_region; + } + + status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, mask, + extents->x, extents->y, + extents, + clip_region); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + if (clip_surface) + status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); + + _cairo_pattern_init_for_surface (mask_pattern, mask); + + CLEANUP_SURFACE: + if (fallback_region) + cairo_region_destroy (fallback_region); + cairo_surface_destroy (mask); + + return status; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t mask_pattern; + cairo_status_t status; + + status = _create_composite_mask_pattern (&mask_pattern, + clip, + draw_func, draw_closure, + dst, extents); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_surface_composite (op, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + + _cairo_pattern_fini (&mask_pattern.base); + } + + return status; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *intermediate; + cairo_surface_pattern_t pattern; + cairo_surface_pattern_t clip_pattern; + cairo_surface_t *clip_surface; + int clip_x, clip_y; + cairo_status_t status; + + /* We'd be better off here creating a surface identical in format + * to dst, but we have no way of getting that information. Instead + * we ask the backend to create a similar surface of identical content, + * in the belief that the backend will do something useful - like use + * an identical format. For example, the xlib backend will endeavor to + * use a compatible depth to enable core protocol routines. + */ + intermediate = + _cairo_surface_create_similar_scratch (dst, dst->content, + extents->width, + extents->height); + if (intermediate == NULL) { + intermediate = + _cairo_image_surface_create_with_content (dst->content, + extents->width, + extents->width); + } + if (unlikely (intermediate->status)) + return intermediate->status; + + /* Initialize the intermediate surface from the destination surface */ + _cairo_pattern_init_for_surface (&pattern, dst); + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL, intermediate, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + status = (*draw_func) (draw_closure, op, + src, intermediate, + extents->x, extents->y, + extents, + NULL); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); + + /* Combine that with the clip */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, + &clip_pattern.base, NULL, intermediate, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); + if (unlikely (status)) + goto CLEANUP_CLIP; + + /* Punch the clip out of the destination */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &clip_pattern.base, NULL, dst, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + if (unlikely (status)) + goto CLEANUP_CLIP; + + /* Now add the two results together */ + _cairo_pattern_init_for_surface (&pattern, intermediate); + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &pattern.base, NULL, dst, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); + + CLEANUP_CLIP: + _cairo_pattern_fini (&clip_pattern.base); + CLEANUP_SURFACE: + cairo_surface_destroy (intermediate); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t mask_pattern; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + } + + /* Create a surface that is mask IN clip */ + status = _create_composite_mask_pattern (&mask_pattern, + clip, + draw_func, draw_closure, + dst, extents); + if (unlikely (status)) + return status; + + /* Compute dest' = dest OUT (mask IN clip) */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &mask_pattern.base, NULL, dst, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + clip_region); + + if (unlikely (status)) + goto CLEANUP_MASK_PATTERN; + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + clip_region); + + CLEANUP_MASK_PATTERN: + _cairo_pattern_fini (&mask_pattern.base); + return status; +} + +static int +_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) +{ + return rect->width == 0 || rect->height == 0; +} + +/** + * _clip_and_composite: + * @clip: a #cairo_clip_t + * @op: the operator to draw with + * @src: source pattern + * @draw_func: function that can be called to draw with the mask onto a surface. + * @draw_closure: data to pass to @draw_func. + * @dst: destination surface + * @extents: rectangle holding a bounding box for the operation; this + * rectangle will be used as the size for the temporary + * surface. + * + * When there is a surface clip, we typically need to create an intermediate + * surface. This function handles the logic of creating a temporary surface + * drawing to it, then compositing the result onto the target surface. + * + * @draw_func is to called to draw the mask; it will be called no more + * than once. + * + * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. + **/ +static cairo_status_t +_clip_and_composite (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + + if (_cairo_rectangle_empty (extents)) + /* Nothing to do */ + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR) { + src = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, + src, + draw_func, draw_closure, + dst, extents); + } else { + cairo_bool_t clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_surface) { + if (_cairo_operator_bounded_by_mask (op)) { + status = _clip_and_composite_with_mask (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } else { + status = _clip_and_composite_combine (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } + } else { + status = draw_func (draw_closure, op, + src, dst, + 0, 0, + extents, + clip_region); + } + } + + return status; +} + +/* Composites a region representing a set of trapezoids. + */ +static cairo_status_t +_composite_trap_region (cairo_clip_t *clip, + const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + const cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_surface_pattern_t mask_pattern; + cairo_pattern_t *mask = NULL; + int mask_x = 0, mask_y =0; + + if (clip != NULL) { + cairo_surface_t *clip_surface = NULL; + int clip_x, clip_y; + + clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + src = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); + mask_x = extents->x - clip_x; + mask_y = extents->y - clip_y; + mask = &mask_pattern.base; + } + + status = _cairo_surface_composite (op, src, mask, dst, + extents->x, extents->y, + mask_x, mask_y, + extents->x, extents->y, + extents->width, extents->height, + trap_region); + + if (mask != NULL) + _cairo_pattern_fini (mask); + + return status; +} + +typedef struct { + cairo_traps_t *traps; + cairo_antialias_t antialias; +} cairo_composite_traps_info_t; + +static cairo_status_t +_composite_traps_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_composite_traps_info_t *info = closure; + cairo_status_t status; + cairo_region_t *extents_region = NULL; + + if (dst_x != 0 || dst_y != 0) + _cairo_traps_translate (info->traps, - dst_x, - dst_y); + + if (clip_region == NULL && + !_cairo_operator_bounded_by_source (op)) { + extents_region = cairo_region_create_rectangle (extents); + if (unlikely (extents_region->status)) + return extents_region->status; + cairo_region_translate (extents_region, -dst_x, -dst_y); + clip_region = extents_region; + } + + status = _cairo_surface_composite_trapezoids (op, + src, dst, info->antialias, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + info->traps->traps, + info->traps->num_traps, + clip_region); + + if (extents_region) + cairo_region_destroy (extents_region); + + return status; +} + +enum { + HAS_CLEAR_REGION = 0x1, +}; + +static cairo_status_t +_clip_and_composite_region (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_region_t clear_region; + unsigned int has_region = 0; + cairo_status_t status; + + if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { + /* If we optimize drawing with an unbounded operator to + * _cairo_surface_fill_rectangles() or to drawing with a + * clip region, then we have an additional region to clear. + */ + _cairo_region_init_rectangle (&clear_region, extents); + status = cairo_region_subtract (&clear_region, trap_region); + if (unlikely (status)) + return status; + + if (! cairo_region_is_empty (&clear_region)) + has_region |= HAS_CLEAR_REGION; + } + + if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && + clip == NULL) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + /* Solid rectangles special case */ + status = _cairo_surface_fill_region (dst, op, color, trap_region); + } else { + /* For a simple rectangle, we can just use composite(), for more + * rectangles, we have to set a clip region. The cost of rasterizing + * trapezoids is pretty high for most backends currently, so it's + * worthwhile even if a region is needed. + * + * If we have a clip surface, we set it as the mask; this only works + * for bounded operators other than SOURCE; for unbounded operators, + * clip and mask cannot be interchanged. For SOURCE, the operator + * as implemented by the backends is different in its handling + * of the mask then what we want. + * + * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has + * more than rectangle and the destination doesn't support clip + * regions. In that case, we fall through. + */ + status = _composite_trap_region (clip, src, op, dst, + trap_region, extents); + } + + if (has_region & HAS_CLEAR_REGION) { + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_fill_region (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + } + _cairo_region_fini (&clear_region); + } + + return status; +} + +/* avoid using region code to re-validate boxes */ +static cairo_status_t +_fill_rectangles (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + const cairo_color_t *color; + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_status_t status; + int i; + + if (! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: convert clip region to geometric boxes? */ + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: fallback for the region_subtract() operation */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->has_intersections) { + if (traps->is_rectangular) { + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + } else { + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + } + if (unlikely (status)) + return status; + } + + for (i = 0; i < traps->num_traps; i++) { + if (! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (traps->num_traps, + sizeof (cairo_rectangle_int_t)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < traps->num_traps; i++) { + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + int y1 = _cairo_fixed_integer_part (traps->traps[i].top); + int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + + rects[i].x = x1; + rects[i].y = y1; + rects[i].width = x2 - x1; + rects[i].height = y2 - y1; + } + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); + + if (rects != stack_rects) + free (rects); + + return status; +} + +/* fast-path for very common composite of a single rectangle */ +static cairo_status_t +_composite_rectangle (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + cairo_rectangle_int_t rect; + + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_fixed_is_integer (traps->traps[0].top) || + ! _cairo_fixed_is_integer (traps->traps[0].bottom) || + ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); + rect.y = _cairo_fixed_integer_part (traps->traps[0].top); + rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; + rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; + + return _cairo_surface_composite (op, src, NULL, dst, + rect.x, rect.y, + 0, 0, + rect.x, rect.y, + rect.width, rect.height, + NULL); +} + +/* Warning: This call modifies the coordinates of traps */ +static cairo_status_t +_clip_and_composite_trapezoids (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_composite_traps_info_t traps_info; + cairo_region_t *clip_region = NULL; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; + + if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status))) + return status; + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (! clip_surface || + (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) + { + cairo_region_t *trap_region = NULL; + + if (_cairo_operator_bounded_by_source (op)) { + status = _fill_rectangles (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _composite_rectangle (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_traps_extract_region (traps, &trap_region); + if (unlikely (_cairo_status_is_error (status))) + return status; + + if (trap_region != NULL) { + status = cairo_region_intersect_rectangle (trap_region, extents); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + + if (clip_region != NULL) { + status = cairo_region_intersect (trap_region, clip_region); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + } + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t trap_extents; + + cairo_region_get_extents (trap_region, &trap_extents); + if (! _cairo_rectangle_intersect (extents, &trap_extents)) { + cairo_region_destroy (trap_region); + return CAIRO_STATUS_SUCCESS; + } + } + + status = _clip_and_composite_region (src, op, dst, + trap_region, + clip_surface ? clip : NULL, + extents); + cairo_region_destroy (trap_region); + + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + return status; + } + } + + /* No fast path, exclude self-intersections and clip trapezoids. */ + if (traps->has_intersections) { + if (traps->is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + else if (traps->is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + return status; + } + + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + traps_info.traps = traps; + traps_info.antialias = antialias; + + return _clip_and_composite (clip, op, src, + _composite_traps_draw_func, + &traps_info, dst, extents); +} + +cairo_status_t +_cairo_surface_fallback_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_clip_path_t *clip_path = clip ? clip->path : NULL; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_boxes_t boxes; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + cairo_traps_t traps; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &rect, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && clip_path->prev == NULL && + _cairo_operator_bounded_by_mask (op)) + { + return _cairo_surface_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } + + /* meh, surface-fallback is dying anyway... */ + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _cairo_traps_init_boxes (&traps, &boxes); + if (unlikely (status)) + goto CLEANUP_BOXES; + + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, CAIRO_ANTIALIAS_DEFAULT, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + _cairo_traps_fini (&traps); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +static cairo_status_t +_cairo_surface_mask_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_pattern_t *mask = closure; + cairo_status_t status; + cairo_region_t *extents_region = NULL; + + if (clip_region == NULL && + !_cairo_operator_bounded_by_source (op)) { + extents_region = cairo_region_create_rectangle (extents); + if (unlikely (extents_region->status)) + return extents_region->status; + cairo_region_translate (extents_region, -dst_x, -dst_y); + clip_region = extents_region; + } + + if (src) { + status = _cairo_surface_composite (op, + src, mask, dst, + extents->x, extents->y, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } else { + status = _cairo_surface_composite (op, + mask, NULL, dst, + extents->x, extents->y, + 0, 0, /* unused */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } + + if (extents_region) + cairo_region_destroy (extents_region); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &rect, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + } + + return _clip_and_composite (clip, op, source, + _cairo_surface_mask_draw_func, + (void *) mask, + surface, + extents.is_bounded ? &extents.bounded : &extents.unbounded); +} + +cairo_status_t +_cairo_surface_fallback_stroke (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &rect, + op, source, + path, stroke_style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, clip_boxes, num_boxes); + + if (path->is_rectilinear) { + status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, + stroke_style, + ctm, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) + goto DO_TRAPS; + + if (_cairo_operator_bounded_by_mask (op)) { + _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + CLEANUP: + _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_fill (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_bool_t is_rectilinear; + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &rect, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, clip_boxes, num_boxes); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + if (path->is_empty_fill) + goto DO_TRAPS; + + is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); + if (is_rectilinear) { + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) + goto DO_TRAPS; + + if (_cairo_operator_bounded_by_mask (op)) { + _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + goto CLEANUP; + } + + if (is_rectilinear) { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + fill_rule); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + CLEANUP: + _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +typedef struct { + cairo_scaled_font_t *font; + cairo_glyph_t *glyphs; + int num_glyphs; +} cairo_show_glyphs_info_t; + +static cairo_status_t +_cairo_surface_old_show_glyphs_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_show_glyphs_info_t *glyph_info = closure; + cairo_status_t status; + cairo_region_t *extents_region = NULL; + + if (clip_region == NULL && + !_cairo_operator_bounded_by_source (op)) { + extents_region = cairo_region_create_rectangle (extents); + if (unlikely (extents_region->status)) + return extents_region->status; + cairo_region_translate (extents_region, -dst_x, -dst_y); + clip_region = extents_region; + } + + /* Modifying the glyph array is fine because we know that this function + * will be called only once, and we've already made a copy of the + * glyphs in the wrapper. + */ + if (dst_x != 0 || dst_y != 0) { + int i; + + for (i = 0; i < glyph_info->num_glyphs; ++i) { + ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; + ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; + } + } + + status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, + dst, + extents->x, extents->y, + extents->x - dst_x, + extents->y - dst_y, + extents->width, + extents->height, + glyph_info->glyphs, + glyph_info->num_glyphs, + clip_region); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_scaled_font_show_glyphs (glyph_info->font, + op, + src, dst, + extents->x, extents->y, + extents->x - dst_x, + extents->y - dst_y, + extents->width, extents->height, + glyph_info->glyphs, + glyph_info->num_glyphs, + clip_region); + } + + if (extents_region) + cairo_region_destroy (extents_region); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_show_glyphs_info_t glyph_info; + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &rect, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + NULL); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents.mask)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + } + + glyph_info.font = scaled_font; + glyph_info.glyphs = glyphs; + glyph_info.num_glyphs = num_glyphs; + + return _clip_and_composite (clip, op, source, + _cairo_surface_old_show_glyphs_draw_func, + &glyph_info, + surface, + extents.is_bounded ? &extents.bounded : &extents.unbounded); +} + +cairo_surface_t * +_cairo_surface_fallback_snapshot (cairo_surface_t *surface) +{ + cairo_surface_t *snapshot; + cairo_status_t status; + cairo_format_t format; + cairo_surface_pattern_t pattern; + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (surface, + &image, &image_extra); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + format = image->format; + if (format == CAIRO_FORMAT_INVALID) { + /* Non-standard images formats can be generated when retrieving + * images from unusual xservers, for example. + */ + format = _cairo_format_from_content (image->base.content); + } + snapshot = cairo_image_surface_create (format, + image->width, + image->height); + if (cairo_surface_status (snapshot)) { + _cairo_surface_release_source_image (surface, image, image_extra); + return snapshot; + } + + _cairo_pattern_init_for_surface (&pattern, &image->base); + status = _cairo_surface_paint (snapshot, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + _cairo_surface_release_source_image (surface, image, image_extra); + if (unlikely (status)) { + cairo_surface_destroy (snapshot); + return _cairo_surface_create_in_error (status); + } + + return snapshot; +} + +cairo_status_t +_cairo_surface_fallback_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + fallback_state_t state; + cairo_region_t *fallback_region = NULL; + cairo_status_t status; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (unlikely (status)) + return status; + + /* We know this will never fail with the image backend; but + * instead of calling into it directly, we call + * _cairo_surface_composite so that we get the correct device + * offset handling. + */ + + if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } + + status = _cairo_surface_composite (op, src, mask, + &state.image->base, + src_x, src_y, mask_x, mask_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height, + clip_region); + FAIL: + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); + _fallback_fini (&state); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + fallback_state_t state; + cairo_rectangle_int_t *offset_rects = NULL; + cairo_status_t status; + int x1, y1, x2, y2; + int i; + + assert (surface->snapshot_of == NULL); + + if (num_rects <= 0) + return CAIRO_STATUS_SUCCESS; + + /* Compute the bounds of the rectangles, so that we know what area of the + * destination surface to fetch + */ + x1 = rects[0].x; + y1 = rects[0].y; + x2 = rects[0].x + rects[0].width; + y2 = rects[0].y + rects[0].height; + + for (i = 1; i < num_rects; i++) { + if (rects[i].x < x1) + x1 = rects[i].x; + if (rects[i].y < y1) + y1 = rects[i].y; + + if ((int) (rects[i].x + rects[i].width) > x2) + x2 = rects[i].x + rects[i].width; + if ((int) (rects[i].y + rects[i].height) > y2) + y2 = rects[i].y + rects[i].height; + } + + status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); + if (unlikely (status)) + return status; + + /* If the fetched image isn't at 0,0, we need to offset the rectangles */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); + if (unlikely (offset_rects == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + + for (i = 0; i < num_rects; i++) { + offset_rects[i].x = rects[i].x - state.image_rect.x; + offset_rects[i].y = rects[i].y - state.image_rect.y; + offset_rects[i].width = rects[i].width; + offset_rects[i].height = rects[i].height; + } + + rects = offset_rects; + } + + status = _cairo_surface_fill_rectangles (&state.image->base, + op, color, + rects, num_rects); + + free (offset_rects); + + DONE: + _fallback_fini (&state); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + fallback_state_t state; + cairo_region_t *fallback_region = NULL; + cairo_trapezoid_t *offset_traps = NULL; + cairo_status_t status; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (unlikely (status)) + return status; + + /* If the destination image isn't at 0,0, we need to offset the trapezoids */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); + if (offset_traps == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, + - state.image_rect.x, - state.image_rect.y, + 1.0, 1.0); + traps = offset_traps; + + /* similarly we need to adjust the region */ + if (clip_region != NULL) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto FAIL; + + cairo_region_translate (fallback_region, + -state.image_rect.x, + -state.image_rect.y); + clip_region = fallback_region; + } + } + + status = _cairo_surface_composite_trapezoids (op, pattern, + &state.image->base, + antialias, + src_x, src_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height, + traps, num_traps, + clip_region); + FAIL: + if (offset_traps != NULL) + free (offset_traps); + + if (fallback_region != NULL) + cairo_region_destroy (fallback_region); + + _fallback_fini (&state); + + return status; +} + +cairo_status_t +_cairo_surface_fallback_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_surface_t *new_surface; + cairo_surface_pattern_t pattern; + cairo_status_t status; + + new_surface = _cairo_surface_create_similar_scratch (surface, + src->content, + width, height); + if (new_surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (new_surface->status)) + return new_surface->status; + + /* We have to copy these here, so that the coordinate spaces are correct */ + new_surface->device_transform = src->device_transform; + new_surface->device_transform_inverse = src->device_transform_inverse; + + _cairo_pattern_init_for_surface (&pattern, src); + cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + status = _cairo_surface_paint (new_surface, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (new_surface); + return status; + } + + *clone_offset_x = src_x; + *clone_offset_y = src_y; + *clone_out = new_surface; + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-surface-offset-private.h b/libs/cairo/src/cairo-surface-offset-private.h new file mode 100644 index 000000000..94525a689 --- /dev/null +++ b/libs/cairo/src/cairo-surface-offset-private.h @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_OFFSET_PRIVATE_H +#define CAIRO_SURFACE_OFFSET_PRIVATE_H + +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +cairo_private cairo_status_t +_cairo_surface_offset_paint (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_mask (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_stroke (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_fill (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_glyphs (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip); + +#endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-offset.c b/libs/cairo/src/cairo-surface-offset.c new file mode 100644 index 000000000..a5070617d --- /dev/null +++ b/libs/cairo/src/cairo-surface-offset.c @@ -0,0 +1,309 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-surface-offset-private.h" + +/* A collection of routines to facilitate drawing to an alternate surface. */ + +static void +_copy_transformed_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_pattern_init_static_copy (pattern, original); + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); +} + +cairo_status_t +_cairo_surface_offset_paint (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + + if (unlikely (target->status)) + return target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (x | y) { + cairo_matrix_t m; + + if (clip != NULL) { + cairo_matrix_init_translate (&m, -x, -y); + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_paint (target, op, source, dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_mask (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + cairo_pattern_union_t mask_copy; + + if (unlikely (target->status)) + return target->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (x | y) { + cairo_matrix_t m; + + if (clip != NULL) { + cairo_matrix_init_translate (&m, -x, -y); + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&mask_copy.base, mask, &m); + source = &source_copy.base; + mask = &mask_copy.base; + } + + status = _cairo_surface_mask (target, op, + source, mask, + dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_stroke (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t*stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_matrix_t dev_ctm = *ctm; + cairo_matrix_t dev_ctm_inverse = *ctm_inverse; + cairo_pattern_union_t source_copy; + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (x | y) { + cairo_matrix_t m; + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_translate (&path_copy, + _cairo_fixed_from_int (-x), + _cairo_fixed_from_int (-y)); + dev_path = &path_copy; + + cairo_matrix_init_translate (&m, -x, -y); + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + } + + status = _cairo_surface_stroke (surface, op, source, + dev_path, stroke_style, + &dev_ctm, &dev_ctm_inverse, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_fill (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (x | y) { + cairo_matrix_t m; + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_translate (&path_copy, + _cairo_fixed_from_int (-x), + _cairo_fixed_from_int (-y)); + dev_path = &path_copy; + + if (clip != NULL) { + cairo_matrix_init_translate (&m, -x, -y); + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_fill (surface, op, source, + dev_path, fill_rule, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_glyphs (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + cairo_glyph_t *dev_glyphs; + int i; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (dev_glyphs == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + + if (x | y) { + cairo_matrix_t m; + + if (clip != NULL) { + cairo_matrix_init_translate (&m, -x, -y); + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + + for (i = 0; i < num_glyphs; i++) { + dev_glyphs[i].x -= x; + dev_glyphs[i].y -= y; + } + } + + status = _cairo_surface_show_text_glyphs (surface, op, source, + NULL, 0, + dev_glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + free (dev_glyphs); + + return status; +} diff --git a/libs/cairo/src/cairo-surface-private.h b/libs/cairo/src/cairo-surface-private.h new file mode 100644 index 000000000..96cd4a371 --- /dev/null +++ b/libs/cairo/src/cairo-surface-private.h @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_PRIVATE_H +#define CAIRO_SURFACE_PRIVATE_H + +#include "cairo.h" + +#include "cairo-types-private.h" +#include "cairo-list-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-clip-private.h" + +struct _cairo_surface { + const cairo_surface_backend_t *backend; + cairo_device_t *device; + + /* We allow surfaces to override the backend->type by shoving something + * else into surface->type. This is for "wrapper" surfaces that want to + * hide their internal type from the user-level API. */ + cairo_surface_type_t type; + + cairo_content_t content; + + cairo_reference_count_t ref_count; + cairo_status_t status; + unsigned int unique_id; + + unsigned finished : 1; + unsigned is_clear : 1; + unsigned has_font_options : 1; + unsigned owns_device : 1; + unsigned permit_subpixel_antialiasing : 1; + + cairo_user_data_array_t user_data; + cairo_user_data_array_t mime_data; + + cairo_matrix_t device_transform; + cairo_matrix_t device_transform_inverse; + cairo_list_t device_transform_observers; + + /* The actual resolution of the device, in dots per inch. */ + double x_resolution; + double y_resolution; + + /* The resolution that should be used when generating image-based + * fallback; generally only used by the analysis/paginated + * surfaces + */ + double x_fallback_resolution; + double y_fallback_resolution; + + /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ + cairo_surface_t *snapshot_of; + cairo_surface_func_t snapshot_detach; + /* current snapshots of this surface*/ + cairo_list_t snapshots; + /* place upon snapshot list */ + cairo_list_t snapshot; + + /* + * Surface font options, falling back to backend's default options, + * and set using _cairo_surface_set_font_options(), and propagated by + * cairo_surface_create_similar(). + */ + cairo_font_options_t font_options; +}; + +#endif /* CAIRO_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-snapshot-private.h b/libs/cairo/src/cairo-surface-snapshot-private.h new file mode 100644 index 000000000..a9520332a --- /dev/null +++ b/libs/cairo/src/cairo-surface-snapshot-private.h @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H +#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H + +#include "cairo-surface-private.h" + +struct _cairo_surface_snapshot { + cairo_surface_t base; + + cairo_surface_t *target; + cairo_surface_t *clone; +}; + +#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-snapshot.c b/libs/cairo/src/cairo-surface-snapshot.c new file mode 100644 index 000000000..030636056 --- /dev/null +++ b/libs/cairo/src/cairo-surface-snapshot.c @@ -0,0 +1,220 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-surface-snapshot-private.h" + +static cairo_status_t +_cairo_surface_snapshot_finish (void *abstract_surface) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->clone != NULL) { + cairo_surface_finish (surface->clone); + status = surface->clone->status; + + cairo_surface_destroy (surface->clone); + } + + return status; +} + +static cairo_status_t +_cairo_surface_snapshot_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **extra_out) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + + return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out); +} + +static void +_cairo_surface_snapshot_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *extra) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + + _cairo_surface_release_source_image (surface->target, image, extra); +} + +static cairo_bool_t +_cairo_surface_snapshot_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, extents); +} + +static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT, + + NULL, /* create similar */ + _cairo_surface_snapshot_finish, + + _cairo_surface_snapshot_acquire_source_image, + _cairo_surface_snapshot_release_source_image, + NULL, NULL, /* acquire, release dest */ + NULL, /* clone similar */ + NULL, /* composite */ + NULL, /* fill rectangles */ + NULL, /* composite trapezoids */ + NULL, /* create span renderer */ + NULL, /* check span renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_surface_snapshot_get_extents, +}; + +static void +_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) +{ + cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; + cairo_image_surface_t *image; + cairo_image_surface_t *clone; + void *extra; + cairo_status_t status; + + /* We need to make an image copy of the original surface since the + * snapshot may exceed the lifetime of the original device, i.e. + * when we later need to use the snapshot the data may have already + * been lost. + */ + + status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra); + if (unlikely (status)) { + snapshot->target = _cairo_surface_create_in_error (status); + status = _cairo_surface_set_error (surface, status); + return; + } + + clone = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + image->pixman_format, + image->width, + image->height, + 0); + if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) { + if (clone->stride == image->stride) { + memcpy (clone->data, image->data, image->stride * image->height); + } else { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + } + clone->base.is_clear = FALSE; + + snapshot->clone = &clone->base; + } else { + snapshot->clone = &clone->base; + status = _cairo_surface_set_error (surface, clone->base.status); + } + + _cairo_surface_release_source_image (snapshot->target, image, extra); + snapshot->target = snapshot->clone; + snapshot->base.type = snapshot->target->type; +} + +/** + * _cairo_surface_snapshot + * @surface: a #cairo_surface_t + * + * Make an immutable reference to @surface. It is an error to call a + * surface-modifying function on the result of this function. The + * resulting 'snapshot' is a lazily copied-on-write surface i.e. it + * remains a reference to the original surface until that surface is + * written to again, at which time a copy is made of the original surface + * and the snapshot then points to that instead. Multiple snapshots of the + * same unmodified surface point to the same copy. + * + * The caller owns the return value and should call + * cairo_surface_destroy() when finished with it. This function will not + * return %NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. Note that the return surface + * may not necessarily be of the same type as @surface. + **/ +cairo_surface_t * +_cairo_surface_snapshot (cairo_surface_t *surface) +{ + cairo_surface_snapshot_t *snapshot; + cairo_status_t status; + + if (unlikely (surface->status)) + return _cairo_surface_create_in_error (surface->status); + + if (surface->finished) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (surface->snapshot_of != NULL) + return cairo_surface_reference (surface); + + if (surface->backend->snapshot != NULL) { + cairo_surface_t *snap; + + snap = _cairo_surface_has_snapshot (surface, surface->backend); + if (snap != NULL) + return cairo_surface_reference (snap); + + snap = surface->backend->snapshot (surface); + if (snap != NULL) { + if (unlikely (snap->status)) + return snap; + + status = _cairo_surface_copy_mime_data (snap, surface); + if (unlikely (status)) { + cairo_surface_destroy (snap); + return _cairo_surface_create_in_error (status); + } + + snap->device_transform = surface->device_transform; + snap->device_transform_inverse = surface->device_transform_inverse; + + cairo_surface_attach_snapshot (surface, snap, NULL); + + return snap; + } + } + + snapshot = (cairo_surface_snapshot_t *) + _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend); + if (snapshot != NULL) + return cairo_surface_reference (&snapshot->base); + + snapshot = malloc (sizeof (cairo_surface_snapshot_t)); + if (unlikely (snapshot == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + _cairo_surface_init (&snapshot->base, + &_cairo_surface_snapshot_backend, + NULL, /* device */ + surface->content); + snapshot->base.type = surface->type; + + snapshot->target = surface; + snapshot->clone = NULL; + + status = _cairo_surface_copy_mime_data (&snapshot->base, surface); + if (unlikely (status)) { + cairo_surface_destroy (&snapshot->base); + return _cairo_surface_create_in_error (status); + } + + snapshot->base.device_transform = surface->device_transform; + snapshot->base.device_transform_inverse = surface->device_transform_inverse; + + cairo_surface_attach_snapshot (surface, + &snapshot->base, + _cairo_surface_snapshot_copy_on_write); + + return &snapshot->base; +} diff --git a/libs/cairo/src/cairo-surface-subsurface-private.h b/libs/cairo/src/cairo-surface-subsurface-private.h new file mode 100644 index 000000000..3fe48f83b --- /dev/null +++ b/libs/cairo/src/cairo-surface-subsurface-private.h @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H +#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H + +#include "cairo-surface-private.h" + +struct _cairo_surface_subsurface { + cairo_surface_t base; + + cairo_rectangle_int_t extents; + + cairo_surface_t *target; +}; + +#endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-subsurface.c b/libs/cairo/src/cairo-surface-subsurface.c new file mode 100644 index 000000000..b9d8eacd6 --- /dev/null +++ b/libs/cairo/src/cairo-surface-subsurface.c @@ -0,0 +1,521 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-subsurface-private.h" + +static const cairo_surface_backend_t _cairo_surface_subsurface_backend; + +static cairo_status_t +_cairo_surface_subsurface_finish (void *abstract_surface) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->target); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_surface_subsurface_create_similar (void *other, + cairo_content_t content, + int width, int height) +{ + cairo_surface_subsurface_t *surface = other; + return surface->target->backend->create_similar (surface->target, content, width, height); +} + +static cairo_int_status_t +_cairo_surface_subsurface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; + cairo_status_t status; + cairo_clip_t target_clip; + + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_surface_offset_paint (surface->target, + -surface->extents.x, -surface->extents.y, + op, source, &target_clip); + CLEANUP: + _cairo_clip_fini (&target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; + cairo_status_t status; + cairo_clip_t target_clip; + + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_surface_offset_mask (surface->target, + -surface->extents.x, -surface->extents.y, + op, source, mask, &target_clip); + CLEANUP: + _cairo_clip_fini (&target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; + cairo_status_t status; + cairo_clip_t target_clip; + + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_surface_offset_fill (surface->target, + -surface->extents.x, -surface->extents.y, + op, source, path, fill_rule, tolerance, antialias, + &target_clip); + CLEANUP: + _cairo_clip_fini (&target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; + cairo_status_t status; + cairo_clip_t target_clip; + + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_surface_offset_stroke (surface->target, + -surface->extents.x, -surface->extents.y, + op, source, path, stroke_style, ctm, ctm_inverse, + tolerance, antialias, + &target_clip); + CLEANUP: + _cairo_clip_fini (&target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; + cairo_status_t status; + cairo_clip_t target_clip; + + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &rect); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_surface_offset_glyphs (surface->target, + -surface->extents.x, -surface->extents.y, + op, source, + scaled_font, glyphs, num_glyphs, + &target_clip); + *remaining_glyphs = 0; + CLEANUP: + _cairo_clip_fini (&target_clip); + return status; +} + +static cairo_status_t +_cairo_surface_subsurface_flush (void *abstract_surface) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->flush != NULL) + status = surface->target->backend->flush (surface->target); + + return status; +} + +static cairo_status_t +_cairo_surface_subsurface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->mark_dirty_rectangle != NULL) { + cairo_rectangle_int_t rect, extents; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + extents.x = extents.y = 0; + extents.width = surface->extents.width; + extents.height = surface->extents.height; + + if (_cairo_rectangle_intersect (&rect, &extents)) { + status = surface->target->backend->mark_dirty_rectangle (surface->target, + rect.x + surface->extents.x, + rect.y + surface->extents.y, + rect.width, rect.height); + } + } + + return status; +} + +static cairo_bool_t +_cairo_surface_subsurface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->extents.width; + extents->height = surface->extents.height; + + return TRUE; +} + +static void +_cairo_surface_subsurface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + + if (surface->target->backend->get_font_options != NULL) + surface->target->backend->get_font_options (surface->target, options); +} + +struct extra { + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +cairo_surface_paint_to_target (cairo_surface_t *target, + cairo_surface_subsurface_t *subsurface) +{ + cairo_t *cr; + + cr = cairo_create (target); + + cairo_set_source_surface (cr, + subsurface->target, + - subsurface->extents.x, + - subsurface->extents.y); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + cairo_destroy (cr); +} + +static cairo_status_t +_cairo_surface_subsurface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **extra_out) +{ + cairo_rectangle_int_t target_extents; + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + struct extra *extra; + uint8_t *data; + cairo_bool_t ret; + + if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) { + cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target; + cairo_surface_t *snapshot; + + snapshot = _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); + if (snapshot != NULL) { + *image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot); + *extra_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + + if (! _cairo_surface_has_snapshot (&meta->base, + &_cairo_image_surface_backend)) + { + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_content (meta->content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->base.status)) + return image->base.status; + + cairo_surface_paint_to_target (&image->base, surface); + + cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); + + *image_out = image; + *extra_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + extra = malloc (sizeof (struct extra)); + if (unlikely (extra == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra); + if (unlikely (status)) + goto CLEANUP; + + ret = _cairo_surface_get_extents (&extra->image->base, &target_extents); + assert (ret); + + /* only copy if we need to perform sub-byte manipulation */ + if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 && + target_extents.x <= surface->extents.x && + target_extents.y <= surface->extents.y && + surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width && + surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) { + + assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0); + + data = extra->image->data + surface->extents.y * extra->image->stride; + data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x; + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (data, + extra->image->pixman_format, + surface->extents.width, + surface->extents.height, + extra->image->stride); + if (unlikely ((status = image->base.status))) + goto CLEANUP_IMAGE; + + image->base.is_clear = FALSE; + } else { + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + extra->image->pixman_format, + surface->extents.width, + surface->extents.height, + 0); + if (unlikely ((status = image->base.status))) + goto CLEANUP_IMAGE; + + cairo_surface_paint_to_target (&image->base, surface); + } + + *image_out = image; + *extra_out = extra; + return CAIRO_STATUS_SUCCESS; + +CLEANUP_IMAGE: + _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); +CLEANUP: + free (extra); + return status; +} + +static void +_cairo_surface_subsurface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *abstract_extra) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + + if (abstract_extra != NULL) { + struct extra *extra = abstract_extra; + + _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); + free (extra); + } + + cairo_surface_destroy (&image->base); +} + +static cairo_surface_t * +_cairo_surface_subsurface_snapshot (void *abstract_surface) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_surface_subsurface_t *snapshot; + + snapshot = malloc (sizeof (cairo_surface_subsurface_t)); + if (unlikely (snapshot == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&snapshot->base, + &_cairo_surface_subsurface_backend, + NULL, /* device */ + surface->target->content); + snapshot->target = _cairo_surface_snapshot (surface->target); + if (unlikely (snapshot->target->status)) { + cairo_status_t status; + + status = snapshot->target->status; + free (snapshot); + return _cairo_surface_create_in_error (status); + } + + snapshot->base.type = snapshot->target->type; + snapshot->extents = surface->extents; + + return &snapshot->base; +} + +static const cairo_surface_backend_t _cairo_surface_subsurface_backend = { + CAIRO_SURFACE_TYPE_SUBSURFACE, + _cairo_surface_subsurface_create_similar, + _cairo_surface_subsurface_finish, + + _cairo_surface_subsurface_acquire_source_image, + _cairo_surface_subsurface_release_source_image, + NULL, NULL, /* acquire, release dest */ + NULL, /* clone similar */ + NULL, /* composite */ + NULL, /* fill rectangles */ + NULL, /* composite trapezoids */ + NULL, /* create span renderer */ + NULL, /* check span renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_surface_subsurface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_surface_subsurface_get_font_options, + _cairo_surface_subsurface_flush, + _cairo_surface_subsurface_mark_dirty, + NULL, /* font_fini */ + NULL, /* glyph_fini */ + + _cairo_surface_subsurface_paint, + _cairo_surface_subsurface_mask, + _cairo_surface_subsurface_stroke, + _cairo_surface_subsurface_fill, + _cairo_surface_subsurface_glyphs, + + _cairo_surface_subsurface_snapshot, +}; + +/** + * cairo_surface_create_for_rectangle: + * @target: an existing surface for which the sub-surface will point to + * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units) + * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units) + * @width: width of the sub-surface (in device-space units) + * @height: height of the sub-surface (in device-space units) + * + * Create a new surface that is a rectangle within the target surface. + * All operations drawn to this surface are then clipped and translated + * onto the target surface. Nothing drawn via this sub-surface outside of + * its bounds is drawn onto the target surface, making this a useful method + * for passing constrained child surfaces to library routines that draw + * directly onto the parent surface, i.e. with no further backend allocations, + * double buffering or copies. + * + * The semantics of subsurfaces have not been finalized yet + * unless the rectangle is in full device units, is contained within + * the extents of the target surface, and the target or subsurface's + * device transforms are not changed. + * + * Return value: a pointer to the newly allocated surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if @other is already in an error state + * or any other error occurs. + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_surface_create_for_rectangle (cairo_surface_t *target, + double x, double y, + double width, double height) +{ + cairo_surface_subsurface_t *surface; + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + if (unlikely (target->finished)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + surface = malloc (sizeof (cairo_surface_subsurface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + assert (_cairo_matrix_is_translation (&target->device_transform)); + x += target->device_transform.x0; + y += target->device_transform.y0; + + _cairo_surface_init (&surface->base, + &_cairo_surface_subsurface_backend, + NULL, /* device */ + target->content); + + /* XXX forced integer alignment */ + surface->extents.x = ceil (x); + surface->extents.y = ceil (y); + surface->extents.width = floor (x + width) - surface->extents.x; + surface->extents.height = floor (y + height) - surface->extents.y; + + if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + /* Maintain subsurfaces as 1-depth */ + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target; + surface->extents.x += sub->extents.x; + surface->extents.y += sub->extents.y; + target = sub->target; + } + + surface->target = cairo_surface_reference (target); + + return &surface->base; +} +/* XXX observe mark-dirty */ diff --git a/libs/cairo/src/cairo-surface-wrapper-private.h b/libs/cairo/src/cairo-surface-wrapper-private.h new file mode 100644 index 000000000..c6d2bbe91 --- /dev/null +++ b/libs/cairo/src/cairo-surface-wrapper-private.h @@ -0,0 +1,137 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H +#define CAIRO_SURFACE_WRAPPER_PRIVATE_H + +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo_surface_wrapper { + cairo_surface_t *target; + + cairo_bool_t has_extents; + cairo_rectangle_int_t extents; +}; + +cairo_private void +_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, + cairo_surface_t *target); + +cairo_private void +_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, + const cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); + +cairo_private cairo_status_t +_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra); + + +cairo_private cairo_status_t +_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height); +cairo_private cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, + cairo_font_options_t *options); + +cairo_private cairo_surface_t * +_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper); + +cairo_private cairo_bool_t +_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper); + +cairo_private cairo_status_t +_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper); + +static inline cairo_bool_t +_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) +{ + return wrapper->target != (cairo_surface_t *) 0; +} + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-surface-wrapper.c b/libs/cairo/src/cairo-surface-wrapper.c new file mode 100644 index 000000000..902e717cb --- /dev/null +++ b/libs/cairo/src/cairo-surface-wrapper.c @@ -0,0 +1,680 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-surface-wrapper-private.h" + +/* A collection of routines to facilitate surface wrapping */ + +static void +_copy_transformed_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_pattern_init_static_copy (pattern, original); + + /* Device transform should already have been applied before cairo_surface_wrapper_* functions + * are called in _cairo_gstate_copy_transformed_pattern which all the gstate drawing + * functions call through _cairo_gstate_copy_transformed_source and such. */ + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); +} + +static inline cairo_bool_t +_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) +{ + return ! _cairo_matrix_is_identity (&wrapper->target->device_transform); +} + +static cairo_bool_t +_cairo_surface_wrapper_needs_extents_transform (cairo_surface_wrapper_t *wrapper) +{ + return wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y); +} + +cairo_status_t +_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t **image_out, + void **image_extra) +{ + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + return _cairo_surface_acquire_source_image (wrapper->target, + image_out, image_extra); +} + +void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra) +{ + _cairo_surface_release_source_image (wrapper->target, image, image_extra); +} + +cairo_status_t +_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_paint (wrapper->target, op, source, dev_clip); + + FINISH: + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + cairo_pattern_union_t mask_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + + _copy_transformed_pattern (&mask_copy.base, mask, &m); + mask = &mask_copy.base; + } + + status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip); + + FINISH: + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_matrix_t dev_ctm = *ctm; + cairo_matrix_t dev_ctm_inverse = *ctm_inverse; + cairo_pattern_union_t source_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + else + { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + status = _cairo_surface_stroke (wrapper->target, op, source, + dev_path, stroke_style, + &dev_ctm, &dev_ctm_inverse, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_matrix_t dev_ctm = *stroke_ctm; + cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; + cairo_pattern_union_t stroke_source_copy; + cairo_pattern_union_t fill_source_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + + _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m); + stroke_source = &stroke_source_copy.base; + + _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); + fill_source = &fill_source_copy.base; + } + else + { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + status = _cairo_surface_fill_stroke (wrapper->target, + fill_op, fill_source, fill_rule, + fill_tolerance, fill_antialias, + dev_path, + stroke_op, stroke_source, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_path_fixed_t path_copy, *dev_path = path; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_pattern_union_t source_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + else + { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + status = _cairo_surface_fill (wrapper->target, op, source, + dev_path, fill_rule, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t clip_copy, *dev_clip = clip; + cairo_glyph_t *dev_glyphs = glyphs; + cairo_pattern_union_t source_copy; + cairo_clip_t target_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + if (glyphs == NULL || num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + if (wrapper->has_extents) { + _cairo_clip_init_copy (&target_clip, clip); + status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); + if (unlikely (status)) + goto FINISH; + + dev_clip = clip = &target_clip; + } + + if (clip && clip->all_clipped) { + status = CAIRO_STATUS_SUCCESS; + goto FINISH; + } + + if (_cairo_surface_wrapper_needs_device_transform (wrapper) || + _cairo_surface_wrapper_needs_extents_transform (wrapper)) + { + cairo_matrix_t m; + int i; + + cairo_matrix_init_identity (&m); + + if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) + cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + + if (_cairo_surface_wrapper_needs_device_transform (wrapper)) + cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + + if (clip != NULL) { + status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); + if (unlikely (status)) + goto FINISH; + + dev_clip = &clip_copy; + } + + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (dev_glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FINISH; + } + + for (i = 0; i < num_glyphs; i++) { + dev_glyphs[i] = glyphs[i]; + cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); + } + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + else + { + if (clip != NULL) { + dev_clip = &clip_copy; + _cairo_clip_init_copy (&clip_copy, clip); + } + } + + status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, + utf8, utf8_len, + dev_glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + dev_clip); + + FINISH: + if (dev_clip != clip) + _cairo_clip_reset (dev_clip); + if (wrapper->has_extents) + _cairo_clip_reset (&target_clip); + if (dev_glyphs != glyphs) + free (dev_glyphs); + return status; +} + +cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height) +{ + return _cairo_surface_create_similar_scratch (wrapper->target, + content, width, height); +} + +cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents) +{ + if (wrapper->has_extents) { + if (_cairo_surface_get_extents (wrapper->target, extents)) + _cairo_rectangle_intersect (extents, &wrapper->extents); + else + *extents = wrapper->extents; + + return TRUE; + } else { + return _cairo_surface_get_extents (wrapper->target, extents); + } +} + +void +_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, + const cairo_rectangle_int_t *extents) +{ + if (extents != NULL) { + wrapper->extents = *extents; + wrapper->has_extents = TRUE; + } else { + wrapper->has_extents = FALSE; + } +} + +void +_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, + cairo_font_options_t *options) +{ + cairo_surface_get_font_options (wrapper->target, options); +} + +cairo_surface_t * +_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) +{ + return _cairo_surface_snapshot (wrapper->target); +} + +cairo_bool_t +_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper) +{ + return cairo_surface_has_show_text_glyphs (wrapper->target); +} + +void +_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, + cairo_surface_t *target) +{ + wrapper->target = cairo_surface_reference (target); + wrapper->has_extents = FALSE; +} + +void +_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) +{ + cairo_surface_destroy (wrapper->target); +} + +cairo_status_t +_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper) +{ + if (wrapper->target->backend->flush) { + return wrapper->target->backend->flush(wrapper->target); + } + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-surface.c b/libs/cairo/src/cairo-surface.c new file mode 100644 index 000000000..c80ea8553 --- /dev/null +++ b/libs/cairo/src/cairo-surface.c @@ -0,0 +1,3292 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-surface-fallback-private.h" +#include "cairo-clip-private.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-region-private.h" +#include "cairo-tee-surface-private.h" + +/** + * SECTION:cairo-surface + * @Title: cairo_surface_t + * @Short_Description: Base class for surfaces + * @See_Also: #cairo_t, #cairo_pattern_t + * + * #cairo_surface_t is the abstract type representing all different drawing + * targets that cairo can render to. The actual drawings are + * performed using a cairo context. + * + * A cairo surface is created by using backend-specific + * constructors, typically of the form + * cairo_backend_surface_create(). + * + * Most surface types allow accessing the surface without using Cairo + * functions. If you do this, keep in mind that it is mandatory that you call + * cairo_surface_flush() before reading from or writing to the surface and that + * you must use cairo_surface_mark_dirty() after modifying it. + * + * Directly modifying an image surface + * + * void + * modify_image_surface (cairo_surface_t *surface) + * { + * unsigned char *data; + * int width, height, stride; + * + * // flush to ensure all writing to the image was done + * cairo_surface_flush (surface); + * + * // modify the image + * data = cairo_image_surface_get_data (surface); + * width = cairo_image_surface_get_width (surface); + * height = cairo_image_surface_get_height (surface); + * stride = cairo_image_surface_get_stride (surface); + * modify_image_data (data, width, height, stride); + * + * // mark the image dirty so Cairo clears its caches. + * cairo_surface_mark_dirty (surface); + * } + * + * + * Note that for other surface types it might be necessary to acquire the + * surface's device first. See cairo_device_acquire() for a discussion of + * devices. + */ + +#define DEFINE_NIL_SURFACE(status, name) \ +const cairo_surface_t name = { \ + NULL, /* backend */ \ + NULL, /* device */ \ + CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ + CAIRO_CONTENT_COLOR, /* content */ \ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ + status, /* status */ \ + 0, /* unique id */ \ + FALSE, /* finished */ \ + TRUE, /* is_clear */ \ + FALSE, /* has_font_options */ \ + FALSE, /* owns_device */ \ + FALSE, /* permit_subpixel_antialiasing */ \ + { 0, 0, 0, NULL, }, /* user_data */ \ + { 0, 0, 0, NULL, }, /* mime_data */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ + { NULL, NULL }, /* device_transform_observers */ \ + 0.0, /* x_resolution */ \ + 0.0, /* y_resolution */ \ + 0.0, /* x_fallback_resolution */ \ + 0.0, /* y_fallback_resolution */ \ + NULL, /* snapshot_of */ \ + NULL, /* snapshot_detach */ \ + { NULL, NULL }, /* snapshots */ \ + { NULL, NULL }, /* snapshot */ \ + { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ + CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ + CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \ + CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ + CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \ + CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \ + } /* font_options */ \ +} + +/* XXX error object! */ + +static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error); + +/** + * _cairo_surface_set_error: + * @surface: a surface + * @status: a status value indicating an error + * + * Atomically sets surface->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to surface->status should happen + * through _cairo_surface_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_status_t +_cairo_surface_set_error (cairo_surface_t *surface, + cairo_status_t status) +{ + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + + if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&surface->status, status); + + return _cairo_error (status); +} + +/** + * cairo_surface_get_type: + * @surface: a #cairo_surface_t + * + * This function returns the type of the backend used to create + * a surface. See #cairo_surface_type_t for available types. + * + * Return value: The type of @surface. + * + * Since: 1.2 + **/ +cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface) +{ + /* We don't use surface->backend->type here so that some of the + * special "wrapper" surfaces such as cairo_paginated_surface_t + * can override surface->type with the type of the "child" + * surface. */ + return surface->type; +} +slim_hidden_def (cairo_surface_get_type); + +/** + * cairo_surface_get_content: + * @surface: a #cairo_surface_t + * + * This function returns the content type of @surface which indicates + * whether the surface contains color and/or alpha information. See + * #cairo_content_t. + * + * Return value: The content type of @surface. + * + * Since: 1.2 + **/ +cairo_content_t +cairo_surface_get_content (cairo_surface_t *surface) +{ + return surface->content; +} +slim_hidden_def(cairo_surface_get_content); + +/** + * cairo_surface_status: + * @surface: a #cairo_surface_t + * + * Checks whether an error has previously occurred for this + * surface. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER, + * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR, + * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or + * %CAIRO_STATUS_INVALID_VISUAL. + **/ +cairo_status_t +cairo_surface_status (cairo_surface_t *surface) +{ + return surface->status; +} +slim_hidden_def (cairo_surface_status); + +static unsigned int +_cairo_surface_allocate_unique_id (void) +{ + static cairo_atomic_int_t unique_id; + +#if CAIRO_NO_MUTEX + if (++unique_id == 0) + unique_id = 1; + return unique_id; +#else + cairo_atomic_int_t old, id; + + do { + old = _cairo_atomic_uint_get (&unique_id); + id = old + 1; + if (id == 0) + id = 1; + } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id)); + + return id; +#endif +} + +/** + * cairo_surface_get_device: + * @surface: a #cairo_surface_t + * + * This function returns the device for a @surface. + * See #cairo_device_t. + * + * Return value: The device for @surface or %NULL if the surface does + * not have an associated device. + * + * Since: 1.10 + **/ +cairo_device_t * +cairo_surface_get_device (cairo_surface_t *surface) +{ + if (unlikely (surface->status)) + return _cairo_device_create_in_error (surface->status); + + return surface->device; +} + +static cairo_bool_t +_cairo_surface_has_snapshots (cairo_surface_t *surface) +{ + return ! cairo_list_is_empty (&surface->snapshots); +} + +static cairo_bool_t +_cairo_surface_has_mime_data (cairo_surface_t *surface) +{ + return surface->mime_data.num_elements != 0; +} + +static void +_cairo_surface_detach_mime_data (cairo_surface_t *surface) +{ + if (! _cairo_surface_has_mime_data (surface)) + return; + + _cairo_user_data_array_fini (&surface->mime_data); + _cairo_user_data_array_init (&surface->mime_data); +} + +static void +cairo_surface_detach_snapshots (cairo_surface_t *surface) +{ + while (_cairo_surface_has_snapshots (surface)) { + cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots, + cairo_surface_t, + snapshot)); + } +} + +void +cairo_surface_detach_snapshot (cairo_surface_t *snapshot) +{ + assert (snapshot->snapshot_of != NULL); + + snapshot->snapshot_of = NULL; + cairo_list_del (&snapshot->snapshot); + + if (snapshot->snapshot_detach != NULL) + snapshot->snapshot_detach (snapshot); + + cairo_surface_destroy (snapshot); +} + +void +cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func) +{ + assert (surface != snapshot); + assert (snapshot->snapshot_of != surface); + + cairo_surface_reference (snapshot); + + if (snapshot->snapshot_of != NULL) + cairo_surface_detach_snapshot (snapshot); + + snapshot->snapshot_of = surface; + snapshot->snapshot_detach = detach_func; + + cairo_list_add (&snapshot->snapshot, &surface->snapshots); + + assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot); +} + +cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend) +{ + cairo_surface_t *snapshot; + + cairo_list_foreach_entry (snapshot, cairo_surface_t, + &surface->snapshots, snapshot) + { + /* XXX is_similar? */ + if (snapshot->backend == backend) + return snapshot; + } + + return NULL; +} + +static cairo_bool_t +_cairo_surface_is_writable (cairo_surface_t *surface) +{ + return ! surface->finished && + surface->snapshot_of == NULL && + ! _cairo_surface_has_snapshots (surface) && + ! _cairo_surface_has_mime_data (surface); +} + +static void +_cairo_surface_begin_modification (cairo_surface_t *surface) +{ + assert (surface->status == CAIRO_STATUS_SUCCESS); + assert (! surface->finished); + assert (surface->snapshot_of == NULL); + + cairo_surface_detach_snapshots (surface); + _cairo_surface_detach_mime_data (surface); +} + +void +_cairo_surface_init (cairo_surface_t *surface, + const cairo_surface_backend_t *backend, + cairo_device_t *device, + cairo_content_t content) +{ + CAIRO_MUTEX_INITIALIZE (); + + surface->backend = backend; + surface->device = cairo_device_reference (device); + surface->content = content; + surface->type = backend->type; + + CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); + surface->status = CAIRO_STATUS_SUCCESS; + surface->unique_id = _cairo_surface_allocate_unique_id (); + surface->finished = FALSE; + surface->is_clear = FALSE; + surface->owns_device = (device != NULL); + surface->has_font_options = FALSE; + surface->permit_subpixel_antialiasing = TRUE; + + _cairo_user_data_array_init (&surface->user_data); + _cairo_user_data_array_init (&surface->mime_data); + + cairo_matrix_init_identity (&surface->device_transform); + cairo_matrix_init_identity (&surface->device_transform_inverse); + cairo_list_init (&surface->device_transform_observers); + + surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; + surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; + + surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; + surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; + + cairo_list_init (&surface->snapshots); + surface->snapshot_of = NULL; +} + +static void +_cairo_surface_copy_similar_properties (cairo_surface_t *surface, + cairo_surface_t *other) +{ + if (other->has_font_options || other->backend != surface->backend) { + cairo_font_options_t options; + + cairo_surface_get_font_options (other, &options); + _cairo_surface_set_font_options (surface, &options); + } + + surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; + + cairo_surface_set_fallback_resolution (surface, + other->x_fallback_resolution, + other->y_fallback_resolution); +} + +cairo_surface_t * +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *surface; + + if (unlikely (other->status)) + return _cairo_surface_create_in_error (other->status); + + if (other->backend->create_similar == NULL) + return NULL; + + surface = other->backend->create_similar (other, + content, width, height); + if (surface == NULL || surface->status) + return surface; + + _cairo_surface_copy_similar_properties (surface, other); + + return surface; +} + +/** + * cairo_surface_create_similar: + * @other: an existing surface used to select the backend of the new surface + * @content: the content for the new surface + * @width: width of the new surface, (in device-space units) + * @height: height of the new surface (in device-space units) + * + * Create a new surface that is as compatible as possible with an + * existing surface. For example the new surface will have the same + * fallback resolution and font options as @other. Generally, the new + * surface will also use the same backend as @other, unless that is + * not possible for some reason. The type of the returned surface may + * be examined with cairo_surface_get_type(). + * + * Initially the surface contents are all 0 (transparent if contents + * have transparency, black otherwise.) + * + * Return value: a pointer to the newly allocated surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if @other is already in an error state + * or any other error occurs. + **/ +cairo_surface_t * +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, + int width, + int height) +{ + if (unlikely (other->status)) + return _cairo_surface_create_in_error (other->status); + if (unlikely (other->finished)) + return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + + if (unlikely (! CAIRO_CONTENT_VALID (content))) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + return _cairo_surface_create_similar_solid (other, + content, width, height, + CAIRO_COLOR_TRANSPARENT, + TRUE); +} + +cairo_surface_t * +_cairo_surface_create_similar_solid (cairo_surface_t *other, + cairo_content_t content, + int width, + int height, + const cairo_color_t *color, + cairo_bool_t allow_fallback) +{ + cairo_status_t status; + cairo_surface_t *surface; + cairo_solid_pattern_t pattern; + + surface = _cairo_surface_create_similar_scratch (other, content, + width, height); + if (surface == NULL && allow_fallback) + surface = _cairo_image_surface_create_with_content (content, + width, height); + if (surface == NULL || surface->status) + return surface; + + _cairo_pattern_init_solid (&pattern, color); + status = _cairo_surface_paint (surface, + color == CAIRO_COLOR_TRANSPARENT ? + CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} + +cairo_surface_t * +_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, + const cairo_solid_pattern_t *solid_pattern) +{ + if (other->backend->create_solid_pattern_surface != NULL) { + cairo_surface_t *surface; + + surface = other->backend->create_solid_pattern_surface (other, + solid_pattern); + if (surface) + return surface; + } + + return _cairo_surface_create_similar_solid (other, + _cairo_color_get_content (&solid_pattern->color), + 1, 1, + &solid_pattern->color, + FALSE); +} + +cairo_int_status_t +_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, + cairo_surface_t *solid_surface, + const cairo_solid_pattern_t *solid_pattern) +{ + /* Solid pattern surface for these backends are special and not trivial + * to repaint. Skip repainting. + * + * This does not work optimally with things like analysis surface that + * are proxies. But returning UNSUPPORTED is *safe* as it only + * disables some caching. + */ + if (other->backend->create_solid_pattern_surface != NULL && + ! other->backend->can_repaint_solid_pattern_surface (solid_surface, + solid_pattern)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return _cairo_surface_paint (solid_surface, + CAIRO_OPERATOR_SOURCE, + &solid_pattern->base, + NULL); +} + +/** + * cairo_surface_reference: + * @surface: a #cairo_surface_t + * + * Increases the reference count on @surface by one. This prevents + * @surface from being destroyed until a matching call to + * cairo_surface_destroy() is made. + * + * The number of references to a #cairo_surface_t can be get using + * cairo_surface_get_reference_count(). + * + * Return value: the referenced #cairo_surface_t. + **/ +cairo_surface_t * +cairo_surface_reference (cairo_surface_t *surface) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return surface; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + + _cairo_reference_count_inc (&surface->ref_count); + + return surface; +} +slim_hidden_def (cairo_surface_reference); + +/** + * cairo_surface_destroy: + * @surface: a #cairo_surface_t + * + * Decreases the reference count on @surface by one. If the result is + * zero, then @surface and all associated resources are freed. See + * cairo_surface_reference(). + **/ +void +cairo_surface_destroy (cairo_surface_t *surface) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&surface->ref_count)) + return; + + assert (surface->snapshot_of == NULL); + + if (! surface->finished) + cairo_surface_finish (surface); + + /* paranoid check that nobody took a reference whilst finishing */ + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + + _cairo_user_data_array_fini (&surface->user_data); + _cairo_user_data_array_fini (&surface->mime_data); + + if (surface->owns_device) + cairo_device_destroy (surface->device); + + free (surface); +} +slim_hidden_def(cairo_surface_destroy); + +/** + * cairo_surface_get_reference_count: + * @surface: a #cairo_surface_t + * + * Returns the current reference count of @surface. + * + * Return value: the current reference count of @surface. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_surface_get_reference_count (cairo_surface_t *surface) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count); +} + +/** + * cairo_surface_finish: + * @surface: the #cairo_surface_t to finish + * + * This function finishes the surface and drops all references to + * external resources. For example, for the Xlib backend it means + * that cairo will no longer access the drawable, which can be freed. + * After calling cairo_surface_finish() the only valid operations on a + * surface are getting and setting user, referencing and + * destroying, and flushing and finishing it. + * Further drawing to the surface will not affect the + * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED + * error. + * + * When the last call to cairo_surface_destroy() decreases the + * reference count to zero, cairo will call cairo_surface_finish() if + * it hasn't been called already, before freeing the resources + * associated with the surface. + **/ +void +cairo_surface_finish (cairo_surface_t *surface) +{ + cairo_status_t status; + + if (surface == NULL) + return; + + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return; + + if (surface->finished) + return; + + /* update the snapshots *before* we declare the surface as finished */ + cairo_surface_detach_snapshots (surface); + if (surface->snapshot_of != NULL) + cairo_surface_detach_snapshot (surface); + + cairo_surface_flush (surface); + surface->finished = TRUE; + + /* call finish even if in error mode */ + if (surface->backend->finish) { + status = surface->backend->finish (surface); + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + } +} +slim_hidden_def (cairo_surface_finish); + +/** + * _cairo_surface_release_device_reference: + * @surface: a #cairo_surface_t + * + * This function makes @surface release the reference to its device. The + * function is intended to be used for avoiding cycling references for + * surfaces that are owned by their device, for example cache surfaces. + * Note that the @surface will still assume that the device is available. + * So it is the caller's responsibility to ensure the device stays around + * until the @surface is destroyed. Just calling cairo_surface_finish() is + * not enough. + **/ +void +_cairo_surface_release_device_reference (cairo_surface_t *surface) +{ + assert (surface->owns_device); + + cairo_device_destroy (surface->device); + surface->owns_device = FALSE; +} + +/** + * cairo_surface_get_user_data: + * @surface: a #cairo_surface_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @surface using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +cairo_surface_get_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&surface->user_data, + key); +} + +/** + * cairo_surface_set_user_data: + * @surface: a #cairo_surface_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the surface + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @surface. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +cairo_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return surface->status; + + return _cairo_user_data_array_set_data (&surface->user_data, + key, user_data, destroy); +} + +/** + * cairo_surface_get_mime_data: + * @surface: a #cairo_surface_t + * @mime_type: the mime type of the image data + * @data: the image data to attached to the surface + * @length: the length of the image data + * + * Return mime data previously attached to @surface using the + * specified mime type. If no data has been attached with the given + * mime type, @data is set %NULL. + * + * Since: 1.10 + **/ +void +cairo_surface_get_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char **data, + unsigned long *length) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + *data = NULL; + *length = 0; + if (unlikely (surface->status)) + return; + + /* The number of mime-types attached to a surface is usually small, + * typically zero. Therefore it is quicker to do a strcmp() against + * each key than it is to intern the string (i.e. compute a hash, + * search the hash table, and do a final strcmp). + */ + num_slots = surface->mime_data.num_elements; + slots = _cairo_array_index (&surface->mime_data, 0); + for (i = 0; i < num_slots; i++) { + if (strcmp ((char *) slots[i].key, mime_type) == 0) { + cairo_mime_data_t *mime_data = slots[i].user_data; + + *data = mime_data->data; + *length = mime_data->length; + return; + } + } +} +slim_hidden_def (cairo_surface_get_mime_data); + +static void +_cairo_mime_data_destroy (void *ptr) +{ + cairo_mime_data_t *mime_data = ptr; + + if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count)) + return; + + if (mime_data->destroy && mime_data->closure) + mime_data->destroy (mime_data->closure); + + free (mime_data); +} + +/** + * CAIRO_MIME_TYPE_JP2: + * + * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1). + * + * @Since: 1.10 + */ + +/** + * CAIRO_MIME_TYPE_JPEG: + * + * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1). + * + * @Since: 1.10 + */ + +/** + * CAIRO_MIME_TYPE_PNG: + * + * The Portable Network Graphics image file format (ISO/IEC 15948). + * + * @Since: 1.10 + */ + +/** + * CAIRO_MIME_TYPE_URI: + * + * URI for an image file (unofficial MIME type). + * + * @Since: 1.10 + */ + +/** + * cairo_surface_set_mime_data: + * @surface: a #cairo_surface_t + * @mime_type: the MIME type of the image data + * @data: the image data to attach to the surface + * @length: the length of the image data + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new image data is attached using the + * same mime type. + * @closure: the data to be passed to the @destroy notifier + * + * Attach an image in the format @mime_type to @surface. To remove + * the data from a surface, call this function with same mime type + * and %NULL for @data. + * + * The attached image (or filename) data can later be used by backends + * which support it (currently: PDF, PS, SVG and Win32 Printing + * surfaces) to emit this data instead of making a snapshot of the + * @surface. This approach tends to be faster and requires less + * memory and disk space. + * + * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG, + * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI. + * + * See corresponding backend surface docs for details about which MIME + * types it can handle. Caution: the associated MIME data will be + * discarded if you draw on the surface afterwards. Use this function + * with care. + * + * Since: 1.10 + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +cairo_surface_set_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char *data, + unsigned long length, + cairo_destroy_func_t destroy, + void *closure) +{ + cairo_status_t status; + cairo_mime_data_t *mime_data; + + if (unlikely (surface->status)) + return surface->status; + if (surface->finished) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + status = _cairo_intern_string (&mime_type, -1); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + if (data != NULL) { + mime_data = malloc (sizeof (cairo_mime_data_t)); + if (unlikely (mime_data == NULL)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1); + + mime_data->data = (unsigned char *) data; + mime_data->length = length; + mime_data->destroy = destroy; + mime_data->closure = closure; + } else + mime_data = NULL; + + status = _cairo_user_data_array_set_data (&surface->mime_data, + (cairo_user_data_key_t *) mime_type, + mime_data, + _cairo_mime_data_destroy); + if (unlikely (status)) { + if (mime_data != NULL) + free (mime_data); + + return _cairo_surface_set_error (surface, status); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_surface_set_mime_data); + +static void +_cairo_mime_data_reference (const void *key, void *elt, void *closure) +{ + cairo_mime_data_t *mime_data = elt; + + _cairo_reference_count_inc (&mime_data->ref_count); +} + +cairo_status_t +_cairo_surface_copy_mime_data (cairo_surface_t *dst, + cairo_surface_t *src) +{ + cairo_status_t status; + + if (dst->status) + return dst->status; + + if (src->status) + return _cairo_surface_set_error (dst, src->status); + + /* first copy the mime-data, discarding any already set on dst */ + status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data); + if (unlikely (status)) + return _cairo_surface_set_error (dst, status); + + /* now increment the reference counters for the copies */ + _cairo_user_data_array_foreach (&dst->mime_data, + _cairo_mime_data_reference, + NULL); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_set_font_options: + * @surface: a #cairo_surface_t + * @options: a #cairo_font_options_t object that contains the + * options to use for this surface instead of backend's default + * font options. + * + * Sets the default font rendering options for the surface. + * This is useful to correctly propagate default font options when + * falling back to an image surface in a backend implementation. + * This affects the options returned in cairo_surface_get_font_options(). + * + * If @options is %NULL the surface options are reset to those of + * the backend default. + **/ +void +_cairo_surface_set_font_options (cairo_surface_t *surface, + cairo_font_options_t *options) +{ + cairo_status_t status; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (options) { + surface->has_font_options = TRUE; + _cairo_font_options_init_copy (&surface->font_options, options); + } else { + surface->has_font_options = FALSE; + } +} + +/** + * cairo_surface_get_font_options: + * @surface: a #cairo_surface_t + * @options: a #cairo_font_options_t object into which to store + * the retrieved options. All existing values are overwritten + * + * Retrieves the default font rendering options for the surface. + * This allows display surfaces to report the correct subpixel order + * for rendering on them, print surfaces to disable hinting of + * metrics and so forth. The result can then be used with + * cairo_scaled_font_create(). + **/ +void +cairo_surface_get_font_options (cairo_surface_t *surface, + cairo_font_options_t *options) +{ + if (cairo_font_options_status (options)) + return; + + if (surface->status) { + _cairo_font_options_init_default (options); + return; + } + + if (! surface->has_font_options) { + surface->has_font_options = TRUE; + + _cairo_font_options_init_default (&surface->font_options); + + if (!surface->finished && surface->backend->get_font_options) { + surface->backend->get_font_options (surface, &surface->font_options); + } + } + + _cairo_font_options_init_copy (options, &surface->font_options); +} +slim_hidden_def (cairo_surface_get_font_options); + +/** + * cairo_surface_flush: + * @surface: a #cairo_surface_t + * + * Do any pending drawing for the surface and also restore any + * temporary modifications cairo has made to the surface's + * state. This function must be called before switching from + * drawing on the surface with cairo to drawing on it directly + * with native APIs. If the surface doesn't support direct access, + * then this function does nothing. + **/ +void +cairo_surface_flush (cairo_surface_t *surface) +{ + cairo_status_t status; + + if (surface->status) + return; + + if (surface->finished) + return; + + /* update the current snapshots *before* the user updates the surface */ + cairo_surface_detach_snapshots (surface); + + if (surface->backend->flush) { + status = surface->backend->flush (surface); + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + } +} +slim_hidden_def (cairo_surface_flush); + +/** + * cairo_surface_mark_dirty: + * @surface: a #cairo_surface_t + * + * Tells cairo that drawing has been done to surface using means other + * than cairo, and that cairo should reread any cached areas. Note + * that you must call cairo_surface_flush() before doing such drawing. + */ +void +cairo_surface_mark_dirty (cairo_surface_t *surface) +{ + cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1); +} +slim_hidden_def (cairo_surface_mark_dirty); + +/** + * cairo_surface_mark_dirty_rectangle: + * @surface: a #cairo_surface_t + * @x: X coordinate of dirty rectangle + * @y: Y coordinate of dirty rectangle + * @width: width of dirty rectangle + * @height: height of dirty rectangle + * + * Like cairo_surface_mark_dirty(), but drawing has been done only to + * the specified rectangle, so that cairo can retain cached contents + * for other parts of the surface. + * + * Any cached clip set on the surface will be reset by this function, + * to make sure that future cairo calls have the clip set that they + * expect. + */ +void +cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, + int x, + int y, + int width, + int height) +{ + cairo_status_t status; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + /* The application *should* have called cairo_surface_flush() before + * modifying the surface independently of cairo (and thus having to + * call mark_dirty()). */ + assert (! _cairo_surface_has_snapshots (surface)); + assert (! _cairo_surface_has_mime_data (surface)); + + surface->is_clear = FALSE; + + if (surface->backend->mark_dirty_rectangle != NULL) { + /* XXX: FRAGILE: We're ignoring the scaling component of + * device_transform here. I don't know what the right thing to + * do would actually be if there were some scaling here, but + * we avoid this since device_transfom scaling is not exported + * publicly and mark_dirty is not used internally. */ + status = surface->backend->mark_dirty_rectangle (surface, + x + surface->device_transform.x0, + y + surface->device_transform.y0, + width, height); + + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + } +} +slim_hidden_def (cairo_surface_mark_dirty_rectangle); + +/** + * _cairo_surface_set_device_scale: + * @surface: a #cairo_surface_t + * @sx: a scale factor in the X direction + * @sy: a scale factor in the Y direction + * + * Private function for setting an extra scale factor to affect all + * drawing to a surface. This is used, for example, when replaying a + * recording surface to an image fallback intended for an eventual + * vector-oriented backend. Since the recording surface will record + * coordinates in one backend space, but the image fallback uses a + * different backend space, (differing by the fallback resolution + * scale factors), we need a scale factor correction. + * + * Caution: Not all places we use device transform correctly handle + * both a translate and a scale. An audit would be nice. + **/ +void +_cairo_surface_set_device_scale (cairo_surface_t *surface, + double sx, + double sy) +{ + cairo_status_t status; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + _cairo_surface_begin_modification (surface); + + surface->device_transform.xx = sx; + surface->device_transform.yy = sy; + surface->device_transform.xy = 0.0; + surface->device_transform.yx = 0.0; + + surface->device_transform_inverse = surface->device_transform; + status = cairo_matrix_invert (&surface->device_transform_inverse); + /* should always be invertible unless given pathological input */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_observers_notify (&surface->device_transform_observers, surface); +} + +/** + * cairo_surface_set_device_offset: + * @surface: a #cairo_surface_t + * @x_offset: the offset in the X direction, in device units + * @y_offset: the offset in the Y direction, in device units + * + * Sets an offset that is added to the device coordinates determined + * by the CTM when drawing to @surface. One use case for this function + * is when we want to create a #cairo_surface_t that redirects drawing + * for a portion of an onscreen surface to an offscreen surface in a + * way that is completely invisible to the user of the cairo + * API. Setting a transformation via cairo_translate() isn't + * sufficient to do this, since functions like + * cairo_device_to_user() will expose the hidden offset. + * + * Note that the offset affects drawing to the surface as well as + * using the surface in a source pattern. + **/ +void +cairo_surface_set_device_offset (cairo_surface_t *surface, + double x_offset, + double y_offset) +{ + cairo_status_t status; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + _cairo_surface_begin_modification (surface); + + surface->device_transform.x0 = x_offset; + surface->device_transform.y0 = y_offset; + + surface->device_transform_inverse = surface->device_transform; + status = cairo_matrix_invert (&surface->device_transform_inverse); + /* should always be invertible unless given pathological input */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_observers_notify (&surface->device_transform_observers, surface); +} +slim_hidden_def (cairo_surface_set_device_offset); + +/** + * cairo_surface_get_device_offset: + * @surface: a #cairo_surface_t + * @x_offset: the offset in the X direction, in device units + * @y_offset: the offset in the Y direction, in device units + * + * This function returns the previous device offset set by + * cairo_surface_set_device_offset(). + * + * Since: 1.2 + **/ +void +cairo_surface_get_device_offset (cairo_surface_t *surface, + double *x_offset, + double *y_offset) +{ + if (x_offset) + *x_offset = surface->device_transform.x0; + if (y_offset) + *y_offset = surface->device_transform.y0; +} +slim_hidden_def (cairo_surface_get_device_offset); + +/** + * cairo_surface_set_fallback_resolution: + * @surface: a #cairo_surface_t + * @x_pixels_per_inch: horizontal setting for pixels per inch + * @y_pixels_per_inch: vertical setting for pixels per inch + * + * Set the horizontal and vertical resolution for image fallbacks. + * + * When certain operations aren't supported natively by a backend, + * cairo will fallback by rendering operations to an image and then + * overlaying that image onto the output. For backends that are + * natively vector-oriented, this function can be used to set the + * resolution used for these image fallbacks, (larger values will + * result in more detailed images, but also larger file sizes). + * + * Some examples of natively vector-oriented backends are the ps, pdf, + * and svg backends. + * + * For backends that are natively raster-oriented, image fallbacks are + * still possible, but they are always performed at the native + * device resolution. So this function has no effect on those + * backends. + * + * Note: The fallback resolution only takes effect at the time of + * completing a page (with cairo_show_page() or cairo_copy_page()) so + * there is currently no way to have more than one fallback resolution + * in effect on a single page. + * + * The default fallback resoultion is 300 pixels per inch in both + * dimensions. + * + * Since: 1.2 + **/ +void +cairo_surface_set_fallback_resolution (cairo_surface_t *surface, + double x_pixels_per_inch, + double y_pixels_per_inch) +{ + cairo_status_t status; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) { + /* XXX Could delay raising the error until we fallback, but throwing + * the error here means that we can catch the real culprit. + */ + status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); + return; + } + + _cairo_surface_begin_modification (surface); + + surface->x_fallback_resolution = x_pixels_per_inch; + surface->y_fallback_resolution = y_pixels_per_inch; +} +slim_hidden_def (cairo_surface_set_fallback_resolution); + +/** + * cairo_surface_get_fallback_resolution: + * @surface: a #cairo_surface_t + * @x_pixels_per_inch: horizontal pixels per inch + * @y_pixels_per_inch: vertical pixels per inch + * + * This function returns the previous fallback resolution set by + * cairo_surface_set_fallback_resolution(), or default fallback + * resolution if never set. + * + * Since: 1.8 + **/ +void +cairo_surface_get_fallback_resolution (cairo_surface_t *surface, + double *x_pixels_per_inch, + double *y_pixels_per_inch) +{ + if (x_pixels_per_inch) + *x_pixels_per_inch = surface->x_fallback_resolution; + if (y_pixels_per_inch) + *y_pixels_per_inch = surface->y_fallback_resolution; +} + +int +_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface) +{ + return surface->backend->fill == NULL ? 10240 : 256; +} + +cairo_bool_t +_cairo_surface_has_device_transform (cairo_surface_t *surface) +{ + return ! _cairo_matrix_is_identity (&surface->device_transform); +} + +/** + * _cairo_surface_acquire_source_image: + * @surface: a #cairo_surface_t + * @image_out: location to store a pointer to an image surface that + * has identical contents to @surface. This surface could be @surface + * itself, a surface held internal to @surface, or it could be a new + * surface with a copy of the relevant portion of @surface. + * @image_extra: location to store image specific backend data + * + * Gets an image surface to use when drawing as a fallback when drawing with + * @surface as a source. _cairo_surface_release_source_image() must be called + * when finished. + * + * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out. + * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified + * surface. Or %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_status_t status; + + if (surface->status) + return surface->status; + + assert (!surface->finished); + + if (surface->backend->acquire_source_image == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = surface->backend->acquire_source_image (surface, + image_out, image_extra); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + if (PIXMAN_FORMAT_BPP((*image_out)->pixman_format) == 0) { + volatile char* acquire_source_image_ptr[10]; + volatile char* crasher; + int i; + for (i = 0; i < 10; i++) { + acquire_source_image_ptr[i] = (char*)surface->backend->acquire_source_image; + } + crasher = NULL; + *crasher = acquire_source_image_ptr[5]; + } + _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_release_source_image: + * @surface: a #cairo_surface_t + * @image_extra: same as return from the matching _cairo_surface_acquire_source_image() + * + * Releases any resources obtained with _cairo_surface_acquire_source_image() + **/ +void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + assert (!surface->finished); + + if (surface->backend->release_source_image) + surface->backend->release_source_image (surface, image, image_extra); +} + +/** + * _cairo_surface_acquire_dest_image: + * @surface: a #cairo_surface_t + * @interest_rect: area of @surface for which fallback drawing is being done. + * A value of %NULL indicates that the entire surface is desired. + * XXXX I'd like to get rid of being able to pass %NULL here (nothing seems to) + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * If a new surface is created, it should have the same channels and depth + * as @surface so that copying to and from it is exact. + * @image_rect: location to store area of the original surface occupied + * by the surface stored in @image. + * @image_extra: location to store image specific backend data + * + * Retrieves a local image for a surface for implementing a fallback drawing + * operation. After calling this function, the implementation of the fallback + * drawing operation draws the primitive to the surface stored in @image_out + * then calls _cairo_surface_release_dest_image(), + * which, if a temporary surface was created, copies the bits back to the + * main surface and frees the temporary surface. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that + * the backend can't draw with fallbacks. It's possible for the routine + * to store %NULL in @local_out and return %CAIRO_STATUS_SUCCESS; + * that indicates that no part of @interest_rect is visible, so no drawing + * is necessary. _cairo_surface_release_dest_image() should not be called in that + * case. + **/ +cairo_status_t +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_status_t status; + + if (surface->status) + return surface->status; + + assert (_cairo_surface_is_writable (surface)); + + if (surface->backend->acquire_dest_image == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = surface->backend->acquire_dest_image (surface, + interest_rect, + image_out, + image_rect, + image_extra); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_release_dest_image: + * @surface: a #cairo_surface_t + * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() + * @image: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if + * necessary, copying the image from @image back to @surface and freeing any + * resources that were allocated. + **/ +void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + assert (_cairo_surface_is_writable (surface)); + + if (surface->backend->release_dest_image) + surface->backend->release_dest_image (surface, interest_rect, + image, image_rect, image_extra); +} + +static cairo_status_t +_cairo_recording_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_recording_surface_t *recorder = (cairo_recording_surface_t *) src; + cairo_surface_t *similar; + cairo_status_t status; + + similar = _cairo_surface_has_snapshot (src, surface->backend); + if (similar != NULL) { + *clone_out = cairo_surface_reference (similar); + *clone_offset_x = 0; + *clone_offset_y = 0; + return CAIRO_STATUS_SUCCESS; + } + + if (recorder->unbounded || + width*height*8 < recorder->extents.width*recorder->extents.height) + { + similar = _cairo_surface_create_similar_solid (surface, + src->content, + width, height, + CAIRO_COLOR_TRANSPARENT, + FALSE); + if (similar == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (similar->status)) + return similar->status; + + cairo_surface_set_device_offset (similar, -src_x, -src_y); + + status = _cairo_recording_surface_replay (src, similar); + if (unlikely (status)) { + cairo_surface_destroy (similar); + return status; + } + } else { + similar = _cairo_surface_create_similar_scratch (surface, + src->content, + recorder->extents.width, + recorder->extents.height); + if (similar == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (similar->status)) + return similar->status; + + status = _cairo_recording_surface_replay (src, similar); + if (unlikely (status)) { + cairo_surface_destroy (similar); + return status; + } + + cairo_surface_attach_snapshot (src, similar, NULL); + + src_x = src_y = 0; + } + + *clone_out = similar; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + return CAIRO_STATUS_SUCCESS; +} + +struct acquire_source_image_data +{ + cairo_surface_t *src; + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +_wrap_release_source_image (void *data) +{ + struct acquire_source_image_data *acquire_data = data; + _cairo_surface_release_source_image (acquire_data->src, + acquire_data->image, + acquire_data->image_extra); + free(data); +} + +static cairo_status_t +_wrap_image (cairo_surface_t *src, + cairo_image_surface_t *image, + void *image_extra, + cairo_image_surface_t **out) +{ + static cairo_user_data_key_t wrap_image_key; + cairo_image_surface_t *surface; + cairo_status_t status; + + struct acquire_source_image_data *data = malloc (sizeof (*data)); + if (unlikely (data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + data->src = src; + data->image = image; + data->image_extra = image_extra; + + surface = (cairo_image_surface_t*) + _cairo_image_surface_create_with_pixman_format (image->data, + image->pixman_format, + image->width, + image->height, + image->stride); + status = surface->base.status; + if (status) { + free (data); + return status; + } + + status = _cairo_user_data_array_set_data (&surface->base.user_data, + &wrap_image_key, + data, + _wrap_release_source_image); + if (status) { + cairo_surface_destroy (&surface->base); + free (data); + return status; + } + + pixman_image_set_component_alpha ( + surface->pixman_image, + pixman_image_get_component_alpha (image->pixman_image)); + + *out = surface; + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_clone_similar: + * @surface: a #cairo_surface_t + * @src: the source image + * @src_x: extent for the rectangle in src we actually care about + * @src_y: extent for the rectangle in src we actually care about + * @width: extent for the rectangle in src we actually care about + * @height: extent for the rectangle in src we actually care about + * @clone_out: location to store a surface compatible with @surface + * and with contents identical to @src. The caller must call + * cairo_surface_destroy() on the result. + * + * Creates a surface with contents identical to @src but that + * can be used efficiently with @surface. If @surface and @src are + * already compatible then it may return a new reference to @src. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored + * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another + * error like %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + cairo_image_surface_t *image; + void *image_extra; + + if (unlikely (surface->status)) + return surface->status; + + if (unlikely (surface->finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + +#if CAIRO_HAS_TEE_SURFACE + + if (src->type == CAIRO_SURFACE_TYPE_TEE) { + cairo_surface_t *match; + + match = _cairo_tee_surface_find_match (src, + surface->backend, + src->content); + if (match != NULL) + src = match; + } + +#endif + + if (surface->backend->clone_similar != NULL) { + status = surface->backend->clone_similar (surface, src, + src_x, src_y, + width, height, + clone_offset_x, + clone_offset_y, + clone_out); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (_cairo_surface_is_image (src)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* First check to see if we can replay to a similar surface */ + if (_cairo_surface_is_recording (src)) { + return _cairo_recording_surface_clone_similar (surface, src, + src_x, src_y, + width, height, + clone_offset_x, + clone_offset_y, + clone_out); + } + + /* If we failed, try again with an image surface */ + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (status == CAIRO_STATUS_SUCCESS) { + status = _wrap_image(src, image, image_extra, &image); + if (status != CAIRO_STATUS_SUCCESS) { + _cairo_surface_release_source_image (src, image, image_extra); + } else { + status = + surface->backend->clone_similar (surface, &image->base, + src_x, src_y, + width, height, + clone_offset_x, + clone_offset_y, + clone_out); + cairo_surface_destroy(&image->base); + } + } + } + } + + /* If we're still unsupported, hit our fallback path to get a clone */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = + _cairo_surface_fallback_clone_similar (surface, src, + src_x, src_y, + width, height, + clone_offset_x, + clone_offset_y, + clone_out); + } + + if (unlikely (status)) + return status; + + /* Update the clone's device_transform (which the underlying surface + * backend knows nothing about) */ + if (*clone_out != src) { + (*clone_out)->device_transform = src->device_transform; + (*clone_out)->device_transform_inverse = src->device_transform_inverse; + } + + return status; +} + +/** + * _cairo_surface_is_similar + * @surface_a: a #cairo_surface_t + * @surface_b: a #cairo_surface_t + * @content: a #cairo_content_t + * + * Find out whether the given surfaces share the same backend, + * and if so, whether they can be considered similar. + * + * The definition of "similar" depends on the backend. In + * general, it means that the surface is equivalent to one + * that would have been generated by a call to cairo_surface_create_similar(). + * + * Return value: %TRUE if the surfaces are similar. + **/ +cairo_bool_t +_cairo_surface_is_similar (cairo_surface_t *surface_a, + cairo_surface_t *surface_b) +{ + if (surface_a->backend != surface_b->backend) + return FALSE; + + if (surface_a->backend->is_similar != NULL) + return surface_a->backend->is_similar (surface_a, surface_b); + + return TRUE; +} + +cairo_status_t +_cairo_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_int_status_t status; + + if (unlikely (dst->status)) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + + if (mask) { + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); + } + + if (dst->backend->composite) { + status = dst->backend->composite (op, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height, + clip_region); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_surface_set_error (dst, status); + } + + return _cairo_surface_set_error (dst, + _cairo_surface_fallback_composite (op, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height, + clip_region)); +} + +/** + * _cairo_surface_fill_rectangle: + * @surface: a #cairo_surface_t + * @op: the operator to apply to the rectangle + * @color: the source color + * @x: X coordinate of rectangle, in backend coordinates + * @y: Y coordinate of rectangle, in backend coordinates + * @width: width of rectangle, in backend coordinates + * @height: height of rectangle, in backend coordinates + * + * Applies an operator to a rectangle using a solid color as the source. + * See _cairo_surface_fill_rectangles() for full details. + * + * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred + **/ +cairo_status_t +_cairo_surface_fill_rectangle (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + int x, + int y, + int width, + int height) +{ + cairo_rectangle_int_t rect; + + if (surface->status) + return surface->status; + + assert (_cairo_surface_is_writable (surface)); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + return _cairo_surface_fill_rectangles (surface, op, color, &rect, 1); +} + +/** + * _cairo_surface_fill_region: + * @surface: a #cairo_surface_t + * @op: the operator to apply to the region + * @color: the source color + * @region: the region to modify, in backend coordinates + * + * Applies an operator to a set of rectangles specified as a + * #cairo_region_t using a solid color as the source. + * See _cairo_surface_fill_rectangles() for full details. + * + * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred + **/ +cairo_status_t +_cairo_surface_fill_region (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_region_t *region) +{ + int num_rects; + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_status_t status; + int i; + + if (surface->status) + return surface->status; + + assert (_cairo_surface_is_writable (surface)); + + num_rects = cairo_region_num_rectangles (region); + if (num_rects == 0) + return CAIRO_STATUS_SUCCESS; + + /* catch a common reduction of _cairo_clip_combine_with_surface() */ + if (op == CAIRO_OPERATOR_IN && + _cairo_color_equal (color, CAIRO_COLOR_WHITE)) + { + return CAIRO_STATUS_SUCCESS; + } + + if (num_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (num_rects, + sizeof (cairo_rectangle_int_t)); + if (rects == NULL) { + return _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + for (i = 0; i < num_rects; i++) + cairo_region_get_rectangle (region, i, &rects[i]); + + status = _cairo_surface_fill_rectangles (surface, + op, color, rects, num_rects); + + if (rects != stack_rects) + free (rects); + + return _cairo_surface_set_error (surface, status); +} + +/** + * _cairo_surface_fill_rectangles: + * @surface: a #cairo_surface_t + * @op: the operator to apply to the region + * @color: the source color + * @rects: the rectangles to modify, in backend coordinates + * @num_rects: the number of rectangles in @rects + * + * Applies an operator to a set of rectangles using a solid color + * as the source. Note that even if the operator is an unbounded operator + * such as %CAIRO_OPERATOR_IN, only the given set of rectangles + * is affected. This differs from _cairo_surface_composite_trapezoids() + * where the entire destination rectangle is cleared. + * + * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred + **/ +cairo_status_t +_cairo_surface_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_int_status_t status; + + if (surface->status) + return surface->status; + + assert (_cairo_surface_is_writable (surface)); + + if (num_rects == 0) + return CAIRO_STATUS_SUCCESS; + + if (surface->backend->fill_rectangles) { + status = surface->backend->fill_rectangles (surface, + op, color, + rects, num_rects); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_surface_set_error (surface, status); + } + + return _cairo_surface_set_error (surface, + _cairo_surface_fallback_fill_rectangles (surface, + op, color, + rects, num_rects)); +} + +static cairo_status_t +_pattern_has_error (const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *spattern; + + if (unlikely (pattern->status)) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_STATUS_SUCCESS; + + spattern = (const cairo_surface_pattern_t *) pattern; + if (unlikely (spattern->surface->status)) + return spattern->surface->status; + + if (unlikely (spattern->surface->finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_rectangle_int_t extents; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_clear (source)) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (surface->backend->paint != NULL) { + status = surface->backend->paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fallback_paint (surface, op, source, clip); + + FINISH: + surface->is_clear = op == CAIRO_OPERATOR_CLEAR && + (clip == NULL || + (_cairo_surface_get_extents (surface, &extents) && + _cairo_clip_contains_rectangle (clip, &extents))); + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + /* If the mask is blank, this is just an expensive no-op */ + if (_cairo_pattern_is_clear (mask) && + _cairo_operator_bounded_by_mask (op)) + { + return CAIRO_STATUS_SUCCESS; + } + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_clear (source)) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + status = _pattern_has_error (mask); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (surface->backend->mask != NULL) { + status = surface->backend->mask (surface, op, source, mask, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fallback_mask (surface, op, source, mask, clip); + + FINISH: + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_fill_stroke (cairo_surface_t *surface, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (surface->is_clear && + fill_op == CAIRO_OPERATOR_CLEAR && + stroke_op == CAIRO_OPERATOR_CLEAR) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (fill_source); + if (unlikely (status)) + return status; + + status = _pattern_has_error (stroke_source); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (surface->backend->fill_stroke) { + cairo_matrix_t dev_ctm = *stroke_ctm; + cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; + + status = surface->backend->fill_stroke (surface, + fill_op, fill_source, fill_rule, + fill_tolerance, fill_antialias, + path, + stroke_op, stroke_source, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fill (surface, fill_op, fill_source, path, + fill_rule, fill_tolerance, fill_antialias, + clip); + if (unlikely (status)) + goto FINISH; + + status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, + stroke_style, stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip); + if (unlikely (status)) + goto FINISH; + + FINISH: + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_stroke (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_clear (source)) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (surface->backend->stroke != NULL) { + status = surface->backend->stroke (surface, op, source, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fallback_stroke (surface, op, source, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + + FINISH: + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_fill (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_clear (source)) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (surface->backend->fill != NULL) { + status = surface->backend->fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fallback_fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + + FINISH: + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_int_status_t status; + + if (dst->status) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); + + if (dst->backend->composite_trapezoids) { + status = dst->backend->composite_trapezoids (op, + pattern, dst, + antialias, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps, + clip_region); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_surface_set_error (dst, status); + } + + return _cairo_surface_set_error (dst, + _cairo_surface_fallback_composite_trapezoids (op, pattern, dst, + antialias, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps, + clip_region)); +} + +cairo_span_renderer_t * +_cairo_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region) +{ + assert (dst->snapshot_of == NULL); + + if (unlikely (dst->status)) + return _cairo_span_renderer_create_in_error (dst->status); + + if (unlikely (dst->finished)) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + + if (dst->backend->create_span_renderer) { + return dst->backend->create_span_renderer (op, + pattern, dst, + antialias, + rects, + clip_region); + } + ASSERT_NOT_REACHED; + return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED); +} + +cairo_bool_t +_cairo_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias) +{ + assert (dst->snapshot_of == NULL); + assert (dst->status == CAIRO_STATUS_SUCCESS); + assert (! dst->finished); + + /* XXX: Currently we have no mono span renderer */ + if (antialias == CAIRO_ANTIALIAS_NONE) + return FALSE; + + if (dst->backend->check_span_renderer != NULL) + return dst->backend->check_span_renderer (op, pattern, dst, antialias); + + return FALSE; +} + +/** + * cairo_surface_copy_page: + * @surface: a #cairo_surface_t + * + * Emits the current page for backends that support multiple pages, + * but doesn't clear it, so that the contents of the current page will + * be retained for the next page. Use cairo_surface_show_page() if you + * want to get an empty page after the emission. + * + * There is a convenience function for this that takes a #cairo_t, + * namely cairo_copy_page(). + * + * Since: 1.6 + */ +void +cairo_surface_copy_page (cairo_surface_t *surface) +{ + cairo_status_t status_ignored; + + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + /* It's fine if some backends don't implement copy_page */ + if (surface->backend->copy_page == NULL) + return; + + status_ignored = _cairo_surface_set_error (surface, + surface->backend->copy_page (surface)); +} +slim_hidden_def (cairo_surface_copy_page); + +/** + * cairo_surface_show_page: + * @surface: a #cairo_Surface_t + * + * Emits and clears the current page for backends that support multiple + * pages. Use cairo_surface_copy_page() if you don't want to clear the page. + * + * There is a convenience function for this that takes a #cairo_t, + * namely cairo_show_page(). + * + * Since: 1.6 + **/ +void +cairo_surface_show_page (cairo_surface_t *surface) +{ + cairo_status_t status_ignored; + + if (surface->status) + return; + + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + _cairo_surface_begin_modification (surface); + + /* It's fine if some backends don't implement show_page */ + if (surface->backend->show_page == NULL) + return; + + status_ignored = _cairo_surface_set_error (surface, + surface->backend->show_page (surface)); +} +slim_hidden_def (cairo_surface_show_page); + +/** + * _cairo_surface_get_extents: + * @surface: the #cairo_surface_t to fetch extents for + * + * This function returns a bounding box for the surface. The surface + * bounds are defined as a region beyond which no rendering will + * possibly be recorded, in other words, it is the maximum extent of + * potentially usable coordinates. + * + * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface + * might be conceived as unbounded, but we force the user to provide a + * maximum size at the time of surface_create. So get_extents uses + * that size. + * + * Note: The coordinates returned are in "backend" space rather than + * "surface" space. That is, they are relative to the true (0,0) + * origin rather than the device_transform origin. This might seem a + * bit inconsistent with other #cairo_surface_t interfaces, but all + * current callers are within the surface layer where backend space is + * desired. + * + * This behavior would have to be changed is we ever exported a public + * variant of this function. + */ +cairo_bool_t +_cairo_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t bounded; + + bounded = FALSE; + if (! surface->status && surface->backend->get_extents != NULL) + bounded = surface->backend->get_extents (surface, extents); + + if (! bounded) + _cairo_unbounded_rectangle_init (extents); + + return bounded; +} + +/** + * cairo_surface_has_show_text_glyphs: + * @surface: a #cairo_surface_t + * + * Returns whether the surface supports + * sophisticated cairo_show_text_glyphs() operations. That is, + * whether it actually uses the provided text and cluster data + * to a cairo_show_text_glyphs() call. + * + * Note: Even if this function returns %FALSE, a + * cairo_show_text_glyphs() operation targeted at @surface will + * still succeed. It just will + * act like a cairo_show_glyphs() operation. Users can use this + * function to avoid computing UTF-8 text and cluster mapping if the + * target surface does not use it. + * + * Return value: %TRUE if @surface supports + * cairo_show_text_glyphs(), %FALSE otherwise + * + * Since: 1.8 + **/ +cairo_bool_t +cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) +{ + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + CAIRO_STATUS_SURFACE_FINISHED); + return FALSE; + } + + if (surface->backend->has_show_text_glyphs) + return surface->backend->has_show_text_glyphs (surface); + else + return surface->backend->show_text_glyphs != NULL; +} +slim_hidden_def (cairo_surface_has_show_text_glyphs); + +/** + * cairo_surface_set_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Sets whether the surface permits subpixel antialiasing. By default, + * surfaces permit subpixel antialiasing. + * + * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally + * requires that the pixels in the areas under a subpixel antialiasing + * operation already be opaque. + * + * Since: 1.12 + **/ +void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled) +{ + if (surface->status) + return; + + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + surface->permit_subpixel_antialiasing = + enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; +} +slim_hidden_def (cairo_surface_set_subpixel_antialiasing); + +/** + * cairo_surface_get_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Gets whether the surface supports subpixel antialiasing. By default, + * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other + * surfaces do not. + * + * Since: 1.12 + **/ +cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) +{ + if (surface->status) + return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + + return surface->permit_subpixel_antialiasing ? + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; +} +slim_hidden_def (cairo_surface_get_subpixel_antialiasing); + +/* Note: the backends may modify the contents of the glyph array as long as + * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to + * avoid copying the array again and again, and edit it in-place. + * Backends are in fact free to use the array as a generic buffer as they + * see fit. + * + * For show_glyphs backend method, and NOT for show_text_glyphs method, + * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify + * that they have successfully rendered some of the glyphs (from the beginning + * of the array), but not all. If they don't touch remaining_glyphs, it + * defaults to all glyphs. + * + * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and + * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale. + */ +cairo_status_t +_cairo_surface_show_text_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_scaled_font_t *dev_scaled_font = scaled_font; + + if (unlikely (surface->status)) + return surface->status; + + if (num_glyphs == 0 && utf8_len == 0) + return CAIRO_STATUS_SUCCESS; + + if (clip && clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return CAIRO_STATUS_SUCCESS; + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + _cairo_surface_begin_modification (surface); + + if (_cairo_surface_has_device_transform (surface) && + ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL)) + { + cairo_font_options_t font_options; + cairo_matrix_t dev_ctm, font_matrix; + + cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); + cairo_scaled_font_get_ctm (scaled_font, &dev_ctm); + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform); + cairo_scaled_font_get_font_options (scaled_font, &font_options); + dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font), + &font_matrix, + &dev_ctm, + &font_options); + } + status = cairo_scaled_font_status (dev_scaled_font); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + status = CAIRO_INT_STATUS_UNSUPPORTED; + + /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and + * show_text_glyphs. Keep in synch. */ + if (clusters) { + /* A real show_text_glyphs call. Try show_text_glyphs backend + * method first */ + if (surface->backend->show_text_glyphs != NULL) { + status = surface->backend->show_text_glyphs (surface, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags, + dev_scaled_font, + clip); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->backend->show_glyphs) + { + int remaining_glyphs = num_glyphs; + status = surface->backend->show_glyphs (surface, op, + source, + glyphs, num_glyphs, + dev_scaled_font, + clip, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) + status = CAIRO_STATUS_SUCCESS; + } + } else { + /* A mere show_glyphs call. Try show_glyphs backend method first */ + if (surface->backend->show_glyphs != NULL) { + int remaining_glyphs = num_glyphs; + status = surface->backend->show_glyphs (surface, op, + source, + glyphs, num_glyphs, + dev_scaled_font, + clip, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) + status = CAIRO_STATUS_SUCCESS; + } else if (surface->backend->show_text_glyphs != NULL) { + /* Intentionally only try show_text_glyphs method for show_glyphs + * calls if backend does not have show_glyphs. If backend has + * both methods implemented, we don't fallback from show_glyphs to + * show_text_glyphs, and hence the backend can assume in its + * show_text_glyphs call that clusters is not NULL (which also + * implies that UTF-8 is not NULL, unless the text is + * zero-length). + */ + status = surface->backend->show_text_glyphs (surface, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags, + dev_scaled_font, + clip); + } + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_surface_fallback_show_glyphs (surface, op, + source, + glyphs, num_glyphs, + dev_scaled_font, + clip); + } + + if (dev_scaled_font != scaled_font) + cairo_scaled_font_destroy (dev_scaled_font); + + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + +/* XXX: Previously, we had a function named _cairo_surface_show_glyphs + * with not-so-useful semantics. We've now got a + * _cairo_surface_show_text_glyphs with the proper semantics, and its + * fallback still uses this old function (which still needs to be + * cleaned up in terms of both semantics and naming). */ +cairo_status_t +_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region) +{ + cairo_status_t status; + + if (dst->status) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + + if (dst->backend->old_show_glyphs) { + status = dst->backend->old_show_glyphs (scaled_font, + op, pattern, dst, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs, + clip_region); + } else + status = CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_surface_set_error (dst, status); +} + +static cairo_status_t +_cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, + cairo_rectangle_int_t *src_rectangle, + cairo_rectangle_int_t *mask_rectangle, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_rectangle_int_t dst_rectangle; + cairo_region_t clear_region; + cairo_status_t status; + + /* The area that was drawn is the area in the destination rectangle but + * not within the source or the mask. + */ + dst_rectangle.x = dst_x; + dst_rectangle.y = dst_y; + dst_rectangle.width = width; + dst_rectangle.height = height; + + _cairo_region_init_rectangle (&clear_region, &dst_rectangle); + + if (clip_region != NULL) { + status = cairo_region_intersect (&clear_region, clip_region); + if (unlikely (status)) + goto CLEANUP_REGIONS; + } + + if (src_rectangle != NULL) { + if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) + goto EMPTY; + } + + if (mask_rectangle != NULL) { + if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) + goto EMPTY; + } + + /* Now compute the area that is in dst but not drawn */ + status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle); + if (unlikely (status) || cairo_region_is_empty (&clear_region)) + goto CLEANUP_REGIONS; + + EMPTY: + status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + + CLEANUP_REGIONS: + _cairo_region_fini (&clear_region); + + return _cairo_surface_set_error (dst, status); +} + +/** + * _cairo_surface_composite_fixup_unbounded: + * @dst: the destination surface + * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) + * @src_width: width of source surface + * @src_height: height of source surface + * @mask_attr: mask surface attributes or %NULL if no mask + * @mask_width: width of mask surface + * @mask_height: height of mask surface + * @src_x: @src_x from _cairo_surface_composite() + * @src_y: @src_y from _cairo_surface_composite() + * @mask_x: @mask_x from _cairo_surface_composite() + * @mask_y: @mask_y from _cairo_surface_composite() + * @dst_x: @dst_x from _cairo_surface_composite() + * @dst_y: @dst_y from _cairo_surface_composite() + * @width: @width from _cairo_surface_composite() + * @height: @height_x from _cairo_surface_composite() + * + * Eeek! Too many parameters! This is a helper function to take care of fixing + * up for bugs in libpixman and RENDER where, when asked to composite an + * untransformed surface with an unbounded operator (like CLEAR or SOURCE) + * only the region inside both the source and the mask is affected. + * This function clears the region that should have been drawn but was wasn't. + **/ +cairo_status_t +_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, + cairo_surface_attributes_t *src_attr, + int src_width, + int src_height, + cairo_surface_attributes_t *mask_attr, + int mask_width, + int mask_height, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_rectangle_int_t src_tmp, mask_tmp; + cairo_rectangle_int_t *src_rectangle = NULL; + cairo_rectangle_int_t *mask_rectangle = NULL; + + if (unlikely (dst->status)) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + + /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, + * non-repeating sources and masks. Other sources and masks can be ignored. + */ + if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + src_attr->extend == CAIRO_EXTEND_NONE) + { + src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); + src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); + src_tmp.width = src_width; + src_tmp.height = src_height; + + src_rectangle = &src_tmp; + } + + if (mask_attr && + _cairo_matrix_is_integer_translation (&mask_attr->matrix, NULL, NULL) && + mask_attr->extend == CAIRO_EXTEND_NONE) + { + mask_tmp.x = (dst_x - (mask_x + mask_attr->x_offset)); + mask_tmp.y = (dst_y - (mask_y + mask_attr->y_offset)); + mask_tmp.width = mask_width; + mask_tmp.height = mask_height; + + mask_rectangle = &mask_tmp; + } + + return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, + dst_x, dst_y, width, height, + clip_region); +} + +/** + * _cairo_surface_composite_shape_fixup_unbounded: + * @dst: the destination surface + * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) + * @src_width: width of source surface + * @src_height: height of source surface + * @mask_width: width of mask surface + * @mask_height: height of mask surface + * @src_x: @src_x from _cairo_surface_composite() + * @src_y: @src_y from _cairo_surface_composite() + * @mask_x: @mask_x from _cairo_surface_composite() + * @mask_y: @mask_y from _cairo_surface_composite() + * @dst_x: @dst_x from _cairo_surface_composite() + * @dst_y: @dst_y from _cairo_surface_composite() + * @width: @width from _cairo_surface_composite() + * @height: @height_x from _cairo_surface_composite() + * + * Like _cairo_surface_composite_fixup_unbounded(), but instead of + * handling the case where we have a source pattern and a mask + * pattern, handle the case where we are compositing a source pattern + * using a mask we create ourselves, as in + * _cairo_surface_composite_glyphs() or _cairo_surface_composite_trapezoids() + **/ +cairo_status_t +_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, + cairo_surface_attributes_t *src_attr, + int src_width, + int src_height, + int mask_width, + int mask_height, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_rectangle_int_t src_tmp, *src= NULL; + cairo_rectangle_int_t mask; + + if (dst->status) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + + /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, + * non-repeating sources and masks. Other sources and masks can be ignored. + */ + if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + src_attr->extend == CAIRO_EXTEND_NONE) + { + src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); + src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); + src_tmp.width = src_width; + src_tmp.height = src_height; + + src = &src_tmp; + } + + mask.x = dst_x - mask_x; + mask.y = dst_y - mask_y; + mask.width = mask_width; + mask.height = mask_height; + + return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask, + dst_x, dst_y, width, height, + clip_region); +} + +/** + * _cairo_surface_set_resolution + * @surface: the surface + * @x_res: x resolution, in dpi + * @y_res: y resolution, in dpi + * + * Set the actual surface resolution of @surface to the given x and y DPI. + * Mainly used for correctly computing the scale factor when fallback + * rendering needs to take place in the paginated surface. + */ +void +_cairo_surface_set_resolution (cairo_surface_t *surface, + double x_res, + double y_res) +{ + if (surface->status) + return; + + surface->x_resolution = x_res; + surface->y_resolution = y_res; +} + +/* Generic methods for determining operation extents. */ + +static void +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +{ + const cairo_rectangle_int_t *clip_extents; + cairo_bool_t is_empty; + + clip_extents = NULL; + if (clip != NULL) + clip_extents = _cairo_clip_get_extents (clip); + + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (extents, clip_extents); +} + +static void +_cairo_surface_operation_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + is_empty = _cairo_surface_get_extents (surface, extents); + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + + _cairo_pattern_get_extents (source, &source_extents); + is_empty = _cairo_rectangle_intersect (extents, &source_extents); + } + + _rectangle_intersect_clip (extents, clip); +} + +cairo_status_t +_cairo_surface_paint_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + _cairo_surface_operation_extents (surface, op, source, clip, extents); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_mask_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + _cairo_pattern_get_extents (mask, &mask_extents); + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_stroke_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_fill_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t mask_extents; + + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + is_empty = _cairo_rectangle_intersect (extents, &mask_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_surface_glyphs_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_bool_t is_empty; + + _cairo_surface_operation_extents (surface, op, source, clip, extents); + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t glyph_extents; + + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + is_empty = _cairo_rectangle_intersect (extents, &glyph_extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_surface_create_in_error (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_surface_t *) &_cairo_surface_nil; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch; + case CAIRO_STATUS_INVALID_STATUS: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_status; + case CAIRO_STATUS_INVALID_CONTENT: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_content; + case CAIRO_STATUS_INVALID_FORMAT: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_format; + case CAIRO_STATUS_INVALID_VISUAL: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual; + case CAIRO_STATUS_READ_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_read_error; + case CAIRO_STATUS_WRITE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_write_error; + case CAIRO_STATUS_FILE_NOT_FOUND: + return (cairo_surface_t *) &_cairo_surface_nil_file_not_found; + case CAIRO_STATUS_TEMP_FILE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error; + case CAIRO_STATUS_INVALID_STRIDE: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; + case CAIRO_STATUS_INVALID_SIZE: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_size; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch; + case CAIRO_STATUS_DEVICE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_device_error; + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + case CAIRO_STATUS_INVALID_SLANT: + case CAIRO_STATUS_INVALID_WEIGHT: + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + default: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } +} + +/* LocalWords: rasterized + */ diff --git a/libs/cairo/src/cairo-svg-surface-private.h b/libs/cairo/src/cairo-svg-surface-private.h new file mode 100644 index 000000000..eea51cbbf --- /dev/null +++ b/libs/cairo/src/cairo-svg-surface-private.h @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-svg-surface.c b/libs/cairo/src/cairo-svg-surface.c new file mode 100644 index 000000000..b18527f0c --- /dev/null +++ b/libs/cairo/src/cairo-svg-surface.c @@ -0,0 +1,2811 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for snprintf() */ +#include "cairoint.h" +#include "cairo-svg.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" +#include "cairo-image-info-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-paginated-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-svg-surface-private.h" + +/** + * SECTION:cairo-svg + * @Title: SVG Surfaces + * @Short_Description: Rendering SVG documents + * @See_Also: #cairo_surface_t + * + * The SVG surface is used to render cairo graphics to + * SVG files and is a multi-page vector surface backend. + */ + +/** + * CAIRO_HAS_SVG_SURFACE: + * + * Defined if the SVG surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +typedef struct cairo_svg_page cairo_svg_page_t; + +static const int invalid_pattern_id = -1; + +static const cairo_svg_version_t _cairo_svg_versions[] = +{ + CAIRO_SVG_VERSION_1_1, + CAIRO_SVG_VERSION_1_2 +}; + +#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) + +static void +_cairo_svg_surface_emit_path (cairo_output_stream_t *output, + cairo_path_fixed_t *path, + const cairo_matrix_t *ctm_inverse); + +static cairo_bool_t +_cairo_svg_version_has_page_set_support (cairo_svg_version_t version) +{ + return version > CAIRO_SVG_VERSION_1_1; +} + +static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] = +{ + "SVG 1.1", + "SVG 1.2" +}; + +static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] = +{ + "1.1", + "1.2" +}; + +struct cairo_svg_page { + unsigned int surface_id; + unsigned int clip_level; + cairo_output_stream_t *xml_node; +}; + +struct cairo_svg_document { + cairo_output_stream_t *output_stream; + unsigned long refcount; + cairo_surface_t *owner; + cairo_bool_t finished; + + double width; + double height; + + cairo_output_stream_t *xml_node_defs; + cairo_output_stream_t *xml_node_glyphs; + + unsigned int linear_pattern_id; + unsigned int radial_pattern_id; + unsigned int pattern_id; + unsigned int filter_id; + unsigned int clip_id; + unsigned int mask_id; + + cairo_bool_t alpha_filter; + + cairo_svg_version_t svg_version; + + cairo_scaled_font_subsets_t *font_subsets; +}; + +static cairo_status_t +_cairo_svg_document_create (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version, + cairo_svg_document_t **document_out); + +static cairo_status_t +_cairo_svg_document_destroy (cairo_svg_document_t *document); + +static cairo_status_t +_cairo_svg_document_finish (cairo_svg_document_t *document); + +static cairo_svg_document_t * +_cairo_svg_document_reference (cairo_svg_document_t *document); + +static unsigned int +_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document); + +static cairo_surface_t * +_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, + cairo_content_t content, + double width, + double height); +static cairo_surface_t * +_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version); + +static const cairo_surface_backend_t cairo_svg_surface_backend; +static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend; + +/** + * cairo_svg_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * incrementally to the stream represented by @write_func and @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + */ +cairo_surface_t * +cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); +} + +/** + * cairo_svg_surface_create: + * @filename: a filename for the SVG output (must be writable), %NULL may be + * used to specify no output. This will generate a SVG surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * to @filename. + * + * The SVG surface backend recognizes the following MIME types for the + * data attached to a surface (see cairo_surface_set_mime_data()) when + * it is used as a source pattern for drawing on this surface: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, + * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend + * emits a href with the content of MIME data instead of a surface + * snapshot (PNG, Base64-encoded) in the corresponding image tag. + * + * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined + * first. If present, the URI is emitted as is: assuring the + * correctness of URI is left to the client code. + * + * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG + * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is + * Base64-encoded and emitted. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_svg_surface_create (const char *filename, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); +} + +static cairo_bool_t +_cairo_surface_is_svg (cairo_surface_t *surface) +{ + return surface->backend == &cairo_svg_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a svg_surface, then set svg_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_svg_surface (cairo_surface_t *surface, + cairo_svg_surface_t **svg_surface) +{ + cairo_surface_t *target; + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + target = _cairo_paginated_surface_get_target (surface); + if (target->status) { + status_ignored = _cairo_surface_set_error (surface, + target->status); + return FALSE; + } + if (target->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_svg (target)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *svg_surface = (cairo_svg_surface_t *) target; + return TRUE; +} + +/** + * cairo_svg_surface_restrict_to_version: + * @surface: a SVG #cairo_surface_t + * @version: SVG version + * + * Restricts the generated SVG file to @version. See cairo_svg_get_versions() + * for a list of available version values that can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.2 + **/ +void +cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_svg_version_t version) +{ + cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_svg_surface (abstract_surface, &surface)) + return; + + if (version < CAIRO_SVG_VERSION_LAST) + surface->document->svg_version = version; +} + +/** + * cairo_svg_get_versions: + * @versions: supported version list + * @num_versions: list length + * + * Used to retrieve the list of supported versions. See + * cairo_svg_surface_restrict_to_version(). + * + * Since: 1.2 + **/ +void +cairo_svg_get_versions (cairo_svg_version_t const **versions, + int *num_versions) +{ + if (versions != NULL) + *versions = _cairo_svg_versions; + + if (num_versions != NULL) + *num_versions = CAIRO_SVG_VERSION_LAST; +} + +/** + * cairo_svg_version_to_string: + * @version: a version id + * + * Get the string representation of the given @version id. This function + * will return %NULL if @version isn't valid. See cairo_svg_get_versions() + * for a way to get the list of valid version ids. + * + * Return value: the string associated to given version. + * + * Since: 1.2 + **/ +const char * +cairo_svg_version_to_string (cairo_svg_version_t version) +{ + if (version >= CAIRO_SVG_VERSION_LAST) + return NULL; + + return _cairo_svg_version_strings[version]; +} + +static cairo_bool_t +_cliprect_covers_surface (cairo_svg_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + if (box.p1.x <= 0 && + box.p1.y <= 0 && + _cairo_fixed_to_double (box.p2.x) >= surface->width && + _cairo_fixed_to_double (box.p2.y) >= surface->height) + { + return TRUE; + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_svg_surface_t *surface = cairo_container_of (clipper, + cairo_svg_surface_t, + clipper); + cairo_svg_document_t *document = surface->document; + unsigned int i; + + if (path == NULL) { + for (i = 0; i < surface->clip_level; i++) + _cairo_output_stream_printf (surface->xml_node, "\n"); + + surface->clip_level = 0; + return CAIRO_STATUS_SUCCESS; + } + + /* skip trivial whole-page clips */ + if (_cliprect_covers_surface (surface, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " clip_id); + _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); + + _cairo_output_stream_printf (document->xml_node_defs, + "/>\n" + "\n"); + + _cairo_output_stream_printf (surface->xml_node, + "\n", + document->clip_id, + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); + + document->clip_id++; + surface->clip_level++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, + cairo_content_t content, + double width, + double height) +{ + cairo_svg_surface_t *surface; + cairo_surface_t *paginated; + cairo_status_t status, status_ignored; + + surface = malloc (sizeof (cairo_svg_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &cairo_svg_surface_backend, + NULL, /* device */ + content); + + surface->width = width; + surface->height = height; + + surface->document = _cairo_svg_document_reference (document); + + surface->clip_level = 0; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_svg_surface_clipper_intersect_clip_path); + + surface->base_clip = document->clip_id++; + surface->is_base_clip_emitted = FALSE; + + surface->xml_node = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (surface->xml_node); + if (unlikely (status)) + goto CLEANUP; + + _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); + + if (content == CAIRO_CONTENT_COLOR) { + _cairo_output_stream_printf (surface->xml_node, + "\n", + width, height); + status = _cairo_output_stream_get_status (surface->xml_node); + if (unlikely (status)) + goto CLEANUP; + } + + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->force_fallbacks = FALSE; + surface->content = content; + + paginated = _cairo_paginated_surface_create (&surface->base, + surface->content, + &cairo_svg_surface_paginated_backend); + status = paginated->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return paginated; + } + + /* ignore status as we are on the error path */ +CLEANUP: + status_ignored = _cairo_output_stream_destroy (surface->xml_node); + status_ignored = _cairo_svg_document_destroy (document); + + free (surface); + + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version) +{ + cairo_svg_document_t *document = NULL; /* silence compiler */ + cairo_surface_t *surface; + cairo_status_t status; + + status = _cairo_svg_document_create (stream, + width, height, version, + &document); + if (unlikely (status)) { + surface = _cairo_surface_create_in_error (status); + /* consume the output stream on behalf of caller */ + status = _cairo_output_stream_destroy (stream); + return surface; + } + + surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, + width, height); + if (surface->status) { + status = _cairo_svg_document_destroy (document); + return surface; + } + + document->owner = surface; + status = _cairo_svg_document_destroy (document); + /* the ref count should be 2 at this point */ + assert (status == CAIRO_STATUS_SUCCESS); + + return surface; +} + +static cairo_svg_page_t * +_cairo_svg_surface_store_page (cairo_svg_surface_t *surface) +{ + unsigned int i; + cairo_svg_page_t page; + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (stream)) { + status = _cairo_output_stream_destroy (stream); + return NULL; + } + + page.surface_id = surface->base.unique_id; + page.clip_level = surface->clip_level; + page.xml_node = surface->xml_node; + + if (_cairo_array_append (&surface->page_set, &page)) { + status = _cairo_output_stream_destroy (stream); + return NULL; + } + + surface->xml_node = stream; + surface->clip_level = 0; + for (i = 0; i < page.clip_level; i++) + _cairo_output_stream_printf (page.xml_node, "\n"); + + _cairo_surface_clipper_reset (&surface->clipper); + + return _cairo_array_index (&surface->page_set, + surface->page_set.num_elements - 1); +} + +static cairo_int_status_t +_cairo_svg_surface_copy_page (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_page_t *page; + + page = _cairo_svg_surface_store_page (surface); + if (unlikely (page == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_memory_stream_copy (page->xml_node, surface->xml_node); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_show_page (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_transform (cairo_output_stream_t *output, + char const *attribute_str, + const cairo_matrix_t *object_matrix, + const cairo_matrix_t *parent_matrix) +{ + cairo_matrix_t matrix = *object_matrix; + + if (parent_matrix != NULL) + cairo_matrix_multiply (&matrix, &matrix, parent_matrix); + + if (!_cairo_matrix_is_identity (&matrix)) + _cairo_output_stream_printf (output, + "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"", + attribute_str, + matrix.xx, matrix.yx, + matrix.xy, matrix.yy, + matrix.x0, matrix.y0); +} + +typedef struct { + cairo_output_stream_t *output; + const cairo_matrix_t *ctm_inverse; +} svg_path_info_t; + +static cairo_status_t +_cairo_svg_path_move_to (void *closure, + const cairo_point_t *point) +{ + svg_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->ctm_inverse) + cairo_matrix_transform_point (info->ctm_inverse, &x, &y); + + _cairo_output_stream_printf (info->output, "M %f %f ", x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_line_to (void *closure, + const cairo_point_t *point) +{ + svg_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->ctm_inverse) + cairo_matrix_transform_point (info->ctm_inverse, &x, &y); + + _cairo_output_stream_printf (info->output, "L %f %f ", x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + svg_path_info_t *info = closure; + double bx = _cairo_fixed_to_double (b->x); + double by = _cairo_fixed_to_double (b->y); + double cx = _cairo_fixed_to_double (c->x); + double cy = _cairo_fixed_to_double (c->y); + double dx = _cairo_fixed_to_double (d->x); + double dy = _cairo_fixed_to_double (d->y); + + if (info->ctm_inverse) { + cairo_matrix_transform_point (info->ctm_inverse, &bx, &by); + cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy); + cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); + } + + _cairo_output_stream_printf (info->output, + "C %f %f %f %f %f %f ", + bx, by, cx, cy, dx, dy); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_close_path (void *closure) +{ + svg_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output, "Z "); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_path (cairo_output_stream_t *output, + cairo_path_fixed_t *path, + const cairo_matrix_t *ctm_inverse) +{ + cairo_status_t status; + svg_path_info_t info; + + _cairo_output_stream_printf (output, "d=\""); + + info.output = output; + info.ctm_inverse = ctm_inverse; + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_svg_path_move_to, + _cairo_svg_path_line_to, + _cairo_svg_path_curve_to, + _cairo_svg_path_close_path, + &info); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_output_stream_printf (output, "\""); +} + +static cairo_int_status_t +_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_int_status_t status; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_glyphs, + "xml_node_glyphs, + scaled_glyph->path, NULL); + + _cairo_output_stream_printf (document->xml_node_glyphs, + "/>\n"); + + return status; +} + +static cairo_int_status_t +_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_image_surface_t *image; + cairo_status_t status; + uint8_t *row, *byte; + int rows, cols; + int x, y, bit; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface, + CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, " transform", + &image->base.device_transform_inverse, NULL); + _cairo_output_stream_printf (document->xml_node_glyphs, ">/n"); + + for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) { + for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { + uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + for (bit = 7; bit >= 0 && x < image->width; bit--, x++) { + if (output_byte & (1 << bit)) { + _cairo_output_stream_printf (document->xml_node_glyphs, + "\n", + x, y); + } + } + } + } + _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); + + cairo_surface_destroy (&image->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_document_emit_glyph (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + unsigned int font_id, + unsigned int subset_glyph_index) +{ + cairo_status_t status; + + _cairo_output_stream_printf (document->xml_node_glyphs, + "\n", + font_id, + subset_glyph_index); + + status = _cairo_svg_document_emit_outline_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_svg_document_emit_bitmap_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_svg_document_t *document = closure; + unsigned int i; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (font_subset->scaled_font); + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_svg_document_emit_glyph (document, + font_subset->scaled_font, + font_subset->glyphs[i], + font_subset->font_id, i); + if (unlikely (status)) + break; + } + _cairo_scaled_font_thaw_cache (font_subset->scaled_font); + + return status; +} + +static cairo_status_t +_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) +{ + cairo_status_t status; + + status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, + _cairo_svg_document_emit_font_subset, + document); + if (unlikely (status)) + goto FAIL; + + status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, + _cairo_svg_document_emit_font_subset, + document); + + FAIL: + _cairo_scaled_font_subsets_destroy (document->font_subsets); + document->font_subsets = NULL; + + return status; +} + +static char const * +_cairo_svg_surface_operators[] = { + "clear", + + "src", "src-over", "src-in", + "src-out", "src-atop", + + "dst", "dst-over", "dst-in", + "dst-out", "dst-atop", + + "xor", "plus", + "color-dodge", /* FIXME: saturate ? */ + + "multiply", "screen", "overlay", + "darken", "lighten", + "color-dodge", "color-burn", + "hard-light", "soft-light", + "difference", "exclusion" +}; + +static cairo_bool_t +_cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + /* guard against newly added operators */ + if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* allow operators being NULL if they are unsupported */ + if (_cairo_svg_surface_operators[op] == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + cairo_svg_document_t *document = surface->document; + + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* SVG doesn't support extend reflect for image pattern */ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && + pattern->extend == CAIRO_EXTEND_REFLECT) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (document->svg_version >= CAIRO_SVG_VERSION_1_2) + return _cairo_svg_surface_analyze_operator (surface, op); + + if (op == CAIRO_OPERATOR_OVER) + return CAIRO_STATUS_SUCCESS; + + /* The SOURCE operator is only supported if there is nothing + * painted underneath. */ + if (op == CAIRO_OPERATOR_SOURCE) + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_svg_surface_finish (void *abstract_surface) +{ + cairo_status_t status, status2; + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + cairo_svg_page_t *page; + unsigned int i; + + if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) + status = _cairo_svg_document_finish (document); + else + status = CAIRO_STATUS_SUCCESS; + + if (surface->xml_node != NULL) { + status2 = _cairo_output_stream_destroy (surface->xml_node); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + + for (i = 0; i < surface->page_set.num_elements; i++) { + page = _cairo_array_index (&surface->page_set, i); + status2 = _cairo_output_stream_destroy (page->xml_node); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + _cairo_array_fini (&surface->page_set); + + _cairo_surface_clipper_reset (&surface->clipper); + + status2 = _cairo_svg_document_destroy (document); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + + +static void +_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) +{ + if (document->alpha_filter) + return; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n"); + + document->alpha_filter = TRUE; +} + +typedef struct { + cairo_output_stream_t *output; + unsigned int in_mem; + unsigned int trailing; + unsigned char src[3]; +} base64_write_closure_t; + +static char const base64_table[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static cairo_status_t +base64_write_func (void *closure, + const unsigned char *data, + unsigned int length) +{ + base64_write_closure_t *info = (base64_write_closure_t *) closure; + unsigned int i; + unsigned char *src; + + src = info->src; + + if (info->in_mem + length < 3) { + for (i = 0; i < length; i++) { + src[i + info->in_mem] = *data++; + } + info->in_mem += length; + return CAIRO_STATUS_SUCCESS; + } + + do { + unsigned char dst[4]; + + for (i = info->in_mem; i < 3; i++) { + src[i] = *data++; + length--; + } + info->in_mem = 0; + + dst[0] = base64_table[src[0] >> 2]; + dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; + dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; + dst[3] = base64_table[src[2] & 0xfc >> 2]; + /* Special case for the last missing bits */ + switch (info->trailing) { + case 2: + dst[2] = '='; + case 1: + dst[3] = '='; + default: + break; + } + _cairo_output_stream_write (info->output, dst, 4); + } while (length >= 3); + + for (i = 0; i < length; i++) { + src[i] = *data++; + } + info->in_mem = length; + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_int_status_t +_cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t image_info; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t +_cairo_surface_base64_encode_png (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (unlikely (surface->status)) + return surface->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (output, "data:image/png;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t +_cairo_surface_base64_encode (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + cairo_status_t status; + base64_write_closure_t info; + + status = _cairo_surface_base64_encode_jpeg (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_base64_encode_png (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + _cairo_output_stream_printf (info.output, "data:image/png;base64,"); + + status = cairo_surface_write_to_png_stream (surface, base64_write_func, + (void *) &info); + + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static void +_cairo_svg_surface_emit_operator (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]); + if (!_cairo_operator_bounded_by_source (op)) + _cairo_output_stream_printf (output, " clip-to-self=\"true\""); + } +} + +static void +_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]); + if (!_cairo_operator_bounded_by_source (op)) + _cairo_output_stream_printf (output, "clip-to-self:true;"); + } +} + +/** + * _cairo_svg_surface_emit_attr_value: + * + * Write the value to output the stream as a sequence of characters, + * while escaping those which have special meaning in the XML + * attribute's value context: & and ". + **/ +static void +_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, + const unsigned char *value, + unsigned int length) +{ + const unsigned char *p; + const unsigned char *q; + unsigned int i; + + /* we'll accumulate non-special chars in [q, p) range */ + p = value; + q = p; + for (i = 0; i < length; i++, p++) { + if (*p == '&' || *p == '"') { + /* flush what's left before special char */ + if (p != q) { + _cairo_output_stream_write (stream, q, p - q); + q = p + 1; + } + + if (*p == '&') + _cairo_output_stream_printf (stream, "&"); + else // p == '"' + _cairo_output_stream_printf (stream, """); + } + } + + /* flush the trailing chars if any */ + if (p != q) + _cairo_output_stream_write (stream, q, p - q); +} + +static cairo_status_t +_cairo_svg_surface_emit_surface (cairo_svg_document_t *document, + cairo_surface_t *surface) +{ + 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, + "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, + "\n "); + } + + _cairo_output_stream_printf (output, + "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, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, + cairo_recording_surface_t *source) +{ + cairo_status_t status; + cairo_surface_t *paginated_surface; + cairo_svg_surface_t *svg_surface; + cairo_array_t *page_set; + + cairo_output_stream_t *contents; + + if (_cairo_user_data_array_get_data (&source->base.user_data, + (cairo_user_data_key_t *) document)) + { + return CAIRO_STATUS_SUCCESS; + } + + paginated_surface = _cairo_svg_surface_create_for_document (document, + source->content, + source->extents_pixels.width, + source->extents_pixels.height); + if (unlikely (paginated_surface->status)) + return paginated_surface->status; + + svg_surface = (cairo_svg_surface_t *) + _cairo_paginated_surface_get_target (paginated_surface); + cairo_surface_set_fallback_resolution (paginated_surface, + document->owner->x_fallback_resolution, + document->owner->y_fallback_resolution); + cairo_surface_set_device_offset (&svg_surface->base, + -source->extents_pixels.x, + -source->extents_pixels.y); + + status = _cairo_recording_surface_replay (&source->base, paginated_surface); + if (unlikely (status)) { + cairo_surface_destroy (paginated_surface); + return status; + } + + cairo_surface_show_page (paginated_surface); + status = cairo_surface_status (paginated_surface); + if (unlikely (status)) { + cairo_surface_destroy (paginated_surface); + return status; + } + + if (! svg_surface->is_base_clip_emitted) { + svg_surface->is_base_clip_emitted = TRUE; + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n", + svg_surface->base_clip, + svg_surface->width, + svg_surface->height); + } + + if (source->content == CAIRO_CONTENT_ALPHA) { + _cairo_svg_surface_emit_alpha_filter (document); + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + source->base.unique_id, + svg_surface->base_clip); + } else { + _cairo_output_stream_printf (document->xml_node_defs, + "\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, "\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, + "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, + "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, "\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, + "\n", + pattern->stops[0].offset, + pattern->stops[0].color.red * 100.0, + pattern->stops[0].color.green * 100.0, + pattern->stops[0].color.blue * 100.0, + pattern->stops[0].color.alpha); + return CAIRO_STATUS_SUCCESS; + } + + if (emulate_reflect || reverse_stops) { + n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; + stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < pattern->n_stops; i++) { + if (reverse_stops) { + stops[i] = pattern->stops[pattern->n_stops - i - 1]; + stops[i].offset = 1.0 - stops[i].offset; + } else + stops[i] = pattern->stops[i]; + if (emulate_reflect) { + stops[i].offset /= 2; + if (i > 0 && i < (pattern->n_stops - 1)) { + if (reverse_stops) { + stops[i + pattern->n_stops - 1] = pattern->stops[i]; + stops[i + pattern->n_stops - 1].offset = + 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset; + } else { + stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1]; + stops[i + pattern->n_stops - 1].offset = + 1 - 0.5 * stops[i + pattern->n_stops - 1].offset; + } + } + } + } + } else { + n_stops = pattern->n_stops; + stops = pattern->stops; + } + + if (start_offset >= 0.0) + for (i = 0; i < n_stops; i++) { + offset = start_offset + (1 - start_offset ) * stops[i].offset; + _cairo_output_stream_printf (output, + "\n", + offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + else { + cairo_bool_t found = FALSE; + unsigned int offset_index; + cairo_color_stop_t offset_color_start, offset_color_stop; + + for (i = 0; i < n_stops; i++) { + if (stops[i].offset >= -start_offset) { + if (i > 0) { + if (stops[i].offset != stops[i-1].offset) { + double x0, x1; + cairo_color_stop_t *color0, *color1; + + x0 = stops[i-1].offset; + x1 = stops[i].offset; + color0 = &stops[i-1].color; + color1 = &stops[i].color; + offset_color_start.red = color0->red + (color1->red - color0->red) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.green = color0->green + (color1->green - color0->green) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.blue = color0->blue + (color1->blue - color0->blue) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha) + * (-start_offset - x0) / (x1 - x0); + offset_color_stop = offset_color_start; + } else { + offset_color_stop = stops[i-1].color; + offset_color_start = stops[i].color; + } + } else + offset_color_stop = offset_color_start = stops[i].color; + offset_index = i; + found = TRUE; + break; + } + } + + if (!found) { + offset_index = n_stops - 1; + offset_color_stop = offset_color_start = stops[offset_index].color; + } + + _cairo_output_stream_printf (output, + "\n", + offset_color_start.red * 100.0, + offset_color_start.green * 100.0, + offset_color_start.blue * 100.0, + offset_color_start.alpha); + for (i = offset_index; i < n_stops; i++) { + _cairo_output_stream_printf (output, + "\n", + stops[i].offset + start_offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + for (i = 0; i < offset_index; i++) { + _cairo_output_stream_printf (output, + "\n", + 1.0 + stops[i].offset + start_offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + + _cairo_output_stream_printf (output, + "\n", + offset_color_stop.red * 100.0, + offset_color_stop.green * 100.0, + offset_color_stop.blue * 100.0, + offset_color_stop.alpha); + + } + + if (reverse_stops || emulate_reflect) + free (stops); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output, + cairo_pattern_t *pattern) +{ + switch (pattern->extend) { + case CAIRO_EXTEND_REPEAT: + _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); + break; + case CAIRO_EXTEND_REFLECT: + _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); + break; + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_PAD: + break; + } +} + +static cairo_status_t +_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, + cairo_linear_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + cairo_svg_document_t *document = surface->document; + double x0, y0, x1, y1; + cairo_matrix_t p2u; + cairo_status_t status; + + p2u = pattern->base.base.matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + x0 = _cairo_fixed_to_double (pattern->p1.x); + y0 = _cairo_fixed_to_double (pattern->p1.y); + x1 = _cairo_fixed_to_double (pattern->p2.x); + y1 = _cairo_fixed_to_double (pattern->p2.y); + + _cairo_output_stream_printf (document->xml_node_defs, + "linear_pattern_id, + x0, y0, x1, y1); + + _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base), + _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, + &pattern->base, 0.0, + FALSE, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s:url(#linear%d);", + is_stroke ? "stroke" : "fill", + document->linear_pattern_id); + + document->linear_pattern_id++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, + cairo_radial_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + cairo_svg_document_t *document = surface->document; + cairo_matrix_t p2u; + cairo_extend_t extend; + double x0, y0, x1, y1, r0, r1; + double fx, fy; + cairo_bool_t reverse_stops; + cairo_status_t status; + cairo_point_t *c0, *c1; + cairo_fixed_t radius0, radius1; + + extend = pattern->base.base.extend; + + if (pattern->r1 < pattern->r2) { + c0 = &pattern->c1; + c1 = &pattern->c2; + radius0 = pattern->r1; + radius1 = pattern->r2; + reverse_stops = FALSE; + } else { + c0 = &pattern->c2; + c1 = &pattern->c1; + radius0 = pattern->r2; + radius1 = pattern->r1; + reverse_stops = TRUE; + } + + x0 = _cairo_fixed_to_double (c0->x); + y0 = _cairo_fixed_to_double (c0->y); + r0 = _cairo_fixed_to_double (radius0); + x1 = _cairo_fixed_to_double (c1->x); + y1 = _cairo_fixed_to_double (c1->y); + r1 = _cairo_fixed_to_double (radius1); + + p2u = pattern->base.base.matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + if (pattern->r1 == pattern->r2) { + unsigned int n_stops = pattern->base.n_stops; + + _cairo_output_stream_printf (document->xml_node_defs, + "radial_pattern_id, + x1, y1, + x1, y1, r1); + _cairo_svg_surface_emit_transform (document->xml_node_defs, + "gradientTransform", + &p2u, parent_matrix); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + if (extend == CAIRO_EXTEND_NONE || n_stops < 1) + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + else { + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + pattern->base.stops[0].color.red * 100.0, + pattern->base.stops[0].color.green * 100.0, + pattern->base.stops[0].color.blue * 100.0, + pattern->base.stops[0].color.alpha); + if (n_stops > 1) + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + pattern->base.stops[n_stops - 1].color.red * 100.0, + pattern->base.stops[n_stops - 1].color.green * 100.0, + pattern->base.stops[n_stops - 1].color.blue * 100.0, + pattern->base.stops[n_stops - 1].color.alpha); + } + + } else { + double offset, r, x, y; + cairo_bool_t emulate_reflect = FALSE; + + fx = (r1 * x0 - r0 * x1) / (r1 - r0); + fy = (r1 * y0 - r0 * y1) / (r1 - r0); + + /* SVG doesn't support the inner circle and use instead a gradient focal. + * That means we need to emulate the cairo behaviour by processing the + * cairo gradient stops. + * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, + * it's just a matter of stop position translation and calculation of + * the corresponding SVG radial gradient focal. + * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new + * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT + * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop + * list that maps to the original cairo stop list. + */ + if ((extend == CAIRO_EXTEND_REFLECT + || extend == CAIRO_EXTEND_REPEAT) + && r0 > 0.0) { + double r_org = r1; + + if (extend == CAIRO_EXTEND_REFLECT) { + r1 = 2 * r1 - r0; + emulate_reflect = TRUE; + } + + offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; + r = r1 - r0; + + /* New position of outer circle. */ + x = r * (x1 - fx) / r_org + fx; + y = r * (y1 - fy) / r_org + fy; + + x1 = x; + y1 = y; + r1 = r; + r0 = 0.0; + } else { + offset = r0 / r1; + } + + _cairo_output_stream_printf (document->xml_node_defs, + "radial_pattern_id, + x1, y1, + fx, fy, r1); + + if (emulate_reflect) + _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" "); + else + _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base); + _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + /* To support cairo's EXTEND_NONE, (for which SVG has no similar + * notion), we add transparent color stops on either end of the + * user-provided stops. */ + if (extend == CAIRO_EXTEND_NONE) { + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + if (r0 != 0.0) + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + r0 / r1); + } + status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, + &pattern->base, offset, + reverse_stops, + emulate_reflect); + if (unlikely (status)) + return status; + + if (pattern->base.base.extend == CAIRO_EXTEND_NONE) + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + } + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s:url(#radial%d);", + is_stroke ? "stroke" : "fill", + document->radial_pattern_id); + + document->radial_pattern_id++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_output_stream_t *output, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, + output, is_stroke); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, + output, is_stroke, parent_matrix); + + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, + output, is_stroke, parent_matrix); + + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, + output, is_stroke, parent_matrix); + } + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); +} + +static cairo_status_t +_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_fill_rule_t fill_rule, + const cairo_matrix_t *parent_matrix) +{ + _cairo_output_stream_printf (output, + "fill-rule:%s;", + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); +} + +static cairo_status_t +_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *parent_matrix) +{ + cairo_status_t status; + const char *line_cap, *line_join; + unsigned int i; + + switch (stroke_style->line_cap) { + case CAIRO_LINE_CAP_BUTT: + line_cap = "butt"; + break; + case CAIRO_LINE_CAP_ROUND: + line_cap = "round"; + break; + case CAIRO_LINE_CAP_SQUARE: + line_cap = "square"; + break; + default: + ASSERT_NOT_REACHED; + } + + switch (stroke_style->line_join) { + case CAIRO_LINE_JOIN_MITER: + line_join = "miter"; + break; + case CAIRO_LINE_JOIN_ROUND: + line_join = "round"; + break; + case CAIRO_LINE_JOIN_BEVEL: + line_join = "bevel"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (output, + "stroke-width:%f;" + "stroke-linecap:%s;" + "stroke-linejoin:%s;", + stroke_style->line_width, + line_cap, + line_join); + + status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); + if (unlikely (status)) + return status; + + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + + if (stroke_style->num_dashes > 0) { + _cairo_output_stream_printf (output, "stroke-dasharray:"); + for (i = 0; i < stroke_style->num_dashes; i++) { + _cairo_output_stream_printf (output, "%f", + stroke_style->dash[i]); + if (i + 1 < stroke_style->num_dashes) + _cairo_output_stream_printf (output, ","); + else + _cairo_output_stream_printf (output, ";"); + } + if (stroke_style->dash_offset != 0.0) { + _cairo_output_stream_printf (output, + "stroke-dashoffset:%f;", + stroke_style->dash_offset); + } + } + + _cairo_output_stream_printf (output, + "stroke-miterlimit:%f;", + stroke_style->miter_limit); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_fill_stroke (void *abstract_surface, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, fill_op, + fill_source, fill_rule, stroke_ctm_inverse); + if (unlikely (status)) + return status; + + status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, + stroke_source, stroke_style, stroke_ctm_inverse); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "\" "); + + _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); + + _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); + _cairo_output_stream_printf (surface->xml_node, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "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, + "width, surface->height); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (output, "stroke:none;\""); + + if (extra_attributes) + _cairo_output_stream_printf (output, " %s", extra_attributes); + + _cairo_output_stream_printf (output, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_svg_surface_t *surface = abstract_surface; + + /* Emulation of clear and source operators, when no clipping region + * is defined. We just delete existing content of surface root node, + * and exit early if operator is clear. + */ + if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && + clip == NULL) + { + switch (surface->paginated_mode) { + case CAIRO_PAGINATED_MODE_FALLBACK: + ASSERT_NOT_REACHED; + case CAIRO_PAGINATED_MODE_ANALYZE: + return CAIRO_STATUS_SUCCESS; + + case CAIRO_PAGINATED_MODE_RENDER: + status = _cairo_output_stream_destroy (surface->xml_node); + if (unlikely (status)) { + surface->xml_node = NULL; + return status; + } + + surface->xml_node = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (surface->xml_node)) { + status = _cairo_output_stream_destroy (surface->xml_node); + surface->xml_node = NULL; + return status; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->content == CAIRO_CONTENT_COLOR) { + _cairo_output_stream_printf (surface->xml_node, + "\n", + surface->width, surface->height); + } + return CAIRO_STATUS_SUCCESS; + } + break; + } + } else { + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_svg_surface_emit_paint (surface->xml_node, + surface, op, source, 0, NULL); +} + +static cairo_int_status_t +_cairo_svg_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + cairo_output_stream_t *mask_stream; + char buffer[64]; + cairo_bool_t discard_filter = FALSE; + unsigned int mask_id; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + cairo_status_t source_status, mask_status; + + source_status = _cairo_svg_surface_analyze_operation (surface, op, source); + if (_cairo_status_is_error (source_status)) + return source_status; + + if (mask->has_component_alpha) { + mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); + if (_cairo_status_is_error (mask_status)) + return mask_status; + } + + return _cairo_analysis_surface_merge_status (source_status, + mask_status); + } + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; + cairo_content_t content = cairo_surface_get_content (surface_pattern->surface); + if (content == CAIRO_CONTENT_ALPHA) + discard_filter = TRUE; + } + + if (!discard_filter) + _cairo_svg_surface_emit_alpha_filter (document); + + /* _cairo_svg_surface_emit_paint() will output a pattern definition to + * document->xml_node_defs so we need to write the mask element to + * a temporary stream and then copy that to xml_node_defs. */ + mask_stream = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (mask_stream)) + return _cairo_output_stream_destroy (mask_stream); + + mask_id = _cairo_svg_document_allocate_mask_id (document); + + _cairo_output_stream_printf (mask_stream, + "\n" + "%s", + mask_id, + discard_filter ? "" : " \n"); + status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); + if (unlikely (status)) { + cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); + return status; + (void) ignore; + } + + _cairo_output_stream_printf (mask_stream, + "%s" + "\n", + discard_filter ? "" : " \n"); + _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); + + status = _cairo_output_stream_destroy (mask_stream); + if (unlikely (status)) + return status; + + snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", + mask_id); + status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_stroke (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_svg_surface_t *surface = abstract_dst; + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, + source, stroke_style, ctm_inverse); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "\" "); + + _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); + + _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); + _cairo_output_stream_printf (surface->xml_node, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + cairo_path_fixed_t path; + cairo_status_t status; + cairo_scaled_font_subsets_glyph_t subset_glyph; + int i; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, pattern); + + assert (_cairo_svg_surface_operation_supported (surface, op, pattern)); + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + /* FIXME it's probably possible to apply a pattern of a gradient to + * a group of symbols, but I don't know how yet. Gradients or patterns + * are translated by x and y properties of use element. */ + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + goto FALLBACK; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, FALSE, NULL); + if (unlikely (status)) + return status; + + _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); + + _cairo_output_stream_printf (surface->xml_node, "\">\n"); + + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, + scaled_font, glyphs[i].index, + NULL, 0, + &subset_glyph); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_output_stream_printf (surface->xml_node, "\n"); + + glyphs += i; + num_glyphs -= i; + goto FALLBACK; + } + + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, + " \n", + subset_glyph.font_id, + subset_glyph.subset_glyph_index, + glyphs[i].x, glyphs[i].y); + } + + _cairo_output_stream_printf (surface->xml_node, "\n"); + + return CAIRO_STATUS_SUCCESS; + +FALLBACK: + _cairo_path_fixed_init (&path); + + status = _cairo_scaled_font_glyph_path (scaled_font, + (cairo_glyph_t *) glyphs, + num_glyphs, &path); + + if (unlikely (status)) { + _cairo_path_fixed_fini (&path); + return status; + } + + status = _cairo_svg_surface_fill (abstract_surface, op, pattern, + &path, CAIRO_FILL_RULE_WINDING, + 0.0, CAIRO_ANTIALIAS_SUBPIXEL, + clip); + + _cairo_path_fixed_fini (&path); + + return status; +} + +static void +_cairo_svg_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); +} + +static const cairo_surface_backend_t cairo_svg_surface_backend = { + CAIRO_SURFACE_TYPE_SVG, + NULL, /* create_similar: handled by wrapper */ + _cairo_svg_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* _cairo_svg_surface_composite, */ + NULL, /* _cairo_svg_surface_fill_rectangles, */ + NULL, /* _cairo_svg_surface_composite_trapezoids,*/ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + _cairo_svg_surface_copy_page, + _cairo_svg_surface_show_page, + _cairo_svg_surface_get_extents, + NULL, /* _cairo_svg_surface_old_show_glyphs, */ + _cairo_svg_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark dirty rectangle */ + NULL, /* scaled font fini */ + NULL, /* scaled glyph fini */ + _cairo_svg_surface_paint, + _cairo_svg_surface_mask, + _cairo_svg_surface_stroke, + _cairo_svg_surface_fill, + _cairo_svg_surface_show_glyphs, + NULL, /* snapshot */ + NULL, /* is_similar */ + _cairo_svg_surface_fill_stroke +}; + +static cairo_status_t +_cairo_svg_document_create (cairo_output_stream_t *output_stream, + double width, + double height, + cairo_svg_version_t version, + cairo_svg_document_t **document_out) +{ + cairo_svg_document_t *document; + cairo_status_t status, status_ignored; + + if (output_stream->status) + return output_stream->status; + + document = malloc (sizeof (cairo_svg_document_t)); + if (unlikely (document == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* The use of defs for font glyphs imposes no per-subset limit. */ + document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); + if (unlikely (document->font_subsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DOCUMENT; + } + + document->output_stream = output_stream; + document->refcount = 1; + document->owner = NULL; + document->finished = FALSE; + document->width = width; + document->height = height; + + document->linear_pattern_id = 0; + document->radial_pattern_id = 0; + document->pattern_id = 0; + document->filter_id = 0; + document->clip_id = 0; + document->mask_id = 0; + + document->xml_node_defs = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (document->xml_node_defs); + if (unlikely (status)) + goto CLEANUP_NODE_DEFS; + + document->xml_node_glyphs = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (document->xml_node_glyphs); + if (unlikely (status)) + goto CLEANUP_NODE_GLYPHS; + + document->alpha_filter = FALSE; + + document->svg_version = version; + + *document_out = document; + return CAIRO_STATUS_SUCCESS; + + CLEANUP_NODE_GLYPHS: + status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs); + CLEANUP_NODE_DEFS: + status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); + _cairo_scaled_font_subsets_destroy (document->font_subsets); + CLEANUP_DOCUMENT: + free (document); + return status; +} + +static cairo_svg_document_t * +_cairo_svg_document_reference (cairo_svg_document_t *document) +{ + document->refcount++; + + return document; +} + +static unsigned int +_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document) +{ + return document->mask_id++; +} + +static cairo_status_t +_cairo_svg_document_destroy (cairo_svg_document_t *document) +{ + cairo_status_t status; + + document->refcount--; + if (document->refcount > 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_svg_document_finish (document); + + free (document); + + return status; +} + +static cairo_status_t +_cairo_svg_document_finish (cairo_svg_document_t *document) +{ + cairo_status_t status, status2; + cairo_output_stream_t *output = document->output_stream; + cairo_svg_page_t *page; + unsigned int i; + + if (document->finished) + return CAIRO_STATUS_SUCCESS; + + /* + * Should we add DOCTYPE? + * + * Google says no. + * + * http://tech.groups.yahoo.com/group/svg-developers/message/48562: + * There's a bunch of issues, but just to pick a few: + * - they'll give false positives. + * - they'll give false negatives. + * - they're namespace-unaware. + * - they don't wildcard. + * So when they say OK they really haven't checked anything, when + * they say NOT OK they might be on crack, and like all + * namespace-unaware things they're a dead branch of the XML tree. + * + * http://jwatt.org/svg/authoring/: + * Unfortunately the SVG DTDs are a source of so many issues that the + * SVG WG has decided not to write one for the upcoming SVG 1.2 + * standard. In fact SVG WG members are even telling people not to use + * a DOCTYPE declaration in SVG 1.0 and 1.1 documents. + */ + + _cairo_output_stream_printf (output, + "\n" + "\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, "\n"); + if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { + _cairo_output_stream_printf (output, "\n"); + _cairo_memory_stream_copy (document->xml_node_glyphs, output); + _cairo_output_stream_printf (output, "\n"); + } + _cairo_memory_stream_copy (document->xml_node_defs, output); + _cairo_output_stream_printf (output, "\n"); + } + + if (document->owner != NULL) { + cairo_svg_surface_t *surface; + + surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); + if (surface->xml_node != NULL && + _cairo_memory_stream_length (surface->xml_node) > 0) { + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (surface->page_set.num_elements > 1 && + _cairo_svg_version_has_page_set_support (document->svg_version)) { + _cairo_output_stream_printf (output, "\n"); + for (i = 0; i < surface->page_set.num_elements; i++) { + page = _cairo_array_index (&surface->page_set, i); + _cairo_output_stream_printf (output, "\n"); + _cairo_output_stream_printf (output, + "\n", + page->surface_id); + _cairo_memory_stream_copy (page->xml_node, output); + _cairo_output_stream_printf (output, "\n\n"); + } + _cairo_output_stream_printf (output, "\n"); + } else if (surface->page_set.num_elements > 0) { + page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); + _cairo_output_stream_printf (output, + "\n", + page->surface_id); + _cairo_memory_stream_copy (page->xml_node, output); + _cairo_output_stream_printf (output, "\n"); + } + } + + _cairo_output_stream_printf (output, "\n"); + + status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (document->xml_node_defs); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (output); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + document->finished = TRUE; + + return status; +} + +static void +_cairo_svg_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_svg_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + +static cairo_bool_t +_cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) { + status = _cairo_svg_surface_analyze_operator (surface, + CAIRO_OPERATOR_SOURCE); + } + + return status == CAIRO_STATUS_SUCCESS; +} + +static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { + NULL /*_cairo_svg_surface_start_page*/, + _cairo_svg_surface_set_paginated_mode, + NULL, /* _cairo_svg_surface_set_bounding_box */ + NULL, /* _cairo_svg_surface_set_fallback_images_required */ + _cairo_svg_surface_supports_fine_grained_fallbacks, + +}; diff --git a/libs/cairo/src/cairo-svg.h b/libs/cairo/src/cairo-svg.h new file mode 100644 index 000000000..2c07aeedf --- /dev/null +++ b/libs/cairo/src/cairo-svg.h @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_SVG_H +#define CAIRO_SVG_H + +#include "cairo.h" + +#if CAIRO_HAS_SVG_SURFACE + +CAIRO_BEGIN_DECLS + +/** + * cairo_svg_version_t: + * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. + * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. + * + * #cairo_svg_version_t is used to describe the version number of the SVG + * specification that a generated SVG file will conform to. + */ +typedef enum _cairo_svg_version { + CAIRO_SVG_VERSION_1_1, + CAIRO_SVG_VERSION_1_2 +} cairo_svg_version_t; + +cairo_public cairo_surface_t * +cairo_svg_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_svg_surface_restrict_to_version (cairo_surface_t *surface, + cairo_svg_version_t version); + +cairo_public void +cairo_svg_get_versions (cairo_svg_version_t const **versions, + int *num_versions); + +cairo_public const char * +cairo_svg_version_to_string (cairo_svg_version_t version); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_SVG_SURFACE */ +# error Cairo was not compiled with support for the svg backend +#endif /* CAIRO_HAS_SVG_SURFACE */ + +#endif /* CAIRO_SVG_H */ diff --git a/libs/cairo/src/cairo-system.c b/libs/cairo/src/cairo-system.c new file mode 100644 index 000000000..22e4934c7 --- /dev/null +++ b/libs/cairo/src/cairo-system.c @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This file should include code that is system-specific, not + * feature-specific. For example, the DLL initialization/finalization + * code on Win32 or OS/2 must live here (not in cairo-whatever-surface.c). + * Same about possible ELF-specific code. + * + * And no other function should live here. + */ + + +#include "cairoint.h" + + + +#if CAIRO_MUTEX_IMPL_WIN32 +#if !CAIRO_WIN32_STATIC_BUILD + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +#include "cairo-clip-private.h" +#include "cairo-paginated-private.h" +#include "cairo-win32-private.h" +#include "cairo-scaled-font-subsets-private.h" + +#include + +/* declare to avoid "no previous prototype for 'DllMain'" warning */ +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); + +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + CAIRO_MUTEX_INITIALIZE (); + break; + + case DLL_PROCESS_DETACH: + CAIRO_MUTEX_FINALIZE (); + break; + } + + return TRUE; +} + +#endif +#endif + diff --git a/libs/cairo/src/cairo-tee-surface-private.h b/libs/cairo/src/cairo-tee-surface-private.h new file mode 100644 index 000000000..dd8aeda4d --- /dev/null +++ b/libs/cairo/src/cairo-tee-surface-private.h @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-tee-surface.c b/libs/cairo/src/cairo-tee-surface.c new file mode 100644 index 000000000..755a0b7ee --- /dev/null +++ b/libs/cairo/src/cairo-tee-surface.c @@ -0,0 +1,685 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This surface supports redirecting all its input to multiple surfaces. + */ + +#include "cairoint.h" + +#include "cairo-tee.h" + +#include "cairo-error-private.h" +#include "cairo-tee-surface-private.h" +#include "cairo-surface-wrapper-private.h" + +typedef struct _cairo_tee_surface { + cairo_surface_t base; + + cairo_surface_wrapper_t master; + cairo_array_t slaves; +} cairo_tee_surface_t; + +slim_hidden_proto (cairo_tee_surface_create); +slim_hidden_proto (cairo_tee_surface_add); + +static cairo_surface_t * +_cairo_tee_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + + cairo_tee_surface_t *other = abstract_surface; + cairo_surface_t *similar; + cairo_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + similar = _cairo_surface_wrapper_create_similar (&other->master, + content, width, height); + surface = cairo_tee_surface_create (similar); + cairo_surface_destroy (similar); + if (unlikely (surface->status)) + return surface; + + num_slaves = _cairo_array_num_elements (&other->slaves); + slaves = _cairo_array_index (&other->slaves, 0); + for (n = 0; n < num_slaves; n++) { + + similar = _cairo_surface_wrapper_create_similar (&slaves[n], + content, + width, height); + cairo_tee_surface_add (surface, similar); + cairo_surface_destroy (similar); + } + + if (unlikely (surface->status)) { + cairo_status_t status = surface->status; + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} + +static cairo_status_t +_cairo_tee_surface_finish (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + _cairo_surface_wrapper_fini (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) + _cairo_surface_wrapper_fini (&slaves[n]); + + _cairo_array_fini (&surface->slaves); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tee_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a real image surface if available */ + if (_cairo_surface_is_image (surface->master.target)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_image (slaves[n].target)) { + return _cairo_surface_wrapper_acquire_source_image (&slaves[n], + image_out, + image_extra); + } + } + + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); +} + +static void +_cairo_tee_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_release_source_image (&surface->master, + image, image_extra); +} + +static cairo_surface_t * +_cairo_tee_surface_snapshot (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a recording surface for our snapshots */ + if (_cairo_surface_is_recording (surface->master.target)) + return _cairo_surface_wrapper_snapshot (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_recording (slaves[n].target)) + return _cairo_surface_wrapper_snapshot (&slaves[n]); + } + + return _cairo_surface_wrapper_snapshot (&surface->master); +} + +static cairo_bool_t +_cairo_tee_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_tee_surface_t *surface = abstract_surface; + + return _cairo_surface_wrapper_get_extents (&surface->master, rectangle); +} + +static void +_cairo_tee_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_get_font_options (&surface->master, options); +} + +static const cairo_pattern_t * +_cairo_tee_surface_match_source (cairo_tee_surface_t *surface, + const cairo_pattern_t *source, + int index, + cairo_surface_wrapper_t *dest, + cairo_surface_pattern_t *temp) +{ + cairo_surface_t *s; + cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s); + if (status == CAIRO_STATUS_SUCCESS && + cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) { + cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index); + if (tee_surf->status == CAIRO_STATUS_SUCCESS && + tee_surf->backend == dest->target->backend) { + status = _cairo_pattern_init_copy (&temp->base, source); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy (temp->surface); + temp->surface = tee_surf; + cairo_surface_reference (temp->surface); + return &temp->base; + } + } + } + + return source; +} + +static cairo_int_status_t +_cairo_tee_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_mask (&surface->master, + op, matched_source, mask, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_mask (&slaves[n], + op, matched_source, mask, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_stroke (&surface->master, + op, matched_source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_stroke (&slaves[n], + op, matched_source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_tee_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_fill (&surface->master, + op, matched_source, + path, fill_rule, + tolerance, antialias, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_fill (&slaves[n], + op, matched_source, + path, fill_rule, + tolerance, antialias, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_tee_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + cairo_glyph_t *glyphs_copy; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + + /* XXX: This copying is ugly. */ + glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (glyphs_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, + matched_source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + goto CLEANUP; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, + matched_source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } + if (unlikely (status)) + goto CLEANUP; + } + + CLEANUP: + free (glyphs_copy); + return status; +} + +static cairo_status_t +_cairo_tee_surface_flush (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + status = _cairo_surface_wrapper_flush(&surface->master); + if (unlikely (status)) + return status; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + status = _cairo_surface_wrapper_flush(&slaves[n]); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_tee_surface_backend = { + CAIRO_SURFACE_TYPE_TEE, + _cairo_tee_surface_create_similar, + _cairo_tee_surface_finish, + _cairo_tee_surface_acquire_source_image, + _cairo_tee_surface_release_source_image, + NULL, NULL, /* dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_tee_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_tee_surface_get_font_options, + _cairo_tee_surface_flush, + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _cairo_tee_surface_paint, + _cairo_tee_surface_mask, + _cairo_tee_surface_stroke, + _cairo_tee_surface_fill, + NULL, /* replaced by show_text_glyphs */ + + _cairo_tee_surface_snapshot, + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + _cairo_tee_surface_has_show_text_glyphs, + _cairo_tee_surface_show_text_glyphs +}; + +cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master) +{ + cairo_tee_surface_t *surface; + + if (unlikely (master->status)) + return _cairo_surface_create_in_error (master->status); + + surface = malloc (sizeof (cairo_tee_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &cairo_tee_surface_backend, + master->device, + master->content); + + _cairo_surface_wrapper_init (&surface->master, master); + /* we trust that these are already set and remain constant */ + surface->base.device_transform = master->device_transform; + surface->base.device_transform_inverse = master->device_transform_inverse; + + _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); + + return &surface->base; +} +slim_hidden_def (cairo_tee_surface_create); + +void +cairo_tee_surface_add (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t slave; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (abstract_surface->backend != &cairo_tee_surface_backend) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (unlikely (target->status)) { + status = _cairo_surface_set_error (abstract_surface, target->status); + return; + } + + surface = (cairo_tee_surface_t *) abstract_surface; + + _cairo_surface_wrapper_init (&slave, target); + status = _cairo_array_append (&surface->slaves, &slave); + if (unlikely (status)) { + _cairo_surface_wrapper_fini (&slave); + status = _cairo_surface_set_error (&surface->base, status); + } +} +slim_hidden_def (cairo_tee_surface_add); + +void +cairo_tee_surface_remove (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (abstract_surface->backend != &cairo_tee_surface_backend) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + surface = (cairo_tee_surface_t *) abstract_surface; + if (target == surface->master.target) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target == target) + break; + } + + if (n == num_slaves) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + _cairo_surface_wrapper_fini (&slaves[n]); + for (n++; n < num_slaves; n++) + slaves[n-1] = slaves[n]; + surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */ +} + +cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *abstract_surface, + int index) +{ + cairo_tee_surface_t *surface; + + if (unlikely (abstract_surface->status)) + return _cairo_surface_create_in_error (abstract_surface->status); + if (unlikely (abstract_surface->finished)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (abstract_surface->backend != &cairo_tee_surface_backend) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + surface = (cairo_tee_surface_t *) abstract_surface; + if (index == 0) { + return surface->master.target; + } else { + cairo_surface_wrapper_t *slave; + + index--; + + if (index >= _cairo_array_num_elements (&surface->slaves)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); + + slave = _cairo_array_index (&surface->slaves, index); + return slave->target; + } +} + +cairo_surface_t * +_cairo_tee_surface_find_match (void *abstract_surface, + const cairo_surface_backend_t *backend, + cairo_content_t content) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* exact match first */ + if (surface->master.target->backend == backend && + surface->master.target->content == content) + { + return surface->master.target; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend && + slaves[n].target->content == content) + { + return slaves[n].target; + } + } + + /* matching backend? */ + if (surface->master.target->backend == backend) + return surface->master.target; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend) + return slaves[n].target; + } + + return NULL; +} diff --git a/libs/cairo/src/cairo-tee.h b/libs/cairo/src/cairo-tee.h new file mode 100644 index 000000000..c60cb5327 --- /dev/null +++ b/libs/cairo/src/cairo-tee.h @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_TEE_H +#define CAIRO_TEE_H + +#include "cairo.h" + +#if CAIRO_HAS_TEE_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master); + +cairo_public void +cairo_tee_surface_add (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public void +cairo_tee_surface_remove (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *surface, + int index); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_TEE_SURFACE*/ +# error Cairo was not compiled with support for the TEE backend +#endif /*CAIRO_HAS_TEE_SURFACE*/ + +#endif /*CAIRO_TEE_H*/ diff --git a/libs/cairo/src/cairo-tor-scan-converter.c b/libs/cairo/src/cairo-tor-scan-converter.c new file mode 100644 index 000000000..0dc20fef5 --- /dev/null +++ b/libs/cairo/src/cairo-tor-scan-converter.c @@ -0,0 +1,2221 @@ +/* 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 +#include +#include +#include + +/*------------------------------------------------------------------------- + * cairo specific config + */ +#define I static + +/* Prefer cairo's status type. */ +#define GLITTER_HAVE_STATUS_T 1 +#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS +#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY +typedef cairo_status_t glitter_status_t; + +/* The input coordinate scale and the rasterisation grid scales. */ +#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_Y 15 + +/* Set glitter up to use a cairo span renderer to do the coverage + * blitting. */ +struct pool; +struct cell_list; + +static glitter_status_t +blit_with_span_renderer( + struct cell_list *coverages, + cairo_span_renderer_t *span_renderer, + struct pool *span_pool, + int y, + int height, + int xmin, + int xmax); + +static glitter_status_t +blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height); + +#define GLITTER_BLIT_COVERAGES_ARGS \ + cairo_span_renderer_t *span_renderer, \ + struct pool *span_pool + +#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \ + cairo_status_t status = blit_with_span_renderer (cells, \ + span_renderer, \ + span_pool, \ + y, height, \ + xmin, xmax); \ + if (unlikely (status)) \ + return status; \ +} while (0) + +#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \ + cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \ + if (unlikely (status)) \ + return status; \ +} while (0) + +/*------------------------------------------------------------------------- + * glitter-paths.h + */ + +/* "Input scaled" numbers are fixed precision reals with multiplier + * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as + * pixel scaled numbers. These get converted to the internal grid + * scaled numbers as soon as possible. Internal overflow is possible + * if GRID_X/Y inside glitter-paths.c is larger than + * 1< +#include +#include + +/* All polygon coordinates are snapped onto a subsample grid. "Grid + * scaled" numbers are fixed precision reals with multiplier GRID_X or + * GRID_Y. */ +typedef int grid_scaled_t; +typedef int grid_scaled_x_t; +typedef int grid_scaled_y_t; + +/* Default x/y scale factors. + * You can either define GRID_X/Y_BITS to get a power-of-two scale + * or define GRID_X/Y separately. */ +#if !defined(GRID_X) && !defined(GRID_X_BITS) +# define GRID_X_BITS 8 +#endif +#if !defined(GRID_Y) && !defined(GRID_Y_BITS) +# define GRID_Y 15 +#endif + +/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ +#ifdef GRID_X_BITS +# define GRID_X (1 << GRID_X_BITS) +#endif +#ifdef GRID_Y_BITS +# define GRID_Y (1 << GRID_Y_BITS) +#endif + +/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into + * integer and fractional parts. The integer part is floored. */ +#if defined(GRID_X_TO_INT_FRAC) + /* do nothing */ +#elif defined(GRID_X_BITS) +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) +#else +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) +#endif + +#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ + (i) = (t) / (m); \ + (f) = (t) % (m); \ + if ((f) < 0) { \ + --(i); \ + (f) += (m); \ + } \ +} while (0) + +#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ + (f) = (t) & ((1 << (b)) - 1); \ + (i) = (t) >> (b); \ +} while (0) + +/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want + * to be able to represent exactly areas of subpixel trapezoids whose + * vertices are given in grid scaled coordinates. The scale factor + * comes from needing to accurately represent the area 0.5*dx*dy of a + * triangle with base dx and height dy in grid scaled numbers. */ +typedef int grid_area_t; +#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ + +/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ +#if GRID_XY == 510 +# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) +#elif GRID_XY == 255 +# define GRID_AREA_TO_ALPHA(c) (c) +#elif GRID_XY == 64 +# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) +#elif GRID_XY == 128 +# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) +#elif GRID_XY == 256 +# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) +#elif GRID_XY == 15 +# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) +#elif GRID_XY == 2*256*15 +# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) +#else +# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) +#endif + +#define UNROLL3(x) x x x + +struct quorem { + int32_t quo; + int32_t rem; +}; + +/* Header for a chunk of memory in a memory pool. */ +struct _pool_chunk { + /* # bytes used in this chunk. */ + size_t size; + + /* # bytes total in this chunk */ + size_t capacity; + + /* Pointer to the previous chunk or %NULL if this is the sentinel + * chunk in the pool header. */ + struct _pool_chunk *prev_chunk; + + /* Actual data starts here. Well aligned for pointers. */ +}; + +/* A memory pool. This is supposed to be embedded on the stack or + * within some other structure. It may optionally be followed by an + * embedded array from which requests are fulfilled until + * malloc needs to be called to allocate a first real chunk. */ +struct pool { + /* Chunk we're allocating from. */ + struct _pool_chunk *current; + + /* Free list of previously allocated chunks. All have >= default + * capacity. */ + struct _pool_chunk *first_free; + + /* The default capacity of a chunk. */ + size_t default_capacity; + + /* Header for the sentinel chunk. Directly following the pool + * struct should be some space for embedded elements from which + * the sentinel chunk allocates from. */ + struct _pool_chunk sentinel[1]; +}; + +/* A polygon edge. */ +struct edge { + /* Next in y-bucket or active list. */ + struct edge *next; + + /* Current x coordinate while the edge is on the active + * list. Initialised to the x coordinate of the top of the + * edge. The quotient is in grid_scaled_x_t units and the + * remainder is mod dy in grid_scaled_y_t units.*/ + struct quorem x; + + /* Advance of the current x when moving down a subsample line. */ + struct quorem dxdy; + + /* Advance of the current x when moving down a full pixel + * row. Only initialised when the height of the edge is large + * enough that there's a chance the edge could be stepped by a + * full row's worth of subsample rows at a time. */ + struct quorem dxdy_full; + + /* The clipped y of the top of the edge. */ + grid_scaled_y_t ytop; + + /* y2-y1 after orienting the edge downwards. */ + grid_scaled_y_t dy; + + /* Number of subsample rows remaining to scan convert of this + * edge. */ + grid_scaled_y_t height_left; + + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; + int vertical; +}; + +/* Number of subsample rows per y-bucket. Must be GRID_Y. */ +#define EDGE_Y_BUCKET_HEIGHT GRID_Y + +#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) + +struct bucket { + /* Unsorted list of edges starting within this bucket. */ + struct edge *edges; + + /* Set to non-zero if there are edges starting strictly within the + * bucket. */ + unsigned have_inside_edges; +}; + +/* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ +struct polygon { + /* The clip extents. */ + grid_scaled_x_t xmin, xmax; + grid_scaled_y_t ymin, ymax; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ + struct bucket *y_buckets; + struct bucket y_buckets_embedded[64]; + + struct { + struct pool base[1]; + struct edge embedded[32]; + } edge_pool; +}; + +/* A cell records the effect on pixel coverage of polygon edges + * passing through a pixel. It contains two accumulators of pixel + * coverage. + * + * Consider the effects of a polygon edge on the coverage of a pixel + * it intersects and that of the following one. The coverage of the + * following pixel is the height of the edge multiplied by the width + * of the pixel, and the coverage of the pixel itself is the area of + * the trapezoid formed by the edge and the right side of the pixel. + * + * +-----------------------+-----------------------+ + * | | | + * | | | + * |_______________________|_______________________| + * | \...................|.......................|\ + * | \..................|.......................| | + * | \.................|.......................| | + * | \....covered.....|.......................| | + * | \....area.......|.......................| } covered height + * | \..............|.......................| | + * |uncovered\.............|.......................| | + * | area \............|.......................| | + * |___________\...........|.......................|/ + * | | | + * | | | + * | | | + * +-----------------------+-----------------------+ + * + * Since the coverage of the following pixel will always be a multiple + * of the width of the pixel, we can store the height of the covered + * area instead. The coverage of the pixel itself is the total + * coverage minus the area of the uncovered area to the left of the + * edge. As it's faster to compute the uncovered area we only store + * that and subtract it from the total coverage later when forming + * spans to blit. + * + * The heights and areas are signed, with left edges of the polygon + * having positive sign and right edges having negative sign. When + * two edges intersect they swap their left/rightness so their + * contribution above and below the intersection point must be + * computed separately. */ +struct cell { + struct cell *next; + int x; + grid_area_t uncovered_area; + grid_scaled_y_t covered_height; +}; + +/* A cell list represents the scan line sparsely as cells ordered by + * ascending x. It is geared towards scanning the cells in order + * using an internal cursor. */ +struct cell_list { + /* Points to the left-most cell in the scan line. */ + struct cell *head; + /* Sentinel node */ + struct cell tail; + + /* Cursor state for iterating through the cell list. Points to + * a pointer to the current cell: either &cell_list->head or the next + * field of the previous cell. */ + struct cell **cursor; + + /* Cells in the cell list are owned by the cell list and are + * allocated from this pool. */ + struct { + struct pool base[1]; + struct cell embedded[32]; + } cell_pool; +}; + +struct cell_pair { + struct cell *cell1; + struct cell *cell2; +}; + +/* The active list contains edges in the current scan line ordered by + * the x-coordinate of the intercept of the edge and the scan line. */ +struct active_list { + /* Leftmost edge on the current scan line. */ + struct edge *head; + + /* A lower bound on the height of the active edges is used to + * estimate how soon some active edge ends. We can't advance the + * scan conversion by a full pixel row if an edge ends somewhere + * within it. */ + grid_scaled_y_t min_height; +}; + +struct glitter_scan_converter { + struct polygon polygon[1]; + struct active_list active[1]; + struct cell_list coverages[1]; + + /* Clip box. */ + grid_scaled_x_t xmin, xmax; + grid_scaled_y_t ymin, ymax; +}; + +/* Compute the floored division a/b. Assumes / and % perform symmetric + * division. */ +inline static struct quorem +floored_divrem(int a, int b) +{ + struct quorem qr; + qr.quo = a/b; + qr.rem = a%b; + if ((a^b)<0 && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric + * division. */ +static struct quorem +floored_muldivrem(int x, int a, int b) +{ + struct quorem qr; + long long xa = (long long)x*a; + qr.quo = xa/b; + qr.rem = xa%b; + if ((xa>=0) != (b>=0) && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +static void +_pool_chunk_init( + struct _pool_chunk *p, + struct _pool_chunk *prev_chunk, + size_t capacity) +{ + p->prev_chunk = prev_chunk; + p->size = 0; + p->capacity = capacity; +} + +static struct _pool_chunk * +_pool_chunk_create( + struct _pool_chunk *prev_chunk, + size_t size) +{ + struct _pool_chunk *p; + size_t size_with_head = size + sizeof(struct _pool_chunk); + if (size_with_head < size) + return NULL; + p = malloc(size_with_head); + if (p) + _pool_chunk_init(p, prev_chunk, size); + return p; +} + +static void +pool_init( + struct pool *pool, + size_t default_capacity, + size_t embedded_capacity) +{ + pool->current = pool->sentinel; + pool->first_free = NULL; + pool->default_capacity = default_capacity; + _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); +} + +static void +pool_fini(struct pool *pool) +{ + struct _pool_chunk *p = pool->current; + do { + while (NULL != p) { + struct _pool_chunk *prev = p->prev_chunk; + if (p != pool->sentinel) + free(p); + p = prev; + } + p = pool->first_free; + pool->first_free = NULL; + } while (NULL != p); + pool_init(pool, 0, 0); +} + +/* Satisfy an allocation by first allocating a new large enough chunk + * and adding it to the head of the pool's chunk list. This function + * is called as a fallback if pool_alloc() couldn't do a quick + * allocation from the current chunk in the pool. */ +static void * +_pool_alloc_from_new_chunk( + struct pool *pool, + size_t size) +{ + struct _pool_chunk *chunk; + void *obj; + size_t capacity; + + /* If the allocation is smaller than the default chunk size then + * try getting a chunk off the free list. Force alloc of a new + * chunk for large requests. */ + capacity = size; + chunk = NULL; + if (size < pool->default_capacity) { + capacity = pool->default_capacity; + chunk = pool->first_free; + if (chunk) { + pool->first_free = chunk->prev_chunk; + _pool_chunk_init(chunk, pool->current, chunk->capacity); + } + } + + if (NULL == chunk) { + chunk = _pool_chunk_create (pool->current, capacity); + if (unlikely (NULL == chunk)) + return NULL; + } + pool->current = chunk; + + obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; +} + +/* Allocate size bytes from the pool. The first allocated address + * returned from a pool is aligned to sizeof(void*). Subsequent + * addresses will maintain alignment as long as multiples of void* are + * allocated. Returns the address of a new memory area or %NULL on + * allocation failures. The pool retains ownership of the returned + * memory. */ +inline static void * +pool_alloc (struct pool *pool, size_t size) +{ + struct _pool_chunk *chunk = pool->current; + + if (size <= chunk->capacity - chunk->size) { + void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; + } else { + return _pool_alloc_from_new_chunk(pool, size); + } +} + +/* Relinquish all pool_alloced memory back to the pool. */ +static void +pool_reset (struct pool *pool) +{ + /* Transfer all used chunks to the chunk free list. */ + struct _pool_chunk *chunk = pool->current; + if (chunk != pool->sentinel) { + while (chunk->prev_chunk != pool->sentinel) { + chunk = chunk->prev_chunk; + } + chunk->prev_chunk = pool->first_free; + pool->first_free = pool->current; + } + /* Reset the sentinel as the current chunk. */ + pool->current = pool->sentinel; + pool->sentinel->size = 0; +} + +/* Rewinds the cell list's cursor to the beginning. After rewinding + * we're good to cell_list_find() the cell any x coordinate. */ +inline static void +cell_list_rewind (struct cell_list *cells) +{ + cells->cursor = &cells->head; +} + +/* Rewind the cell list if its cursor has been advanced past x. */ +inline static void +cell_list_maybe_rewind (struct cell_list *cells, int x) +{ + struct cell *tail = *cells->cursor; + if (tail->x > x) + cell_list_rewind (cells); +} + +static void +cell_list_init(struct cell_list *cells) +{ + pool_init(cells->cell_pool.base, + 256*sizeof(struct cell), + sizeof(cells->cell_pool.embedded)); + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->head = &cells->tail; + cell_list_rewind (cells); +} + +static void +cell_list_fini(struct cell_list *cells) +{ + pool_fini (cells->cell_pool.base); +} + +/* Empty the cell list. This is called at the start of every pixel + * row. */ +inline static void +cell_list_reset (struct cell_list *cells) +{ + cell_list_rewind (cells); + cells->head = &cells->tail; + pool_reset (cells->cell_pool.base); +} + +static struct cell * +cell_list_alloc (struct cell_list *cells, + struct cell **cursor, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); + if (unlikely (NULL == cell)) + return NULL; + + *cursor = cell; + cell->next = tail; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + return cell; +} + +/* Find a cell at the given x-coordinate. Returns %NULL if a new cell + * needed to be allocated but couldn't be. Cells must be found with + * non-decreasing x-coordinate until the cell list is rewound using + * cell_list_rewind(). Ownership of the returned cell is retained by + * the cell list. */ +inline static struct cell * +cell_list_find (struct cell_list *cells, int x) +{ + struct cell **cursor = cells->cursor; + struct cell *tail; + + while (1) { + UNROLL3({ + tail = *cursor; + if (tail->x >= x) { + break; + } + cursor = &tail->next; + }); + } + cells->cursor = cursor; + + if (tail->x == x) + return tail; + + return cell_list_alloc (cells, cursor, tail, x); +} + +/* Find two cells at x1 and x2. This is exactly equivalent + * to + * + * pair.cell1 = cell_list_find(cells, x1); + * pair.cell2 = cell_list_find(cells, x2); + * + * except with less function call overhead. */ +inline static struct cell_pair +cell_list_find_pair(struct cell_list *cells, int x1, int x2) +{ + struct cell_pair pair; + struct cell **cursor = cells->cursor; + struct cell *cell1; + struct cell *cell2; + struct cell *newcell; + + /* Find first cell at x1. */ + while (1) { + UNROLL3({ + cell1 = *cursor; + if (cell1->x > x1) + break; + + if (cell1->x == x1) + goto found_first; + + cursor = &cell1->next; + }); + } + + /* New first cell at x1. */ + newcell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + if (likely (NULL != newcell)) { + *cursor = newcell; + newcell->next = cell1; + newcell->x = x1; + newcell->uncovered_area = 0; + newcell->covered_height = 0; + } + cell1 = newcell; + found_first: + + /* Find second cell at x2. */ + while (1) { + UNROLL3({ + cell2 = *cursor; + if (cell2->x > x2) + break; + if (cell2->x == x2) + goto found_second; + cursor = &cell2->next; + }); + } + + /* New second cell at x2. */ + newcell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + if (likely (NULL != newcell)) { + *cursor = newcell; + newcell->next = cell2; + newcell->x = x2; + newcell->uncovered_area = 0; + newcell->covered_height = 0; + } + cell2 = newcell; + found_second: + + cells->cursor = cursor; + pair.cell1 = cell1; + pair.cell2 = cell2; + return pair; +} + +/* Add an unbounded subpixel span covering subpixels >= x to the + * coverage cells. */ +static glitter_status_t +cell_list_add_unbounded_subspan (struct cell_list *cells, + grid_scaled_x_t x) +{ + struct cell *cell; + int ix, fx; + + GRID_X_TO_INT_FRAC(x, ix, fx); + + cell = cell_list_find (cells, ix); + if (likely (cell != NULL)) { + cell->uncovered_area += 2*fx; + cell->covered_height++; + return GLITTER_STATUS_SUCCESS; + } + + return GLITTER_STATUS_NO_MEMORY; +} + +/* Add a subpixel span covering [x1, x2) to the coverage cells. */ +inline static glitter_status_t +cell_list_add_subspan( + struct cell_list *cells, + grid_scaled_x_t x1, + grid_scaled_x_t x2) +{ + int ix1, fx1; + int ix2, fx2; + + GRID_X_TO_INT_FRAC(x1, ix1, fx1); + GRID_X_TO_INT_FRAC(x2, ix2, fx2); + + if (ix1 != ix2) { + struct cell_pair p; + p = cell_list_find_pair(cells, ix1, ix2); + if (likely (p.cell1 != NULL && p.cell2 != NULL)) { + p.cell1->uncovered_area += 2*fx1; + ++p.cell1->covered_height; + p.cell2->uncovered_area -= 2*fx2; + --p.cell2->covered_height; + return GLITTER_STATUS_SUCCESS; + } + } else { + struct cell *cell = cell_list_find(cells, ix1); + if (likely (cell != NULL)) { + cell->uncovered_area += 2*(fx1-fx2); + return GLITTER_STATUS_SUCCESS; + } + } + return GLITTER_STATUS_NO_MEMORY; +} + +/* Adds the analytical coverage of an edge crossing the current pixel + * row to the coverage cells and advances the edge's x position to the + * following row. + * + * This function is only called when we know that during this pixel row: + * + * 1) The relative order of all edges on the active list doesn't + * change. In particular, no edges intersect within this row to pixel + * precision. + * + * 2) No new edges start in this row. + * + * 3) No existing edges end mid-row. + * + * This function depends on being called with all edges from the + * active list in the order they appear on the list (i.e. with + * non-decreasing x-coordinate.) */ +static glitter_status_t +cell_list_render_edge( + struct cell_list *cells, + struct edge *edge, + int sign) +{ + grid_scaled_y_t y1, y2, dy; + grid_scaled_x_t dx; + int ix1, ix2; + grid_scaled_x_t fx1, fx2; + + struct quorem x1 = edge->x; + struct quorem x2 = x1; + + if (! edge->vertical) { + x2.quo += edge->dxdy_full.quo; + x2.rem += edge->dxdy_full.rem; + if (x2.rem >= 0) { + ++x2.quo; + x2.rem -= edge->dy; + } + + edge->x = x2; + } + + GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); + GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); + + /* Edge is entirely within a column? */ + if (ix1 == ix2) { + /* We always know that ix1 is >= the cell list cursor in this + * case due to the no-intersections precondition. */ + struct cell *cell = cell_list_find(cells, ix1); + if (unlikely (NULL == cell)) + return GLITTER_STATUS_NO_MEMORY; + + cell->covered_height += sign*GRID_Y; + cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; + return GLITTER_STATUS_SUCCESS; + } + + /* Orient the edge left-to-right. */ + dx = x2.quo - x1.quo; + if (dx >= 0) { + y1 = 0; + y2 = GRID_Y; + } else { + int tmp; + tmp = ix1; ix1 = ix2; ix2 = tmp; + tmp = fx1; fx1 = fx2; fx2 = tmp; + dx = -dx; + sign = -sign; + y1 = GRID_Y; + y2 = 0; + } + dy = y2 - y1; + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct cell_pair pair; + struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); + + /* When rendering a previous edge on the active list we may + * advance the cell list cursor past the leftmost pixel of the + * current edge even though the two edges don't intersect. + * e.g. consider two edges going down and rightwards: + * + * --\_+---\_+-----+-----+---- + * \_ \_ | | + * | \_ | \_ | | + * | \_| \_| | + * | \_ \_ | + * ----+-----+-\---+-\---+---- + * + * The left edge touches cells past the starting cell of the + * right edge. Fortunately such cases are rare. + * + * The rewinding is never necessary if the current edge stays + * within a single column because we've checked before calling + * this function that the active list order won't change. */ + cell_list_maybe_rewind(cells, ix1); + + pair = cell_list_find_pair(cells, ix1, ix1+1); + if (unlikely (!pair.cell1 || !pair.cell2)) + return GLITTER_STATUS_NO_MEMORY; + + pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); + pair.cell1->covered_height += sign*y.quo; + y.quo += y1; + + if (ix1+1 < ix2) { + struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); + struct cell *cell = pair.cell2; + + ++ix1; + do { + grid_scaled_y_t y_skip = dydx_full.quo; + y.rem += dydx_full.rem; + if (y.rem >= dx) { + ++y_skip; + y.rem -= dx; + } + + y.quo += y_skip; + + y_skip *= sign; + cell->uncovered_area += y_skip*GRID_X; + cell->covered_height += y_skip; + + ++ix1; + cell = cell_list_find(cells, ix1); + if (unlikely (NULL == cell)) + return GLITTER_STATUS_NO_MEMORY; + } while (ix1 != ix2); + + pair.cell2 = cell; + } + pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; + pair.cell2->covered_height += sign*(y2 - y.quo); + } + + return GLITTER_STATUS_SUCCESS; +} + +static void +polygon_init (struct polygon *polygon) +{ + polygon->ymin = polygon->ymax = 0; + polygon->xmin = polygon->xmax = 0; + polygon->y_buckets = polygon->y_buckets_embedded; + pool_init (polygon->edge_pool.base, + 8192 - sizeof (struct _pool_chunk), + sizeof (polygon->edge_pool.embedded)); +} + +static void +polygon_fini (struct polygon *polygon) +{ + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + pool_fini (polygon->edge_pool.base); +} + +/* Empties the polygon of all edges. The polygon is then prepared to + * receive new edges and clip them to the vertical range + * [ymin,ymax). */ +static glitter_status_t +polygon_reset (struct polygon *polygon, + grid_scaled_x_t xmin, + grid_scaled_x_t xmax, + grid_scaled_y_t ymin, + grid_scaled_y_t ymax) +{ + unsigned h = ymax - ymin; + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, + ymin); + + pool_reset(polygon->edge_pool.base); + + if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) + goto bail_no_mem; /* even if you could, you wouldn't want to. */ + + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + polygon->y_buckets = polygon->y_buckets_embedded; + if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (num_buckets, + sizeof (struct bucket)); + if (unlikely (NULL == polygon->y_buckets)) + goto bail_no_mem; + } + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket)); + + polygon->ymin = ymin; + polygon->ymax = ymax; + polygon->xmin = xmin; + polygon->xmax = xmax; + return GLITTER_STATUS_SUCCESS; + + bail_no_mem: + polygon->ymin = 0; + polygon->ymax = 0; + return GLITTER_STATUS_NO_MEMORY; +} + +static void +_polygon_insert_edge_into_its_y_bucket( + struct polygon *polygon, + struct edge *e) +{ + unsigned j = e->ytop - polygon->ymin; + unsigned ix = j / EDGE_Y_BUCKET_HEIGHT; + unsigned offset = j % EDGE_Y_BUCKET_HEIGHT; + struct edge **ptail = &polygon->y_buckets[ix].edges; + e->next = *ptail; + *ptail = e; + polygon->y_buckets[ix].have_inside_edges |= offset; +} + +inline static glitter_status_t +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge) +{ + struct edge *e; + grid_scaled_x_t dx; + grid_scaled_y_t dy; + grid_scaled_y_t ytop, ybot; + grid_scaled_y_t ymin = polygon->ymin; + grid_scaled_y_t ymax = polygon->ymax; + + assert (edge->bottom > edge->top); + + if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) + return GLITTER_STATUS_SUCCESS; + + e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); + if (unlikely (NULL == e)) + return GLITTER_STATUS_NO_MEMORY; + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; + e->dir = edge->dir; + + ytop = edge->top >= ymin ? edge->top : ymin; + ybot = edge->bottom <= ymax ? edge->bottom : ymax; + e->ytop = ytop; + e->height_left = ybot - ytop; + + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + + /* Drop edges to the right of the clip extents. */ + if (e->x.quo >= polygon->xmax) + return GLITTER_STATUS_SUCCESS; + + /* Offset vertical edges at the left side of the clip extents + * to just shy of the left side. We depend on this when + * checking for possible intersections within the clip + * rectangle. */ + if (e->x.quo <= polygon->xmin) { + e->x.quo = polygon->xmin - 1; + } + } else { + e->vertical = FALSE; + e->dxdy = floored_divrem (dx, dy); + if (ytop == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); + e->x.quo += edge->line.p1.x; + } + + if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0) + return GLITTER_STATUS_SUCCESS; + + if (e->height_left >= GRID_Y) { + e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); + } else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } + } + + _polygon_insert_edge_into_its_y_bucket (polygon, e); + + e->x.rem -= dy; /* Bias the remainder for faster + * edge advancement. */ + return GLITTER_STATUS_SUCCESS; +} + +static void +active_list_reset (struct active_list *active) +{ + active->head = NULL; + active->min_height = 0; +} + +static void +active_list_init(struct active_list *active) +{ + active_list_reset(active); +} + +/* + * Merge two sorted edge lists. + * Input: + * - head_a: The head of the first list. + * - head_b: The head of the second list; head_b cannot be NULL. + * Output: + * Returns the head of the merged list. + * + * Implementation notes: + * To make it fast (in particular, to reduce to an insertion sort whenever + * one of the two input lists only has a single element) we iterate through + * a list until its head becomes greater than the head of the other list, + * then we switch their roles. As soon as one of the two lists is empty, we + * just attach the other one to the current list and exit. + * Writes to memory are only needed to "switch" lists (as it also requires + * attaching to the output list the list which we will be iterating next) and + * to attach the last non-empty list. + */ +static struct edge * +merge_sorted_edges (struct edge *head_a, struct edge *head_b) +{ + struct edge *head, **next; + + head = head_a; + next = &head; + + while (1) { + while (head_a != NULL && head_a->x.quo <= head_b->x.quo) { + next = &head_a->next; + head_a = head_a->next; + } + + *next = head_b; + if (head_a == NULL) + return head; + + while (head_b != NULL && head_b->x.quo <= head_a->x.quo) { + next = &head_b->next; + head_b = head_b->next; + } + + *next = head_a; + if (head_b == NULL) + return head; + } +} + +/* + * Sort (part of) a list. + * Input: + * - list: The list to be sorted; list cannot be NULL. + * - limit: Recursion limit. + * Output: + * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the + * input list; if the input list has fewer elements, head_out be a sorted list + * containing all the elements of the input list. + * Returns the head of the list of unprocessed elements (NULL if the sorted list contains + * all the elements of the input list). + * + * Implementation notes: + * Special case single element list, unroll/inline the sorting of the first two elements. + * Some tail recursion is used since we iterate on the bottom-up solution of the problem + * (we start with a small sorted list and keep merging other lists of the same size to it). + */ +static struct edge * +sort_edges (struct edge *list, + unsigned int level, + struct edge **head_out) +{ + struct edge *head_other, *remaining; + unsigned int i; + + head_other = list->next; + + /* Single element list -> return */ + if (head_other == NULL) { + *head_out = list; + return NULL; + } + + /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges): + * - Initialize remaining to be the list containing the elements after the second in the input list. + * - Initialize *head_out to be the sorted list containing the first two element. + */ + remaining = head_other->next; + if (list->x.quo <= head_other->x.quo) { + *head_out = list; + /* list->next = head_other; */ /* The input list is already like this. */ + head_other->next = NULL; + } else { + *head_out = head_other; + head_other->next = list; + list->next = NULL; + } + + for (i = 0; i < level && remaining; i++) { + /* Extract a sorted list of the same size as *head_out + * (2^(i+1) elements) from the list of remaining elements. */ + remaining = sort_edges (remaining, i, &head_other); + *head_out = merge_sorted_edges (*head_out, head_other); + } + + /* *head_out now contains (at most) 2^(level+1) elements. */ + + return remaining; +} + +/* Test if the edges on the active list can be safely advanced by a + * full row without intersections or any edges ending. */ +inline static int +active_list_can_step_full_row (struct active_list *active, + grid_scaled_x_t xmin) +{ + const struct edge *e; + grid_scaled_x_t prev_x = INT_MIN; + + /* Recomputes the minimum height of all edges on the active + * list if we have been dropping edges. */ + if (active->min_height <= 0) { + int min_height = INT_MAX; + + e = active->head; + while (NULL != e) { + if (e->height_left < min_height) + min_height = e->height_left; + e = e->next; + } + + active->min_height = min_height; + } + + if (active->min_height < GRID_Y) + return 0; + + /* Check for intersections as no edges end during the next row. */ + e = active->head; + while (NULL != e) { + struct quorem x = e->x; + + if (! e->vertical) { + x.quo += e->dxdy_full.quo; + x.rem += e->dxdy_full.rem; + if (x.rem >= 0) + ++x.quo; + } + + /* There's may be an intersection if the edge sort order might + * change. */ + if (x.quo <= prev_x) { + /* Ignore intersections to the left of the clip extents. + * This assumes that all vertical edges on or at the left + * side of the clip rectangle have been shifted slightly + * to the left in polygon_add_edge(). */ + if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin) + return 0; + } + else { + prev_x = x.quo; + } + e = e->next; + } + + return 1; +} + +/* Merges edges on the given subpixel row from the polygon to the + * active_list. */ +inline static void +active_list_merge_edges_from_polygon( + struct active_list *active, + grid_scaled_y_t y, + struct polygon *polygon) +{ + /* Split off the edges on the current subrow and merge them into + * the active list. */ + unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); + int min_height = active->min_height; + struct edge *subrow_edges = NULL; + struct edge **ptail = &polygon->y_buckets[ix].edges; + + while (1) { + struct edge *tail = *ptail; + if (NULL == tail) break; + + if (y == tail->ytop) { + *ptail = tail->next; + tail->next = subrow_edges; + subrow_edges = tail; + if (tail->height_left < min_height) + min_height = tail->height_left; + } else { + ptail = &tail->next; + } + } + if (subrow_edges) { + sort_edges (subrow_edges, UINT_MAX, &subrow_edges); + active->head = merge_sorted_edges (active->head, subrow_edges); + active->min_height = min_height; + } +} + +/* Advance the edges on the active list by one subsample row by + * updating their x positions. Drop edges from the list that end. */ +inline static void +active_list_substep_edges( + struct active_list *active) +{ + struct edge **cursor = &active->head; + grid_scaled_x_t prev_x = INT_MIN; + struct edge *unsorted = NULL; + + while (1) { + struct edge *edge; + + UNROLL3({ + edge = *cursor; + if (NULL == edge) + break; + + if (0 != --edge->height_left) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + + if (edge->x.quo < prev_x) { + *cursor = edge->next; + edge->next = unsorted; + unsorted = edge; + } else { + prev_x = edge->x.quo; + cursor = &edge->next; + } + + } else { + *cursor = edge->next; + } + }); + } + + if (unsorted) { + sort_edges (unsorted, UINT_MAX, &unsorted); + active->head = merge_sorted_edges (active->head, unsorted); + } +} + +inline static glitter_status_t +apply_nonzero_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int winding = 0; + int xstart; + int xend; + int status; + + cell_list_rewind (coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + winding = edge->dir; + while (1) { + edge = edge->next; + if (NULL == edge) + return cell_list_add_unbounded_subspan (coverages, xstart); + + winding += edge->dir; + if (0 == winding) { + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + } + } + + xend = edge->x.quo; + status = cell_list_add_subspan (coverages, xstart, xend); + if (unlikely (status)) + return status; + + edge = edge->next; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_evenodd_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int xstart; + int xend; + int status; + + cell_list_rewind (coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + + while (1) { + edge = edge->next; + if (NULL == edge) + return cell_list_add_unbounded_subspan (coverages, xstart); + + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + + edge = edge->next; + } + + xend = edge->x.quo; + status = cell_list_add_subspan (coverages, xstart, xend); + if (unlikely (status)) + return status; + + edge = edge->next; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_nonzero_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + int status; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + int winding = left_edge->dir; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) + cursor = &left_edge->next; + else + *cursor = left_edge->next; + + while (1) { + right_edge = *cursor; + if (NULL == right_edge) + return cell_list_render_edge (coverages, left_edge, +1); + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) + cursor = &right_edge->next; + else + *cursor = right_edge->next; + + winding += right_edge->dir; + if (0 == winding) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + } + + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } + } + + status = cell_list_render_edge (coverages, left_edge, +1); + if (unlikely (status)) + return status; + + status = cell_list_render_edge (coverages, right_edge, -1); + if (unlikely (status)) + return status; + + left_edge = *cursor; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_evenodd_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + int status; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + int winding = left_edge->dir; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) + cursor = &left_edge->next; + else + *cursor = left_edge->next; + + while (1) { + right_edge = *cursor; + if (NULL == right_edge) + return cell_list_render_edge (coverages, left_edge, +1); + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) + cursor = &right_edge->next; + else + *cursor = right_edge->next; + + winding += right_edge->dir; + if ((winding & 1) == 0) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + } + + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } + } + + status = cell_list_render_edge (coverages, left_edge, +1); + if (unlikely (status)) + return status; + + status = cell_list_render_edge (coverages, right_edge, -1); + if (unlikely (status)) + return status; + + left_edge = *cursor; + } + + return GLITTER_STATUS_SUCCESS; +} + +/* If the user hasn't configured a coverage blitter, use a default one + * that blits spans directly to an A8 raster. */ +#ifndef GLITTER_BLIT_COVERAGES + +inline static void +blit_span( + unsigned char *row_pixels, + int x, unsigned len, + grid_area_t coverage) +{ + int alpha = GRID_AREA_TO_ALPHA(coverage); + if (1 == len) { + row_pixels[x] = alpha; + } + else { + memset(row_pixels + x, alpha, len); + } +} + +#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \ + do { \ + int __y = y; \ + int __h = height; \ + do { \ + blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \ + } while (--__h); \ + } while (0) + +static void +blit_cells( + struct cell_list *cells, + unsigned char *row_pixels, + int xmin, int xmax) +{ + struct cell *cell = cells->head; + int prev_x = xmin; + int coverage = 0; + if (NULL == cell) + return; + + while (NULL != cell && cell->x < xmin) { + coverage += cell->covered_height; + cell = cell->next; + } + coverage *= GRID_X*2; + + for (; NULL != cell; cell = cell->next) { + int x = cell->x; + int area; + if (x >= xmax) + break; + if (x > prev_x && 0 != coverage) { + blit_span(row_pixels, prev_x, x - prev_x, coverage); + } + + coverage += cell->covered_height * GRID_X*2; + area = coverage - cell->uncovered_area; + if (area) { + blit_span(row_pixels, x, 1, area); + } + prev_x = x+1; + } + + if (0 != coverage && prev_x < xmax) { + blit_span(row_pixels, prev_x, xmax - prev_x, coverage); + } +} +#endif /* GLITTER_BLIT_COVERAGES */ + +static void +_glitter_scan_converter_init(glitter_scan_converter_t *converter) +{ + polygon_init(converter->polygon); + active_list_init(converter->active); + cell_list_init(converter->coverages); + converter->xmin=0; + converter->ymin=0; + converter->xmax=0; + converter->ymax=0; +} + +static void +_glitter_scan_converter_fini(glitter_scan_converter_t *converter) +{ + polygon_fini(converter->polygon); + cell_list_fini(converter->coverages); + converter->xmin=0; + converter->ymin=0; + converter->xmax=0; + converter->ymax=0; +} + +static grid_scaled_t +int_to_grid_scaled(int i, int scale) +{ + /* Clamp to max/min representable scaled number. */ + if (i >= 0) { + if (i >= INT_MAX/scale) + i = INT_MAX/scale; + } + else { + if (i <= INT_MIN/scale) + i = INT_MIN/scale; + } + return i*scale; +} + +#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) +#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) + +I glitter_status_t +glitter_scan_converter_reset( + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax) +{ + glitter_status_t status; + + converter->xmin = 0; converter->xmax = 0; + converter->ymin = 0; converter->ymax = 0; + + xmin = int_to_grid_scaled_x(xmin); + ymin = int_to_grid_scaled_y(ymin); + xmax = int_to_grid_scaled_x(xmax); + ymax = int_to_grid_scaled_y(ymax); + + active_list_reset(converter->active); + cell_list_reset(converter->coverages); + status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax); + if (status) + return status; + + converter->xmin = xmin; + converter->xmax = xmax; + converter->ymin = ymin; + converter->ymax = ymax; + return GLITTER_STATUS_SUCCESS; +} + +/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) + * These macros convert an input coordinate in the client's + * device space to the rasterisation grid. + */ +/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use + * shifts if possible, and something saneish if not. + */ +#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) +#else +# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) +#endif + +#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) +#else +# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) +#endif + +#define INPUT_TO_GRID_general(in, out, grid_scale) do { \ + long long tmp__ = (long long)(grid_scale) * (in); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ +} while (0) + +I glitter_status_t +glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + const cairo_edge_t *edge) +{ + cairo_edge_t e; + + INPUT_TO_GRID_Y (edge->top, e.top); + INPUT_TO_GRID_Y (edge->bottom, e.bottom); + if (e.top >= e.bottom) + return GLITTER_STATUS_SUCCESS; + + /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ + INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); + INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); + if (e.line.p1.y == e.line.p2.y) + return GLITTER_STATUS_SUCCESS; + + INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); + INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); + + e.dir = edge->dir; + + return polygon_add_edge (converter->polygon, &e); +} + +#ifndef GLITTER_BLIT_COVERAGES_BEGIN +# define GLITTER_BLIT_COVERAGES_BEGIN +#endif + +#ifndef GLITTER_BLIT_COVERAGES_END +# define GLITTER_BLIT_COVERAGES_END +#endif + +#ifndef GLITTER_BLIT_COVERAGES_EMPTY +# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax) +#endif + +static cairo_bool_t +active_list_is_vertical (struct active_list *active) +{ + struct edge *e; + + for (e = active->head; e != NULL; e = e->next) { + if (! e->vertical) + return FALSE; + } + + return TRUE; +} + +static void +step_edges (struct active_list *active, int count) +{ + struct edge **cursor = &active->head; + struct edge *edge; + + for (edge = *cursor; edge != NULL; edge = *cursor) { + edge->height_left -= GRID_Y * count; + if (edge->height_left) + cursor = &edge->next; + else + *cursor = edge->next; + } +} + +I glitter_status_t +glitter_scan_converter_render( + glitter_scan_converter_t *converter, + int nonzero_fill, + GLITTER_BLIT_COVERAGES_ARGS) +{ + int i, j; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int xmin_i, xmax_i; + grid_scaled_x_t xmin = converter->xmin; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; + struct active_list *active = converter->active; + + xmin_i = converter->xmin / GRID_X; + xmax_i = converter->xmax / GRID_X; + if (xmin_i >= xmax_i) + return GLITTER_STATUS_SUCCESS; + + /* Let the coverage blitter initialise itself. */ + GLITTER_BLIT_COVERAGES_BEGIN; + + /* Render each pixel row. */ + for (i = 0; i < h; i = j) { + int do_full_step = 0; + glitter_status_t status = 0; + + j = i + 1; + + /* Determine if we can ignore this row or use the full pixel + * stepper. */ + if (polygon->y_buckets[i].edges == NULL) { + if (! active->head) { + for (; j < h && ! polygon->y_buckets[j].edges; j++) + ; + GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); + continue; + } + do_full_step = active_list_can_step_full_row (active, xmin); + } + else if (! polygon->y_buckets[i].have_inside_edges) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y; + active_list_merge_edges_from_polygon (active, y, polygon); + do_full_step = active_list_can_step_full_row (active, xmin); + } + + if (do_full_step) { + /* Step by a full pixel row's worth. */ + if (nonzero_fill) { + status = apply_nonzero_fill_rule_and_step_edges (active, + coverages); + } else { + status = apply_evenodd_fill_rule_and_step_edges (active, + coverages); + } + + if (active_list_is_vertical (active)) { + while (j < h && + polygon->y_buckets[j].edges == NULL && + active->min_height >= 2*GRID_Y) + { + active->min_height -= GRID_Y; + j++; + } + if (j != i + 1) + step_edges (active, j - (i + 1)); + } + } else { + /* Supersample this row. */ + grid_scaled_y_t suby; + for (suby = 0; suby < GRID_Y; suby++) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; + + active_list_merge_edges_from_polygon (active, y, polygon); + + if (nonzero_fill) { + status |= apply_nonzero_fill_rule_for_subrow (active, + coverages); + } else { + status |= apply_evenodd_fill_rule_for_subrow (active, + coverages); + } + + active_list_substep_edges(active); + } + } + + if (unlikely (status)) + return status; + + GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i); + cell_list_reset (coverages); + + if (! active->head) + active->min_height = INT_MAX; + else + active->min_height -= GRID_Y; + } + + /* Clean up the coverage blitter. */ + GLITTER_BLIT_COVERAGES_END; + + return GLITTER_STATUS_SUCCESS; +} + +/*------------------------------------------------------------------------- + * cairo specific implementation: the coverage blitter and + * scan converter subclass. */ + +static glitter_status_t +blit_with_span_renderer (struct cell_list *cells, + cairo_span_renderer_t *renderer, + struct pool *span_pool, + int y, int height, + int xmin, int xmax) +{ + struct cell *cell = cells->head; + int prev_x = xmin; + int cover = 0; + cairo_half_open_span_t *spans; + unsigned num_spans; + + if (cell == NULL) + return blit_empty_with_span_renderer (renderer, y, height); + + /* Skip cells to the left of the clip region. */ + while (cell != NULL && cell->x < xmin) { + cover += cell->covered_height; + cell = cell->next; + } + cover *= GRID_X*2; + + /* Count number of cells remaining. */ + { + struct cell *next = cell; + num_spans = 1; + while (next != NULL) { + next = next->next; + ++num_spans; + } + num_spans = 2*num_spans; + } + + /* Allocate enough spans for the row. */ + pool_reset (span_pool); + spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); + if (unlikely (spans == NULL)) + return GLITTER_STATUS_NO_MEMORY; + + num_spans = 0; + + /* Form the spans from the coverages and areas. */ + for (; cell != NULL; cell = cell->next) { + int x = cell->x; + int area; + + if (x >= xmax) + break; + + if (x > prev_x) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + spans[num_spans].x = x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); + ++num_spans; + + prev_x = x+1; + } + + if (prev_x <= xmax) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + ++num_spans; + } + + if (prev_x < xmax && cover) { + spans[num_spans].x = xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + + /* Dump them into the renderer. */ + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + +static glitter_status_t +blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height) +{ + return renderer->render_rows (renderer, y, height, NULL, 0); +} + +struct _cairo_tor_scan_converter { + cairo_scan_converter_t base; + + glitter_scan_converter_t converter[1]; + cairo_fill_rule_t fill_rule; + + struct { + struct pool base[1]; + cairo_half_open_span_t embedded[32]; + } span_pool; +}; + +typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; + +static void +_cairo_tor_scan_converter_destroy (void *converter) +{ + cairo_tor_scan_converter_t *self = converter; + if (self == NULL) { + return; + } + _glitter_scan_converter_fini (self->converter); + pool_fini (self->span_pool.base); + free(self); +} + +static cairo_status_t +_cairo_tor_scan_converter_add_edge (void *converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_tor_scan_converter_t *self = converter; + cairo_status_t status; + cairo_edge_t edge; + + edge.line.p1 = *p1; + edge.line.p2 = *p2; + edge.top = top; + edge.bottom = bottom; + edge.dir = dir; + + status = glitter_scan_converter_add_edge (self->converter, &edge); + if (unlikely (status)) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tor_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) +{ + cairo_tor_scan_converter_t *self = converter; + cairo_status_t status; + int i; + + for (i = 0; i < polygon->num_edges; i++) { + status = glitter_scan_converter_add_edge (self->converter, + &polygon->edges[i]); + if (unlikely (status)) { + return _cairo_scan_converter_set_error (self, + _cairo_error (status)); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_tor_scan_converter_t *self = converter; + cairo_status_t status; + + status = glitter_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING, + renderer, + self->span_pool.base); + if (unlikely (status)) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_scan_converter_t * +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule) +{ + cairo_tor_scan_converter_t *self; + cairo_status_t status; + + self = calloc (1, sizeof(struct _cairo_tor_scan_converter)); + if (unlikely (self == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail_nomem; + } + + self->base.destroy = _cairo_tor_scan_converter_destroy; + self->base.add_edge = _cairo_tor_scan_converter_add_edge; + self->base.add_polygon = _cairo_tor_scan_converter_add_polygon; + self->base.generate = _cairo_tor_scan_converter_generate; + + pool_init (self->span_pool.base, + 250 * sizeof(self->span_pool.embedded[0]), + sizeof(self->span_pool.embedded)); + + _glitter_scan_converter_init (self->converter); + status = glitter_scan_converter_reset (self->converter, + xmin, ymin, xmax, ymax); + if (unlikely (status)) + goto bail; + + self->fill_rule = fill_rule; + + return &self->base; + + bail: + self->base.destroy(&self->base); + bail_nomem: + return _cairo_scan_converter_create_in_error (status); +} diff --git a/libs/cairo/src/cairo-toy-font-face.c b/libs/cairo/src/cairo-toy-font-face.c new file mode 100644 index 000000000..92045ba3e --- /dev/null +++ b/libs/cairo/src/cairo-toy-font-face.c @@ -0,0 +1,489 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + + +static const cairo_font_face_t _cairo_font_face_null_pointer = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NULL_POINTER, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_string = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_STRING, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_slant = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_SLANT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_weight = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_WEIGHT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend; + +static int +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b); + +/* We maintain a hash table from family/weight/slant => + * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of + * this mapping is to provide unique #cairo_font_face_t values so that + * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t + * works. Once the corresponding #cairo_font_face_t objects fall out of + * downstream caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_toy_font_face_mutex. + */ +static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; + +static cairo_hash_table_t * +_cairo_toy_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + + if (cairo_toy_font_face_hash_table == NULL) + { + cairo_toy_font_face_hash_table = + _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); + + if (cairo_toy_font_face_hash_table == NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + return NULL; + } + } + + return cairo_toy_font_face_hash_table; +} + +static void +_cairo_toy_font_face_hash_table_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); +} + +/** + * _cairo_toy_font_face_init_key: + * + * Initialize those portions of #cairo_toy_font_face_t needed to use + * it as a hash table key, including the hash code buried away in + * font_face->base.hash_entry. No memory allocation is performed here + * so that no fini call is needed. We do this to make it easier to use + * an automatic #cairo_toy_font_face_t variable as a key. + **/ +static void +_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + unsigned long hash; + + key->family = family; + key->owns_family = FALSE; + + key->slant = slant; + key->weight = weight; + + /* 1607 and 1451 are just a couple of arbitrary primes. */ + hash = _cairo_hash_string (family); + hash += ((unsigned long) slant) * 1607; + hash += ((unsigned long) weight) * 1451; + + assert (hash != 0); + key->base.hash_entry.hash = hash; +} + +static cairo_status_t +_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face, + cairo_font_face_t **impl_font_face) +{ + const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (font_face->base.status)) + return font_face->base.status; + + if (backend->create_for_toy != NULL && + 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT, + strlen (CAIRO_USER_FONT_FAMILY_DEFAULT))) + { + status = backend->create_for_toy (font_face, impl_font_face); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + backend = &_cairo_user_font_face_backend; + status = backend->create_for_toy (font_face, impl_font_face); + } + + return status; +} + +static cairo_status_t +_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + char *family_copy; + cairo_status_t status; + + family_copy = strdup (family); + if (unlikely (family_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight); + font_face->owns_family = TRUE; + + _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); + + status = _cairo_toy_font_face_create_impl_face (font_face, + &font_face->impl_face); + if (unlikely (status)) { + free (family_copy); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) +{ + /* We assert here that we own font_face->family before casting + * away the const qualifer. */ + assert (font_face->owns_family); + free ((char*) font_face->family); + + if (font_face->impl_face) + cairo_font_face_destroy (font_face->impl_face); +} + +static int +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_toy_font_face_t *face_a = key_a; + const cairo_toy_font_face_t *face_b = key_b; + + return (strcmp (face_a->family, face_b->family) == 0 && + face_a->slant == face_b->slant && + face_a->weight == face_b->weight); +} + +/** + * cairo_toy_font_face_create: + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Creates a font face from a triplet of family, slant, and weight. + * These font faces are used in implementation of the the #cairo_t "toy" + * font API. + * + * If @family is the zero-length string "", the platform-specific default + * family is assumed. The default family then can be queried using + * cairo_toy_font_face_get_family(). + * + * The cairo_select_font_face() function uses this to create font faces. + * See that function for limitations and other details of toy font faces. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_status_t status; + cairo_toy_font_face_t key, *font_face; + cairo_hash_table_t *hash_table; + + if (family == NULL) + return (cairo_font_face_t*) &_cairo_font_face_null_pointer; + + /* Make sure we've got valid UTF-8 for the family */ + status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); + if (unlikely (status)) { + if (status == CAIRO_STATUS_INVALID_STRING) + return (cairo_font_face_t*) &_cairo_font_face_invalid_string; + + return (cairo_font_face_t*) &_cairo_font_face_nil; + } + + switch (slant) { + case CAIRO_FONT_SLANT_NORMAL: + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; + } + + switch (weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + case CAIRO_FONT_WEIGHT_BOLD: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; + } + + if (*family == '\0') + family = CAIRO_FONT_FAMILY_DEFAULT; + + hash_table = _cairo_toy_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) + goto UNWIND; + + _cairo_toy_font_face_init_key (&key, family, slant, weight); + + /* Return existing font_face if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + if (font_face->base.status == CAIRO_STATUS_SUCCESS) { + /* We increment the reference count here manually to avoid + double-locking. */ + _cairo_reference_count_inc (&font_face->base.ref_count); + _cairo_toy_font_face_hash_table_unlock (); + return &font_face->base; + } + + /* remove the bad font from the hash table */ + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + font_face->base.hash_entry.hash = 0; + } + + /* Otherwise create it and insert into hash table. */ + font_face = malloc (sizeof (cairo_toy_font_face_t)); + if (unlikely (font_face == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto UNWIND_HASH_TABLE_LOCK; + } + + status = _cairo_toy_font_face_init (font_face, family, slant, weight); + if (unlikely (status)) + goto UNWIND_FONT_FACE_MALLOC; + + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); + if (unlikely (status)) + goto UNWIND_FONT_FACE_INIT; + + _cairo_toy_font_face_hash_table_unlock (); + + return &font_face->base; + + UNWIND_FONT_FACE_INIT: + _cairo_toy_font_face_fini (font_face); + UNWIND_FONT_FACE_MALLOC: + free (font_face); + UNWIND_HASH_TABLE_LOCK: + _cairo_toy_font_face_hash_table_unlock (); + UNWIND: + return (cairo_font_face_t*) &_cairo_font_face_nil; +} +slim_hidden_def (cairo_toy_font_face_create); + +static void +_cairo_toy_font_face_destroy (void *abstract_face) +{ + cairo_toy_font_face_t *font_face = abstract_face; + cairo_hash_table_t *hash_table; + + if (font_face == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count)) + return; + + hash_table = _cairo_toy_font_face_hash_table_lock (); + /* All created objects must have been mapped in the hash table. */ + assert (hash_table != NULL); + + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_toy_font_face_hash_table_unlock (); + return; + } + + if (font_face->base.hash_entry.hash != 0) + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + + _cairo_toy_font_face_hash_table_unlock (); + + _cairo_toy_font_face_fini (font_face); +} + +static cairo_status_t +_cairo_toy_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font) +{ + cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face; + + ASSERT_NOT_REACHED; + + return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH); +} + +static cairo_font_face_t * +_cairo_toy_font_face_get_implementation (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_toy_font_face_t *font_face = abstract_font_face; + + if (font_face->impl_face) { + cairo_font_face_t *impl = font_face->impl_face; + + if (impl->backend->get_implementation != NULL) { + return impl->backend->get_implementation (impl, + font_matrix, + ctm, + options); + } + + return cairo_font_face_reference (impl); + } + + return abstract_font_face; +} + +static cairo_bool_t +_cairo_font_face_is_toy (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_toy_font_face_backend; +} + +/** + * cairo_toy_font_face_get_family: + * @font_face: A toy font face + * + * Gets the familly name of a toy font. + * + * Return value: The family name. This string is owned by the font face + * and remains valid as long as the font face is alive (referenced). + * + * Since: 1.8 + **/ +const char * +cairo_toy_font_face_get_family (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_FAMILY_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_FAMILY_DEFAULT; + } + assert (toy_font_face->owns_family); + return toy_font_face->family; +} + +/** + * cairo_toy_font_face_get_slant: + * @font_face: A toy font face + * + * Gets the slant a toy font. + * + * Return value: The slant value + * + * Since: 1.8 + **/ +cairo_font_slant_t +cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_SLANT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_SLANT_DEFAULT; + } + return toy_font_face->slant; +} +slim_hidden_def (cairo_toy_font_face_get_slant); + +/** + * cairo_toy_font_face_get_weight: + * @font_face: A toy font face + * + * Gets the weight a toy font. + * + * Return value: The weight value + * + * Since: 1.8 + **/ +cairo_font_weight_t +cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_WEIGHT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_WEIGHT_DEFAULT; + } + return toy_font_face->weight; +} +slim_hidden_def (cairo_toy_font_face_get_weight); + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { + CAIRO_FONT_TYPE_TOY, + NULL, /* create_for_toy */ + _cairo_toy_font_face_destroy, + _cairo_toy_font_face_scaled_font_create, + _cairo_toy_font_face_get_implementation +}; + +void +_cairo_toy_font_face_reset_static_data (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * cairo_toy_font_face_hash_table_lock simply to avoid + * creating the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + hash_table = cairo_toy_font_face_hash_table; + cairo_toy_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); +} diff --git a/libs/cairo/src/cairo-traps.c b/libs/cairo/src/cairo-traps.c new file mode 100644 index 000000000..c3f1d2e35 --- /dev/null +++ b/libs/cairo/src/cairo-traps.c @@ -0,0 +1,570 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-slope-private.h" + +/* private functions */ + +void +_cairo_traps_init (cairo_traps_t *traps) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); + + traps->status = CAIRO_STATUS_SUCCESS; + + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + + traps->num_traps = 0; + + traps->traps_size = ARRAY_LENGTH (traps->traps_embedded); + traps->traps = traps->traps_embedded; + + traps->num_limits = 0; + traps->has_intersections = FALSE; +} + +void +_cairo_traps_limit (cairo_traps_t *traps, + const cairo_box_t *limits, + int num_limits) +{ + traps->limits = limits; + traps->num_limits = num_limits; +} + +void +_cairo_traps_clear (cairo_traps_t *traps) +{ + traps->status = CAIRO_STATUS_SUCCESS; + + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + + traps->num_traps = 0; + traps->has_intersections = FALSE; +} + +void +_cairo_traps_fini (cairo_traps_t *traps) +{ + if (traps->traps != traps->traps_embedded) + free (traps->traps); + + VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); +} + +/* make room for at least one more trap */ +static cairo_bool_t +_cairo_traps_grow (cairo_traps_t *traps) +{ + cairo_trapezoid_t *new_traps; + int new_size = 4 * traps->traps_size; + + if (CAIRO_INJECT_FAULT ()) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + if (traps->traps == traps->traps_embedded) { + new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); + if (new_traps != NULL) + memcpy (new_traps, traps->traps, sizeof (traps->traps_embedded)); + } else { + new_traps = _cairo_realloc_ab (traps->traps, + new_size, sizeof (cairo_trapezoid_t)); + } + + if (unlikely (new_traps == NULL)) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + traps->traps = new_traps; + traps->traps_size = new_size; + return TRUE; +} + +void +_cairo_traps_add_trap (cairo_traps_t *traps, + cairo_fixed_t top, cairo_fixed_t bottom, + cairo_line_t *left, cairo_line_t *right) +{ + cairo_trapezoid_t *trap; + + if (unlikely (traps->num_traps == traps->traps_size)) { + if (unlikely (! _cairo_traps_grow (traps))) + return; + } + + trap = &traps->traps[traps->num_traps++]; + trap->top = top; + trap->bottom = bottom; + trap->left = *left; + trap->right = *right; +} + +/** + * _cairo_traps_init_box: + * @traps: a #cairo_traps_t + * @box: an array box that will each be converted to a single trapezoid + * to store in @traps. + * + * Initializes a #cairo_traps_t to contain an array of rectangular + * trapezoids. + **/ +cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_boxes_t *boxes) +{ + cairo_trapezoid_t *trap; + const struct _cairo_boxes_chunk *chunk; + + _cairo_traps_init (traps); + + while (traps->traps_size < boxes->num_boxes) { + if (unlikely (! _cairo_traps_grow (traps))) { + _cairo_traps_fini (traps); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + traps->num_traps = boxes->num_boxes; + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + traps->maybe_region = boxes->is_pixel_aligned; + + trap = &traps->traps[0]; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box; + int i; + + box = chunk->base; + for (i = 0; i < chunk->count; i++) { + trap->top = box->p1.y; + trap->bottom = box->p2.y; + + trap->left.p1 = box->p1; + trap->left.p2.x = box->p1.x; + trap->left.p2.y = box->p2.y; + + trap->right.p1.x = box->p2.x; + trap->right.p1.y = box->p1.y; + trap->right.p2 = box->p2; + + box++, trap++; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, + const cairo_point_t *top_left, + const cairo_point_t *bottom_right) +{ + cairo_line_t left; + cairo_line_t right; + cairo_fixed_t top, bottom; + + if (top_left->y == bottom_right->y) + return CAIRO_STATUS_SUCCESS; + + if (top_left->x == bottom_right->x) + return CAIRO_STATUS_SUCCESS; + + left.p1.x = left.p2.x = top_left->x; + left.p1.y = right.p1.y = top_left->y; + right.p1.x = right.p2.x = bottom_right->x; + left.p2.y = right.p2.y = bottom_right->y; + + top = top_left->y; + bottom = bottom_right->y; + + if (traps->num_limits) { + cairo_bool_t reversed; + int n; + + /* support counter-clockwise winding for rectangular tessellation */ + reversed = top_left->x > bottom_right->x; + if (reversed) { + right.p1.x = right.p2.x = top_left->x; + left.p1.x = left.p2.x = bottom_right->x; + } + + for (n = 0; n < traps->num_limits; n++) { + const cairo_box_t *limits = &traps->limits[n]; + cairo_line_t _left, _right; + cairo_fixed_t _top, _bottom; + + if (top >= limits->p2.y) + continue; + if (bottom <= limits->p1.y) + continue; + + /* Trivially reject if trapezoid is entirely to the right or + * to the left of the limits. */ + if (left.p1.x >= limits->p2.x) + continue; + if (right.p1.x <= limits->p1.x) + continue; + + /* Otherwise, clip the trapezoid to the limits. */ + _top = top; + if (_top < limits->p1.y) + _top = limits->p1.y; + + _bottom = bottom; + if (_bottom > limits->p2.y) + _bottom = limits->p2.y; + + if (_bottom <= _top) + continue; + + _left = left; + if (_left.p1.x < limits->p1.x) { + _left.p1.x = limits->p1.x; + _left.p1.y = limits->p1.y; + _left.p2.x = limits->p1.x; + _left.p2.y = limits->p2.y; + } + + _right = right; + if (_right.p1.x > limits->p2.x) { + _right.p1.x = limits->p2.x; + _right.p1.y = limits->p1.y; + _right.p2.x = limits->p2.x; + _right.p2.y = limits->p2.y; + } + + if (left.p1.x >= right.p1.x) + continue; + + if (reversed) + _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left); + else + _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right); + } + } else { + _cairo_traps_add_trap (traps, top, bottom, &left, &right); + } + + return traps->status; +} + +void +_cairo_traps_translate (cairo_traps_t *traps, int x, int y) +{ + cairo_fixed_t xoff, yoff; + cairo_trapezoid_t *t; + int i; + + /* Ugh. The cairo_composite/(Render) interface doesn't allow + an offset for the trapezoids. Need to manually shift all + the coordinates to align with the offset origin of the + intermediate surface. */ + + xoff = _cairo_fixed_from_int (x); + yoff = _cairo_fixed_from_int (y); + + for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { + t->top += yoff; + t->bottom += yoff; + t->left.p1.x += xoff; + t->left.p1.y += yoff; + t->left.p2.x += xoff; + t->left.p2.y += yoff; + t->right.p1.x += xoff; + t->right.p1.y += yoff; + t->right.p2.x += xoff; + t->right.p2.y += yoff; + } +} + +void +_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, + cairo_trapezoid_t *src_traps, + int num_traps, + double tx, double ty, + double sx, double sy) +{ + int i; + cairo_fixed_t xoff = _cairo_fixed_from_double (tx); + cairo_fixed_t yoff = _cairo_fixed_from_double (ty); + + if (sx == 1.0 && sy == 1.0) { + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = src_traps[i].top + yoff; + offset_traps[i].bottom = src_traps[i].bottom + yoff; + offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff; + offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff; + offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff; + offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff; + offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff; + offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff; + offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff; + offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff; + } + } else { + cairo_fixed_t xsc = _cairo_fixed_from_double (sx); + cairo_fixed_t ysc = _cairo_fixed_from_double (sy); + + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc); + offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc); + offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc); + offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc); + offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc); + offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc); + offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc); + offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc); + offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc); + offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc); + } + } +} + +static cairo_bool_t +_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) +{ + cairo_slope_t slope_left, slope_pt, slope_right; + + if (t->top > pt->y) + return FALSE; + if (t->bottom < pt->y) + return FALSE; + + _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); + _cairo_slope_init (&slope_pt, &t->left.p1, pt); + + if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) + return FALSE; + + _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); + _cairo_slope_init (&slope_pt, &t->right.p1, pt); + + if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) + return FALSE; + + return TRUE; +} + +cairo_bool_t +_cairo_traps_contain (const cairo_traps_t *traps, + double x, double y) +{ + int i; + cairo_point_t point; + + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); + + for (i = 0; i < traps->num_traps; i++) { + if (_cairo_trap_contains (&traps->traps[i], &point)) + return TRUE; + } + + return FALSE; +} + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y); +} + +void +_cairo_traps_extents (const cairo_traps_t *traps, + cairo_box_t *extents) +{ + int i; + + if (traps->num_traps == 0) { + extents->p1.x = extents->p1.y = 0; + extents->p2.x = extents->p2.y = 0; + return; + } + + extents->p1.x = extents->p1.y = INT32_MAX; + extents->p2.x = extents->p2.y = INT32_MIN; + + for (i = 0; i < traps->num_traps; i++) { + const cairo_trapezoid_t *trap = &traps->traps[i]; + + if (trap->top < extents->p1.y) + extents->p1.y = trap->top; + if (trap->bottom > extents->p2.y) + extents->p2.y = trap->bottom; + + if (trap->left.p1.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p1.x; + if (trap->top != trap->left.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->top); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + if (trap->left.p2.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p2.x; + if (trap->bottom != trap->left.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->bottom); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + + if (trap->right.p1.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p1.x; + if (trap->top != trap->right.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->top); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; + } + if (trap->right.p2.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p2.x; + if (trap->bottom != trap->right.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->bottom); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; + } + } +} + + +/** + * _cairo_traps_extract_region: + * @traps: a #cairo_traps_t + * @region: a #cairo_region_t + * + * Determines if a set of trapezoids are exactly representable as a + * cairo region. If so, the passed-in region is initialized to + * the area representing the given traps. It should be finalized + * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED + * is returned. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED + * or %CAIRO_STATUS_NO_MEMORY + **/ +cairo_int_status_t +_cairo_traps_extract_region (cairo_traps_t *traps, + cairo_region_t **region) +{ + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_int_status_t status; + int i, rect_count; + + /* we only treat this a hint... */ + if (! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t)); + + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + rect_count = 0; + for (i = 0; i < traps->num_traps; i++) { + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + int y1 = _cairo_fixed_integer_part (traps->traps[i].top); + int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + + rects[rect_count].x = x1; + rects[rect_count].y = y1; + rects[rect_count].width = x2 - x1; + rects[rect_count].height = y2 - y1; + + rect_count++; + } + + *region = cairo_region_create_rectangles (rects, rect_count); + status = (*region)->status; + + if (rects != stack_rects) + free (rects); + + return status; +} + +/* moves trap points such that they become the actual corners of the trapezoid */ +static void +_sanitize_trap (cairo_trapezoid_t *t) +{ + cairo_trapezoid_t s = *t; + +#define FIX(lr, tb, p) \ + if (t->lr.p.y != t->tb) { \ + t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ + t->lr.p.y = s.tb; \ + } + FIX (left, top, p1); + FIX (left, bottom, p2); + FIX (right, top, p1); + FIX (right, bottom, p2); +} + +cairo_private cairo_status_t +_cairo_traps_path (const cairo_traps_t *traps, + cairo_path_fixed_t *path) +{ + int i; + + for (i = 0; i < traps->num_traps; i++) { + cairo_status_t status; + cairo_trapezoid_t trap = traps->traps[i]; + + if (trap.top == trap.bottom) + continue; + + _sanitize_trap (&trap); + + status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom); + if (unlikely (status)) return status; + status = _cairo_path_fixed_close_path (path); + if (unlikely (status)) return status; + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/libs/cairo/src/cairo-truetype-subset-private.h b/libs/cairo/src/cairo-truetype-subset-private.h new file mode 100644 index 000000000..fee112b97 --- /dev/null +++ b/libs/cairo/src/cairo-truetype-subset-private.h @@ -0,0 +1,171 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H +#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H + +#include "cairoint.h" + +CAIRO_BEGIN_DECLS + +#if CAIRO_HAS_FONT_SUBSET + +/* The structs defined here should strictly follow the TrueType + * specification and not be padded. We use only 16-bit integer + * in their definition to guarantee that. The fields of type + * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit + * parts, and 64-bit members are broken into four. + * + * The test truetype-tables in the test suite makes sure that + * these tables have the right size. Please update that test + * if you add new tables/structs that should be packed. + */ + +#define MAKE_TT_TAG(a, b, c, d) (a<<24 | b<<16 | c<<8 | d) +#define TT_TAG_CFF MAKE_TT_TAG('C','F','F',' ') +#define TT_TAG_cmap MAKE_TT_TAG('c','m','a','p') +#define TT_TAG_cvt MAKE_TT_TAG('c','v','t',' ') +#define TT_TAG_fpgm MAKE_TT_TAG('f','p','g','m') +#define TT_TAG_glyf MAKE_TT_TAG('g','l','y','f') +#define TT_TAG_head MAKE_TT_TAG('h','e','a','d') +#define TT_TAG_hhea MAKE_TT_TAG('h','h','e','a') +#define TT_TAG_hmtx MAKE_TT_TAG('h','m','t','x') +#define TT_TAG_loca MAKE_TT_TAG('l','o','c','a') +#define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p') +#define TT_TAG_name MAKE_TT_TAG('n','a','m','e') +#define TT_TAG_post MAKE_TT_TAG('p','o','s','t') +#define TT_TAG_prep MAKE_TT_TAG('p','r','e','p') + +/* All tt_* structs are big-endian */ +typedef struct _tt_cmap_index { + uint16_t platform; + uint16_t encoding; + uint32_t offset; +} tt_cmap_index_t; + +typedef struct _tt_cmap { + uint16_t version; + uint16_t num_tables; + tt_cmap_index_t index[1]; +} tt_cmap_t; + +typedef struct _segment_map { + uint16_t format; + uint16_t length; + uint16_t version; + uint16_t segCountX2; + uint16_t searchRange; + uint16_t entrySelector; + uint16_t rangeShift; + uint16_t endCount[1]; +} tt_segment_map_t; + +typedef struct _tt_head { + int16_t version_1; + int16_t version_2; + int16_t revision_1; + int16_t revision_2; + uint16_t checksum_1; + uint16_t checksum_2; + uint16_t magic_1; + uint16_t magic_2; + uint16_t flags; + uint16_t units_per_em; + int16_t created_1; + int16_t created_2; + int16_t created_3; + int16_t created_4; + int16_t modified_1; + int16_t modified_2; + int16_t modified_3; + int16_t modified_4; + int16_t x_min; /* FWORD */ + int16_t y_min; /* FWORD */ + int16_t x_max; /* FWORD */ + int16_t y_max; /* FWORD */ + uint16_t mac_style; + uint16_t lowest_rec_pppem; + int16_t font_direction_hint; + int16_t index_to_loc_format; + int16_t glyph_data_format; +} tt_head_t; + +typedef struct _tt_hhea { + int16_t version_1; + int16_t version_2; + int16_t ascender; /* FWORD */ + int16_t descender; /* FWORD */ + int16_t line_gap; /* FWORD */ + uint16_t advance_max_width; /* UFWORD */ + int16_t min_left_side_bearing; /* FWORD */ + int16_t min_right_side_bearing; /* FWORD */ + int16_t x_max_extent; /* FWORD */ + int16_t caret_slope_rise; + int16_t caret_slope_run; + int16_t reserved[5]; + int16_t metric_data_format; + uint16_t num_hmetrics; +} tt_hhea_t; + +typedef struct _tt_maxp { + int16_t version_1; + int16_t version_2; + uint16_t num_glyphs; + uint16_t max_points; + uint16_t max_contours; + uint16_t max_composite_points; + uint16_t max_composite_contours; + uint16_t max_zones; + uint16_t max_twilight_points; + uint16_t max_storage; + uint16_t max_function_defs; + uint16_t max_instruction_defs; + uint16_t max_stack_elements; + uint16_t max_size_of_instructions; + uint16_t max_component_elements; + uint16_t max_component_depth; +} tt_maxp_t; + +typedef struct _tt_name_record { + uint16_t platform; + uint16_t encoding; + uint16_t language; + uint16_t name; + uint16_t length; + uint16_t offset; +} tt_name_record_t; + +typedef struct _tt_name { + uint16_t format; + uint16_t num_records; + uint16_t strings_offset; + tt_name_record_t records[1]; +} tt_name_t; + + + +/* composite_glyph_t flags */ +#define TT_ARG_1_AND_2_ARE_WORDS 0x0001 +#define TT_WE_HAVE_A_SCALE 0x0008 +#define TT_MORE_COMPONENTS 0x0020 +#define TT_WE_HAVE_AN_X_AND_Y_SCALE 0x0040 +#define TT_WE_HAVE_A_TWO_BY_TWO 0x0080 + +typedef struct _tt_composite_glyph { + uint16_t flags; + uint16_t index; + uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ +} tt_composite_glyph_t; + +typedef struct _tt_glyph_data { + int16_t num_contours; + int8_t data[8]; + tt_composite_glyph_t glyph; +} tt_glyph_data_t; + +#endif /* CAIRO_HAS_FONT_SUBSET */ + +CAIRO_END_DECLS + +#endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-truetype-subset.c b/libs/cairo/src/cairo-truetype-subset.c new file mode 100644 index 000000000..219adbeb6 --- /dev/null +++ b/libs/cairo/src/cairo-truetype-subset.c @@ -0,0 +1,1401 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Useful links: + * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html + * http://www.microsoft.com/typography/specs/default.htm + */ + +#define _BSD_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-truetype-subset-private.h" + + +typedef struct subset_glyph subset_glyph_t; +struct subset_glyph { + int parent_index; + unsigned long location; +}; + +typedef struct _cairo_truetype_font cairo_truetype_font_t; + +typedef struct table table_t; +struct table { + unsigned long tag; + cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag); + int pos; /* position in the font directory */ +}; + +struct _cairo_truetype_font { + + cairo_scaled_font_subset_t *scaled_font_subset; + + table_t truetype_tables[10]; + int num_tables; + + struct { + char *font_name; + char *ps_name; + unsigned int num_glyphs; + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; + int units_per_em; + } base; + + subset_glyph_t *glyphs; + const cairo_scaled_font_backend_t *backend; + int num_glyphs_in_face; + int checksum_index; + cairo_array_t output; + cairo_array_t string_offsets; + unsigned long last_offset; + unsigned long last_boundary; + int *parent_to_subset; + cairo_status_t status; + +}; + +/* + * Test that the structs we define for TrueType tables have the + * correct size, ie. they are not padded. + */ +#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) +check (tt_head_t, 54); +check (tt_hhea_t, 36); +check (tt_maxp_t, 32); +check (tt_name_record_t, 12); +check (tt_name_t, 18); +check (tt_name_t, 18); +check (tt_composite_glyph_t, 16); +check (tt_glyph_data_t, 26); +#undef check + +static cairo_status_t +cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, + unsigned short glyph, + unsigned short *out); + +#define SFNT_VERSION 0x00010000 +#define SFNT_STRING_MAX_LENGTH 65535 + +static cairo_status_t +_cairo_truetype_font_set_error (cairo_truetype_font_t *font, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + _cairo_status_set_error (&font->status, status); + + return _cairo_error (status); +} + +static cairo_status_t +_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_truetype_font_t **font_return) +{ + cairo_status_t status; + cairo_truetype_font_t *font; + const cairo_scaled_font_backend_t *backend; + tt_head_t head; + tt_hhea_t hhea; + tt_maxp_t maxp; + unsigned long size; + + backend = scaled_font_subset->scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* FIXME: We should either support subsetting vertical fonts, or fail on + * vertical. Currently font_options_t doesn't have vertical flag, but + * it should be added in the future. For now, the freetype backend + * returns UNSUPPORTED in load_truetype_table if the font is vertical. + * + * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font)) + * return CAIRO_INT_STATUS_UNSUPPORTED; + */ + + size = sizeof (tt_head_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char *) &head, + &size); + if (unlikely (status)) + return status; + + size = sizeof (tt_maxp_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_maxp, 0, + (unsigned char *) &maxp, + &size); + if (unlikely (status)) + return status; + + size = sizeof (tt_hhea_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char *) &hhea, + &size); + if (unlikely (status)) + return status; + + font = malloc (sizeof (cairo_truetype_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = backend; + font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); + font->scaled_font_subset = scaled_font_subset; + + font->last_offset = 0; + font->last_boundary = 0; + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail1; + + font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); + if (unlikely (font->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); + if (unlikely (font->parent_to_subset == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + + font->base.num_glyphs = 0; + font->base.x_min = (int16_t) be16_to_cpu (head.x_min); + font->base.y_min = (int16_t) be16_to_cpu (head.y_min); + font->base.x_max = (int16_t) be16_to_cpu (head.x_max); + font->base.y_max = (int16_t) be16_to_cpu (head.y_max); + font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender); + font->base.descent = (int16_t) be16_to_cpu (hhea.descender); + font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em); + if (font->base.units_per_em == 0) + font->base.units_per_em = 2048; + + font->base.ps_name = NULL; + font->base.font_name = NULL; + status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + &font->base.ps_name, + &font->base.font_name); + if (_cairo_status_is_error (status)) + goto fail3; + + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->base.ps_name == NULL) { + font->base.ps_name = malloc (30); + if (unlikely (font->base.ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", + scaled_font_subset->font_id, + scaled_font_subset->subset_id); + } + + font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); + if (unlikely (font->base.widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); + status = _cairo_array_grow_by (&font->string_offsets, 10); + if (unlikely (status)) + goto fail5; + + font->status = CAIRO_STATUS_SUCCESS; + + *font_return = font; + + return CAIRO_STATUS_SUCCESS; + + fail5: + _cairo_array_fini (&font->string_offsets); + free (font->base.widths); + fail4: + free (font->base.ps_name); + fail3: + free (font->parent_to_subset); + if (font->base.font_name) + free (font->base.font_name); + fail2: + free (font->glyphs); + fail1: + _cairo_array_fini (&font->output); + free (font); + + return status; +} + +static void +cairo_truetype_font_destroy (cairo_truetype_font_t *font) +{ + _cairo_array_fini (&font->string_offsets); + free (font->base.widths); + free (font->base.ps_name); + if (font->base.font_name) + free (font->base.font_name); + free (font->parent_to_subset); + free (font->glyphs); + _cairo_array_fini (&font->output); + free (font); +} + +static cairo_status_t +cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, + size_t length, + unsigned char **buffer) +{ + cairo_status_t status; + + if (font->status) + return font->status; + + status = _cairo_array_allocate (&font->output, length, (void **) buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_truetype_font_write (cairo_truetype_font_t *font, + const void *data, + size_t length) +{ + cairo_status_t status; + + if (font->status) + return; + + status = _cairo_array_append_multiple (&font->output, data, length); + if (unlikely (status)) + status = _cairo_truetype_font_set_error (font, status); +} + +static void +cairo_truetype_font_write_be16 (cairo_truetype_font_t *font, + uint16_t value) +{ + uint16_t be16_value; + + if (font->status) + return; + + be16_value = cpu_to_be16 (value); + cairo_truetype_font_write (font, &be16_value, sizeof be16_value); +} + +static void +cairo_truetype_font_write_be32 (cairo_truetype_font_t *font, + uint32_t value) +{ + uint32_t be32_value; + + if (font->status) + return; + + be32_value = cpu_to_be32 (value); + cairo_truetype_font_write (font, &be32_value, sizeof be32_value); +} + +static cairo_status_t +cairo_truetype_font_align_output (cairo_truetype_font_t *font, + unsigned long *aligned) +{ + int length, pad; + unsigned char *padding; + + length = _cairo_array_num_elements (&font->output); + *aligned = (length + 3) & ~3; + pad = *aligned - length; + + if (pad) { + cairo_status_t status; + + status = cairo_truetype_font_allocate_write_buffer (font, pad, + &padding); + if (unlikely (status)) + return status; + + memset (padding, 0, pad); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, + unsigned long boundary) +{ + cairo_status_t status; + + if (font->status) + return font->status; + + if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH) + { + status = _cairo_array_append (&font->string_offsets, + &font->last_boundary); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + font->last_offset = font->last_boundary; + } + font->last_boundary = boundary; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned int i; + + cairo_truetype_font_write_be16 (font, 0); /* Table version */ + cairo_truetype_font_write_be16 (font, 2); /* Num tables */ + + cairo_truetype_font_write_be16 (font, 3); /* Platform */ + cairo_truetype_font_write_be16 (font, 0); /* Encoding */ + cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */ + + cairo_truetype_font_write_be16 (font, 1); /* Platform */ + cairo_truetype_font_write_be16 (font, 0); /* Encoding */ + cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */ + + /* Output a format 4 encoding table. */ + + cairo_truetype_font_write_be16 (font, 4); /* Format */ + cairo_truetype_font_write_be16 (font, 32); /* Length */ + cairo_truetype_font_write_be16 (font, 0); /* Version */ + cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */ + cairo_truetype_font_write_be16 (font, 4); /* searchrange */ + cairo_truetype_font_write_be16 (font, 1); /* entry selector */ + cairo_truetype_font_write_be16 (font, 0); /* rangeshift */ + cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */ + cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */ + cairo_truetype_font_write_be16 (font, 0); /* reserved */ + cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */ + cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */ + cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */ + cairo_truetype_font_write_be16 (font, 1); /* delta[1] */ + cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */ + cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */ + + /* Output a format 6 encoding table. */ + + cairo_truetype_font_write_be16 (font, 6); + cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs); + cairo_truetype_font_write_be16 (font, 0); + cairo_truetype_font_write_be16 (font, 0); /* First character */ + cairo_truetype_font_write_be16 (font, font->base.num_glyphs); + for (i = 0; i < font->base.num_glyphs; i++) + cairo_truetype_font_write_be16 (font, i); + + return font->status; +} + +static cairo_status_t +cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + cairo_status_t status; + unsigned char *buffer; + unsigned long size; + + if (font->status) + return font->status; + + size = 0; + status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, + tag, 0, NULL, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, buffer, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, + unsigned char *buffer, + unsigned long size) +{ + tt_glyph_data_t *glyph_data; + tt_composite_glyph_t *composite_glyph; + int num_args; + int has_more_components; + unsigned short flags; + unsigned short index; + cairo_status_t status; + unsigned char *end = buffer + size; + + if (font->status) + return font->status; + + glyph_data = (tt_glyph_data_t *) buffer; + if ((unsigned char *)(&glyph_data->data) >= end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0) + return CAIRO_STATUS_SUCCESS; + + composite_glyph = &glyph_data->glyph; + do { + if ((unsigned char *)(&composite_glyph->args[1]) > end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + flags = be16_to_cpu (composite_glyph->flags); + has_more_components = flags & TT_MORE_COMPONENTS; + status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); + if (unlikely (status)) + return status; + + composite_glyph->index = cpu_to_be16 (index); + num_args = 1; + if (flags & TT_ARG_1_AND_2_ARE_WORDS) + num_args += 1; + + if (flags & TT_WE_HAVE_A_SCALE) + num_args += 1; + else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) + num_args += 2; + else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) + num_args += 4; + + composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); + } while (has_more_components); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned long start_offset, index, size, next; + tt_head_t header; + unsigned long begin, end; + unsigned char *buffer; + unsigned int i; + union { + unsigned char *bytes; + uint16_t *short_offsets; + uint32_t *long_offsets; + } u; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_head_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char*) &header, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (be16_to_cpu (header.index_to_loc_format) == 0) + size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); + else + size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); + + u.bytes = malloc (size); + if (unlikely (u.bytes == NULL)) + return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_loca, 0, u.bytes, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + start_offset = _cairo_array_num_elements (&font->output); + for (i = 0; i < font->base.num_glyphs; i++) { + index = font->glyphs[i].parent_index; + if (be16_to_cpu (header.index_to_loc_format) == 0) { + begin = be16_to_cpu (u.short_offsets[index]) * 2; + end = be16_to_cpu (u.short_offsets[index + 1]) * 2; + } + else { + begin = be32_to_cpu (u.long_offsets[index]); + end = be32_to_cpu (u.long_offsets[index + 1]); + } + + /* quick sanity check... */ + if (end < begin) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + size = end - begin; + status = cairo_truetype_font_align_output (font, &next); + if (unlikely (status)) + goto FAIL; + + status = cairo_truetype_font_check_boundary (font, next); + if (unlikely (status)) + goto FAIL; + + font->glyphs[i].location = next - start_offset; + + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + goto FAIL; + + if (size != 0) { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_glyf, begin, buffer, &size); + if (unlikely (status)) + goto FAIL; + + status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); + if (unlikely (status)) + goto FAIL; + } + } + + status = cairo_truetype_font_align_output (font, &next); + if (unlikely (status)) + goto FAIL; + + font->glyphs[i].location = next - start_offset; + + status = font->status; +FAIL: + free (u.bytes); + + return _cairo_truetype_font_set_error (font, status); +} + +static cairo_status_t +cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned char *buffer; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = 0; + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, NULL, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + font->checksum_index = _cairo_array_num_elements (&font->output) + 8; + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, buffer, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + /* set checkSumAdjustment to 0 for table checksum calculation */ + *(uint32_t *)(buffer + 8) = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag) +{ + tt_hhea_t *hhea; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_hhea_t); + status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, (unsigned char *) hhea, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned long size; + unsigned long long_entry_size; + unsigned long short_entry_size; + short *p; + unsigned int i; + tt_hhea_t hhea; + int num_hmetrics; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_hhea_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char*) &hhea, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + num_hmetrics = be16_to_cpu(hhea.num_hmetrics); + + for (i = 0; i < font->base.num_glyphs; i++) { + long_entry_size = 2 * sizeof (int16_t); + short_entry_size = sizeof (int16_t); + status = cairo_truetype_font_allocate_write_buffer (font, + long_entry_size, + (unsigned char **) &p); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (font->glyphs[i].parent_index < num_hmetrics) { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + font->glyphs[i].parent_index * long_entry_size, + (unsigned char *) p, &long_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + } + else + { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + (num_hmetrics - 1) * long_entry_size, + (unsigned char *) p, &short_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + num_hmetrics * long_entry_size + + (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, + (unsigned char *) (p + 1), &short_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + } + font->base.widths[i] = be16_to_cpu (p[0]); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned int i; + tt_head_t header; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof(tt_head_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char*) &header, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (be16_to_cpu (header.index_to_loc_format) == 0) + { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2); + } else { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_truetype_font_write_be32 (font, font->glyphs[i].location); + } + + return font->status; +} + +static cairo_status_t +cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + tt_maxp_t *maxp; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_maxp_t); + status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, (unsigned char *) maxp, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) +{ + cairo_status_t status; + unsigned char *table_buffer; + size_t table_buffer_length; + unsigned short search_range, entry_selector, range_shift; + + if (font->status) + return font->status; + + search_range = 1; + entry_selector = 0; + while (search_range * 2 <= font->num_tables) { + search_range *= 2; + entry_selector++; + } + search_range *= 16; + range_shift = font->num_tables * 16 - search_range; + + cairo_truetype_font_write_be32 (font, SFNT_VERSION); + cairo_truetype_font_write_be16 (font, font->num_tables); + cairo_truetype_font_write_be16 (font, search_range); + cairo_truetype_font_write_be16 (font, entry_selector); + cairo_truetype_font_write_be16 (font, range_shift); + + /* Allocate space for the table directory. Each directory entry + * will be filled in by cairo_truetype_font_update_entry() after + * the table is written. */ + table_buffer_length = font->num_tables * 16; + status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, + &table_buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static uint32_t +cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font, + unsigned long start, + unsigned long end) +{ + uint32_t *padded_end; + uint32_t *p; + uint32_t checksum; + char *data; + + checksum = 0; + data = _cairo_array_index (&font->output, 0); + p = (uint32_t *) (data + start); + padded_end = (uint32_t *) (data + ((end + 3) & ~3)); + while (p < padded_end) + checksum += be32_to_cpu(*p++); + + return checksum; +} + +static void +cairo_truetype_font_update_entry (cairo_truetype_font_t *font, + int index, + unsigned long tag, + unsigned long start, + unsigned long end) +{ + uint32_t *entry; + + entry = _cairo_array_index (&font->output, 12 + 16 * index); + entry[0] = cpu_to_be32 ((uint32_t)tag); + entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end)); + entry[2] = cpu_to_be32 ((uint32_t)start); + entry[3] = cpu_to_be32 ((uint32_t)(end - start)); +} + +static cairo_status_t +cairo_truetype_font_generate (cairo_truetype_font_t *font, + const char **data, + unsigned long *length, + const unsigned long **string_offsets, + unsigned long *num_strings) +{ + cairo_status_t status; + unsigned long start, end, next; + uint32_t checksum, *checksum_location; + int i; + + if (font->status) + return font->status; + + status = cairo_truetype_font_write_offset_table (font); + if (unlikely (status)) + goto FAIL; + + status = cairo_truetype_font_align_output (font, &start); + if (unlikely (status)) + goto FAIL; + + end = 0; + for (i = 0; i < font->num_tables; i++) { + status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); + if (unlikely (status)) + goto FAIL; + + end = _cairo_array_num_elements (&font->output); + status = cairo_truetype_font_align_output (font, &next); + if (unlikely (status)) + goto FAIL; + + cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, + font->truetype_tables[i].tag, start, end); + status = cairo_truetype_font_check_boundary (font, next); + if (unlikely (status)) + goto FAIL; + + start = next; + } + + checksum = + 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end); + checksum_location = _cairo_array_index (&font->output, font->checksum_index); + *checksum_location = cpu_to_be32 (checksum); + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + *num_strings = _cairo_array_num_elements (&font->string_offsets); + if (*num_strings != 0) + *string_offsets = _cairo_array_index (&font->string_offsets, 0); + else + *string_offsets = NULL; + + FAIL: + return _cairo_truetype_font_set_error (font, status); +} + +static cairo_status_t +cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, + unsigned short glyph, + unsigned short *out) +{ + if (glyph >= font->num_glyphs_in_face) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (font->parent_to_subset[glyph] == 0) { + font->parent_to_subset[glyph] = font->base.num_glyphs; + font->glyphs[font->base.num_glyphs].parent_index = glyph; + font->base.num_glyphs++; + } + + *out = font->parent_to_subset[glyph]; + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, + unsigned long tag, + cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag), + int pos) +{ + font->truetype_tables[font->num_tables].tag = tag; + font->truetype_tables[font->num_tables].write = write; + font->truetype_tables[font->num_tables].pos = pos; + font->num_tables++; +} + +/* cairo_truetype_font_create_truetype_table_list() builds the list of + * truetype tables to be embedded in the subsetted font. Each call to + * cairo_truetype_font_add_truetype_table() adds a table, the callback + * for generating the table, and the position in the table directory + * to the truetype_tables array. + * + * As we write out the glyf table we remap composite glyphs. + * Remapping composite glyphs will reference the sub glyphs the + * composite glyph is made up of. The "glyf" table callback needs to + * be called first so we have all the glyphs in the subset before + * going further. + * + * The order in which tables are added to the truetype_table array + * using cairo_truetype_font_add_truetype_table() specifies the order + * in which the callback functions will be called. + * + * The tables in the table directory must be listed in alphabetical + * order. The "cvt", "fpgm", and "prep" are optional tables. They + * will only be embedded in the subset if they exist in the source + * font. The pos parameter of cairo_truetype_font_add_truetype_table() + * specifies the position of the table in the table directory. + */ +static void +cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) +{ + cairo_bool_t has_cvt = FALSE; + cairo_bool_t has_fpgm = FALSE; + cairo_bool_t has_prep = FALSE; + unsigned long size; + int pos; + + size = 0; + if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_cvt, 0, NULL, + &size) == CAIRO_STATUS_SUCCESS) + has_cvt = TRUE; + + size = 0; + if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_fpgm, 0, NULL, + &size) == CAIRO_STATUS_SUCCESS) + has_fpgm = TRUE; + + size = 0; + if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_prep, 0, NULL, + &size) == CAIRO_STATUS_SUCCESS) + has_prep = TRUE; + + font->num_tables = 0; + pos = 1; + if (has_cvt) + pos++; + if (has_fpgm) + pos++; + cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); + + pos = 0; + cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); + if (has_cvt) + cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++); + if (has_fpgm) + cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++); + pos++; + cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++); + if (has_prep) + cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); +} + +cairo_status_t +_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_truetype_font_t *font = NULL; + cairo_status_t status; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned long offsets_length; + unsigned int i; + const unsigned long *string_offsets = NULL; + unsigned long num_strings = 0; + + status = _cairo_truetype_font_create (font_subset, &font); + if (unlikely (status)) + return status; + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; + status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); + if (unlikely (status)) + goto fail1; + } + + cairo_truetype_font_create_truetype_table_list (font); + status = cairo_truetype_font_generate (font, &data, &length, + &string_offsets, &num_strings); + if (unlikely (status)) + goto fail1; + + truetype_subset->ps_name = strdup (font->base.ps_name); + if (unlikely (truetype_subset->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + if (font->base.font_name != NULL) { + truetype_subset->font_name = strdup (font->base.font_name); + if (unlikely (truetype_subset->font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + } else { + truetype_subset->font_name = NULL; + } + + /* The widths array returned must contain only widths for the + * glyphs in font_subset. Any subglyphs appended after + * font_subset->num_glyphs are omitted. */ + truetype_subset->widths = calloc (sizeof (double), + font->scaled_font_subset->num_glyphs); + if (unlikely (truetype_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; + + truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em; + truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em; + truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em; + truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em; + truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em; + truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; + + if (length) { + truetype_subset->data = malloc (length); + if (unlikely (truetype_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (truetype_subset->data, data, length); + } else + truetype_subset->data = NULL; + truetype_subset->data_length = length; + + if (num_strings) { + offsets_length = num_strings * sizeof (unsigned long); + truetype_subset->string_offsets = malloc (offsets_length); + if (unlikely (truetype_subset->string_offsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail5; + } + + memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); + truetype_subset->num_string_offsets = num_strings; + } else { + truetype_subset->string_offsets = NULL; + truetype_subset->num_string_offsets = 0; + } + + cairo_truetype_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail5: + free (truetype_subset->data); + fail4: + free (truetype_subset->widths); + fail3: + if (truetype_subset->font_name) + free (truetype_subset->font_name); + fail2: + free (truetype_subset->ps_name); + fail1: + cairo_truetype_font_destroy (font); + + return status; +} + +void +_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) +{ + free (subset->ps_name); + if (subset->font_name) + free (subset->font_name); + free (subset->widths); + free (subset->data); + free (subset->string_offsets); +} + +static cairo_int_status_t +_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, + unsigned long table_offset, + unsigned long index, + uint32_t *ucs4) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_segment_map_t *map; + char buf[4]; + unsigned int num_segments, i; + unsigned long size; + uint16_t *start_code; + uint16_t *end_code; + uint16_t *delta; + uint16_t *range_offset; + uint16_t *glyph_array; + uint16_t c; + + backend = scaled_font->backend; + size = 4; + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, table_offset, + (unsigned char *) &buf, + &size); + if (unlikely (status)) + return status; + + /* All table formats have the same first two words */ + map = (tt_segment_map_t *) buf; + if (be16_to_cpu (map->format) != 4) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = be16_to_cpu (map->length); + map = malloc (size); + if (unlikely (map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, table_offset, + (unsigned char *) map, + &size); + if (unlikely (status)) + goto fail; + + num_segments = be16_to_cpu (map->segCountX2)/2; + + /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of + * uint16_t each num_segments long. */ + if (size < (8 + 4*num_segments)*sizeof(uint16_t)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + end_code = map->endCount; + start_code = &(end_code[num_segments + 1]); + delta = &(start_code[num_segments]); + range_offset = &(delta[num_segments]); + glyph_array = &(range_offset[num_segments]); + + /* search for glyph in segments with rangeOffset=0 */ + for (i = 0; i < num_segments; i++) { + c = index - be16_to_cpu (delta[i]); + if (range_offset[i] == 0 && + c >= be16_to_cpu (start_code[i]) && + c <= be16_to_cpu (end_code[i])) + { + *ucs4 = c; + goto found; + } + } + + /* search for glyph in segments with rangeOffset=1 */ + for (i = 0; i < num_segments; i++) { + if (range_offset[i] != 0) { + uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; + int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; + uint16_t g_id_be = cpu_to_be16 (index); + int j; + + if (range_size > 0) { + if ((char*)glyph_ids + 2*range_size > (char*)map + size) + return CAIRO_INT_STATUS_UNSUPPORTED; + + for (j = 0; j < range_size; j++) { + if (glyph_ids[j] == g_id_be) { + *ucs4 = be16_to_cpu (start_code[i]) + j; + goto found; + } + } + } + } + } + + /* glyph not found */ + *ucs4 = -1; + +found: + status = CAIRO_STATUS_SUCCESS; + +fail: + free (map); + + return status; +} + +cairo_int_status_t +_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + const cairo_scaled_font_backend_t *backend; + tt_cmap_t *cmap; + char buf[4]; + int num_tables, i; + unsigned long size; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 4; + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, 0, + (unsigned char *) &buf, + &size); + if (unlikely (status)) + return status; + + cmap = (tt_cmap_t *) buf; + num_tables = be16_to_cpu (cmap->num_tables); + size = 4 + num_tables*sizeof(tt_cmap_index_t); + cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); + if (unlikely (cmap == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, 0, + (unsigned char *) cmap, + &size); + if (unlikely (status)) + goto cleanup; + + /* Find a table with Unicode mapping */ + for (i = 0; i < num_tables; i++) { + if (be16_to_cpu (cmap->index[i].platform) == 3 && + be16_to_cpu (cmap->index[i].encoding) == 1) { + status = _cairo_truetype_reverse_cmap (scaled_font, + be32_to_cpu (cmap->index[i].offset), + index, + ucs4); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + break; + } + } + +cleanup: + free (cmap); + + return status; +} + +cairo_int_status_t +_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, + char **ps_name_out, + char **font_name_out) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_name_t *name; + tt_name_record_t *record; + unsigned long size; + int i, j; + char *ps_name = NULL; + char *font_name = NULL; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + NULL, + &size); + if (status) + return status; + + name = malloc (size); + if (name == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + (unsigned char *) name, + &size); + if (status) + goto fail; + + /* Extract the font name and PS name from the name table. At + * present this just looks for the Mac platform/Roman encoded font + * name. It should be extended to use any suitable font name in + * the name table. + */ + for (i = 0; i < be16_to_cpu(name->num_records); i++) { + record = &(name->records[i]); + if ((be16_to_cpu (record->platform) == 1) && + (be16_to_cpu (record->encoding) == 0)) { + + if (be16_to_cpu (record->name) == 4) { + font_name = malloc (be16_to_cpu(record->length) + 1); + if (font_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + strncpy(font_name, + ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), + be16_to_cpu (record->length)); + font_name[be16_to_cpu (record->length)] = 0; + } + + if (be16_to_cpu (record->name) == 6) { + ps_name = malloc (be16_to_cpu(record->length) + 1); + if (ps_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + strncpy(ps_name, + ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), + be16_to_cpu (record->length)); + ps_name[be16_to_cpu (record->length)] = 0; + } + + if (font_name && ps_name) + break; + } + } + + free (name); + + /* Ensure PS name does not contain any spaces */ + if (ps_name) { + for (i = 0, j = 0; ps_name[j]; j++) { + if (ps_name[j] == ' ') + continue; + ps_name[i++] = ps_name[j]; + } + ps_name[i] = '\0'; + } + + *ps_name_out = ps_name; + *font_name_out = font_name; + + return CAIRO_STATUS_SUCCESS; + +fail: + free (name); + + if (ps_name != NULL) + free (ps_name); + + if (font_name != NULL) + free (font_name); + + *ps_name_out = NULL; + *font_name_out = NULL; + + return status; +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-type1-fallback.c b/libs/cairo/src/cairo-type1-fallback.c new file mode 100644 index 000000000..cc3a26966 --- /dev/null +++ b/libs/cairo/src/cairo-type1-fallback.c @@ -0,0 +1,856 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define _BSD_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type1-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-output-stream-private.h" + +typedef enum { + CAIRO_CHARSTRING_TYPE1, + CAIRO_CHARSTRING_TYPE2 +} cairo_charstring_type_t; + +typedef struct _cairo_type1_font { + int *widths; + + cairo_scaled_font_subset_t *scaled_font_subset; + cairo_scaled_font_t *type1_scaled_font; + + cairo_array_t contents; + + double x_min, y_min, x_max, y_max; + + const char *data; + unsigned long header_size; + unsigned long data_size; + unsigned long trailer_size; + int bbox_position; + int bbox_max_chars; + + cairo_output_stream_t *output; + + unsigned short eexec_key; + cairo_bool_t hex_encode; + int hex_column; +} cairo_type1_font_t; + +static cairo_status_t +cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_type1_font_t **subset_return, + cairo_bool_t hex_encode) +{ + cairo_type1_font_t *font; + cairo_font_face_t *font_face; + cairo_matrix_t font_matrix; + cairo_matrix_t ctm; + cairo_font_options_t font_options; + cairo_status_t status; + + font = calloc (1, sizeof (cairo_type1_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { + free (font); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + font->scaled_font_subset = scaled_font_subset; + font->hex_encode = hex_encode; + + font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font); + + cairo_matrix_init_scale (&font_matrix, 1000, -1000); + cairo_matrix_init_identity (&ctm); + + _cairo_font_options_init_default (&font_options); + cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); + + font->type1_scaled_font = cairo_scaled_font_create (font_face, + &font_matrix, + &ctm, + &font_options); + status = font->type1_scaled_font->status; + if (unlikely (status)) + goto fail; + + _cairo_array_init (&font->contents, sizeof (unsigned char)); + font->output = NULL; + + *subset_return = font; + + return CAIRO_STATUS_SUCCESS; + +fail: + free (font->widths); + free (font); + + return status; +} + +/* Charstring commands. If the high byte is 0 the command is encoded + * with a single byte. */ +#define CHARSTRING_sbw 0x0c07 +#define CHARSTRING_rmoveto 0x0015 +#define CHARSTRING_rlineto 0x0005 +#define CHARSTRING_rcurveto 0x0008 +#define CHARSTRING_closepath 0x0009 +#define CHARSTRING_endchar 0x000e + +/* Before calling this function, the caller must allocate sufficient + * space in data (see _cairo_array_grow_by). The maximum number of + * bytes that will be used is 2. + */ +static void +charstring_encode_command (cairo_array_t *data, int command) +{ + cairo_status_t status; + int orig_size; + unsigned char buf[5]; + unsigned char *p = buf; + + if (command & 0xff00) + *p++ = command >> 8; + *p++ = command & 0x00ff; + + /* Ensure the array doesn't grow, which allows this function to + * have no possibility of failure. */ + orig_size = _cairo_array_size (data); + status = _cairo_array_append_multiple (data, buf, p - buf); + + assert (status == CAIRO_STATUS_SUCCESS); + assert (_cairo_array_size (data) == orig_size); +} + +/* Before calling this function, the caller must allocate sufficient + * space in data (see _cairo_array_grow_by). The maximum number of + * bytes that will be used is 5. + */ +static void +charstring_encode_integer (cairo_array_t *data, + int i, + cairo_charstring_type_t type) +{ + cairo_status_t status; + int orig_size; + unsigned char buf[10]; + unsigned char *p = buf; + + if (i >= -107 && i <= 107) { + *p++ = i + 139; + } else if (i >= 108 && i <= 1131) { + i -= 108; + *p++ = (i >> 8)+ 247; + *p++ = i & 0xff; + } else if (i >= -1131 && i <= -108) { + i = -i - 108; + *p++ = (i >> 8)+ 251; + *p++ = i & 0xff; + } else { + if (type == CAIRO_CHARSTRING_TYPE1) { + *p++ = 0xff; + *p++ = i >> 24; + *p++ = (i >> 16) & 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + } else { + *p++ = 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + *p++ = 0; + *p++ = 0; + } + } + + /* Ensure the array doesn't grow, which allows this function to + * have no possibility of failure. */ + orig_size = _cairo_array_size (data); + status = _cairo_array_append_multiple (data, buf, p - buf); + + assert (status == CAIRO_STATUS_SUCCESS); + assert (_cairo_array_size (data) == orig_size); +} + +typedef struct _ps_path_info { + cairo_array_t *data; + int current_x, current_y; + cairo_charstring_type_t type; +} t1_path_info_t; + +static cairo_status_t +_charstring_move_to (void *closure, + const cairo_point_t *point) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx, dy; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 12); + if (unlikely (status)) + return status; + + dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; + dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; + charstring_encode_integer (path_info->data, dx, path_info->type); + charstring_encode_integer (path_info->data, dy, path_info->type); + path_info->current_x += dx; + path_info->current_y += dy; + + charstring_encode_command (path_info->data, CHARSTRING_rmoveto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_line_to (void *closure, + const cairo_point_t *point) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx, dy; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 12); + if (unlikely (status)) + return status; + + dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; + dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; + charstring_encode_integer (path_info->data, dx, path_info->type); + charstring_encode_integer (path_info->data, dy, path_info->type); + path_info->current_x += dx; + path_info->current_y += dy; + + charstring_encode_command (path_info->data, CHARSTRING_rlineto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_curve_to (void *closure, + const cairo_point_t *point1, + const cairo_point_t *point2, + const cairo_point_t *point3) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx1, dy1, dx2, dy2, dx3, dy3; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 32); + if (unlikely (status)) + return status; + + dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; + dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y; + dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1; + dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1; + dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2; + dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2; + charstring_encode_integer (path_info->data, dx1, path_info->type); + charstring_encode_integer (path_info->data, dy1, path_info->type); + charstring_encode_integer (path_info->data, dx2, path_info->type); + charstring_encode_integer (path_info->data, dy2, path_info->type); + charstring_encode_integer (path_info->data, dx3, path_info->type); + charstring_encode_integer (path_info->data, dy3, path_info->type); + path_info->current_x += dx1 + dx2 + dx3; + path_info->current_y += dy1 + dy2 + dy3; + charstring_encode_command (path_info->data, CHARSTRING_rcurveto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_close_path (void *closure) +{ + cairo_status_t status; + t1_path_info_t *path_info = (t1_path_info_t *) closure; + + if (path_info->type == CAIRO_CHARSTRING_TYPE2) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_array_grow_by (path_info->data, 2); + if (unlikely (status)) + return status; + + charstring_encode_command (path_info->data, CHARSTRING_closepath); + + return CAIRO_STATUS_SUCCESS; +} + +static void +charstring_encrypt (cairo_array_t *data) +{ + unsigned char *d, *end; + uint16_t c, p, r; + + r = CAIRO_TYPE1_CHARSTRING_KEY; + d = (unsigned char *) _cairo_array_index (data, 0); + end = d + _cairo_array_num_elements (data); + while (d < end) { + p = *d; + c = p ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + *d++ = c; + } +} + +static cairo_int_status_t +cairo_type1_font_create_charstring (cairo_type1_font_t *font, + int subset_index, + int glyph_index, + cairo_charstring_type_t type, + cairo_array_t *data) +{ + cairo_int_status_t status; + cairo_scaled_glyph_t *scaled_glyph; + t1_path_info_t path_info; + cairo_text_extents_t *metrics; + cairo_bool_t emit_path = TRUE; + + /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */ + status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + + /* It is ok for the .notdef glyph to not have a path available. We + * just need the metrics to emit an empty glyph. */ + if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) { + emit_path = FALSE; + status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + } + if (unlikely (status)) + return status; + + metrics = &scaled_glyph->metrics; + if (subset_index == 0) { + font->x_min = metrics->x_bearing; + font->y_min = metrics->y_bearing; + font->x_max = metrics->x_bearing + metrics->width; + font->y_max = metrics->y_bearing + metrics->height; + } else { + if (metrics->x_bearing < font->x_min) + font->x_min = metrics->x_bearing; + if (metrics->y_bearing < font->y_min) + font->y_min = metrics->y_bearing; + if (metrics->x_bearing + metrics->width > font->x_max) + font->x_max = metrics->x_bearing + metrics->width; + if (metrics->y_bearing + metrics->height > font->y_max) + font->y_max = metrics->y_bearing + metrics->height; + } + font->widths[subset_index] = metrics->x_advance; + + status = _cairo_array_grow_by (data, 30); + if (unlikely (status)) + return status; + + if (type == CAIRO_CHARSTRING_TYPE1) { + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type); + charstring_encode_command (data, CHARSTRING_sbw); + + path_info.current_x = (int) scaled_glyph->metrics.x_bearing; + path_info.current_y = (int) scaled_glyph->metrics.y_bearing; + } else { + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); + + path_info.current_x = 0; + path_info.current_y = 0; + } + path_info.data = data; + path_info.type = type; + if (emit_path) { + status = _cairo_path_fixed_interpret (scaled_glyph->path, + CAIRO_DIRECTION_FORWARD, + _charstring_move_to, + _charstring_line_to, + _charstring_curve_to, + _charstring_close_path, + &path_info); + if (unlikely (status)) + return status; + } + + status = _cairo_array_grow_by (data, 1); + if (unlikely (status)) + return status; + charstring_encode_command (path_info.data, CHARSTRING_endchar); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_write_charstrings (cairo_type1_font_t *font, + cairo_output_stream_t *encrypted_output) +{ + cairo_status_t status; + unsigned char zeros[] = { 0, 0, 0, 0 }; + cairo_array_t data; + unsigned int i; + int length; + + _cairo_array_init (&data, sizeof (unsigned char)); + status = _cairo_array_grow_by (&data, 1024); + if (unlikely (status)) + goto fail; + + _cairo_output_stream_printf (encrypted_output, + "2 index /CharStrings %d dict dup begin\n", + font->scaled_font_subset->num_glyphs + 1); + + _cairo_scaled_font_freeze_cache (font->type1_scaled_font); + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + _cairo_array_truncate (&data, 0); + /* four "random" bytes required by encryption algorithm */ + status = _cairo_array_append_multiple (&data, zeros, 4); + if (unlikely (status)) + break; + + status = cairo_type1_font_create_charstring (font, i, + font->scaled_font_subset->glyphs[i], + CAIRO_CHARSTRING_TYPE1, + &data); + if (unlikely (status)) + break; + + charstring_encrypt (&data); + length = _cairo_array_num_elements (&data); + if (font->scaled_font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (encrypted_output, "/%s %d RD ", + font->scaled_font_subset->glyph_names[i], + length); + } else if (i == 0) { + _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length); + } else { + _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length); + } + _cairo_output_stream_write (encrypted_output, + _cairo_array_index (&data, 0), + length); + _cairo_output_stream_printf (encrypted_output, " ND\n"); + } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + +fail: + _cairo_array_fini (&data); + return status; +} + +static void +cairo_type1_font_write_header (cairo_type1_font_t *font, + const char *name) +{ + unsigned int i; + const char spaces[50] = " "; + + _cairo_output_stream_printf (font->output, + "%%!FontType1-1.1 %s 1.0\n" + "11 dict begin\n" + "/FontName /%s def\n" + "/PaintType 0 def\n" + "/FontType 1 def\n" + "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n", + name, + name); + + /* We don't know the bbox values until after the charstrings have + * been generated. Reserve some space and fill in the bbox + * later. */ + + /* Worst case for four signed ints with spaces between each number */ + font->bbox_max_chars = 50; + + _cairo_output_stream_printf (font->output, "/FontBBox {"); + font->bbox_position = _cairo_output_stream_get_position (font->output); + _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars); + + _cairo_output_stream_printf (font->output, + "} readonly def\n" + "/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n"); + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + if (font->scaled_font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (font->output, "dup %d /%s put\n", + i, font->scaled_font_subset->glyph_names[i]); + } else { + _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); + } + } + _cairo_output_stream_printf (font->output, + "readonly def\n" + "currentdict end\n" + "currentfile eexec\n"); +} + +static cairo_status_t +cairo_type1_write_stream_encrypted (void *closure, + const unsigned char *data, + unsigned int length) +{ + const unsigned char *in, *end; + uint16_t c, p; + static const char hex_digits[16] = "0123456789abcdef"; + char digits[3]; + cairo_type1_font_t *font = closure; + + in = (const unsigned char *) data; + end = (const unsigned char *) data + length; + while (in < end) { + p = *in++; + c = p ^ (font->eexec_key >> 8); + font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + if (font->hex_encode) { + digits[0] = hex_digits[c >> 4]; + digits[1] = hex_digits[c & 0x0f]; + digits[2] = '\n'; + font->hex_column += 2; + + if (font->hex_column == 78) { + _cairo_output_stream_write (font->output, digits, 3); + font->hex_column = 0; + } else { + _cairo_output_stream_write (font->output, digits, 2); + } + } else { + digits[0] = c; + _cairo_output_stream_write (font->output, digits, 1); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_write_private_dict (cairo_type1_font_t *font, + const char *name) +{ + cairo_int_status_t status; + cairo_status_t status2; + cairo_output_stream_t *encrypted_output; + + font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; + font->hex_column = 0; + encrypted_output = _cairo_output_stream_create ( + cairo_type1_write_stream_encrypted, + NULL, + font); + if (_cairo_output_stream_get_status (encrypted_output)) + return _cairo_output_stream_destroy (encrypted_output); + + /* Note: the first four spaces at the start of this private dict + * are the four "random" bytes of plaintext required by the + * encryption algorithm */ + _cairo_output_stream_printf (encrypted_output, + " dup /Private 9 dict dup begin\n" + "/RD {string currentfile exch readstring pop}" + " bind executeonly def\n" + "/ND {noaccess def} executeonly def\n" + "/NP {noaccess put} executeonly def\n" + "/BlueValues [] def\n" + "/MinFeature {16 16} def\n" + "/lenIV 4 def\n" + "/password 5839 def\n"); + + status = cairo_type1_font_write_charstrings (font, encrypted_output); + if (unlikely (status)) + goto fail; + + _cairo_output_stream_printf (encrypted_output, + "end\n" + "end\n" + "readonly put\n" + "noaccess put\n" + "dup /FontName get exch definefont pop\n" + "mark currentfile closefile\n"); + + fail: + status2 = _cairo_output_stream_destroy (encrypted_output); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static void +cairo_type1_font_write_trailer(cairo_type1_font_t *font) +{ + int i; + static const char zeros[65] = + "0000000000000000000000000000000000000000000000000000000000000000\n"; + + for (i = 0; i < 8; i++) + _cairo_output_stream_write (font->output, zeros, sizeof zeros); + + _cairo_output_stream_printf (font->output, "cleartomark\n"); +} + +static cairo_status_t +cairo_type1_write_stream (void *closure, + const unsigned char *data, + unsigned int length) +{ + cairo_type1_font_t *font = closure; + + return _cairo_array_append_multiple (&font->contents, data, length); +} + +static cairo_int_status_t +cairo_type1_font_write (cairo_type1_font_t *font, + const char *name) +{ + cairo_int_status_t status; + + cairo_type1_font_write_header (font, name); + font->header_size = _cairo_output_stream_get_position (font->output); + + status = cairo_type1_font_write_private_dict (font, name); + if (unlikely (status)) + return status; + + font->data_size = _cairo_output_stream_get_position (font->output) - + font->header_size; + + cairo_type1_font_write_trailer (font); + font->trailer_size = + _cairo_output_stream_get_position (font->output) - + font->header_size - font->data_size; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) +{ + cairo_int_status_t status; + + status = _cairo_array_grow_by (&font->contents, 4096); + if (unlikely (status)) + return status; + + font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); + if (_cairo_output_stream_get_status (font->output)) + return _cairo_output_stream_destroy (font->output); + + status = cairo_type1_font_write (font, name); + if (unlikely (status)) + return status; + + font->data = _cairo_array_index (&font->contents, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_destroy (cairo_type1_font_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + free (font->widths); + cairo_scaled_font_destroy (font->type1_scaled_font); + _cairo_array_fini (&font->contents); + if (font->output) + status = _cairo_output_stream_destroy (font->output); + free (font); + + return status; +} + +static cairo_status_t +_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t hex_encode) +{ + cairo_type1_font_t *font; + cairo_status_t status; + unsigned long length; + unsigned int i, len; + + status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); + if (unlikely (status)) + return status; + + status = cairo_type1_font_generate (font, name); + if (unlikely (status)) + goto fail1; + + type1_subset->base_font = strdup (name); + if (unlikely (type1_subset->base_font == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (type1_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + type1_subset->widths[i] = (double)font->widths[i]/1000; + + type1_subset->x_min = (double)font->x_min/1000; + type1_subset->y_min = (double)font->y_min/1000; + type1_subset->x_max = (double)font->x_max/1000; + type1_subset->y_max = (double)font->y_max/1000; + type1_subset->ascent = (double)font->y_max/1000; + type1_subset->descent = (double)font->y_min/1000; + + length = font->header_size + font->data_size + + font->trailer_size; + type1_subset->data = malloc (length); + if (unlikely (type1_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + memcpy (type1_subset->data, + _cairo_array_index (&font->contents, 0), length); + + len = snprintf(type1_subset->data + font->bbox_position, + font->bbox_max_chars, + "%d %d %d %d", + (int)type1_subset->x_min, + (int)type1_subset->y_min, + (int)type1_subset->x_max, + (int)type1_subset->y_max); + type1_subset->data[font->bbox_position + len] = ' '; + + type1_subset->header_length = font->header_size; + type1_subset->data_length = font->data_size; + type1_subset->trailer_length = font->trailer_size; + + return cairo_type1_font_destroy (font); + + fail3: + free (type1_subset->widths); + fail2: + free (type1_subset->base_font); + fail1: + /* status is already set, ignore further errors */ + cairo_type1_font_destroy (font); + + return status; +} + +cairo_status_t +_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + return _cairo_type1_fallback_init_internal (type1_subset, + name, + scaled_font_subset, FALSE); +} + +cairo_status_t +_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + return _cairo_type1_fallback_init_internal (type1_subset, + name, + scaled_font_subset, TRUE); +} + +void +_cairo_type1_fallback_fini (cairo_type1_subset_t *subset) +{ + free (subset->base_font); + free (subset->widths); + free (subset->data); +} + +cairo_status_t +_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + cairo_type1_font_t *font; + cairo_status_t status; + unsigned int i; + cairo_array_t charstring; + + status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); + if (unlikely (status)) + return status; + + _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); + + type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); + if (unlikely (type2_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + _cairo_scaled_font_freeze_cache (font->type1_scaled_font); + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + _cairo_array_init (&charstring, sizeof (unsigned char)); + status = _cairo_array_grow_by (&charstring, 32); + if (unlikely (status)) + goto fail2; + + status = cairo_type1_font_create_charstring (font, i, + font->scaled_font_subset->glyphs[i], + CAIRO_CHARSTRING_TYPE2, + &charstring); + if (unlikely (status)) + goto fail2; + + status = _cairo_array_append (&type2_subset->charstrings, &charstring); + if (unlikely (status)) + goto fail2; + } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + type2_subset->widths[i] = font->widths[i]; + + type2_subset->x_min = (int) font->x_min; + type2_subset->y_min = (int) font->y_min; + type2_subset->x_max = (int) font->x_max; + type2_subset->y_max = (int) font->y_max; + type2_subset->ascent = (int) font->y_max; + type2_subset->descent = (int) font->y_min; + + return cairo_type1_font_destroy (font); + +fail2: + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + _cairo_array_fini (&charstring); + _cairo_type2_charstrings_fini (type2_subset); +fail1: + cairo_type1_font_destroy (font); + return status; +} + +void +_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset) +{ + unsigned int i, num_charstrings; + cairo_array_t *charstring; + + num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings); + for (i = 0; i < num_charstrings; i++) { + charstring = _cairo_array_index (&type2_subset->charstrings, i); + _cairo_array_fini (charstring); + } + _cairo_array_fini (&type2_subset->charstrings); + + free (type2_subset->widths); +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-type1-private.h b/libs/cairo/src/cairo-type1-private.h new file mode 100644 index 000000000..0634b7061 --- /dev/null +++ b/libs/cairo/src/cairo-type1-private.h @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-type1-subset.c b/libs/cairo/src/cairo-type1-subset.c new file mode 100644 index 000000000..7abcb10cb --- /dev/null +++ b/libs/cairo/src/cairo-type1-subset.c @@ -0,0 +1,1403 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Useful links: + * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF + */ + + +#define _BSD_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type1-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-output-stream-private.h" + +/* XXX: Eventually, we need to handle other font backends */ +#if CAIRO_HAS_FT_FONT + +#include "cairo-ft-private.h" + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_TYPE1_TABLES_H + +#include + +typedef struct _cairo_type1_font_subset { + cairo_scaled_font_subset_t *scaled_font_subset; + + struct { + cairo_unscaled_font_t *unscaled_font; + unsigned int font_id; + char *base_font; + unsigned int num_glyphs; + double x_min, y_min, x_max, y_max; + double ascent, descent; + + const char *data; + unsigned long header_size; + unsigned long data_size; + unsigned long trailer_size; + } base; + + FT_Face face; + int num_glyphs; + + struct { + int subset_index; + double width; + char *name; + } *glyphs; + + cairo_output_stream_t *output; + cairo_array_t contents; + + const char *rd, *nd; + + char *type1_data; + unsigned int type1_length; + char *type1_end; + + char *header_segment; + int header_segment_size; + char *eexec_segment; + int eexec_segment_size; + cairo_bool_t eexec_segment_is_ascii; + + char *cleartext; + char *cleartext_end; + + int header_size; + + unsigned short eexec_key; + cairo_bool_t hex_encode; + int hex_column; +} cairo_type1_font_subset_t; + + +static cairo_status_t +_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, + cairo_unscaled_font_t *unscaled_font, + cairo_bool_t hex_encode) +{ + cairo_ft_unscaled_font_t *ft_unscaled_font; + cairo_status_t status; + FT_Face face; + PS_FontInfoRec font_info; + int i, j; + + ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font; + + face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); + if (unlikely (face == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (FT_Get_PS_Font_Info(face, &font_info) != 0) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto fail1; + } + + /* OpenType/CFF fonts also have a PS_FontInfoRec */ +#if HAVE_FT_LOAD_SFNT_TABLE + if (FT_IS_SFNT (face)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto fail1; + } +#endif + + memset (font, 0, sizeof (*font)); + font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font); + font->base.num_glyphs = face->num_glyphs; + font->base.x_min = face->bbox.xMin / (double)face->units_per_EM; + font->base.y_min = face->bbox.yMin / (double)face->units_per_EM; + font->base.x_max = face->bbox.xMax / (double)face->units_per_EM; + font->base.y_max = face->bbox.yMax / (double)face->units_per_EM; + font->base.ascent = face->ascender / (double)face->units_per_EM; + font->base.descent = face->descender / (double)face->units_per_EM; + + if (face->family_name) { + font->base.base_font = strdup (face->family_name); + if (unlikely (font->base.base_font == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + for (i = 0, j = 0; font->base.base_font[j]; j++) { + if (font->base.base_font[j] == ' ') + continue; + font->base.base_font[i++] = font->base.base_font[j]; + } + font->base.base_font[i] = '\0'; + } + + font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]); + if (unlikely (font->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + font->hex_encode = hex_encode; + font->num_glyphs = 0; + for (i = 0; i < face->num_glyphs; i++) + font->glyphs[i].subset_index = -1; + + _cairo_array_init (&font->contents, sizeof (char)); + + _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); + + return CAIRO_STATUS_SUCCESS; + + fail3: + if (font->base.base_font) + free (font->base.base_font); + fail2: + _cairo_unscaled_font_destroy (unscaled_font); + fail1: + _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); + + return status; +} + +static void +cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph) +{ + if (font->glyphs[glyph].subset_index >= 0) + return; + + font->glyphs[glyph].subset_index = font->num_glyphs++; +} + +static cairo_bool_t +is_ps_delimiter(int c) +{ + static const char delimiters[] = "()[]{}<>/% \t\r\n"; + + return strchr (delimiters, c) != NULL; +} + +static const char * +find_token (const char *buffer, const char *end, const char *token) +{ + int i, length; + /* FIXME: find substring really must be find_token */ + + if (buffer == NULL) + return NULL; + + length = strlen (token); + for (i = 0; buffer + i < end - length + 1; i++) + if (memcmp (buffer + i, token, length) == 0) + if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) && + (buffer + i == end - length || is_ps_delimiter(buffer[i + length]))) + return buffer + i; + + return NULL; +} + +static cairo_status_t +cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) +{ + unsigned char *p; + const char *eexec_token; + int size, i; + + p = (unsigned char *) font->type1_data; + font->type1_end = font->type1_data + font->type1_length; + if (p[0] == 0x80 && p[1] == 0x01) { + font->header_segment_size = + p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); + font->header_segment = (char *) p + 6; + + p += 6 + font->header_segment_size; + font->eexec_segment_size = + p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); + font->eexec_segment = (char *) p + 6; + font->eexec_segment_is_ascii = (p[1] == 1); + + p += 6 + font->eexec_segment_size; + while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) { + size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); + p += 6 + size; + } + font->type1_end = (char *) p; + } else { + eexec_token = find_token ((char *) p, font->type1_end, "eexec"); + if (eexec_token == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n"); + font->header_segment = (char *) p; + font->eexec_segment_size = font->type1_length - font->header_segment_size; + font->eexec_segment = (char *) p + font->header_segment_size; + font->eexec_segment_is_ascii = TRUE; + for (i = 0; i < 4; i++) { + if (!isxdigit(font->eexec_segment[i])) + font->eexec_segment_is_ascii = FALSE; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Search for the definition of key and erase it by overwriting with spaces. + * This function is looks for definitions of the form: + * + * /key1 1234 def + * /key2 [12 34 56] def + * + * ie a key defined as an integer or array of integers. + * + */ +static void +cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, + const char *key) +{ + const char *start, *p, *segment_end; + + segment_end = font->header_segment + font->header_segment_size; + + start = font->header_segment; + do { + start = find_token (start, segment_end, key); + if (start) { + p = start + strlen(key); + /* skip integers or array of integers */ + while (p < segment_end && + (_cairo_isspace(*p) || + _cairo_isdigit(*p) || + *p == '[' || + *p == ']')) + { + p++; + } + + if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) { + /* erase definition of the key */ + memset((char *) start, ' ', p + 3 - start); + } + start += strlen(key); + } + } while (start); +} + +static cairo_status_t +cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, + const char *name) +{ + const char *start, *end, *segment_end; + unsigned int i; + + /* FIXME: + * This function assumes that /FontName always appears + * before /Encoding. This appears to always be the case with Type1 + * fonts. + * + * The more recently added code for removing the UniqueID and XUID + * keys can not make any assumptions about the position of the + * keys in the dictionary so it is implemented by overwriting the + * key definition with spaces before we start copying the font to + * the output. + * + * This code should be rewritten to not make any assumptions about + * the order of dictionary keys. This will allow UniqueID to be + * stripped out instead of leaving a bunch of spaces in the + * output. + */ + cairo_type1_font_erase_dict_key (font, "/UniqueID"); + cairo_type1_font_erase_dict_key (font, "/XUID"); + + segment_end = font->header_segment + font->header_segment_size; + + /* Type 1 fonts created by Fontforge have some PostScript code at + * the start of the font that skips the font if the printer has a + * cached copy of the font with the same unique id. This breaks + * our subsetted font so we disable it by searching for the + * PostScript operator "known" when used to check for the + * "/UniqueID" dictionary key. We append " pop false " after it to + * pop the result of this check off the stack and replace it with + * "false" to make the PostScript code think "/UniqueID" does not + * exist. + */ + end = font->header_segment; + start = find_token (font->header_segment, segment_end, "/UniqueID"); + if (start) { + start += 9; + while (start < segment_end && _cairo_isspace (*start)) + start++; + if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { + _cairo_output_stream_write (font->output, font->header_segment, + start + 5 - font->header_segment); + _cairo_output_stream_printf (font->output, " pop false "); + end = start + 5; + } + } + + start = find_token (end, segment_end, "/FontName"); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_write (font->output, end, + start - end); + + _cairo_output_stream_printf (font->output, "/FontName /%s def", name); + + end = find_token (start, segment_end, "def"); + if (end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + end += 3; + + start = find_token (end, segment_end, "/Encoding"); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_output_stream_write (font->output, end, start - end); + + _cairo_output_stream_printf (font->output, + "/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n"); + for (i = 1; i < font->base.num_glyphs; i++) { + if (font->glyphs[i].subset_index < 0) + continue; + _cairo_output_stream_printf (font->output, + "dup %d /%s put\n", + font->glyphs[i].subset_index, + font->glyphs[i].name); + } + _cairo_output_stream_printf (font->output, "readonly def"); + + end = find_token (start, segment_end, "def"); + if (end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + end += 3; + + _cairo_output_stream_write (font->output, end, segment_end - end); + + return font->output->status; +} + +static int +hex_to_int (int ch) +{ + if (ch <= '9') + return ch - '0'; + else if (ch <= 'F') + return ch - 'A' + 10; + else + return ch - 'a' + 10; +} + +static cairo_status_t +cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font, + const char *data, unsigned int length) +{ + const unsigned char *in, *end; + int c, p; + static const char hex_digits[16] = "0123456789abcdef"; + char digits[3]; + + in = (const unsigned char *) data; + end = (const unsigned char *) data + length; + while (in < end) { + p = *in++; + c = p ^ (font->eexec_key >> 8); + font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + if (font->hex_encode) { + digits[0] = hex_digits[c >> 4]; + digits[1] = hex_digits[c & 0x0f]; + digits[2] = '\n'; + font->hex_column += 2; + + if (font->hex_column == 78) { + _cairo_output_stream_write (font->output, digits, 3); + font->hex_column = 0; + } else { + _cairo_output_stream_write (font->output, digits, 2); + } + } else { + digits[0] = c; + _cairo_output_stream_write (font->output, digits, 1); + } + } + + return font->output->status; +} + +static cairo_status_t +cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) +{ + unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY; + unsigned char *in, *end; + char *out; + int c, p; + int i; + + in = (unsigned char *) font->eexec_segment; + end = (unsigned char *) in + font->eexec_segment_size; + + font->cleartext = malloc (font->eexec_segment_size); + if (unlikely (font->cleartext == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + out = font->cleartext; + while (in < end) { + if (font->eexec_segment_is_ascii) { + c = *in++; + if (_cairo_isspace (c)) + continue; + c = (hex_to_int (c) << 4) | hex_to_int (*in++); + } else { + c = *in++; + } + p = c ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + *out++ = p; + } + font->cleartext_end = out; + + /* Overwrite random bytes with spaces. + * + * The first 4 bytes of the cleartext are the random bytes + * required by the encryption algorithm. When encrypting the + * cleartext, the first ciphertext byte must not be a white space + * character and the first 4 bytes must not be an ASCII Hex + * character. Some fonts do not check that their randomly chosen + * bytes results in ciphertext that complies with this + * restriction. This may cause problems for some PDF consumers. By + * replacing the random bytes with spaces, the first four bytes of + * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies + * with this restriction. Using spaces also means we don't have to + * skip over the random bytes when parsing the cleartext. + */ + for (i = 0; i < 4 && i < font->eexec_segment_size; i++) + font->cleartext[i] = ' '; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +skip_token (const char *p, const char *end) +{ + while (p < end && _cairo_isspace(*p)) + p++; + + while (p < end && !_cairo_isspace(*p)) + p++; + + if (p == end) + return NULL; + + return p; +} + +static int +cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font, + const char *glyph_name, int length) +{ + unsigned int i; + + for (i = 0; i < font->base.num_glyphs; i++) { + if (font->glyphs[i].name && + strncmp (font->glyphs[i].name, glyph_name, length) == 0 && + font->glyphs[i].name[length] == '\0') + return i; + } + + return -1; +} + +static cairo_status_t +cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font) +{ + unsigned int i; + char buffer[256]; + FT_Error error; + + /* Get glyph names and width using the freetype API */ + for (i = 0; i < font->base.num_glyphs; i++) { + if (font->glyphs[i].name != NULL) + continue; + + error = FT_Load_Glyph (font->face, i, + FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | + FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); + if (error != FT_Err_Ok) { + /* propagate fatal errors from FreeType */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM; + + error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer); + if (error != FT_Err_Ok) { + /* propagate fatal errors from FreeType */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + font->glyphs[i].name = strdup (buffer); + if (unlikely (font->glyphs[i].name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) +{ + unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY; + int c, p, i; + + for (i = 0; i < size; i++) { + c = *in++; + p = c ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + *out++ = p; + } +} + +static const unsigned char * +cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) +{ + if (*p <= 246) { + *integer = *p++ - 139; + } else if (*p <= 250) { + *integer = (p[0] - 247) * 256 + p[1] + 108; + p += 2; + } else if (*p <= 254) { + *integer = -(p[0] - 251) * 256 - p[1] - 108; + p += 2; + } else { + *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; + p += 5; + } + + return p; +} + +#if 0 +/* + * The two tables that follow are generated using this perl code: + */ + +@encoding = ( + /* 0 */ + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* 16 */ + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* 32 */ + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + /* 48 */ + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + /* 64 */ + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + /* 80 */ + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + /* 96 */ + "quoteleft", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + /* 112 */ + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", NULL, + /* 128 */ + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* 144 */ + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* 160 */ + NULL, "exclamdown", "cent", "sterling", + "fraction", "yen", "florin", "section", + "currency", "quotesingle", "quotedblleft", "guillemotleft", + "guilsinglleft","guilsinglright","fi", "fl", + /* 176 */ + NULL, "endash", "dagger", "daggerdbl", + "periodcentered",NULL, "paragraph", "bullet", + "quotesinglbase","quotedblbase","quotedblright","guillemotright", + "ellipsis", "perthousand", NULL, "questiondown", + /* 192 */ + NULL, "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", + "dieresis", NULL, "ring", "cedilla", + NULL, "hungarumlaut", "ogonek", "caron", + /* 208 */ + "emdash", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* 224 */ + NULL, "AE", NULL, "ordfeminine", + NULL, NULL, NULL, NULL, + "Lslash", "Oslash", "OE", "ordmasculine", + NULL, NULL, NULL, NULL, + /* 240 */ + NULL, "ae", NULL, NULL, + NULL, "dotlessi", NULL, NULL, + "lslash", "oslash", "oe", "germandbls", + NULL, NULL, NULL, NULL + ); + +print "static const char ps_standard_encoding_symbol[] = {\n"; +$s = qq( "\\0"); +for $sym (@encoding) { + if (! ($sym eq NULL)) { + $ss = qq( "$sym\\0"); + if (length($s) + length($ss) > 78) { + print qq( $s\n); + $s = ""; + } + $s .= $ss; + } +} +print qq( $s\n); +print "};\n\n"; +print "static const int16_t ps_standard_encoding_offset[256] = {\n"; +$offset = 1; +$s = qq(); +for $sym (@encoding) { + if (! ($sym eq NULL)) { + $ss = qq( $offset/*$sym*/,); + $offset += length($sym) + 1; + } else { + $ss = qq( 0,); + } + if (length($s) + length($ss) > 78) { + print qq( $s\n); + $s = ""; + } + $s .= $ss; +} +print qq( $s\n); +print "};\n"; +exit; +#endif + +static const char ps_standard_encoding_symbol[] = { + "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0" + "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0" + "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0" + "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0" + "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0" + "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0" + "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0" + "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0" + "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0" + "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0" + "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0" + "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0" + "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0" + "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0" + "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0" + "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0" + "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0" + "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0" + "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0" + "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0" + "oslash\0" "oe\0" "germandbls\0" +}; + +static const int16_t ps_standard_encoding_offset[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, + 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/, + 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, + 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, + 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, + 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, + 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, + 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, + 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, + 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, + 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, + 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/, + 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, + 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, + 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, + 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, + 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/, + 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/, + 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/, + 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/, + 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/, + 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/, + 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0, + 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/, + 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/, + 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/, + 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/, + 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0, + 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/, + 900/*germandbls*/, 0, 0, 0, 0, +}; + +#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL) + +static cairo_status_t +use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index) +{ + const char *glyph_name; + + if (index < 0 || index > 255) + return CAIRO_STATUS_SUCCESS; + + glyph_name = ps_standard_encoding(index); + if (glyph_name == NULL) + return CAIRO_STATUS_SUCCESS; + + index = cairo_type1_font_subset_lookup_glyph (font, + glyph_name, + strlen(glyph_name)); + if (index < 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_type1_font_subset_use_glyph (font, index); + + return CAIRO_STATUS_SUCCESS; +} + +#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12) +#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6) + +static cairo_status_t +cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, + const char *name, int name_length, + const char *encrypted_charstring, int encrypted_charstring_length) +{ + cairo_status_t status; + unsigned char *charstring; + const unsigned char *end; + const unsigned char *p; + int stack[5], sp, value; + int command; + + charstring = malloc (encrypted_charstring_length); + if (unlikely (charstring == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) + encrypted_charstring, + encrypted_charstring_length, + charstring); + end = charstring + encrypted_charstring_length; + + p = charstring + 4; + sp = 0; + + while (p < end) { + if (*p < 32) { + command = *p++; + + if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE) + command = 32 + *p++; + + switch (command) { + case TYPE1_CHARSTRING_COMMAND_SEAC: + /* The seac command takes five integer arguments. The + * last two are glyph indices into the PS standard + * encoding give the names of the glyphs that this + * glyph is composed from. All we need to do is to + * make sure those glyphs are present in the subset + * under their standard names. */ + if (unlikely (sp < 5)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = use_standard_encoding_glyph (font, stack[3]); + if (unlikely (status)) + return status; + + status = use_standard_encoding_glyph (font, stack[4]); + if (unlikely (status)) + return status; + + sp = 0; + break; + + default: + sp = 0; + break; + } + } else { + /* integer argument */ + p = cairo_type1_font_subset_decode_integer (p, &value); + if (sp < 5) + stack[sp++] = value; + } + } + + free (charstring); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +write_used_glyphs (cairo_type1_font_subset_t *font, + const char *name, int name_length, + const char *charstring, int charstring_length) +{ + cairo_status_t status; + char buffer[256]; + int length; + + length = snprintf (buffer, sizeof buffer, + "/%.*s %d %s ", + name_length, name, charstring_length, font->rd); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_write_encrypted (font, + charstring, + charstring_length); + if (unlikely (status)) + return status; + + length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font, + const char *name, int name_length, + const char *charstring, int charstring_length); + +static cairo_status_t +cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, + const char *dict_start, + const char *dict_end, + glyph_func_t func, + const char **dict_out) +{ + int charstring_length, name_length, glyph_index; + const char *p, *charstring, *name; + char *end; + + /* We're looking at '/' in the name of the first glyph. The glyph + * definitions are on the form: + * + * /name 23 RD <23 binary bytes> ND + * + * or alternatively using -| and |- instead of RD and ND. + * + * We parse the glyph name and see if it is in the subset. If it + * is, we call the specified callback with the glyph name and + * glyph data, otherwise we just skip it. We need to parse + * through a glyph definition; we can't just find the next '/', + * since the binary data could contain a '/'. + */ + + p = dict_start; + + while (*p == '/') { + name = p + 1; + p = skip_token (p, dict_end); + name_length = p - name; + + charstring_length = strtol (p, &end, 10); + if (p == end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Skip past -| or RD to binary data. There is exactly one space + * between the -| or RD token and the encrypted data, thus '+ 1'. */ + charstring = skip_token (end, dict_end) + 1; + + /* Skip binary data and |- or ND token. */ + p = skip_token (charstring + charstring_length, dict_end); + while (p < dict_end && _cairo_isspace(*p)) + p++; + + /* In case any of the skip_token() calls above reached EOF, p will + * be equal to dict_end. */ + if (p == dict_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + glyph_index = cairo_type1_font_subset_lookup_glyph (font, + name, name_length); + if (font->glyphs[glyph_index].subset_index >= 0) { + cairo_status_t status = func (font, + name, name_length, + charstring, charstring_length); + if (unlikely (status)) + return status; + } + } + + *dict_out = p; + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, + const char *name) +{ + cairo_status_t status; + const char *p, *charstrings, *dict_start; + const char *closefile_token; + char buffer[32], *glyph_count_end; + int num_charstrings, length; + + /* The private dict holds hint information, common subroutines and + * the actual glyph definitions (charstrings). + * + * FIXME: update this comment. + * + * What we do here is scan directly the /CharString token, which + * marks the beginning of the glyph definitions. Then we parse + * through the glyph definitions and weed out the glyphs not in + * our subset. Everything else before and after the glyph + * definitions is copied verbatim to the output. It might be + * worthwile to figure out which of the common subroutines are + * used by the glyphs in the subset and get rid of the rest. */ + + /* FIXME: The /Subrs array contains binary data and could + * conceivably have "/CharStrings" in it, so we might need to skip + * this more cleverly. */ + charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings"); + if (charstrings == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Scan past /CharStrings and the integer following it. */ + p = charstrings + strlen ("/CharStrings"); + num_charstrings = strtol (p, &glyph_count_end, 10); + if (p == glyph_count_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Look for a '/' which marks the beginning of the first glyph + * definition. */ + for (p = glyph_count_end; p < font->cleartext_end; p++) + if (*p == '/') + break; + if (p == font->cleartext_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + dict_start = p; + + status = cairo_type1_font_subset_get_glyph_names_and_widths (font); + if (unlikely (status)) + return status; + + /* Now that we have the private dictionary broken down in + * sections, do the first pass through the glyph definitions to + * figure out which subrs and othersubrs are use and which extra + * glyphs may be required by the seac operator. */ + status = cairo_type1_font_subset_for_each_glyph (font, + dict_start, + font->cleartext_end, + cairo_type1_font_subset_look_for_seac, + &p); + if (unlikely (status)) + return status; + + closefile_token = find_token (p, font->cleartext_end, "closefile"); + if (closefile_token == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = cairo_type1_font_subset_get_glyph_names_and_widths (font); + if (unlikely (status)) + return status; + + /* We're ready to start outputting. First write the header, + * i.e. the public part of the font dict.*/ + status = cairo_type1_font_subset_write_header (font, name); + if (unlikely (status)) + return status; + + font->base.header_size = _cairo_output_stream_get_position (font->output); + + + /* Start outputting the private dict. First output everything up + * to the /CharStrings token. */ + status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, + charstrings - font->cleartext); + if (unlikely (status)) + return status; + + /* Write out new charstring count */ + length = snprintf (buffer, sizeof buffer, + "/CharStrings %d", font->num_glyphs); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + /* Write out text between the charstring count and the first + * charstring definition */ + status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, + dict_start - glyph_count_end); + if (unlikely (status)) + return status; + + /* Write out the charstring definitions for each of the glyphs in + * the subset. */ + status = cairo_type1_font_subset_for_each_glyph (font, + dict_start, + font->cleartext_end, + write_used_glyphs, + &p); + if (unlikely (status)) + return status; + + /* Output what's left between the end of the glyph definitions and + * the end of the private dict to the output. */ + status = cairo_type1_font_subset_write_encrypted (font, p, + closefile_token - p + strlen ("closefile") + 1); + if (unlikely (status)) + return status; + + if (font->hex_encode) + _cairo_output_stream_write (font->output, "\n", 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) +{ + const char *cleartomark_token; + int i; + static const char zeros[65] = + "0000000000000000000000000000000000000000000000000000000000000000\n"; + + + for (i = 0; i < 8; i++) + _cairo_output_stream_write (font->output, zeros, sizeof zeros); + + cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); + if (cleartomark_token) { + /* Some fonts have conditional save/restore around the entire + * font dict, so we need to retain whatever postscript code + * that may come after 'cleartomark'. */ + + _cairo_output_stream_write (font->output, cleartomark_token, + font->type1_end - cleartomark_token); + } else if (!font->eexec_segment_is_ascii) { + /* Fonts embedded in PDF may omit the fixed-content portion + * that includes the 'cleartomark' operator. Type 1 in PDF is + * always binary. */ + + _cairo_output_stream_printf (font->output, "cleartomark"); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* some fonts do not have a newline at the end of the last line */ + _cairo_output_stream_printf (font->output, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +type1_font_write (void *closure, const unsigned char *data, unsigned int length) +{ + cairo_type1_font_subset_t *font = closure; + + return _cairo_array_append_multiple (&font->contents, data, length); +} + +static cairo_status_t +cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, + const char *name) +{ + cairo_status_t status; + + status = cairo_type1_font_subset_find_segments (font); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_decrypt_eexec_segment (font); + if (unlikely (status)) + return status; + + /* Determine which glyph definition delimiters to use. */ + if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) { + font->rd = "-|"; + font->nd = "|-"; + } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) { + font->rd = "RD"; + font->nd = "ND"; + } else { + /* Don't know *what* kind of font this is... */ + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; + font->hex_column = 0; + + status = cairo_type1_font_subset_write_private_dict (font, name); + if (unlikely (status)) + return status; + + font->base.data_size = _cairo_output_stream_get_position (font->output) - + font->base.header_size; + + status = cairo_type1_font_subset_write_trailer (font); + if (unlikely (status)) + return status; + + font->base.trailer_size = + _cairo_output_stream_get_position (font->output) - + font->base.header_size - font->base.data_size; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_generate (void *abstract_font, + const char *name) + +{ + cairo_type1_font_subset_t *font = abstract_font; + cairo_ft_unscaled_font_t *ft_unscaled_font; + unsigned long ret; + cairo_status_t status; + + ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font; + font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); + if (unlikely (font->face == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->type1_length = font->face->stream->size; + font->type1_data = malloc (font->type1_length); + if (unlikely (font->type1_data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + if (font->face->stream->read != NULL) { + /* Note that read() may be implemented as a macro, thanks POSIX!, so we + * need to wrap the following usage in parentheses in order to + * disambiguate it for the pre-processor - using the verbose function + * pointer dereference for clarity. + */ + ret = (* font->face->stream->read) (font->face->stream, 0, + (unsigned char *) font->type1_data, + font->type1_length); + if (ret != font->type1_length) { + status = _cairo_error (CAIRO_STATUS_READ_ERROR); + goto fail; + } + } else { + memcpy (font->type1_data, + font->face->stream->base, font->type1_length); + } + + status = _cairo_array_grow_by (&font->contents, 4096); + if (unlikely (status)) + goto fail; + + font->output = _cairo_output_stream_create (type1_font_write, NULL, font); + if (unlikely ((status = font->output->status))) + goto fail; + + status = cairo_type1_font_subset_write (font, name); + if (unlikely (status)) + goto fail; + + font->base.data = _cairo_array_index (&font->contents, 0); + + fail: + _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); + + return status; +} + +static cairo_status_t +_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + unsigned int i; + + /* If the subset generation failed, some of the pointers below may + * be NULL depending on at which point the error occurred. */ + + _cairo_array_fini (&font->contents); + + free (font->type1_data); + if (font->glyphs != NULL) { + for (i = 0; i < font->base.num_glyphs; i++) + free (font->glyphs[i].name); + } + + _cairo_unscaled_font_destroy (font->base.unscaled_font); + + if (font->output != NULL) + status = _cairo_output_stream_destroy (font->output); + + if (font->base.base_font) + free (font->base.base_font); + free (font->glyphs); + + return status; +} + +cairo_status_t +_cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t hex_encode) +{ + cairo_type1_font_subset_t font; + cairo_status_t status, status_ignored; + unsigned long parent_glyph, length; + unsigned int i; + cairo_unscaled_font_t *unscaled_font; + char buf[30]; + + /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */ + if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font); + + status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode); + if (unlikely (status)) + return status; + + for (i = 0; i < scaled_font_subset->num_glyphs; i++) { + parent_glyph = scaled_font_subset->glyphs[i]; + cairo_type1_font_subset_use_glyph (&font, parent_glyph); + } + + status = cairo_type1_font_subset_generate (&font, name); + if (unlikely (status)) + goto fail1; + + if (font.base.base_font) { + type1_subset->base_font = strdup (font.base.base_font); + } else { + snprintf(buf, sizeof (buf), "CairoFont-%u-%u", + scaled_font_subset->font_id, scaled_font_subset->subset_id); + type1_subset->base_font = strdup (buf); + } + if (unlikely (type1_subset->base_font == NULL)) + goto fail1; + + type1_subset->widths = calloc (sizeof (double), font.num_glyphs); + if (unlikely (type1_subset->widths == NULL)) + goto fail2; + for (i = 0; i < font.base.num_glyphs; i++) { + if (font.glyphs[i].subset_index < 0) + continue; + type1_subset->widths[font.glyphs[i].subset_index] = + font.glyphs[i].width; + } + + type1_subset->x_min = font.base.x_min/1000.0; + type1_subset->y_min = font.base.y_min/1000.0; + type1_subset->x_max = font.base.x_max/1000.0; + type1_subset->y_max = font.base.y_max/1000.0; + type1_subset->ascent = font.base.ascent/1000.0; + type1_subset->descent = font.base.descent/1000.0; + + length = font.base.header_size + + font.base.data_size + + font.base.trailer_size; + type1_subset->data = malloc (length); + if (unlikely (type1_subset->data == NULL)) + goto fail3; + + memcpy (type1_subset->data, + _cairo_array_index (&font.contents, 0), length); + + type1_subset->header_length = font.base.header_size; + type1_subset->data_length = font.base.data_size; + type1_subset->trailer_length = font.base.trailer_size; + + return _cairo_type1_font_subset_fini (&font); + + fail3: + free (type1_subset->widths); + fail2: + free (type1_subset->base_font); + fail1: + status_ignored = _cairo_type1_font_subset_fini (&font); + + return status; +} + +void +_cairo_type1_subset_fini (cairo_type1_subset_t *subset) +{ + free (subset->base_font); + free (subset->widths); + free (subset->data); +} + +cairo_bool_t +_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_unscaled_font_t *unscaled; + FT_Face face; + PS_FontInfoRec font_info; + cairo_bool_t is_type1 = FALSE; + + if (!_cairo_scaled_font_is_ft (scaled_font)) + return FALSE; + unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font); + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return FALSE; + + if (FT_Get_PS_Font_Info(face, &font_info) == 0) + is_type1 = TRUE; + + /* OpenType/CFF fonts also have a PS_FontInfoRec */ +#if HAVE_FT_LOAD_SFNT_TABLE + if (FT_IS_SFNT (face)) + is_type1 = FALSE; +#endif + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return is_type1; +} + +#endif /* CAIRO_HAS_FT_FONT */ + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-type3-glyph-surface-private.h b/libs/cairo/src/cairo-type3-glyph-surface-private.h new file mode 100644 index 000000000..b101431fc --- /dev/null +++ b/libs/cairo/src/cairo-type3-glyph-surface-private.h @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-type3-glyph-surface.c b/libs/cairo/src/cairo-type3-glyph-surface.c new file mode 100644 index 000000000..e0ad08032 --- /dev/null +++ b/libs/cairo/src/cairo-type3-glyph-surface.c @@ -0,0 +1,531 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type3-glyph-surface-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" +#include "cairo-surface-clipper-private.h" + +static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; + +static cairo_status_t +_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper, + cairo_type3_glyph_surface_t, + clipper); + + if (path == NULL) { + _cairo_output_stream_printf (surface->stream, "Q q\n"); + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + +cairo_surface_t * +_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, + cairo_output_stream_t *stream, + cairo_type3_glyph_surface_emit_image_t emit_image, + cairo_scaled_font_subsets_t *font_subsets) +{ + cairo_type3_glyph_surface_t *surface; + cairo_matrix_t invert_y_axis; + + if (unlikely (stream != NULL && stream->status)) + return _cairo_surface_create_in_error (stream->status); + + surface = malloc (sizeof (cairo_type3_glyph_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &cairo_type3_glyph_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + surface->scaled_font = scaled_font; + surface->stream = stream; + surface->emit_image = emit_image; + + /* Setup the transform from the user-font device space to Type 3 + * font space. The Type 3 font space is defined by the FontMatrix + * entry in the Type 3 dictionary. In the PDF backend this is an + * identity matrix. */ + surface->cairo_to_pdf = scaled_font->scale_inverse; + cairo_matrix_init_scale (&invert_y_axis, 1, -1); + cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis); + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->stream, + &surface->cairo_to_pdf, + font_subsets); + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_type3_glyph_surface_clipper_intersect_clip_path); + + return &surface->base; +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, + cairo_image_surface_t *image, + cairo_matrix_t *image_matrix) +{ + cairo_status_t status; + + /* The only image type supported by Type 3 fonts are 1-bit masks */ + image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "q %f %f %f %f %f %f cm\n", + image_matrix->xx, + image_matrix->xy, + image_matrix->yx, + image_matrix->yy, + image_matrix->x0, + image_matrix->y0); + + status = surface->emit_image (image, surface->stream); + cairo_surface_destroy (&image->base); + + _cairo_output_stream_printf (surface->stream, + "Q\n"); + + return status; +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface, + cairo_image_surface_t *image, + const cairo_matrix_t *pattern_matrix) +{ + cairo_matrix_t mat, upside_down; + cairo_status_t status; + + if (image->width == 0 || image->height == 0) + return CAIRO_STATUS_SUCCESS; + + mat = *pattern_matrix; + + /* Get the pattern space to user space matrix */ + status = cairo_matrix_invert (&mat); + + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + /* Make this a pattern space to Type 3 font space matrix */ + cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf); + + /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by + * 1 image upside down to convert to flip the Y-axis going from + * cairo to PDF. Then scale the image up to the required size. */ + cairo_matrix_scale (&mat, image->width, image->height); + cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1); + cairo_matrix_multiply (&mat, &upside_down, &mat); + + return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); +} + +static cairo_status_t +_cairo_type3_glyph_surface_finish (void *abstract_surface) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + + return _cairo_pdf_operators_fini (&surface->pdf_operators); +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + const cairo_surface_pattern_t *pattern; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + pattern = (const cairo_surface_pattern_t *) source; + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); + if (unlikely (status)) + goto fail; + + status = _cairo_type3_glyph_surface_emit_image_pattern (surface, + image, + &pattern->base.matrix); + +fail: + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + return _cairo_type3_glyph_surface_paint (abstract_surface, + op, mask, + clip); +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_scaled_font_t *font; + cairo_matrix_t new_ctm, invert_y_axis; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + cairo_matrix_init_scale (&invert_y_axis, 1, -1); + cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm); + cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm); + font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &new_ctm, + &scaled_font->options); + if (unlikely (font->status)) + return font->status; + + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + font); + + cairo_scaled_font_destroy (font); + + return status; +} + +static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, + NULL, /* _cairo_type3_glyph_surface_create_similar */ + _cairo_type3_glyph_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* cairo_type3_glyph_surface_copy_page */ + NULL, /* _cairo_type3_glyph_surface_show_page */ + NULL, /* _cairo_type3_glyph_surface_get_extents */ + NULL, /* old_show_glyphs */ + NULL, /* _cairo_type3_glyph_surface_get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _cairo_type3_glyph_surface_paint, + _cairo_type3_glyph_surface_mask, + _cairo_type3_glyph_surface_stroke, + _cairo_type3_glyph_surface_fill, + _cairo_type3_glyph_surface_show_glyphs, + NULL, /* snapshot */ +}; + +static void +_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface, + cairo_output_stream_t *stream) +{ + surface->stream = stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream); +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + cairo_image_surface_t *image; + cairo_matrix_t mat; + double x, y; + + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + return status; + + image = scaled_glyph->surface; + if (image->width == 0 || image->height == 0) + return CAIRO_STATUS_SUCCESS; + + x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); + mat.xx = image->width; + mat.xy = 0; + mat.yx = 0; + mat.yy = image->height; + mat.x0 = x; + mat.y0 = y; + cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); + mat.y0 *= -1; + + return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); +} + +void +_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + + if (unlikely (surface->base.status)) + return; + + _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, + use_font_subset, + closure); +} + +cairo_status_t +_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, + unsigned long glyph_index) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status, status2; + cairo_output_stream_t *null_stream; + + if (unlikely (surface->base.status)) + return surface->base.status; + + null_stream = _cairo_null_stream_create (); + if (unlikely (null_stream->status)) + return null_stream->status; + + _cairo_type3_glyph_surface_set_stream (surface, null_stream); + + _cairo_scaled_font_freeze_cache (surface->scaled_font); + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + + if (_cairo_status_is_error (status)) + goto cleanup; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = CAIRO_STATUS_SUCCESS; + goto cleanup; + } + + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) + status = CAIRO_STATUS_SUCCESS; + +cleanup: + _cairo_scaled_font_thaw_cache (surface->scaled_font); + + status2 = _cairo_output_stream_destroy (null_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +cairo_status_t +_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, + cairo_output_stream_t *stream, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status, status2; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + + if (unlikely (surface->base.status)) + return surface->base.status; + + _cairo_type3_glyph_surface_set_stream (surface, stream); + + _cairo_scaled_font_freeze_cache (surface->scaled_font); + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status == CAIRO_STATUS_SUCCESS) + status = CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + if (_cairo_status_is_error (status)) { + _cairo_scaled_font_thaw_cache (surface->scaled_font); + return status; + } + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = surface->scaled_font->font_matrix; + status2 = cairo_matrix_invert (&font_matrix_inverse); + + /* The invertability of font_matrix is tested in + * pdf_operators_show_glyphs before any glyphs are mapped to the + * subset. */ + assert (status2 == CAIRO_STATUS_SUCCESS); + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->stream, + "%f 0 %f %f %f %f d1\n", + x_advance, + _cairo_fixed_to_double (bbox->p1.x), + - _cairo_fixed_to_double (bbox->p2.y), + _cairo_fixed_to_double (bbox->p2.x), + - _cairo_fixed_to_double (bbox->p1.y)); + + if (status == CAIRO_STATUS_SUCCESS) { + cairo_output_stream_t *mem_stream; + + mem_stream = _cairo_memory_stream_create (); + status = mem_stream->status; + if (unlikely (status)) + goto FAIL; + + _cairo_type3_glyph_surface_set_stream (surface, mem_stream); + + _cairo_output_stream_printf (surface->stream, "q\n"); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + + status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_printf (surface->stream, "Q\n"); + + _cairo_type3_glyph_surface_set_stream (surface, stream); + if (status == CAIRO_STATUS_SUCCESS) + _cairo_memory_stream_copy (mem_stream, stream); + + status2 = _cairo_output_stream_destroy (mem_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + + if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) + status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); + + FAIL: + _cairo_scaled_font_thaw_cache (surface->scaled_font); + + return status; +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/libs/cairo/src/cairo-types-private.h b/libs/cairo/src/cairo-types-private.h new file mode 100644 index 000000000..00b9269c7 --- /dev/null +++ b/libs/cairo/src/cairo-types-private.h @@ -0,0 +1,450 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_TYPES_PRIVATE_H +#define CAIRO_TYPES_PRIVATE_H + +#include "cairo.h" +#include "cairo-fixed-type-private.h" +#include "cairo-list-private.h" +#include "cairo-reference-count-private.h" + +/** + * SECTION:cairo-types + * @Title: Types + * @Short_Description: Generic data types + * + * This section lists generic data types used in the cairo API. + */ + +typedef struct _cairo_array cairo_array_t; +typedef struct _cairo_backend cairo_backend_t; +typedef struct _cairo_boxes_t cairo_boxes_t; +typedef struct _cairo_cache cairo_cache_t; +typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t; +typedef struct _cairo_clip cairo_clip_t; +typedef struct _cairo_clip_path cairo_clip_path_t; +typedef struct _cairo_color cairo_color_t; +typedef struct _cairo_color_stop cairo_color_stop_t; +typedef struct _cairo_device_backend cairo_device_backend_t; +typedef struct _cairo_font_face_backend cairo_font_face_backend_t; +typedef struct _cairo_gstate cairo_gstate_t; +typedef struct _cairo_hash_entry cairo_hash_entry_t; +typedef struct _cairo_hash_table cairo_hash_table_t; +typedef struct _cairo_image_surface cairo_image_surface_t; +typedef struct _cairo_mime_data cairo_mime_data_t; +typedef struct _cairo_observer cairo_observer_t; +typedef struct _cairo_output_stream cairo_output_stream_t; +typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; +typedef struct _cairo_path_fixed cairo_path_fixed_t; +typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; +typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; +typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; +typedef struct _cairo_solid_pattern cairo_solid_pattern_t; +typedef struct _cairo_surface_backend cairo_surface_backend_t; +typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t; +typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t; +typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t; +typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; +typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t; + +typedef cairo_array_t cairo_user_data_array_t; + +struct _cairo_observer { + cairo_list_t link; + void (*callback) (cairo_observer_t *self, void *arg); +}; + +/** + * cairo_hash_entry_t: + * + * A #cairo_hash_entry_t contains both a key and a value for + * #cairo_hash_table_t. User-derived types for #cairo_hash_entry_t must + * be type-compatible with this structure (eg. they must have an + * unsigned long as the first parameter. The easiest way to get this + * is to use: + * + * typedef _my_entry { + * cairo_hash_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the #cairo_hash_table_t functions as follows without requiring a cast: + * + * _cairo_hash_table_insert (hash_table, &my_entry->base); + * + * IMPORTANT: The caller is reponsible for initializing + * my_entry->base.hash with a hash code derived from the key. The + * essential property of the hash code is that keys_equal must never + * return %TRUE for two keys that have different hashes. The best hash + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns %FALSE. + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the #cairo_hash_table_t functions accept an entry + * which will be used exclusively as a "key", (indicated by a + * parameter name of key). In these cases, the value-related fields of + * the entry need not be initialized if so desired. + **/ +struct _cairo_hash_entry { + unsigned long hash; +}; + +struct _cairo_array { + unsigned int size; + unsigned int num_elements; + unsigned int element_size; + char **elements; + + cairo_bool_t is_snapshot; +}; + +/** + * cairo_lcd_filter_t: + * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for + * font backend and target device + * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering + * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter + * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel + * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel + * + * The LCD filter specifies the low-pass filter applied to LCD-optimized + * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. + * + * Note: This API was temporarily made available in the public + * interface during the 1.7.x development series, but was made private + * before 1.8. + **/ +typedef enum _cairo_lcd_filter { + CAIRO_LCD_FILTER_DEFAULT, + CAIRO_LCD_FILTER_NONE, + CAIRO_LCD_FILTER_INTRA_PIXEL, + CAIRO_LCD_FILTER_FIR3, + CAIRO_LCD_FILTER_FIR5 +} cairo_lcd_filter_t; + +typedef enum _cairo_round_glyph_positions { + CAIRO_ROUND_GLYPH_POS_DEFAULT, + CAIRO_ROUND_GLYPH_POS_ON, + CAIRO_ROUND_GLYPH_POS_OFF +} cairo_round_glyph_positions_t; + +struct _cairo_font_options { + cairo_antialias_t antialias; + cairo_subpixel_order_t subpixel_order; + cairo_lcd_filter_t lcd_filter; + cairo_hint_style_t hint_style; + cairo_hint_metrics_t hint_metrics; + cairo_round_glyph_positions_t round_glyph_positions; +}; + +/* XXX: Right now, the _cairo_color structure puts unpremultiplied + color in the doubles and premultiplied color in the shorts. Yes, + this is crazy insane, (but at least we don't export this + madness). I'm still working on a cleaner API, but in the meantime, + at least this does prevent precision loss in color when changing + alpha. */ +struct _cairo_color { + double red; + double green; + double blue; + double alpha; + + unsigned short red_short; + unsigned short green_short; + unsigned short blue_short; + unsigned short alpha_short; +}; + +struct _cairo_color_stop { + /* unpremultiplied */ + double red; + double green; + double blue; + double alpha; + + /* unpremultipled, for convenience */ + uint16_t red_short; + uint16_t green_short; + uint16_t blue_short; + uint16_t alpha_short; +}; + +typedef enum _cairo_paginated_mode { + CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */ + CAIRO_PAGINATED_MODE_RENDER, /* render page contents */ + CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ +} cairo_paginated_mode_t; + +/* Sure wish C had a real enum type so that this would be distinct + * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 + * offset. We want to keep it fit in int8_t as the compiler may choose + * that for #cairo_status_t */ +typedef enum _cairo_int_status { + CAIRO_INT_STATUS_UNSUPPORTED = 100, + CAIRO_INT_STATUS_DEGENERATE, + CAIRO_INT_STATUS_NOTHING_TO_DO, + CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, + CAIRO_INT_STATUS_IMAGE_FALLBACK, + CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, + + CAIRO_INT_STATUS_LAST_STATUS +} cairo_int_status_t; + +typedef enum _cairo_internal_surface_type { + CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000, + CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, + CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH +} cairo_internal_surface_type_t; + +#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1 +#define CAIRO_HAS_TEST_NULL_SURFACE 1 +#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1 + +typedef struct _cairo_slope { + cairo_fixed_t dx; + cairo_fixed_t dy; +} cairo_slope_t, cairo_distance_t; + +typedef struct _cairo_point_double { + double x; + double y; +} cairo_point_double_t; + +typedef struct _cairo_distance_double { + double dx; + double dy; +} cairo_distance_double_t; + +typedef struct _cairo_line { + cairo_point_t p1; + cairo_point_t p2; +} cairo_line_t, cairo_box_t; + +typedef struct _cairo_trapezoid { + cairo_fixed_t top, bottom; + cairo_line_t left, right; +} cairo_trapezoid_t; + +typedef struct _cairo_point_int { + int x, y; +} cairo_point_int_t; + +#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) +#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) + +typedef enum _cairo_direction { + CAIRO_DIRECTION_FORWARD, + CAIRO_DIRECTION_REVERSE +} cairo_direction_t; + +typedef struct _cairo_edge { + cairo_line_t line; + int top, bottom; + int dir; +} cairo_edge_t; + +typedef struct _cairo_polygon { + cairo_status_t status; + + cairo_point_t first_point; + cairo_point_t last_point; + cairo_point_t current_point; + cairo_slope_t current_edge; + cairo_bool_t has_current_point; + cairo_bool_t has_current_edge; + + cairo_box_t extents; + cairo_box_t limit; + const cairo_box_t *limits; + int num_limits; + + int num_edges; + int edges_size; + cairo_edge_t *edges; + cairo_edge_t edges_embedded[32]; +} cairo_polygon_t; + +typedef cairo_warn cairo_status_t +(*cairo_spline_add_point_func_t) (void *closure, + const cairo_point_t *point); + +typedef struct _cairo_spline_knots { + cairo_point_t a, b, c, d; +} cairo_spline_knots_t; + +typedef struct _cairo_spline { + cairo_spline_add_point_func_t add_point_func; + void *closure; + + cairo_spline_knots_t knots; + + cairo_slope_t initial_slope; + cairo_slope_t final_slope; + + cairo_bool_t has_point; + cairo_point_t last_point; +} cairo_spline_t; + +typedef struct _cairo_pen_vertex { + cairo_point_t point; + + cairo_slope_t slope_ccw; + cairo_slope_t slope_cw; +} cairo_pen_vertex_t; + +typedef struct _cairo_pen { + double radius; + double tolerance; + + int num_vertices; + cairo_pen_vertex_t *vertices; + cairo_pen_vertex_t vertices_embedded[32]; +} cairo_pen_t; + +typedef struct _cairo_stroke_style { + double line_width; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + double miter_limit; + double *dash; + unsigned int num_dashes; + double dash_offset; +} cairo_stroke_style_t; + +typedef struct _cairo_format_masks { + int bpp; + unsigned long alpha_mask; + unsigned long red_mask; + unsigned long green_mask; + unsigned long blue_mask; +} cairo_format_masks_t; + +typedef enum { + CAIRO_STOCK_WHITE, + CAIRO_STOCK_BLACK, + CAIRO_STOCK_TRANSPARENT, + CAIRO_STOCK_NUM_COLORS, +} cairo_stock_t; + +typedef enum _cairo_image_transparency { + CAIRO_IMAGE_IS_OPAQUE, + CAIRO_IMAGE_HAS_BILEVEL_ALPHA, + CAIRO_IMAGE_HAS_ALPHA, + CAIRO_IMAGE_UNKNOWN +} cairo_image_transparency_t; + +struct _cairo_mime_data { + cairo_reference_count_t ref_count; + unsigned char *data; + unsigned long length; + cairo_destroy_func_t destroy; + void *closure; +}; + +struct _cairo_pattern { + cairo_pattern_type_t type; + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + + cairo_matrix_t matrix; + cairo_filter_t filter; + cairo_extend_t extend; + + cairo_bool_t has_component_alpha; +}; + +struct _cairo_solid_pattern { + cairo_pattern_t base; + cairo_color_t color; +}; + +typedef struct _cairo_surface_pattern { + cairo_pattern_t base; + + cairo_surface_t *surface; +} cairo_surface_pattern_t; + +typedef struct _cairo_gradient_stop { + double offset; + cairo_color_stop_t color; +} cairo_gradient_stop_t; + +typedef struct _cairo_gradient_pattern { + cairo_pattern_t base; + + unsigned int n_stops; + unsigned int stops_size; + cairo_gradient_stop_t *stops; + cairo_gradient_stop_t stops_embedded[2]; +} cairo_gradient_pattern_t; + +typedef struct _cairo_linear_pattern { + cairo_gradient_pattern_t base; + + cairo_point_t p1; + cairo_point_t p2; +} cairo_linear_pattern_t; + +typedef struct _cairo_radial_pattern { + cairo_gradient_pattern_t base; + + cairo_point_t c1; + cairo_fixed_t r1; + cairo_point_t c2; + cairo_fixed_t r2; +} cairo_radial_pattern_t; + +typedef union { + cairo_gradient_pattern_t base; + + cairo_linear_pattern_t linear; + cairo_radial_pattern_t radial; +} cairo_gradient_pattern_union_t; + +typedef union { + cairo_pattern_type_t type; + cairo_pattern_t base; + + cairo_solid_pattern_t solid; + cairo_surface_pattern_t surface; + cairo_gradient_pattern_union_t gradient; +} cairo_pattern_union_t; + +/* + * A #cairo_unscaled_font_t is just an opaque handle we use in the + * glyph cache. + */ +typedef struct _cairo_unscaled_font { + cairo_hash_entry_t hash_entry; + cairo_reference_count_t ref_count; + const cairo_unscaled_font_backend_t *backend; +} cairo_unscaled_font_t; + +typedef struct _cairo_scaled_glyph { + cairo_hash_entry_t hash_entry; + + cairo_text_extents_t metrics; /* user-space metrics */ + cairo_text_extents_t fs_metrics; /* font-space metrics */ + cairo_box_t bbox; /* device-space bounds */ + int16_t x_advance; /* device-space rounded X advance */ + int16_t y_advance; /* device-space rounded Y advance */ + + unsigned int has_info; + cairo_image_surface_t *surface; /* device-space image */ + cairo_path_fixed_t *path; /* device-space outline */ + cairo_surface_t *recording_surface; /* device-space recording-surface */ + + void *surface_private; /* for the surface backend */ +} cairo_scaled_glyph_t; +#endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-unicode.c b/libs/cairo/src/cairo-unicode.c new file mode 100644 index 000000000..3a60ff611 --- /dev/null +++ b/libs/cairo/src/cairo-unicode.c @@ -0,0 +1,384 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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/libs/cairo/src/cairo-user-font-private.h b/libs/cairo/src/cairo-user-font-private.h new file mode 100644 index 000000000..c5995978f --- /dev/null +++ b/libs/cairo/src/cairo-user-font-private.h @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo-user-font.c b/libs/cairo/src/cairo-user-font.c new file mode 100644 index 000000000..9219a7301 --- /dev/null +++ b/libs/cairo/src/cairo-user-font.c @@ -0,0 +1,795 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-user-font-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-user-fonts + * @Title:User Fonts + * @Short_Description: Font support with font data provided by the user + * + * The user-font feature allows the cairo user to provide drawings for glyphs + * in a font. This is most useful in implementing fonts in non-standard + * formats, like SVG fonts and Flash fonts, but can also be used by games and + * other application to draw "funky" fonts. + */ + +/** + * CAIRO_HAS_USER_FONT: + * + * Defined if the user font backend is available. + * This macro can be used to conditionally compile backend-specific code. + * The user font backend is always built in versions of cairo that support + * this feature (1.8 and later). + * + * @Since: 1.8 + */ + +typedef struct _cairo_user_scaled_font_methods { + cairo_user_scaled_font_init_func_t init; + cairo_user_scaled_font_render_glyph_func_t render_glyph; + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; +} cairo_user_scaled_font_methods_t; + +typedef struct _cairo_user_font_face { + cairo_font_face_t base; + + /* Set to true after first scaled font is created. At that point, + * the scaled_font_methods cannot change anymore. */ + cairo_bool_t immutable; + + cairo_user_scaled_font_methods_t scaled_font_methods; +} cairo_user_font_face_t; + +typedef struct _cairo_user_scaled_font { + cairo_scaled_font_t base; + + cairo_text_extents_t default_glyph_extents; + + /* space to compute extents in, and factors to convert back to user space */ + cairo_matrix_t extent_scale; + double extent_x_scale; + double extent_y_scale; + + /* multiplier for metrics hinting */ + double snap_x_scale; + double snap_y_scale; + +} cairo_user_scaled_font_t; + +/* #cairo_user_scaled_font_t */ + +static cairo_surface_t * +_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font) +{ + cairo_content_t content; + + content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? + CAIRO_CONTENT_COLOR_ALPHA : + CAIRO_CONTENT_ALPHA; + + return cairo_recording_surface_create (content, NULL); +} + + +static cairo_t * +_cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface) +{ + cairo_t *cr; + + cr = cairo_create (recording_surface); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_matrix_t scale; + scale = scaled_font->base.scale; + scale.x0 = scale.y0 = 0.; + cairo_set_matrix (cr, &scale); + } + + cairo_set_font_size (cr, 1.0); + cairo_set_font_options (cr, &scaled_font->base.options); + cairo_set_source_rgb (cr, 1., 1., 1.); + + return cr; +} + +static cairo_int_status_t +_cairo_user_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_surface_t *recording_surface = scaled_glyph->recording_surface; + + if (!scaled_glyph->recording_surface) { + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + cairo_text_extents_t extents = scaled_font->default_glyph_extents; + cairo_t *cr; + + if (!face->scaled_font_methods.render_glyph) + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font); + + /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface); + status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, + _cairo_scaled_glyph_index(scaled_glyph), + cr, &extents); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + + if (unlikely (status)) { + cairo_surface_destroy (recording_surface); + return status; + } + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface); + + + /* set metrics */ + + if (extents.width == 0.) { + cairo_box_t bbox; + double x1, y1, x2, y2; + double x_scale, y_scale; + + /* Compute extents.x/y/width/height from recording_surface, + * in font space. + */ + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + &scaled_font->extent_scale); + if (unlikely (status)) + return status; + + _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); + + x_scale = scaled_font->extent_x_scale; + y_scale = scaled_font->extent_y_scale; + extents.x_bearing = x1 * x_scale; + extents.y_bearing = y1 * y_scale; + extents.width = (x2 - x1) * x_scale; + extents.height = (y2 - y1) * y_scale; + } + + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale; + extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + cairo_surface_t *surface; + cairo_format_t format; + int width, height; + + /* TODO + * extend the glyph cache to support argb glyphs. + * need to figure out the semantics and interaction with subpixel + * rendering first. + */ + + width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + + switch (scaled_font->base.options.antialias) { + default: + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; + case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; + case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; + } + surface = cairo_image_surface_create (format, width, height); + + cairo_surface_set_device_offset (surface, + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); + status = _cairo_recording_surface_replay (recording_surface, surface); + + if (unlikely (status)) { + cairo_surface_destroy(surface); + return status; + } + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_recording_surface_get_path (recording_surface, path); + if (unlikely (status)) { + _cairo_path_fixed_destroy (path); + return status; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + + return status; +} + +static unsigned long +_cairo_user_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + unsigned long glyph = 0; + + if (face->scaled_font_methods.unicode_to_glyph) { + cairo_status_t status; + + status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, + ucs4, &glyph); + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + goto not_implemented; + + if (status != CAIRO_STATUS_SUCCESS) { + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + glyph = 0; + } + + } else { +not_implemented: + glyph = ucs4; + } + + return glyph; +} + +static cairo_int_status_t +_cairo_user_text_to_glyphs (void *abstract_font, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + + if (face->scaled_font_methods.text_to_glyphs) { + int i; + cairo_glyph_t *orig_glyphs = *glyphs; + int orig_num_glyphs = *num_glyphs; + + status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + + if (status != CAIRO_STATUS_SUCCESS && + status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + return status; + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) { + if (orig_glyphs != *glyphs) { + cairo_glyph_free (*glyphs); + *glyphs = orig_glyphs; + } + *num_glyphs = orig_num_glyphs; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Convert from font space to user space and add x,y */ + for (i = 0; i < *num_glyphs; i++) { + double gx = (*glyphs)[i].x; + double gy = (*glyphs)[i].y; + + cairo_matrix_transform_point (&scaled_font->base.font_matrix, + &gx, &gy); + + (*glyphs)[i].x = gx + x; + (*glyphs)[i].y = gy + y; + } + } + + return status; +} + +static cairo_status_t +_cairo_user_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font); + +static cairo_status_t +_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + return _cairo_font_face_twin_create_for_toy (toy_face, font_face); +} + +static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { + CAIRO_FONT_TYPE_USER, + NULL, /* scaled_font_fini */ + _cairo_user_scaled_glyph_init, + _cairo_user_text_to_glyphs, + _cairo_user_ucs4_to_index, + NULL, /* show_glyphs */ + NULL, /* load_truetype_table */ + NULL /* index_to_ucs4 */ +}; + +/* #cairo_user_font_face_t */ + +static cairo_status_t +_cairo_user_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_font_face_t *font_face = abstract_face; + cairo_user_scaled_font_t *user_scaled_font = NULL; + cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.}; + + font_face->immutable = TRUE; + + user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); + if (unlikely (user_scaled_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_scaled_font_init (&user_scaled_font->base, + &font_face->base, + font_matrix, ctm, options, + &_cairo_user_scaled_font_backend); + + if (unlikely (status)) { + free (user_scaled_font); + return status; + } + + /* XXX metrics hinting? */ + + /* compute a normalized version of font scale matrix to compute + * extents in. This is to minimize error caused by the cairo_fixed_t + * representation. */ + { + double fixed_scale, x_scale, y_scale; + + user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse; + status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale, + &x_scale, &y_scale, + 1); + if (status == CAIRO_STATUS_SUCCESS) { + + if (x_scale == 0) x_scale = 1.; + if (y_scale == 0) y_scale = 1.; + + user_scaled_font->snap_x_scale = x_scale; + user_scaled_font->snap_y_scale = y_scale; + + /* since glyphs are pretty much 1.0x1.0, we can reduce error by + * scaling to a larger square. say, 1024.x1024. */ + fixed_scale = 1024.; + x_scale /= fixed_scale; + y_scale /= fixed_scale; + + cairo_matrix_scale (&user_scaled_font->extent_scale, 1. / x_scale, 1. / y_scale); + + user_scaled_font->extent_x_scale = x_scale; + user_scaled_font->extent_y_scale = y_scale; + } + } + + if (status == CAIRO_STATUS_SUCCESS && + font_face->scaled_font_methods.init != NULL) + { + /* Lock the scaled_font mutex such that user doesn't accidentally try + * to use it just yet. */ + CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex); + + /* Give away fontmap lock such that user-font can use other fonts */ + status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_t *recording_surface; + cairo_t *cr; + + recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font); + cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface); + cairo_surface_destroy (recording_surface); + + status = font_face->scaled_font_methods.init (&user_scaled_font->base, + cr, + &font_extents); + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + status = CAIRO_STATUS_SUCCESS; + + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + + _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base); + } + + CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex); + } + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents); + + if (status != CAIRO_STATUS_SUCCESS) { + _cairo_scaled_font_fini (&user_scaled_font->base); + free (user_scaled_font); + } else { + user_scaled_font->default_glyph_extents.x_bearing = 0.; + user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent; + user_scaled_font->default_glyph_extents.width = 0.; + user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent; + user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance; + user_scaled_font->default_glyph_extents.y_advance = 0.; + + *scaled_font = &user_scaled_font->base; + } + + return status; +} + +const cairo_font_face_backend_t _cairo_user_font_face_backend = { + CAIRO_FONT_TYPE_USER, + _cairo_user_font_face_create_for_toy, + NULL, /* destroy */ + _cairo_user_font_face_scaled_font_create +}; + + +cairo_bool_t +_cairo_font_face_is_user (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_user_font_face_backend; +} + +/* Implement the public interface */ + +/** + * cairo_user_font_face_create: + * + * Creates a new user font-face. + * + * Use the setter functions to associate callbacks with the returned + * user font. The only mandatory callback is render_glyph. + * + * After the font-face is created, the user can attach arbitrary data + * (the actual font data) to it using cairo_font_face_set_user_data() + * and access it from the user-font callbacks by using + * cairo_scaled_font_get_font_face() followed by + * cairo_font_face_get_user_data(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_user_font_face_create (void) +{ + cairo_user_font_face_t *font_face; + + font_face = malloc (sizeof (cairo_user_font_face_t)); + if (!font_face) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); + + font_face->immutable = FALSE; + memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); + + return &font_face->base; +} +slim_hidden_def(cairo_user_font_face_create); + +/* User-font method setters */ + + +/** + * cairo_user_font_face_set_init_func: + * @font_face: A user font face + * @init_func: The init callback, or %NULL + * + * Sets the scaled-font initialization function of a user-font. + * See #cairo_user_scaled_font_init_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.init = init_func; +} +slim_hidden_def(cairo_user_font_face_set_init_func); + +/** + * cairo_user_font_face_set_render_glyph_func: + * @font_face: A user font face + * @render_glyph_func: The render_glyph callback, or %NULL + * + * Sets the glyph rendering function of a user-font. + * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * The render_glyph callback is the only mandatory callback of a user-font. + * If the callback is %NULL and a glyph is tried to be rendered using + * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.render_glyph = render_glyph_func; +} +slim_hidden_def(cairo_user_font_face_set_render_glyph_func); + +/** + * cairo_user_font_face_set_text_to_glyphs_func: + * @font_face: A user font face + * @text_to_glyphs_func: The text_to_glyphs callback, or %NULL + * + * Sets th text-to-glyphs conversion function of a user-font. + * See #cairo_user_scaled_font_text_to_glyphs_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func; +} + +/** + * cairo_user_font_face_set_unicode_to_glyph_func: + * @font_face: A user font face + * @unicode_to_glyph_func: The unicode_to_glyph callback, or %NULL + * + * Sets the unicode-to-glyph conversion function of a user-font. + * See #cairo_user_scaled_font_unicode_to_glyph_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func) +{ + cairo_user_font_face_t *user_font_face; + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func; +} +slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func); + +/* User-font method getters */ + +/** + * cairo_user_font_face_get_init_func: + * @font_face: A user font face + * + * Gets the scaled-font initialization function of a user-font. + * + * Return value: The init callback of @font_face + * or %NULL if none set or an error has occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.init; +} + +/** + * cairo_user_font_face_get_render_glyph_func: + * @font_face: A user font face + * + * Gets the glyph rendering function of a user-font. + * + * Return value: The render_glyph callback of @font_face + * or %NULL if none set or an error has occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.render_glyph; +} + +/** + * cairo_user_font_face_get_text_to_glyphs_func: + * @font_face: A user font face + * + * Gets the text-to-glyphs conversion function of a user-font. + * + * Return value: The text_to_glyphs callback of @font_face + * or %NULL if none set or an error occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.text_to_glyphs; +} + +/** + * cairo_user_font_face_get_unicode_to_glyph_func: + * @font_face: A user font face + * + * Gets the unicode-to-glyph conversion function of a user-font. + * + * Return value: The unicode_to_glyph callback of @font_face + * or %NULL if none set or an error occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.unicode_to_glyph; +} diff --git a/libs/cairo/src/cairo-version.c b/libs/cairo/src/cairo-version.c new file mode 100644 index 000000000..5b7fa6827 --- /dev/null +++ b/libs/cairo/src/cairo-version.c @@ -0,0 +1,210 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define CAIRO_VERSION_H 1 + +#include "cairoint.h" + +/* get the "real" version info instead of dummy cairo-version.h */ +#undef CAIRO_VERSION_H +#include "cairo-features.h" + +/** + * SECTION:cairo-version + * @Title: Version Information + * @Short_Description: Compile-time and run-time version checks. + * + * Cairo has a three-part version number scheme. In this scheme, we use + * even vs. odd numbers to distinguish fixed points in the software + * vs. in-progress development, (such as from git instead of a tar file, + * or as a "snapshot" tar file as opposed to a "release" tar file). + * + * + * _____ Major. Always 1, until we invent a new scheme. + * / ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git) + * | / _ Micro. Even/Odd = Tar-file/git + * | | / + * 1.0.0 + * + * + * Here are a few examples of versions that one might see. + * + * Releases + * -------- + * 1.0.0 - A major release + * 1.0.2 - A subsequent maintenance release + * 1.2.0 - Another major release + * + * Snapshots + * --------- + * 1.1.2 - A snapshot (working toward the 1.2.0 release) + * + * In-progress development (eg. from git) + * -------------------------------------- + * 1.0.1 - Development on a maintenance branch (toward 1.0.2 release) + * 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release) + * + * + * + * Compatibility + * + * The API/ABI compatibility guarantees for various versions are as + * follows. First, let's assume some cairo-using application code that is + * successfully using the API/ABI "from" one version of cairo. Then let's + * ask the question whether this same code can be moved "to" the API/ABI + * of another version of cairo. + * + * Moving from a release to any later version (release, snapshot, + * development) is always guaranteed to provide compatibility. + * + * Moving from a snapshot to any later version is not guaranteed to + * provide compatibility, since snapshots may introduce new API that ends + * up being removed before the next release. + * + * Moving from an in-development version (odd micro component) to any + * later version is not guaranteed to provide compatibility. In fact, + * there's not even a guarantee that the code will even continue to work + * with the same in-development version number. This is because these + * numbers don't correspond to any fixed state of the software, but + * rather the many states between snapshots and releases. + * + * + * + * Examining the version + * + * Cairo provides the ability to examine the version at either + * compile-time or run-time and in both a human-readable form as well as + * an encoded form suitable for direct comparison. Cairo also provides the + * macro CAIRO_VERSION_ENCODE() to perform the encoding. + * + * + * Compile-time + * ------------ + * CAIRO_VERSION_STRING Human-readable + * CAIRO_VERSION Encoded, suitable for comparison + * + * Run-time + * -------- + * cairo_version_string() Human-readable + * cairo_version() Encoded, suitable for comparison + * + * + * For example, checking that the cairo version is greater than or equal + * to 1.0.0 could be achieved at compile-time or run-time as follows: + * + * + * ##if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 0, 0) + * printf ("Compiling with suitable cairo version: %s\n", %CAIRO_VERSION_STRING); + * ##endif + * + * if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 0, 0)) + * printf ("Running with suitable cairo version: %s\n", cairo_version_string ()); + * + * + * + */ + +/** + * CAIRO_VERSION: + * + * The version of cairo available at compile-time, encoded using + * CAIRO_VERSION_ENCODE(). + */ + +/** + * CAIRO_VERSION_MAJOR: + * + * The major component of the version of cairo available at compile-time. + */ + +/** + * CAIRO_VERSION_MINOR: + * + * The minor component of the version of cairo available at compile-time. + */ + +/** + * CAIRO_VERSION_MICRO: + * + * The micro component of the version of cairo available at compile-time. + */ + +/** + * CAIRO_VERSION_STRING: + * + * A human-readable string literal containing the version of cairo available + * at compile-time, in the form of "X.Y.Z". + */ + +/** + * CAIRO_VERSION_ENCODE: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * This macro encodes the given cairo version into an integer. The numbers + * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro. + * Two encoded version numbers can be compared as integers. The encoding ensures + * that later versions compare greater than earlier versions. + * + * @Returns: the encoded version. + */ + +/** + * CAIRO_VERSION_STRINGIZE: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * This macro encodes the given cairo version into an string. The numbers + * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro. + * The parameters to this macro must expand to numerical literals. + * + * @Returns: a string literal containing the version. + * + * @Since: 1.8 + */ + +/** + * cairo_version: + * + * Returns the version of the cairo library encoded in a single + * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that + * later versions compare greater than earlier versions. + * + * A run-time comparison to check that cairo's version is greater than + * or equal to version X.Y.Z could be performed as follows: + * + * + * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...} + * + * + * See also cairo_version_string() as well as the compile-time + * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING. + * + * Return value: the encoded version. + **/ +int +cairo_version (void) +{ + return CAIRO_VERSION; +} + +/** + * cairo_version_string: + * + * Returns the version of the cairo library as a human-readable string + * of the form "X.Y.Z". + * + * See also cairo_version() as well as the compile-time equivalents + * %CAIRO_VERSION_STRING and %CAIRO_VERSION. + * + * Return value: a string containing the version. + **/ +const char* +cairo_version_string (void) +{ + return CAIRO_VERSION_STRING; +} +slim_hidden_def (cairo_version_string); diff --git a/libs/cairo/src/cairo-version.h b/libs/cairo/src/cairo-version.h new file mode 100644 index 000000000..ace09244c --- /dev/null +++ b/libs/cairo/src/cairo-version.h @@ -0,0 +1,16 @@ +/* This is a dummy file. + * The actual version info is in toplevel cairo-version.h. + * The purpose of this file is to make most of the source files NOT depend + * on the real cairo-version.h, and as a result, changing library version + * would not cause a complete rebuild of all object files (just a relink). + * This is useful when bisecting. */ +#ifndef CAIRO_VERSION_H +#define CAIRO_VERSION_H + +#if 0 +#define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD +#define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD +#define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD +#endif + +#endif diff --git a/libs/cairo/src/cairo-vg-surface.c b/libs/cairo/src/cairo-vg-surface.c new file mode 100644 index 000000000..3a3d83e96 --- /dev/null +++ b/libs/cairo/src/cairo-vg-surface.c @@ -0,0 +1,1894 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-vg.h" + +#include "cairo-cache-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-clipper-private.h" + +#include +#include + +//#define OPENVG_DEBUG + +/* + * Work that needs to be done: + * - Glyph cache / proper font support + * + * - First-class paths + * Paths are expensive for OpenVG, reuse paths whenever possible. + * So add a path cache, and first class paths! + */ + +typedef struct _cairo_vg_surface cairo_vg_surface_t; + +/* XXX need GL specific context control. :( */ +struct _cairo_vg_context { + cairo_status_t status; + cairo_reference_count_t ref_count; + + unsigned long target_id; + + VGPaint paint; + cairo_vg_surface_t *source; + double alpha; + + cairo_cache_t snapshot_cache; + + void *display; + void *context; + + cairo_status_t (*create_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + cairo_status_t (*set_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *); +}; + +struct _cairo_vg_surface { + cairo_surface_t base; + + cairo_vg_context_t *context; + + VGImage image; + VGImageFormat format; + int width; + int height; + cairo_bool_t own_image; + + cairo_cache_entry_t snapshot_cache_entry; + + cairo_surface_clipper_t clipper; + + unsigned long target_id; +}; + +static const cairo_surface_backend_t cairo_vg_surface_backend; + +slim_hidden_proto (cairo_vg_surface_create); + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +static cairo_vg_context_t * +_vg_context_reference (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + _cairo_reference_count_inc (&context->ref_count); + + return context; +} + +static cairo_vg_context_t * +_vg_context_lock (cairo_vg_context_t *context) +{ + /* XXX if we need to add locking, then it has to be recursive */ + return context; +} + +static cairo_int_status_t +_vg_context_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + cairo_status_t status; + + if (surface->target_id == 0) { + status = context->create_target (context, surface); + if (unlikely (status)) + return status; + } + + if (context->target_id == surface->target_id) + return CAIRO_STATUS_SUCCESS; + + context->target_id = surface->target_id; + + return context->set_target (context, surface); +} + +static void +_vg_context_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + if (surface->target_id == 0) + return; + + if (context->target_id == surface->target_id) + context->set_target (context, NULL); + + context->destroy_target (context, surface); +} + +static cairo_bool_t +_vg_snapshot_cache_can_remove (const void *entry) +{ + return TRUE; +} + +static void +_vg_snapshot_cache_remove (void *cache_entry) +{ + cairo_vg_surface_t *surface = cairo_container_of (cache_entry, + cairo_vg_surface_t, + snapshot_cache_entry); + surface->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&surface->base); +} + +static cairo_status_t +_vg_context_init (cairo_vg_context_t *context) +{ + cairo_status_t status; + + context->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1); + + status = _cairo_cache_init (&context->snapshot_cache, + NULL, + _vg_snapshot_cache_can_remove, + _vg_snapshot_cache_remove, + 16*1024*1024); + if (unlikely (status)) + return status; + + context->target_id = 0; + context->source = NULL; + context->alpha = 1.0; + + context->paint = vgCreatePaint (); + vgLoadIdentity (); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_context_destroy (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&context->ref_count)) + return; + + if (context->paint != VG_INVALID_HANDLE) + vgDestroyPaint (context->paint); + + _cairo_cache_fini (&context->snapshot_cache); + free (context); +} + +static void +_vg_context_unlock (cairo_vg_context_t *context) +{ +} + +#ifdef OPENVG_DEBUG +static void check_vg_errors(const char*function,int line) +{ + int err = vgGetError(); + if (err != VG_NO_ERROR){ + printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err); + assert(err == VG_NO_ERROR); + } + +} +#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__) +#else +#define CHECK_VG_ERRORS() do{}while(0) +#endif //OPENVG_DEBUG + +static pixman_format_code_t +_vg_format_to_pixman (VGImageFormat format, + cairo_bool_t *needs_premult_fixup) +{ + *needs_premult_fixup = FALSE; + switch (format) { + /* RGB{A,X} channel ordering */ + case VG_sRGBX_8888: return 0; //PIXMAN_r8g8b8x8; + case VG_sRGBA_8888: return 0; + case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8; + case VG_sRGB_565: return PIXMAN_r5g6b5; + case VG_sRGBA_5551: return 0; + case VG_sRGBA_4444: return 0; + case VG_sL_8: return PIXMAN_g8; + case VG_lRGBX_8888: return 0; + case VG_lRGBA_8888: return 0; + case VG_lRGBA_8888_PRE: return 0; + case VG_lL_8: return 0; + case VG_A_8: return PIXMAN_a8; + case VG_BW_1: return PIXMAN_a1; + case VG_A_1: return PIXMAN_a1; + case VG_A_4: return PIXMAN_a4; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return PIXMAN_x8r8g8b8; + case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8; + case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8; + case VG_sARGB_1555: return 0; + case VG_sARGB_4444: return 0; + case VG_lXRGB_8888: return 0; + case VG_lARGB_8888: return 0; + case VG_lARGB_8888_PRE: return 0; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return PIXMAN_b8g8r8x8; + case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8; + case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8; + case VG_sBGR_565: return PIXMAN_b5g6r5; + case VG_sBGRA_5551: return 0; + case VG_sBGRA_4444: return 0; + case VG_lBGRX_8888: return 0; + case VG_lBGRA_8888: return 0; + case VG_lBGRA_8888_PRE: return 0; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return PIXMAN_x8b8g8r8; + case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8; + case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8; + case VG_sABGR_1555: return 0; + case VG_sABGR_4444: return 0; + case VG_lXBGR_8888: return 0; + case VG_lABGR_8888: return 0; + case VG_lABGR_8888_PRE: return 0; + default: return 0; + } +} + +static pixman_format_code_t +_vg_format_to_content (VGImageFormat format) +{ + /* XXX could use more simple bit tests */ + switch (format) { + /* RGB{A,X} channel ordering */ + case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGB_565: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sL_8: return CAIRO_CONTENT_ALPHA; + case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lL_8: return CAIRO_CONTENT_ALPHA; + case VG_A_8: return CAIRO_CONTENT_ALPHA; + case VG_A_4: return CAIRO_CONTENT_ALPHA; + case VG_A_1: return CAIRO_CONTENT_ALPHA; + case VG_BW_1: return CAIRO_CONTENT_ALPHA; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGR_565: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + default: return 0; + } +} + +static VGImageFormat +_vg_format_from_pixman (pixman_format_code_t format) +{ + /* XXX _PRE needs fixup */ + switch ((int) format) { + case PIXMAN_r5g6b5: return VG_sRGB_565; + case PIXMAN_g8: return VG_sL_8; + case PIXMAN_a8: return VG_A_8; + case PIXMAN_a1: return VG_BW_1; + case PIXMAN_x8r8g8b8: return VG_sXRGB_8888; + case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE + case PIXMAN_b8g8r8x8: return VG_sBGRX_8888; + case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE + case PIXMAN_b5g6r5: return VG_sBGR_565; + case PIXMAN_x8b8g8r8: return VG_sXBGR_8888; + case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE + default: return 0; + } +} + +static VGImageFormat +_vg_format_for_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return VG_A_8; + case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888; + default: ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE + } +} + +static cairo_surface_t * +_vg_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_vg_surface_t *surface = abstract_surface; + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + return cairo_vg_surface_create (surface->context, content, width, height); +} + +static cairo_status_t +_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_vg_surface_t *surface = cairo_container_of (clipper, + cairo_vg_surface_t, + clipper); + cairo_vg_surface_t *mask; + cairo_status_t status; + + if (path == NULL) { + vgMask (VG_INVALID_HANDLE, + VG_FILL_MASK, 0, 0, surface->width, surface->height); + vgSeti (VG_MASKING, VG_FALSE); + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; + } + + mask = (cairo_vg_surface_t *) + _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, + surface->width, surface->height); + if (unlikely (mask == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (mask->base.status)) + return mask->base.status; + + status = _cairo_surface_fill (&mask->base, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + NULL); + if (status) { + cairo_surface_destroy (&mask->base); + return status; + } + + vgSeti (VG_MASKING, VG_TRUE); + vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height); + + cairo_surface_destroy (&mask->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_vg_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_vg_surface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + + return TRUE; +} + +#define MAX_SEG 16 /* max number of knots to upload in a batch */ + +typedef struct _vg_path { + VGPath path; + cairo_matrix_t *ctm_inverse; + + VGubyte gseg[MAX_SEG]; + VGfloat gdata[MAX_SEG*3*2]; + int dcount; + int scount; +} vg_path_t; + +static cairo_status_t +_vg_move_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_MOVE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_line_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_LINE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + vg_path_t *path = closure; + double x0 = _cairo_fixed_to_double (p0->x); + double y0 = _cairo_fixed_to_double (p0->y); + double x1 = _cairo_fixed_to_double (p1->x); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + + if (path->ctm_inverse) { + cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0); + cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1); + cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2); + } + + path->gseg[path->scount++] = VG_CUBIC_TO; + path->gdata[path->dcount++] = x0; + path->gdata[path->dcount++] = y0; + path->gdata[path->dcount++] = x1; + path->gdata[path->dcount++] = y1; + path->gdata[path->dcount++] = x2; + path->gdata[path->dcount++] = y2; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData(path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_close_path (void *closure) +{ + vg_path_t *path = closure; + + path->gseg[path->scount++] = VG_CLOSE_PATH; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_path_from_cairo (vg_path_t *vg_path, + const cairo_path_fixed_t *path) +{ + cairo_status_t status; + + vg_path->scount = 0; + vg_path->dcount = 0; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _vg_move_to, + _vg_line_to, + _vg_curve_to, + _vg_close_path, + vg_path); + assert (status == CAIRO_STATUS_SUCCESS); + + vgAppendPathData (vg_path->path, + vg_path->scount, vg_path->gseg, vg_path->gdata); + CHECK_VG_ERRORS(); +} + +static cairo_bool_t +_vg_is_supported_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + return FALSE; + } +} + +static VGBlendMode +_vg_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + return VG_BLEND_SRC; + case CAIRO_OPERATOR_OVER: + return VG_BLEND_SRC_OVER; + case CAIRO_OPERATOR_IN: + return VG_BLEND_SRC_IN; + case CAIRO_OPERATOR_DEST_OVER: + return VG_BLEND_DST_OVER; + case CAIRO_OPERATOR_DEST_IN: + return VG_BLEND_DST_IN; + case CAIRO_OPERATOR_ADD: + return VG_BLEND_ADDITIVE; + default: + ASSERT_NOT_REACHED; + return VG_BLEND_SRC_OVER; + } +} + +static VGFillRule +_vg_fill_rule_from_cairo (cairo_fill_rule_t rule) +{ + switch (rule) { + case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD; + case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO; + } + + ASSERT_NOT_REACHED; + return VG_NON_ZERO; +} + +static VGRenderingQuality +_vg_rendering_quality_from_cairo (cairo_antialias_t aa) +{ + switch (aa) { + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_SUBPIXEL: + return VG_RENDERING_QUALITY_BETTER; + + case CAIRO_ANTIALIAS_GRAY: + return VG_RENDERING_QUALITY_FASTER; + + case CAIRO_ANTIALIAS_NONE: + return VG_RENDERING_QUALITY_NONANTIALIASED; + } + + ASSERT_NOT_REACHED; + return VG_RENDERING_QUALITY_BETTER; +} + +static VGCapStyle +_vg_line_cap_from_cairo (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; + case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; + } + + ASSERT_NOT_REACHED; + return VG_CAP_BUTT; +} + +static VGJoinStyle +_vg_line_join_from_cairo (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; + } + + ASSERT_NOT_REACHED; + return VG_JOIN_MITER; +} + +static void +_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src) +{ + dst[0] = /* sx */ src->xx; + dst[1] = /* shy */ src->yx; + dst[2] = /* w0 */ 0; + dst[3] = /* shx */ src->xy; + dst[4] = /* sy */ src->yy; + dst[5] = /* w1 */ 0; + dst[6] = /* tx */ src->x0; + dst[7] = /* ty */ src->y0; + dst[8] = /* w2 */ 0; +} + +static cairo_status_t +_vg_setup_gradient_stops (cairo_vg_context_t *context, + const cairo_gradient_pattern_t *pattern) +{ + VGint numstops = pattern->n_stops; + VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)]; + int i; + + if (numstops*5 < ARRAY_LENGTH (stack_stops)) { + stops = stack_stops; + } else { + stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < numstops; i++) { + stops[i*5 + 0] = pattern->stops[i].offset; + stops[i*5 + 1] = pattern->stops[i].color.red; + stops[i*5 + 2] = pattern->stops[i].color.green; + stops[i*5 + 3] = pattern->stops[i].color.blue; + stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha; + } + + vgSetParameterfv (context->paint, + VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops); + + if (stops != stack_stops) + free (stops); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_set_source_matrix (const cairo_pattern_t *pat) +{ + cairo_matrix_t mat; + cairo_status_t status; + VGfloat vmat[9]; + + mat = pat->matrix; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _vg_matrix_from_cairo (vmat, &mat); + + vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + + CHECK_VG_ERRORS(); +} + +static cairo_status_t +_vg_setup_linear_source (cairo_vg_context_t *context, + const cairo_linear_pattern_t *lpat) +{ + VGfloat linear[4]; + + linear[0] = _cairo_fixed_to_double (lpat->p1.x); + linear[1] = _cairo_fixed_to_double (lpat->p1.y); + linear[2] = _cairo_fixed_to_double (lpat->p2.x); + linear[3] = _cairo_fixed_to_double (lpat->p2.y); + + vgSetParameteri (context->paint, + VG_PAINT_COLOR_RAMP_SPREAD_MODE, + VG_COLOR_RAMP_SPREAD_PAD); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, + VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_LINEAR_GRADIENT, 4, linear); + + _vg_set_source_matrix (&lpat->base.base); + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &lpat->base); + +} + +static cairo_status_t +_vg_setup_radial_source (cairo_vg_context_t *context, + const cairo_radial_pattern_t *rpat) +{ + VGfloat radial[5]; + + radial[0] = _cairo_fixed_to_double (rpat->c1.x); + radial[1] = _cairo_fixed_to_double (rpat->c1.y); + radial[2] = _cairo_fixed_to_double (rpat->c2.x); + radial[3] = _cairo_fixed_to_double (rpat->c2.y); + radial[4] = _cairo_fixed_to_double (rpat->r2); + + vgSetParameteri (context->paint, + VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_RADIAL_GRADIENT, 5, radial); + + _vg_set_source_matrix (&rpat->base.base); + + /* FIXME: copy/adapt fixes from SVG backend to add inner radius */ + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &rpat->base); +} + +static cairo_status_t +_vg_setup_solid_source (cairo_vg_context_t *context, + const cairo_solid_pattern_t *spat) +{ + VGfloat color[] = { + spat->color.red, + spat->color.green, + spat->color.blue, + spat->color.alpha * context->alpha + }; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_vg_surface_t * +_vg_clone_recording_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + VGImage vg_image; + VGImageFormat format; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + format = _vg_format_for_content (surface->content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + extents.width, extents.height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + extents.width, extents.height); + cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y); + + status = _cairo_recording_surface_replay (surface, &clone->base); + if (unlikely (status)) { + cairo_surface_destroy (&clone->base); + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + } + + return clone; +} + +static cairo_vg_surface_t * +_vg_clone_image_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + VGImage vg_image; + VGImageFormat format; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + if (surface->backend->acquire_source_image == NULL) + return NULL; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + status = _cairo_surface_acquire_source_image (surface, + &image, &image_extra); + if (unlikely (status)) + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + + format = _vg_format_from_pixman (image->pixman_format); + if (format == 0) + format = _vg_format_for_content (image->base.content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + image->width, image->height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + image->width, image->height); + if (unlikely (clone->base.status)) + return clone; + + vgImageSubData (clone->image, + image->data, image->stride, + format, 0, 0, image->width, image->height); + + _cairo_surface_release_source_image (surface, image, image_extra); + + return clone; +} + +static void +_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface; + + if (surface->snapshot_cache_entry.hash) { + cairo_vg_context_t *context; + + context = _vg_context_lock (surface->context); + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + _vg_context_unlock (context); + + surface->snapshot_cache_entry.hash = 0; + } +} + +static cairo_status_t +_vg_setup_surface_source (cairo_vg_context_t *context, + const cairo_surface_pattern_t *spat) +{ + cairo_surface_t *snapshot; + cairo_vg_surface_t *clone; + cairo_status_t status; + + snapshot = _cairo_surface_has_snapshot (spat->surface, + &cairo_vg_surface_backend); + if (snapshot != NULL) { + clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot); + goto DONE; + } + + if (_cairo_surface_is_recording (spat->surface)) + clone = _vg_clone_recording_surface (context, spat->surface); + else + clone = _vg_clone_image_surface (context, spat->surface); + if (clone == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (clone->base.status)) + return clone->base.status; + + clone->snapshot_cache_entry.hash = clone->base.unique_id; + status = _cairo_cache_insert (&context->snapshot_cache, + &clone->snapshot_cache_entry); + if (unlikely (status)) { + clone->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&clone->base); + return status; + } + + cairo_surface_attach_snapshot (spat->surface, &clone->base, + _vg_surface_remove_from_cache); + +DONE: + cairo_surface_destroy (&context->source->base); + context->source = clone; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); + + switch (spat->base.extend) { + case CAIRO_EXTEND_PAD: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_PAD); + break; + + case CAIRO_EXTEND_NONE: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_FILL); + { + VGfloat color[] = {0,0,0,0}; + vgSetfv (VG_TILE_FILL_COLOR, 4, color); + } + break; + + case CAIRO_EXTEND_REPEAT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REPEAT); + break; + + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_REFLECT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REFLECT); + break; + } + vgPaintPattern (context->paint, context->source->image); + + _vg_set_source_matrix (&spat->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +setup_source (cairo_vg_context_t *context, + const cairo_pattern_t *source) +{ + switch (source->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _vg_setup_solid_source (context, + (cairo_solid_pattern_t *) source); + case CAIRO_PATTERN_TYPE_LINEAR: + return _vg_setup_linear_source (context, + (cairo_linear_pattern_t *) source); + case CAIRO_PATTERN_TYPE_RADIAL: + return _vg_setup_radial_source (context, + (cairo_radial_pattern_t *) source); + case CAIRO_PATTERN_TYPE_SURFACE: + return _vg_setup_surface_source (context, + (cairo_surface_pattern_t *) source); + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } +} + +static cairo_int_status_t +_vg_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + VGfloat state[9]; + VGfloat strokeTransform[9]; + vg_path_t vg_path; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, 0, 0, + VG_PATH_CAPABILITY_ALL); + + vgGetMatrix (state); + _vg_matrix_from_cairo (strokeTransform, ctm); + vgMultMatrix (strokeTransform); + + vg_path.ctm_inverse = ctm_inverse; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX DASH_PATTERN, DASH_PHASE */ + vgSetf (VG_STROKE_LINE_WIDTH, style->line_width); + vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit); + vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join)); + vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap)); + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + + vgSetPaint (context->paint, VG_STROKE_PATH); + + vgDrawPath (vg_path.path, VG_STROKE_PATH); + + vgDestroyPath (vg_path.path); + + vgLoadMatrix (state); + + CHECK_VG_ERRORS(); + _vg_context_unlock (context); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + vg_path_t vg_path; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, + 0, 0, + VG_PATH_CAPABILITY_ALL); + vg_path.ctm_inverse = NULL; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX tolerance */ + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule)); + vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias)); + + vgSetPaint (context->paint, VG_FILL_PATH); + + vgDrawPath (vg_path.path, VG_FILL_PATH); + + vgDestroyPath (vg_path.path); + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetPaint (context->paint, VG_FILL_PATH); + + { /* creating a rectangular path that should cover the extent */ + VGubyte segs[] = { + VG_MOVE_TO_ABS, VG_LINE_TO_ABS, + VG_LINE_TO_ABS, VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + VGfloat data[] = { + 0, 0, + surface->width, 0, + surface->width, surface->height, + 0, surface->height + }; + VGPath fullext; + + fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1,0,0,0, VG_PATH_CAPABILITY_ALL); + vgAppendPathData (fullext, sizeof(segs), segs, data); + + vgDrawPath (fullext, VG_FILL_PATH); + + vgDestroyPath (fullext); + } + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Handle paint-with-alpha to do fades cheaply */ + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + double alpha = context->alpha; + + context->alpha = solid->color.alpha; + status = _vg_surface_paint (abstract_surface, op, source, clip); + context->alpha = alpha; + + _vg_context_unlock (context); + + return status; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_vg_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); +} + +static cairo_int_status_t +_vg_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_path_fixed_t path; + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_init (&path); + + /* XXX Glyph cache! OpenVG font support in 1.1? */ + + status = _cairo_scaled_font_glyph_path (scaled_font, + glyphs, num_glyphs, + &path); + if (unlikely (status)) + goto BAIL; + + status = _vg_surface_fill (abstract_surface, + op, source, &path, + CAIRO_FILL_RULE_WINDING, + CAIRO_GSTATE_TOLERANCE_DEFAULT, + CAIRO_ANTIALIAS_SUBPIXEL, + clip); +BAIL: + _cairo_path_fixed_fini (&path); + return status; +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = alpha * color + 0x80; + return (temp + (temp >> 8)) >> 8; +} + +static void +premultiply_argb (uint8_t *data, + int width, + int height, + int stride) +{ + int i; + + while (height --) { + uint32_t *row = (uint32_t *) data; + + for (i = 0; i < width; i++) { + uint32_t p = row[i]; + uint8_t alpha; + + alpha = p >> 24; + if (alpha == 0) { + row[i] = 0; + } else if (alpha != 0xff) { + uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff); + uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff); + uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff); + row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); + } + } + + data += stride; + } +} + +static cairo_int_status_t +_vg_get_image (cairo_vg_surface_t *surface, + int x, int y, + int width, int height, + cairo_image_surface_t **image_out) +{ + cairo_image_surface_t *image; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + cairo_bool_t needs_premultiply; + + pixman_format = _vg_format_to_pixman (surface->format, + &needs_premultiply); + if (pixman_format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pixman_image = pixman_image_create_bits (pixman_format, + width, height, + NULL, 0); + if (unlikely (pixman_image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + vgFinish (); + CHECK_VG_ERRORS(); + + vgGetImageSubData (surface->image, + pixman_image_get_data (pixman_image), + pixman_image_get_stride (pixman_image), + surface->format, + x, y, width, height); + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + if (unlikely (image->base.status)) { + pixman_image_unref (pixman_image); + return image->base.status; + } + + if (needs_premultiply) + premultiply_argb (image->data, width, height, image->stride); + + *image_out = image; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + + CHECK_VG_ERRORS(); + *image_extra = NULL; + return _vg_get_image (surface, + 0, 0, surface->width, surface->height, + image_out); +} + +static void +_vg_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_vg_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + + *image_rect_out = *interest_rect; + *image_extra = NULL; + return _vg_get_image (surface, + interest_rect->x, interest_rect->y, + interest_rect->width, interest_rect->height, + image_out); +} + +static void +unpremultiply_argb (uint8_t *data, + int width, + int height, + int stride) +{ + int i; + + while (height--) { + uint32_t *row = (uint32_t *) data; + + for (i = 0; i < width; i ++) { + uint32_t p = row[i]; + uint8_t alpha; + + alpha = p >> 24; + if (alpha == 0) { + row[i] = 0; + } else if (alpha != 0xff) { + uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha; + uint8_t g = (((p >> 8) & 0xff) * 255 + alpha / 2) / alpha; + uint8_t b = (((p >> 0) & 0xff) * 255 + alpha / 2) / alpha; + row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); + } + } + + data += stride; + } +} + +static void +_vg_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_bool_t needs_unpremultiply; + + _vg_format_to_pixman (surface->format, &needs_unpremultiply); + if (needs_unpremultiply) { + unpremultiply_argb (image->data, + image->width, image->height, + image->stride); + } + + vgImageSubData (surface->image, + image->data, image->stride, + surface->format, + image_rect->x, image_rect->y, + image_rect->width, image_rect->height); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_vg_surface_finish (void *abstract_surface) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + + if (surface->snapshot_cache_entry.hash) { + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + + surface->snapshot_cache_entry.hash = 0; + } + + _cairo_surface_clipper_reset (&surface->clipper); + + if (surface->own_image) + vgDestroyImage (surface->image); + + _vg_context_destroy_target (context, surface); + + _vg_context_unlock (context); + _vg_context_destroy (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_vg_surface_backend = { + CAIRO_SURFACE_TYPE_VG, + _vg_surface_create_similar, + _vg_surface_finish, + + _vg_surface_acquire_source_image, + _vg_surface_release_source_image, + _vg_surface_acquire_dest_image, + _vg_surface_release_dest_image, + + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _vg_surface_get_extents, + NULL, /* old_show_glyphs */ + _vg_surface_get_font_options, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _vg_surface_paint, + _vg_surface_mask, + _vg_surface_stroke, + _vg_surface_fill, + _vg_surface_show_glyphs, + + NULL, /* snapshot */ + NULL, /* is_similar */ +}; + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_vg_surface_t *surface; + + surface = malloc (sizeof (cairo_vg_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->context = _vg_context_reference (context); + + surface->image = image; + surface->format = format; + + _cairo_surface_init (&surface->base, + &cairo_vg_surface_backend, + NULL, /* device */ + _vg_format_to_content (format)); + + surface->width = width; + surface->height = height; + + _cairo_surface_clipper_init (&surface->clipper, + _vg_surface_clipper_intersect_clip_path); + + surface->snapshot_cache_entry.hash = 0; + + surface->target_id = 0; + + CHECK_VG_ERRORS(); + return &surface->base; +} + +cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_bool_t premult; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (_vg_format_to_pixman (format, &premult) == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + return _vg_surface_create_internal (context, image, format, width, height); +} + +cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, + int width, + int height) +{ + VGImage image; + VGImageFormat format; + cairo_surface_t *surface; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + + format = _vg_format_for_content (content); + image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _vg_surface_create_internal (context, + image, format, width, height); + if (unlikely (surface->status)) + return surface; + + ((cairo_vg_surface_t *) surface)->own_image = TRUE; + return surface; +} +slim_hidden_def (cairo_vg_surface_create); + +VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return VG_INVALID_HANDLE; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->image; +} + +int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->width; +} + +int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->height; +} + +VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->format; +} + +/* GL specific context support :-( + * + * OpenVG like cairo defers creation of surface (and the necessary + * paraphernalia to the application. + */ + +static const cairo_vg_context_t _vg_context_nil = { + CAIRO_STATUS_NO_MEMORY, + CAIRO_REFERENCE_COUNT_INVALID +}; + +static const cairo_vg_context_t _vg_context_nil_invalid_visual = { + CAIRO_STATUS_INVALID_VISUAL, + CAIRO_REFERENCE_COUNT_INVALID +}; + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +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/libs/cairo/src/cairo-vg.h b/libs/cairo/src/cairo-vg.h new file mode 100644 index 000000000..7f1097d04 --- /dev/null +++ b/libs/cairo/src/cairo-vg.h @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_VG_H +#define CAIRO_VG_H + +#include "cairo.h" + +#if CAIRO_HAS_VG_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_vg_context cairo_vg_context_t; + +#if CAIRO_HAS_GLX_FUNCTIONS +typedef struct __GLXcontextRec *GLXContext; +typedef struct _XDisplay Display; + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_glx (Display *dpy, + GLXContext ctx); +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +#include + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_egl (EGLDisplay egl_display, + EGLContext egl_context); +#endif + +cairo_public cairo_status_t +cairo_vg_context_status (cairo_vg_context_t *context); + +cairo_public void +cairo_vg_context_destroy (cairo_vg_context_t *context); + +cairo_public cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, int width, int height); + +cairo_public cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +cairo_public VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface); + +cairo_public VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_VG_SURFACE*/ +# error Cairo was not compiled with support for the OpenVG backend +#endif /* CAIRO_HAS_VG_SURFACE*/ + +#endif /* CAIRO_VG_H */ diff --git a/libs/cairo/src/cairo-wideint-private.h b/libs/cairo/src/cairo-wideint-private.h new file mode 100644 index 000000000..a1ae4dce8 --- /dev/null +++ b/libs/cairo/src/cairo-wideint-private.h @@ -0,0 +1,288 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_WIDEINT_H +#define CAIRO_WIDEINT_H + +#include "cairo-wideint-type-private.h" + +#include "cairo-compiler-private.h" + +/* + * 64-bit datatypes. Two separate implementations, one using + * built-in 64-bit signed/unsigned types another implemented + * as a pair of 32-bit ints + */ + +#define I cairo_private cairo_const + +#if !HAVE_UINT64_T + +cairo_uquorem64_t I +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); + +cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i); +#define _cairo_uint64_to_uint32(a) ((a).lo) +cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint32x32_64_mul (uint32_t a, uint32_t b); +cairo_uint64_t I _cairo_uint64_lsl (cairo_uint64_t a, int shift); +cairo_uint64_t I _cairo_uint64_rsl (cairo_uint64_t a, int shift); +cairo_uint64_t I _cairo_uint64_rsa (cairo_uint64_t a, int shift); +int I _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b); +int I _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b); +int I _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint64_negate (cairo_uint64_t a); +#define _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0) +#define _cairo_uint64_negative(a) (((int32_t) ((a).hi)) < 0) +cairo_uint64_t I _cairo_uint64_not (cairo_uint64_t a); + +#define _cairo_uint64_to_int64(i) (i) +#define _cairo_int64_to_uint64(i) (i) + +cairo_int64_t I _cairo_int32_to_int64(int32_t i); +#define _cairo_int64_to_int32(a) ((int32_t) _cairo_uint64_to_uint32(a)) +#define _cairo_int64_add(a,b) _cairo_uint64_add (a,b) +#define _cairo_int64_sub(a,b) _cairo_uint64_sub (a,b) +#define _cairo_int64_mul(a,b) _cairo_uint64_mul (a,b) +cairo_int64_t I _cairo_int32x32_64_mul (int32_t a, int32_t b); +int I _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b); +int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); +#define _cairo_int64_is_zero(a) _cairo_uint64_is_zero (a) +#define _cairo_int64_eq(a,b) _cairo_uint64_eq (a,b) +#define _cairo_int64_lsl(a,b) _cairo_uint64_lsl (a,b) +#define _cairo_int64_rsl(a,b) _cairo_uint64_rsl (a,b) +#define _cairo_int64_rsa(a,b) _cairo_uint64_rsa (a,b) +#define _cairo_int64_negate(a) _cairo_uint64_negate(a) +#define _cairo_int64_negative(a) (((int32_t) ((a).hi)) < 0) +#define _cairo_int64_not(a) _cairo_uint64_not(a) + +#else + +static inline cairo_uquorem64_t +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) +{ + cairo_uquorem64_t qr; + + qr.quo = num / den; + qr.rem = num % den; + return qr; +} + +#define _cairo_uint32_to_uint64(i) ((uint64_t) (i)) +#define _cairo_uint64_to_uint32(i) ((uint32_t) (i)) +#define _cairo_uint64_add(a,b) ((a) + (b)) +#define _cairo_uint64_sub(a,b) ((a) - (b)) +#define _cairo_uint64_mul(a,b) ((a) * (b)) +#define _cairo_uint32x32_64_mul(a,b) ((uint64_t) (a) * (b)) +#define _cairo_uint64_lsl(a,b) ((a) << (b)) +#define _cairo_uint64_rsl(a,b) ((uint64_t) (a) >> (b)) +#define _cairo_uint64_rsa(a,b) ((uint64_t) ((int64_t) (a) >> (b))) +#define _cairo_uint64_lt(a,b) ((a) < (b)) +#define _cairo_uint64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_uint64_is_zero(a) ((a) == 0) +#define _cairo_uint64_eq(a,b) ((a) == (b)) +#define _cairo_uint64_negate(a) ((uint64_t) -((int64_t) (a))) +#define _cairo_uint64_negative(a) ((int64_t) (a) < 0) +#define _cairo_uint64_not(a) (~(a)) + +#define _cairo_uint64_to_int64(i) ((int64_t) (i)) +#define _cairo_int64_to_uint64(i) ((uint64_t) (i)) + +#define _cairo_int32_to_int64(i) ((int64_t) (i)) +#define _cairo_int64_to_int32(i) ((int32_t) (i)) +#define _cairo_int64_add(a,b) ((a) + (b)) +#define _cairo_int64_sub(a,b) ((a) - (b)) +#define _cairo_int64_mul(a,b) ((a) * (b)) +#define _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b)) +#define _cairo_int64_lt(a,b) ((a) < (b)) +#define _cairo_int64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_int64_is_zero(a) ((a) == 0) +#define _cairo_int64_eq(a,b) ((a) == (b)) +#define _cairo_int64_lsl(a,b) ((a) << (b)) +#define _cairo_int64_rsl(a,b) ((int64_t) ((uint64_t) (a) >> (b))) +#define _cairo_int64_rsa(a,b) ((int64_t) (a) >> (b)) +#define _cairo_int64_negate(a) (-(a)) +#define _cairo_int64_negative(a) ((a) < 0) +#define _cairo_int64_not(a) (~(a)) + +#endif + +/* + * 64-bit comparisions derived from lt or eq + */ +#define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b)) +#define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b)) +#define _cairo_uint64_ge(a,b) (!_cairo_uint64_lt(a,b)) +#define _cairo_uint64_gt(a,b) _cairo_uint64_lt(b,a) + +#define _cairo_int64_le(a,b) (!_cairo_int64_gt(a,b)) +#define _cairo_int64_ne(a,b) (!_cairo_int64_eq(a,b)) +#define _cairo_int64_ge(a,b) (!_cairo_int64_lt(a,b)) +#define _cairo_int64_gt(a,b) _cairo_int64_lt(b,a) + +/* + * As the C implementation always computes both, create + * a function which returns both for the 'native' type as well + */ + +static inline cairo_quorem64_t +_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) +{ + int num_neg = _cairo_int64_negative (num); + int den_neg = _cairo_int64_negative (den); + cairo_uquorem64_t uqr; + cairo_quorem64_t qr; + + if (num_neg) + num = _cairo_int64_negate (num); + if (den_neg) + den = _cairo_int64_negate (den); + uqr = _cairo_uint64_divrem (num, den); + if (num_neg) + qr.rem = _cairo_int64_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); + else + qr.quo = (cairo_int64_t) uqr.quo; + return qr; +} + +static inline int32_t +_cairo_int64_32_div (cairo_int64_t num, int32_t den) +{ +#if !HAVE_UINT64_T + return _cairo_int64_to_int32 + (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo); +#else + return num / den; +#endif +} + +/* + * 128-bit datatypes. Again, provide two implementations in + * case the machine has a native 128-bit datatype. GCC supports int128_t + * on ia64 + */ + +#if !HAVE_UINT128_T + +cairo_uint128_t I _cairo_uint32_to_uint128 (uint32_t i); +cairo_uint128_t I _cairo_uint64_to_uint128 (cairo_uint64_t i); +#define _cairo_uint128_to_uint64(a) ((a).lo) +#define _cairo_uint128_to_uint32(a) _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a)) +cairo_uint128_t I _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint128_t I _cairo_uint128_lsl (cairo_uint128_t a, int shift); +cairo_uint128_t I _cairo_uint128_rsl (cairo_uint128_t a, int shift); +cairo_uint128_t I _cairo_uint128_rsa (cairo_uint128_t a, int shift); +int I _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b); +int I _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b); +int I _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b); +#define _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo)) +cairo_uint128_t I _cairo_uint128_negate (cairo_uint128_t a); +#define _cairo_uint128_negative(a) (_cairo_uint64_negative(a.hi)) +cairo_uint128_t I _cairo_uint128_not (cairo_uint128_t a); + +#define _cairo_uint128_to_int128(i) (i) +#define _cairo_int128_to_uint128(i) (i) + +cairo_int128_t I _cairo_int32_to_int128 (int32_t i); +cairo_int128_t I _cairo_int64_to_int128 (cairo_int64_t i); +#define _cairo_int128_to_int64(a) ((cairo_int64_t) (a).lo) +#define _cairo_int128_to_int32(a) _cairo_int64_to_int32(_cairo_int128_to_int64(a)) +#define _cairo_int128_add(a,b) _cairo_uint128_add(a,b) +#define _cairo_int128_sub(a,b) _cairo_uint128_sub(a,b) +#define _cairo_int128_mul(a,b) _cairo_uint128_mul(a,b) +cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b); +#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) +#define _cairo_int128_lsl(a,b) _cairo_uint128_lsl(a,b) +#define _cairo_int128_rsl(a,b) _cairo_uint128_rsl(a,b) +#define _cairo_int128_rsa(a,b) _cairo_uint128_rsa(a,b) +int I _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b); +int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b); +#define _cairo_int128_is_zero(a) _cairo_uint128_is_zero (a) +#define _cairo_int128_eq(a,b) _cairo_uint128_eq (a,b) +#define _cairo_int128_negate(a) _cairo_uint128_negate(a) +#define _cairo_int128_negative(a) (_cairo_uint128_negative(a)) +#define _cairo_int128_not(a) _cairo_uint128_not(a) + +#else /* !HAVE_UINT128_T */ + +#define _cairo_uint32_to_uint128(i) ((uint128_t) (i)) +#define _cairo_uint64_to_uint128(i) ((uint128_t) (i)) +#define _cairo_uint128_to_uint64(i) ((uint64_t) (i)) +#define _cairo_uint128_to_uint32(i) ((uint32_t) (i)) +#define _cairo_uint128_add(a,b) ((a) + (b)) +#define _cairo_uint128_sub(a,b) ((a) - (b)) +#define _cairo_uint128_mul(a,b) ((a) * (b)) +#define _cairo_uint64x64_128_mul(a,b) ((uint128_t) (a) * (b)) +#define _cairo_uint128_lsl(a,b) ((a) << (b)) +#define _cairo_uint128_rsl(a,b) ((uint128_t) (a) >> (b)) +#define _cairo_uint128_rsa(a,b) ((uint128_t) ((int128_t) (a) >> (b))) +#define _cairo_uint128_lt(a,b) ((a) < (b)) +#define _cairo_uint128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_uint128_is_zero(a) ((a) == 0) +#define _cairo_uint128_eq(a,b) ((a) == (b)) +#define _cairo_uint128_negate(a) ((uint128_t) -((int128_t) (a))) +#define _cairo_uint128_negative(a) ((int128_t) (a) < 0) +#define _cairo_uint128_not(a) (~(a)) + +#define _cairo_uint128_to_int128(i) ((int128_t) (i)) +#define _cairo_int128_to_uint128(i) ((uint128_t) (i)) + +#define _cairo_int32_to_int128(i) ((int128_t) (i)) +#define _cairo_int64_to_int128(i) ((int128_t) (i)) +#define _cairo_int128_to_int64(i) ((int64_t) (i)) +#define _cairo_int128_to_int32(i) ((int32_t) (i)) +#define _cairo_int128_add(a,b) ((a) + (b)) +#define _cairo_int128_sub(a,b) ((a) - (b)) +#define _cairo_int128_mul(a,b) ((a) * (b)) +#define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b)) +#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) +#define _cairo_int128_lt(a,b) ((a) < (b)) +#define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_int128_is_zero(a) ((a) == 0) +#define _cairo_int128_eq(a,b) ((a) == (b)) +#define _cairo_int128_lsl(a,b) ((a) << (b)) +#define _cairo_int128_rsl(a,b) ((int128_t) ((uint128_t) (a) >> (b))) +#define _cairo_int128_rsa(a,b) ((int128_t) (a) >> (b)) +#define _cairo_int128_negate(a) (-(a)) +#define _cairo_int128_negative(a) ((a) < 0) +#define _cairo_int128_not(a) (~(a)) + +#endif /* HAVE_UINT128_T */ + +cairo_uquorem128_t I +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den); + +cairo_quorem128_t I +_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den); + +cairo_uquorem64_t I +_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, + cairo_uint64_t den); + +cairo_quorem64_t I +_cairo_int_96by64_32x64_divrem (cairo_int128_t num, + cairo_int64_t den); + +#define _cairo_uint128_le(a,b) (!_cairo_uint128_gt(a,b)) +#define _cairo_uint128_ne(a,b) (!_cairo_uint128_eq(a,b)) +#define _cairo_uint128_ge(a,b) (!_cairo_uint128_lt(a,b)) +#define _cairo_uint128_gt(a,b) _cairo_uint128_lt(b,a) + +#define _cairo_int128_le(a,b) (!_cairo_int128_gt(a,b)) +#define _cairo_int128_ne(a,b) (!_cairo_int128_eq(a,b)) +#define _cairo_int128_ge(a,b) (!_cairo_int128_lt(a,b)) +#define _cairo_int128_gt(a,b) _cairo_int128_lt(b,a) + +#undef I + +#endif /* CAIRO_WIDEINT_H */ diff --git a/libs/cairo/src/cairo-wideint-type-private.h b/libs/cairo/src/cairo-wideint-type-private.h new file mode 100644 index 000000000..d7f0319a2 --- /dev/null +++ b/libs/cairo/src/cairo-wideint-type-private.h @@ -0,0 +1,130 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_WIDEINT_TYPE_H +#define CAIRO_WIDEINT_TYPE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STDINT_H +# include +#elif HAVE_INTTYPES_H +# include +#elif HAVE_SYS_INT_TYPES_H +# include +#elif defined(_MSC_VER) + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 + typedef unsigned __int64 uintptr_t; +#else + typedef unsigned int uintptr_t; +#endif +#endif +# ifndef HAVE_UINT64_T +# define HAVE_UINT64_T 1 +# endif +#else +#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.) +#endif + +#ifndef INT16_MIN +# define INT16_MIN (-32767-1) +#endif +#ifndef INT16_MAX +# define INT16_MAX (32767) +#endif +#ifndef UINT16_MAX +# define UINT16_MAX (65535) +#endif +#ifndef INT32_MIN +# define INT32_MIN (-2147483647-1) +#endif +#ifndef INT32_MAX +# define INT32_MAX (2147483647) +#endif + +#if HAVE_BYTESWAP_H +# include +#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/libs/cairo/src/cairo-wideint.c b/libs/cairo/src/cairo-wideint.c new file mode 100644 index 000000000..90809c005 --- /dev/null +++ b/libs/cairo/src/cairo-wideint.c @@ -0,0 +1,788 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#if HAVE_UINT64_T + +#define uint64_lo32(i) ((i) & 0xffffffff) +#define uint64_hi32(i) ((i) >> 32) +#define uint64_lo(i) ((i) & 0xffffffff) +#define uint64_hi(i) ((i) >> 32) +#define uint64_shift32(i) ((i) << 32) +#define uint64_carry32 (((uint64_t) 1) << 32) + +#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) + +#else + +#define uint64_lo32(i) ((i).lo) +#define uint64_hi32(i) ((i).hi) + +static cairo_uint64_t +uint64_lo (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.lo; + s.hi = 0; + return s; +} + +static cairo_uint64_t +uint64_hi (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.hi; + s.hi = 0; + return s; +} + +static cairo_uint64_t +uint64_shift32 (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = 0; + s.hi = i.lo; + return s; +} + +static const cairo_uint64_t uint64_carry32 = { 0, 1 }; + +cairo_uint64_t +_cairo_uint32_to_uint64 (uint32_t i) +{ + cairo_uint64_t q; + + q.lo = i; + q.hi = 0; + return q; +} + +cairo_int64_t +_cairo_int32_to_int64 (int32_t i) +{ + cairo_uint64_t q; + + q.lo = i; + q.hi = i < 0 ? -1 : 0; + return q; +} + +static cairo_uint64_t +_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l) +{ + cairo_uint64_t q; + + q.lo = l; + q.hi = h; + return q; +} + +cairo_uint64_t +_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s.hi = a.hi + b.hi; + s.lo = a.lo + b.lo; + if (s.lo < a.lo) + s.hi++; + return s; +} + +cairo_uint64_t +_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s.hi = a.hi - b.hi; + s.lo = a.lo - b.lo; + if (s.lo > a.lo) + s.hi--; + return s; +} + +#define uint32_lo(i) ((i) & 0xffff) +#define uint32_hi(i) ((i) >> 16) +#define uint32_carry16 ((1) << 16) + +cairo_uint64_t +_cairo_uint32x32_64_mul (uint32_t a, uint32_t b) +{ + cairo_uint64_t s; + + uint16_t ah, al, bh, bl; + uint32_t r0, r1, r2, r3; + + al = uint32_lo (a); + ah = uint32_hi (a); + bl = uint32_lo (b); + bh = uint32_hi (b); + + r0 = (uint32_t) al * bl; + r1 = (uint32_t) al * bh; + r2 = (uint32_t) ah * bl; + r3 = (uint32_t) ah * bh; + + r1 += uint32_hi(r0); /* no carry possible */ + r1 += r2; /* but this can carry */ + if (r1 < r2) /* check */ + r3 += uint32_carry16; + + s.hi = r3 + uint32_hi(r1); + s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0); + return s; +} + +cairo_int64_t +_cairo_int32x32_64_mul (int32_t a, int32_t b) +{ + cairo_int64_t s; + s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b); + if (a < 0) + s.hi -= b; + if (b < 0) + s.hi -= a; + return s; +} + +cairo_uint64_t +_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s = _cairo_uint32x32_64_mul (a.lo, b.lo); + s.hi += a.lo * b.hi + a.hi * b.lo; + return s; +} + +cairo_uint64_t +_cairo_uint64_lsl (cairo_uint64_t a, int shift) +{ + if (shift >= 32) + { + a.hi = a.lo; + a.lo = 0; + shift -= 32; + } + if (shift) + { + a.hi = a.hi << shift | a.lo >> (32 - shift); + a.lo = a.lo << shift; + } + return a; +} + +cairo_uint64_t +_cairo_uint64_rsl (cairo_uint64_t a, int shift) +{ + if (shift >= 32) + { + a.lo = a.hi; + a.hi = 0; + shift -= 32; + } + if (shift) + { + a.lo = a.lo >> shift | a.hi << (32 - shift); + a.hi = a.hi >> shift; + } + return a; +} + +#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n))) + +cairo_int64_t +_cairo_uint64_rsa (cairo_int64_t a, int shift) +{ + if (shift >= 32) + { + a.lo = a.hi; + a.hi = _cairo_uint32_rsa (a.hi, 31); + shift -= 32; + } + if (shift) + { + a.lo = a.lo >> shift | a.hi << (32 - shift); + a.hi = _cairo_uint32_rsa (a.hi, shift); + } + return a; +} + +int +_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b) +{ + return (a.hi < b.hi || + (a.hi == b.hi && a.lo < b.lo)); +} + +int +_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b) +{ + return a.hi == b.hi && a.lo == b.lo; +} + +int +_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b) +{ + if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) + return 1; + if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) + return 0; + return _cairo_uint64_lt (a, b); +} + +int +_cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b) +{ + if (a.hi < b.hi) + return -1; + else if (a.hi > b.hi) + return 1; + else if (a.lo < b.lo) + return -1; + else if (a.lo > b.lo) + return 1; + else + return 0; +} + +int +_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b) +{ + if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) + return -1; + if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) + return 1; + + return _cairo_uint64_cmp (a, b); +} + +cairo_uint64_t +_cairo_uint64_not (cairo_uint64_t a) +{ + a.lo = ~a.lo; + a.hi = ~a.hi; + return a; +} + +cairo_uint64_t +_cairo_uint64_negate (cairo_uint64_t a) +{ + a.lo = ~a.lo; + a.hi = ~a.hi; + if (++a.lo == 0) + ++a.hi; + return a; +} + +/* + * Simple bit-at-a-time divide. + */ +cairo_uquorem64_t +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) +{ + cairo_uquorem64_t qr; + cairo_uint64_t bit; + cairo_uint64_t quo; + + bit = _cairo_uint32_to_uint64 (1); + + /* normalize to make den >= num, but not overflow */ + while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0) + { + bit = _cairo_uint64_lsl (bit, 1); + den = _cairo_uint64_lsl (den, 1); + } + quo = _cairo_uint32_to_uint64 (0); + + /* generate quotient, one bit at a time */ + while (bit.hi | bit.lo) + { + if (_cairo_uint64_le (den, num)) + { + num = _cairo_uint64_sub (num, den); + quo = _cairo_uint64_add (quo, bit); + } + bit = _cairo_uint64_rsl (bit, 1); + den = _cairo_uint64_rsl (den, 1); + } + qr.quo = quo; + qr.rem = num; + return qr; +} + +#endif /* !HAVE_UINT64_T */ + +#if HAVE_UINT128_T +cairo_uquorem128_t +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) +{ + cairo_uquorem128_t qr; + + qr.quo = num / den; + qr.rem = num % den; + return qr; +} + +#else + +cairo_uint128_t +_cairo_uint32_to_uint128 (uint32_t i) +{ + cairo_uint128_t q; + + q.lo = _cairo_uint32_to_uint64 (i); + q.hi = _cairo_uint32_to_uint64 (0); + return q; +} + +cairo_int128_t +_cairo_int32_to_int128 (int32_t i) +{ + cairo_int128_t q; + + q.lo = _cairo_int32_to_int64 (i); + q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0); + return q; +} + +cairo_uint128_t +_cairo_uint64_to_uint128 (cairo_uint64_t i) +{ + cairo_uint128_t q; + + q.lo = i; + q.hi = _cairo_uint32_to_uint64 (0); + return q; +} + +cairo_int128_t +_cairo_int64_to_int128 (cairo_int64_t i) +{ + cairo_int128_t q; + + q.lo = i; + q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0); + return q; +} + +cairo_uint128_t +_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s.hi = _cairo_uint64_add (a.hi, b.hi); + s.lo = _cairo_uint64_add (a.lo, b.lo); + if (_cairo_uint64_lt (s.lo, a.lo)) + s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1)); + return s; +} + +cairo_uint128_t +_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s.hi = _cairo_uint64_sub (a.hi, b.hi); + s.lo = _cairo_uint64_sub (a.lo, b.lo); + if (_cairo_uint64_gt (s.lo, a.lo)) + s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1)); + return s; +} + +cairo_uint128_t +_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint128_t s; + uint32_t ah, al, bh, bl; + cairo_uint64_t r0, r1, r2, r3; + + al = uint64_lo32 (a); + ah = uint64_hi32 (a); + bl = uint64_lo32 (b); + bh = uint64_hi32 (b); + + r0 = _cairo_uint32x32_64_mul (al, bl); + r1 = _cairo_uint32x32_64_mul (al, bh); + r2 = _cairo_uint32x32_64_mul (ah, bl); + r3 = _cairo_uint32x32_64_mul (ah, bh); + + r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */ + r1 = _cairo_uint64_add (r1, r2); /* but this can carry */ + if (_cairo_uint64_lt (r1, r2)) /* check */ + r3 = _cairo_uint64_add (r3, uint64_carry32); + + s.hi = _cairo_uint64_add (r3, uint64_hi(r1)); + s.lo = _cairo_uint64_add (uint64_shift32 (r1), + uint64_lo (r0)); + return s; +} + +cairo_int128_t +_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b) +{ + cairo_int128_t s; + s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a), + _cairo_int64_to_uint64(b)); + if (_cairo_int64_negative (a)) + s.hi = _cairo_uint64_sub (s.hi, + _cairo_int64_to_uint64 (b)); + if (_cairo_int64_negative (b)) + s.hi = _cairo_uint64_sub (s.hi, + _cairo_int64_to_uint64 (a)); + return s; +} + +cairo_uint128_t +_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s = _cairo_uint64x64_128_mul (a.lo, b.lo); + s.hi = _cairo_uint64_add (s.hi, + _cairo_uint64_mul (a.lo, b.hi)); + s.hi = _cairo_uint64_add (s.hi, + _cairo_uint64_mul (a.hi, b.lo)); + return s; +} + +cairo_uint128_t +_cairo_uint128_lsl (cairo_uint128_t a, int shift) +{ + if (shift >= 64) + { + a.hi = a.lo; + a.lo = _cairo_uint32_to_uint64 (0); + shift -= 64; + } + if (shift) + { + a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift), + _cairo_uint64_rsl (a.lo, (64 - shift))); + a.lo = _cairo_uint64_lsl (a.lo, shift); + } + return a; +} + +cairo_uint128_t +_cairo_uint128_rsl (cairo_uint128_t a, int shift) +{ + if (shift >= 64) + { + a.lo = a.hi; + a.hi = _cairo_uint32_to_uint64 (0); + shift -= 64; + } + if (shift) + { + a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), + _cairo_uint64_lsl (a.hi, (64 - shift))); + a.hi = _cairo_uint64_rsl (a.hi, shift); + } + return a; +} + +cairo_uint128_t +_cairo_uint128_rsa (cairo_int128_t a, int shift) +{ + if (shift >= 64) + { + a.lo = a.hi; + a.hi = _cairo_uint64_rsa (a.hi, 64-1); + shift -= 64; + } + if (shift) + { + a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), + _cairo_uint64_lsl (a.hi, (64 - shift))); + a.hi = _cairo_uint64_rsa (a.hi, shift); + } + return a; +} + +int +_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b) +{ + return (_cairo_uint64_lt (a.hi, b.hi) || + (_cairo_uint64_eq (a.hi, b.hi) && + _cairo_uint64_lt (a.lo, b.lo))); +} + +int +_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b) +{ + if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) + return 1; + if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) + return 0; + return _cairo_uint128_lt (a, b); +} + +int +_cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b) +{ + int cmp; + + cmp = _cairo_uint64_cmp (a.hi, b.hi); + if (cmp) + return cmp; + return _cairo_uint64_cmp (a.lo, b.lo); +} + +int +_cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b) +{ + if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) + return -1; + if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) + return 1; + + return _cairo_uint128_cmp (a, b); +} + +int +_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b) +{ + return (_cairo_uint64_eq (a.hi, b.hi) && + _cairo_uint64_eq (a.lo, b.lo)); +} + +#if HAVE_UINT64_T +#define _cairo_msbset64(q) (q & ((uint64_t) 1 << 63)) +#else +#define _cairo_msbset64(q) (q.hi & ((uint32_t) 1 << 31)) +#endif + +cairo_uquorem128_t +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) +{ + cairo_uquorem128_t qr; + cairo_uint128_t bit; + cairo_uint128_t quo; + + bit = _cairo_uint32_to_uint128 (1); + + /* normalize to make den >= num, but not overflow */ + while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi)) + { + bit = _cairo_uint128_lsl (bit, 1); + den = _cairo_uint128_lsl (den, 1); + } + quo = _cairo_uint32_to_uint128 (0); + + /* generate quotient, one bit at a time */ + while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0))) + { + if (_cairo_uint128_le (den, num)) + { + num = _cairo_uint128_sub (num, den); + quo = _cairo_uint128_add (quo, bit); + } + bit = _cairo_uint128_rsl (bit, 1); + den = _cairo_uint128_rsl (den, 1); + } + qr.quo = quo; + qr.rem = num; + return qr; +} + +cairo_int128_t +_cairo_int128_negate (cairo_int128_t a) +{ + a.lo = _cairo_uint64_not (a.lo); + a.hi = _cairo_uint64_not (a.hi); + return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1)); +} + +cairo_int128_t +_cairo_int128_not (cairo_int128_t a) +{ + a.lo = _cairo_uint64_not (a.lo); + a.hi = _cairo_uint64_not (a.hi); + return a; +} + +#endif /* !HAVE_UINT128_T */ + +cairo_quorem128_t +_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) +{ + int num_neg = _cairo_int128_negative (num); + int den_neg = _cairo_int128_negative (den); + cairo_uquorem128_t uqr; + cairo_quorem128_t qr; + + if (num_neg) + num = _cairo_int128_negate (num); + if (den_neg) + den = _cairo_int128_negate (den); + uqr = _cairo_uint128_divrem (num, den); + if (num_neg) + qr.rem = _cairo_int128_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = _cairo_int128_negate (uqr.quo); + else + qr.quo = uqr.quo; + return qr; +} + +/** + * _cairo_uint_96by64_32x64_divrem: + * + * Compute a 32 bit quotient and 64 bit remainder of a 96 bit unsigned + * dividend and 64 bit divisor. If the quotient doesn't fit into 32 + * bits then the returned remainder is equal to the divisor, and the + * quotient is the largest representable 64 bit integer. It is an + * error to call this function with the high 32 bits of @num being + * non-zero. */ +cairo_uquorem64_t +_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, + cairo_uint64_t den) +{ + cairo_uquorem64_t result; + cairo_uint64_t B = _cairo_uint32s_to_uint64 (1, 0); + + /* These are the high 64 bits of the *96* bit numerator. We're + * going to represent the numerator as xB + y, where x is a 64, + * and y is a 32 bit number. */ + cairo_uint64_t x = _cairo_uint128_to_uint64 (_cairo_uint128_rsl(num, 32)); + + /* Initialise the result to indicate overflow. */ + result.quo = _cairo_uint32s_to_uint64 (-1U, -1U); + result.rem = den; + + /* Don't bother if the quotient is going to overflow. */ + if (_cairo_uint64_ge (x, den)) { + return /* overflow */ result; + } + + if (_cairo_uint64_lt (x, B)) { + /* When the final quotient is known to fit in 32 bits, then + * num < 2^64 if and only if den < 2^32. */ + return _cairo_uint64_divrem (_cairo_uint128_to_uint64 (num), den); + } + else { + /* Denominator is >= 2^32. the numerator is >= 2^64, and the + * division won't overflow: need two divrems. Write the + * numerator and denominator as + * + * num = xB + y x : 64 bits, y : 32 bits + * den = uB + v u, v : 32 bits + */ + uint32_t y = _cairo_uint128_to_uint32 (num); + uint32_t u = uint64_hi32 (den); + uint32_t v = _cairo_uint64_to_uint32 (den); + + /* Compute a lower bound approximate quotient of num/den + * from x/(u+1). Then we have + * + * x = q(u+1) + r ; q : 32 bits, r <= u : 32 bits. + * + * xB + y = q(u+1)B + (rB+y) + * = q(uB + B + v - v) + (rB+y) + * = q(uB + v) + qB - qv + (rB+y) + * = q(uB + v) + q(B-v) + (rB+y) + * + * The true quotient of num/den then is q plus the + * contribution of q(B-v) + (rB+y). The main contribution + * comes from the term q(B-v), with the term (rB+y) only + * contributing at most one part. + * + * The term q(B-v) must fit into 64 bits, since q fits into 32 + * bits on account of being a lower bound to the true + * quotient, and as B-v <= 2^32, we may safely use a single + * 64/64 bit division to find its contribution. */ + + cairo_uquorem64_t quorem; + cairo_uint64_t remainder; /* will contain final remainder */ + uint32_t quotient; /* will contain final quotient. */ + uint32_t q; + uint32_t r; + + /* Approximate quotient by dividing the high 64 bits of num by + * u+1. Watch out for overflow of u+1. */ + if (u+1) { + quorem = _cairo_uint64_divrem (x, _cairo_uint32_to_uint64 (u+1)); + q = _cairo_uint64_to_uint32 (quorem.quo); + r = _cairo_uint64_to_uint32 (quorem.rem); + } + else { + q = uint64_hi32 (x); + r = _cairo_uint64_to_uint32 (x); + } + quotient = q; + + /* Add the main term's contribution to quotient. Note B-v = + * -v as an uint32 (unless v = 0) */ + if (v) + quorem = _cairo_uint64_divrem (_cairo_uint32x32_64_mul (q, -v), den); + else + quorem = _cairo_uint64_divrem (_cairo_uint32s_to_uint64 (q, 0), den); + quotient += _cairo_uint64_to_uint32 (quorem.quo); + + /* Add the contribution of the subterm and start computing the + * true remainder. */ + remainder = _cairo_uint32s_to_uint64 (r, y); + if (_cairo_uint64_ge (remainder, den)) { + remainder = _cairo_uint64_sub (remainder, den); + quotient++; + } + + /* Add the contribution of the main term's remainder. The + * funky test here checks that remainder + main_rem >= den, + * taking into account overflow of the addition. */ + remainder = _cairo_uint64_add (remainder, quorem.rem); + if (_cairo_uint64_ge (remainder, den) || + _cairo_uint64_lt (remainder, quorem.rem)) + { + remainder = _cairo_uint64_sub (remainder, den); + quotient++; + } + + result.quo = _cairo_uint32_to_uint64 (quotient); + result.rem = remainder; + } + return result; +} + +cairo_quorem64_t +_cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den) +{ + int num_neg = _cairo_int128_negative (num); + int den_neg = _cairo_int64_negative (den); + cairo_uint64_t nonneg_den; + cairo_uquorem64_t uqr; + cairo_quorem64_t qr; + + if (num_neg) + num = _cairo_int128_negate (num); + if (den_neg) + nonneg_den = _cairo_int64_negate (den); + else + nonneg_den = den; + + uqr = _cairo_uint_96by64_32x64_divrem (num, nonneg_den); + if (_cairo_uint64_eq (uqr.rem, nonneg_den)) { + /* bail on overflow. */ + qr.quo = _cairo_uint32s_to_uint64 (0x7FFFFFFF, -1U); + qr.rem = den; + return qr; + } + + if (num_neg) + qr.rem = _cairo_int64_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = _cairo_int64_negate (uqr.quo); + else + qr.quo = uqr.quo; + return qr; +} diff --git a/libs/cairo/src/cairo-win32-font.c b/libs/cairo/src/cairo-win32-font.c new file mode 100644 index 000000000..ccdd16187 --- /dev/null +++ b/libs/cairo/src/cairo-win32-font.c @@ -0,0 +1,2319 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +/* We require at least Windows 7 features */ +#if !defined(WINVER) || (WINVER < 0x0601) +# define WINVER 0x0601 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) +# define _WIN32_WINNT 0x0601 +#endif + +#include "cairoint.h" + +#include "cairo-win32-private.h" +#include "cairo-error-private.h" + +#include + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +#define CMAP_TAG 0x70616d63 + +/** + * SECTION:cairo-win32-fonts + * @Title: Win32 Fonts + * @Short_Description: Font support for Microsoft Windows + * @See_Also: #cairo_font_face_t + * + * The Microsoft Windows font backend is primarily used to render text on + * Microsoft Windows systems. + */ + +/** + * CAIRO_HAS_WIN32_FONT: + * + * Defined if the Microsoft Windows font backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend; + +typedef struct { + cairo_scaled_font_t base; + + LOGFONTW logfont; + + BYTE quality; + + /* We do drawing and metrics computation in a "logical space" which + * is similar to font space, except that it is scaled by a factor + * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication + * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision. + */ + double logical_scale; + + /* The size we should actually request the font at from Windows; differs + * from the logical_scale because it is quantized for orthogonal + * transformations + */ + double logical_size; + + /* Transformations from device <=> logical space + */ + cairo_matrix_t logical_to_device; + cairo_matrix_t device_to_logical; + + /* We special case combinations of 90-degree-rotations, scales and + * flips ... that is transformations that take the axes to the + * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y + * encode the 8 possibilities for orientation (4 rotation angles with + * and without a flip), and scale_x, scale_y the scale components. + */ + cairo_bool_t preserve_axes; + cairo_bool_t swap_axes; + cairo_bool_t swap_x; + cairo_bool_t swap_y; + double x_scale; + double y_scale; + + /* The size of the design unit of the font + */ + int em_square; + + HFONT scaled_hfont; + HFONT unscaled_hfont; + + cairo_bool_t is_bitmap; + cairo_bool_t is_type1; + cairo_bool_t delete_scaled_hfont; +} cairo_win32_scaled_font_t; + +static cairo_status_t +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) + +static HDC +_get_global_font_dc (void) +{ + static HDC hdc; + + if (!hdc) { + hdc = CreateCompatibleDC (NULL); + if (!hdc) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + return NULL; + } + + if (!SetGraphicsMode (hdc, GM_ADVANCED)) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + DeleteDC (hdc); + return NULL; + } + } + + return hdc; +} + +static cairo_status_t +_compute_transform (cairo_win32_scaled_font_t *scaled_font, + cairo_matrix_t *sc) +{ + cairo_status_t status; + + if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) && + !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->xx; + scaled_font->swap_x = (sc->xx < 0); + scaled_font->y_scale = sc->yy; + scaled_font->swap_y = (sc->yy < 0); + scaled_font->swap_axes = FALSE; + + } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) && + !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->yx; + scaled_font->swap_x = (sc->yx < 0); + scaled_font->y_scale = sc->xy; + scaled_font->swap_y = (sc->xy < 0); + scaled_font->swap_axes = TRUE; + + } else { + scaled_font->preserve_axes = FALSE; + scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; + } + + if (scaled_font->preserve_axes) { + if (scaled_font->swap_x) + scaled_font->x_scale = - scaled_font->x_scale; + if (scaled_font->swap_y) + scaled_font->y_scale = - scaled_font->y_scale; + + scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE * + _cairo_lround (scaled_font->y_scale); + } + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. + */ + cairo_matrix_init (&scaled_font->logical_to_device, + sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); + + if (!scaled_font->preserve_axes) { + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device, + &scaled_font->x_scale, &scaled_font->y_scale, + TRUE); /* XXX: Handle vertical text */ + if (status) + return status; + + scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE * + scaled_font->y_scale); + scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + } + + cairo_matrix_scale (&scaled_font->logical_to_device, + 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); + + scaled_font->device_to_logical = scaled_font->logical_to_device; + + status = cairo_matrix_invert (&scaled_font->device_to_logical); + if (status) + cairo_matrix_init_identity (&scaled_font->device_to_logical); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_have_cleartype_quality (void) +{ + // All supported versions have cleartype + return TRUE; +} + +BYTE +cairo_win32_get_system_text_quality (void) +{ + BOOL font_smoothing; + UINT smoothing_type; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); + return DEFAULT_QUALITY; + } + + if (font_smoothing) { + if (_have_cleartype_quality ()) { + if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); + return DEFAULT_QUALITY; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } else { + return DEFAULT_QUALITY; + } +} + +/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some + * factor S, ctm must be the identity, logfont->lfHeight must be -S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and face_hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +static cairo_status_t +_win32_scaled_font_create (LOGFONTW *logfont, + HFONT face_hfont, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) +{ + HDC hdc; + cairo_win32_scaled_font_t *f; + cairo_matrix_t scale; + cairo_status_t status; + + hdc = _get_global_font_dc (); + if (hdc == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f = malloc (sizeof(cairo_win32_scaled_font_t)); + if (f == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->logfont = *logfont; + + /* We don't have any control over the hinting style or subpixel + * order in the Win32 font API, so we ignore those parts of + * cairo_font_options_t. We use the 'antialias' field to set + * the 'quality'. + * + * XXX: The other option we could pay attention to, but don't + * here is the hint_metrics options. + */ + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) + f->quality = cairo_win32_get_system_text_quality (); + else { + switch (options->antialias) { + case CAIRO_ANTIALIAS_NONE: + f->quality = NONANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_GRAY: + f->quality = ANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + if (_have_cleartype_quality ()) + f->quality = CLEARTYPE_QUALITY; + else + f->quality = ANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_DEFAULT: + ASSERT_NOT_REACHED; + } + } + + f->em_square = 0; + f->scaled_hfont = NULL; + f->unscaled_hfont = NULL; + + if (f->quality == logfont->lfQuality || + (logfont->lfQuality == DEFAULT_QUALITY && + options->antialias == CAIRO_ANTIALIAS_DEFAULT)) { + /* If face_hfont is non-NULL, then we can use it to avoid creating our + * own --- because the constraints on face_hfont mentioned above + * guarantee it was created in exactly the same way that + * _win32_scaled_font_get_scaled_hfont would create it. + */ + f->scaled_hfont = face_hfont; + } + /* don't delete the hfont if we're using the one passed in to us */ + f->delete_scaled_hfont = !f->scaled_hfont; + + cairo_matrix_multiply (&scale, font_matrix, ctm); + status = _compute_transform (f, &scale); + if (status) + goto FAIL; + + status = _cairo_scaled_font_init (&f->base, font_face, + font_matrix, ctm, options, + &_cairo_win32_scaled_font_backend); + if (status) + goto FAIL; + + status = _cairo_win32_scaled_font_set_metrics (f); + if (status) { + _cairo_scaled_font_fini (&f->base); + goto FAIL; + } + + *font_out = &f->base; + return CAIRO_STATUS_SUCCESS; + + FAIL: + free (f); + return status; +} + +static cairo_status_t +_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); + + if (!SetWorldTransform (hdc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_set_identity_transform (HDC hdc) +{ + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HFONT *hfont_out) +{ + if (!scaled_font->scaled_hfont) { + LOGFONTW logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->logical_size; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); + if (!scaled_font->scaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); + } + + *hfont_out = scaled_font->scaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HDC hdc, + HFONT *hfont_out) +{ + if (scaled_font->unscaled_hfont == NULL) { + OUTLINETEXTMETRIC *otm; + unsigned int otm_size; + HFONT scaled_hfont; + LOGFONTW logfont; + cairo_status_t status; + + status = _win32_scaled_font_get_scaled_hfont (scaled_font, + &scaled_hfont); + if (status) + return status; + + if (! SelectObject (hdc, scaled_hfont)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); + + otm_size = GetOutlineTextMetrics (hdc, 0, NULL); + if (! otm_size) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + + otm = malloc (otm_size); + if (otm == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { + status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + free (otm); + return status; + } + + scaled_font->em_square = otm->otmEMSquare; + free (otm); + + logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->em_square; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); + if (! scaled_font->unscaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); + } + + *hfont_out = scaled_font->unscaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + + status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); + + status = _win32_scaled_font_set_identity_transform (hdc); + if (status) { + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_type1; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_bitmap; +} + +static void +_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) +{ +} + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + LOGFONTW logfont; + uint16_t *face_name; + int face_name_len; + cairo_status_t status; + + status = _cairo_utf8_to_utf16 (toy_face->family, -1, + &face_name, &face_name_len); + if (status) + return status; + + if (face_name_len > LF_FACESIZE - 1) + face_name_len = LF_FACESIZE - 1; + + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); + logfont.lfFaceName[face_name_len] = 0; + free (face_name); + + logfont.lfHeight = 0; /* filled in later */ + logfont.lfWidth = 0; /* filled in later */ + logfont.lfEscapement = 0; /* filled in later */ + logfont.lfOrientation = 0; /* filled in later */ + + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + default: + logfont.lfWeight = FW_NORMAL; + break; + case CAIRO_FONT_WEIGHT_BOLD: + logfont.lfWeight = FW_BOLD; + break; + } + + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_NORMAL: + default: + logfont.lfItalic = FALSE; + break; + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + logfont.lfItalic = TRUE; + break; + } + + logfont.lfUnderline = FALSE; + logfont.lfStrikeOut = FALSE; + /* The docs for LOGFONT discourage using this, since the + * interpretation is locale-specific, but it's not clear what + * would be a better alternative. + */ + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ + logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_scaled_font_fini (void *abstract_font) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font == NULL) + return; + + if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont) + DeleteObject (scaled_font->scaled_hfont); + + if (scaled_font->unscaled_hfont) + DeleteObject (scaled_font->unscaled_hfont); +} + +static cairo_int_status_t +_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + uint16_t *utf16; + int n16; + int i; + WORD *glyph_indices = NULL; + cairo_status_t status; + double x_pos, y_pos; + HDC hdc = NULL; + cairo_matrix_t mat; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD)); + if (!glyph_indices) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL2; + + if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW"); + goto FAIL3; + } + + *num_glyphs = n16; + *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL3; + } + + x_pos = x; + y_pos = y; + + mat = scaled_font->base.ctm; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_scaled_font_freeze_cache (&scaled_font->base); + + for (i = 0; i < n16; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos; + (*glyphs)[i].y = y_pos; + + status = _cairo_scaled_glyph_lookup (&scaled_font->base, + glyph_indices[i], + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + free (*glyphs); + *glyphs = NULL; + break; + } + + x = scaled_glyph->x_advance; + y = scaled_glyph->y_advance; + cairo_matrix_transform_distance (&mat, &x, &y); + x_pos += x; + y_pos += y; + } + + _cairo_scaled_font_thaw_cache (&scaled_font->base); + +FAIL3: + cairo_win32_scaled_font_done_font (&scaled_font->base); +FAIL2: + free (glyph_indices); +FAIL1: + free (utf16); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + uint16_t *utf16; + int n16; + GCP_RESULTSW gcp_results; + unsigned int buffer_size, i; + WCHAR *glyph_indices = NULL; + int *dx = NULL; + cairo_status_t status; + double x_pos, y_pos; + double x_incr, y_incr; + HDC hdc = NULL; + + /* GetCharacterPlacement() returns utf16 instead of glyph indices + * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */ + if (scaled_font->is_type1) + return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font, + x, + y, + utf8, + glyphs, + num_glyphs); + + /* Compute a vector in user space along the baseline of length one logical space unit */ + x_incr = 1; + y_incr = 0; + cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr); + x_incr /= scaled_font->logical_scale; + y_incr /= scaled_font->logical_scale; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + gcp_results.lStructSize = sizeof (GCP_RESULTS); + gcp_results.lpOutString = NULL; + gcp_results.lpOrder = NULL; + gcp_results.lpCaretPos = NULL; + gcp_results.lpClass = NULL; + + buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL1; + + while (TRUE) { + if (glyph_indices) { + free (glyph_indices); + glyph_indices = NULL; + } + if (dx) { + free (dx); + dx = NULL; + } + + glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR)); + dx = _cairo_malloc_ab (buffer_size, sizeof (int)); + if (!glyph_indices || !dx) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + gcp_results.nGlyphs = buffer_size; + gcp_results.lpDx = dx; + gcp_results.lpGlyphs = glyph_indices; + + if (!GetCharacterPlacementW (hdc, utf16, n16, + 0, + &gcp_results, + GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); + goto FAIL2; + } + + if (gcp_results.lpDx && gcp_results.lpGlyphs) + break; + + /* Too small a buffer, try again */ + + buffer_size += buffer_size / 2; + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + } + + *num_glyphs = gcp_results.nGlyphs; + *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + x_pos = x; + y_pos = y; + + for (i = 0; i < gcp_results.nGlyphs; i++) { + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos ; + (*glyphs)[i].y = y_pos; + + x_pos += x_incr * dx[i]; + y_pos += y_incr * dx[i]; + } + + FAIL2: + if (glyph_indices) + free (glyph_indices); + if (dx) + free (dx); + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + FAIL1: + free (utf16); + + return status; +} + +static unsigned long +_cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + wchar_t unicode[2]; + WORD glyph_index; + HDC hdc = NULL; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return 0; + + unicode[0] = ucs4; + unicode[1] = 0; + if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { + _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); + glyph_index = 0; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return glyph_index; +} + +static cairo_status_t +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_font_extents_t extents; + + TEXTMETRIC metrics; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) { + /* For 90-degree rotations (including 0), we get the metrics + * from the GDI in logical space, then convert back to font space + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + GetTextMetrics (hdc, &metrics); + cairo_win32_scaled_font_done_font (&scaled_font->base); + + extents.ascent = metrics.tmAscent / scaled_font->logical_scale; + extents.descent = metrics.tmDescent / scaled_font->logical_scale; + + extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; + extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; + extents.max_y_advance = 0; + + } else { + /* For all other transformations, we use the design metrics + * of the font. The GDI results from GetTextMetrics() on a + * transformed font are inexplicably large and we want to + * avoid them. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + GetTextMetrics (hdc, &metrics); + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.ascent = (double)metrics.tmAscent / scaled_font->em_square; + extents.descent = (double)metrics.tmDescent / scaled_font->em_square; + extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; + extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; + extents.max_y_advance = 0; + + } + + scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR); + + /* Need to determine if this is a Type 1 font for the special + * handling in _text_to_glyphs. Unlike TrueType or OpenType, + * Type1 fonts do not have a "cmap" table (or any other table). + * However GetFontData() will retrieve a Type1 font when + * requesting that GetFontData() retrieve data from the start of + * the file. This is to distinguish Type1 from stroke fonts such + * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant + * but improves performance for the most common fonts. + */ + scaled_font->is_type1 = FALSE; + if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) && + (metrics.tmPitchAndFamily & TMPF_VECTOR)) + { + if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) && + (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR)) + { + scaled_font->is_type1 = TRUE; + } + } + + return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS metrics; + cairo_status_t status; + cairo_text_extents_t extents; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->is_bitmap) { + /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */ + cairo_font_extents_t font_extents; + INT width = 0; + UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph); + + cairo_scaled_font_extents (&scaled_font->base, &font_extents); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); + width = 0; + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + if (status) + return status; + + extents.x_bearing = 0; + extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale); + extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale); + extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale; + extents.x_advance = extents.width; + extents.y_advance = 0; + } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + /* If we aren't rotating / skewing the axes, then we get the metrics + * from the GDI in device space and convert to font space. + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } else { + if (metrics.gmBlackBoxX == 1 && metrics.gmBlackBoxY == 1 && + GetGlyphOutlineW (hdc, + _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == 0) { + /* Workaround for GetGlyphOutline returning 1x1 bounding box + * for glyph that is in fact empty. + */ + metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; + } + else if (metrics.gmBlackBoxX > 0 && + scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { + /* The bounding box reported by Windows supposedly contains the glyph's "black" area; + * however, antialiasing (especially with ClearType) means that the actual image that + * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side. + * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs, + * for example, or other code that uses glyph extents to determine the area to update, + * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI, + * and two pixels to the right (as tests show some glyphs bleed into this column). + */ + metrics.gmptGlyphOrigin.x -= 1; + metrics.gmBlackBoxX += 3; + } + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + + if (scaled_font->swap_axes) { + extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.width = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.height = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale; + } else { + extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.width = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.height = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale; + } + + if (scaled_font->swap_x) { + extents.x_bearing = (- extents.x_bearing - extents.width); + extents.x_advance = - extents.x_advance; + } + + if (scaled_font->swap_y) { + extents.y_bearing = (- extents.y_bearing - extents.height); + extents.y_advance = - extents.y_advance; + } + + } else { + /* For all other transformations, we use the design metrics + * of the font. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; + extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; + extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square; + extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square; + extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; + extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + + return CAIRO_STATUS_SUCCESS; +} + +/* Not currently used code, but may be useful in the future if we add + * back the capability to the scaled font backend interface to get the + * actual device space bbox rather than computing it from the + * font-space metrics. + */ +#if 0 +static cairo_status_t +_cairo_win32_scaled_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + cairo_win32_scaled_font_t *scaled_font = abstract_font; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_glyphs > 0) { + HDC hdc; + GLYPHMETRICS metrics; + cairo_status_t status; + int i; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + for (i = 0; i < num_glyphs; i++) { + int x = _cairo_lround (glyphs[i].x); + int y = _cairo_lround (glyphs[i].y); + + GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) + x1 = x + metrics.gmptGlyphOrigin.x; + if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) + y1 = y - metrics.gmptGlyphOrigin.y; + if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX) + x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX; + if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY) + y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + } + + bbox->p1.x = _cairo_fixed_from_int (x1); + bbox->p1.y = _cairo_fixed_from_int (y1); + bbox->p2.x = _cairo_fixed_from_int (x2); + bbox->p2.y = _cairo_fixed_from_int (y2); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +typedef struct { + cairo_win32_scaled_font_t *scaled_font; + HDC hdc; + + cairo_array_t glyphs; + cairo_array_t dx; + + int start_x; + int last_x; + int last_y; +} cairo_glyph_state_t; + +static void +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + state->hdc = hdc; + state->scaled_font = scaled_font; + + _cairo_array_init (&state->glyphs, sizeof (WCHAR)); + _cairo_array_init (&state->dx, sizeof (int)); +} + +static cairo_status_t +_flush_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + int dx = 0; + WCHAR * elements; + int * dx_elements; + + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + + elements = _cairo_array_index (&state->glyphs, 0); + dx_elements = _cairo_array_index (&state->dx, 0); + if (!ExtTextOutW (state->hdc, + state->start_x, state->last_y, + ETO_GLYPH_INDEX, + NULL, + elements, + state->glyphs.num_elements, + dx_elements)) { + return _cairo_win32_print_gdi_error ("_flush_glyphs"); + } + + _cairo_array_truncate (&state->glyphs, 0); + _cairo_array_truncate (&state->dx, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_add_glyph (cairo_glyph_state_t *state, + unsigned long index, + double device_x, + double device_y) +{ + cairo_status_t status; + double user_x = device_x; + double user_y = device_y; + WCHAR glyph_index = index; + int logical_x, logical_y; + + cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + if (state->glyphs.num_elements > 0) { + int dx; + + if (logical_y != state->last_y) { + status = _flush_glyphs (state); + if (status) + return status; + state->start_x = logical_x; + } else { + dx = logical_x - state->last_x; + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + } + } else { + state->start_x = logical_x; + } + + state->last_x = logical_x; + state->last_y = logical_y; + + status = _cairo_array_append (&state->glyphs, &glyph_index); + if (status) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_finish_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + + status = _flush_glyphs (state); + + _cairo_array_fini (&state->glyphs); + _cairo_array_fini (&state->dx); + + return status; +} + +static cairo_status_t +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_scaled_font_t *scaled_font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glyph_state_t state; + cairo_status_t status, status2; + int i; + + if (!SaveDC (surface->dc)) + return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); + if (status) + goto FAIL1; + + SetTextColor (surface->dc, color); + SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); + SetBkMode (surface->dc, TRANSPARENT); + + _start_glyphs (&state, scaled_font, surface->dc); + + for (i = 0; i < num_glyphs; i++) { + status = _add_glyph (&state, glyphs[i].index, + glyphs[i].x - x_offset, glyphs[i].y - y_offset); + if (status) + goto FAIL2; + } + + FAIL2: + status2 = _finish_glyphs (&state); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + cairo_win32_scaled_font_done_font (&scaled_font->base); + FAIL1: + RestoreDC (surface->dc, -1); + + return status; +} + +/* Duplicate the green channel of a 4-channel mask in the alpha channel, then + * invert the whole mask. + */ +static void +_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); + p++; + } + } +} + +/* Invert a mask + */ +static void +_invert_argb32_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ *p; + p++; + } + } +} + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; + cairo_image_surface_t *image8; + int i, j; + + if (image24->base.status) + return cairo_surface_reference (&image24->base); + + image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, + image24->width, image24->height); + if (image8->base.status) + return &image8->base; + + for (i = 0; i < image24->height; i++) { + uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); + unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); + + for (j = 0; j < image24->width; j++) { + *q = 255 - ((*p & 0x0000ff00) >> 8); + p++; + q++; + } + } + + return &image8->base; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_show_glyphs (void *abstract_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region, + int *remaining_glyphs) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; + cairo_status_t status; + + if (width == 0 || height == 0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_is_win32 (generic_surface) && + surface->format == CAIRO_FORMAT_RGB24 && + (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) && + op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_opaque_solid (pattern)) { + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; + + /* When compositing OVER on a GDI-understood surface, with a + * solid opaque color, we can just call ExtTextOut directly. + */ + COLORREF new_color; + + status = _cairo_win32_surface_set_clip_region (surface, clip_region); + if (unlikely (status)) + return status; + + new_color = RGB (((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + return _draw_glyphs_on_surface (surface, scaled_font, new_color, + 0, 0, + glyphs, num_glyphs); + } else { + /* Otherwise, we need to draw using software fallbacks. We create a mask + * surface by drawing the the glyphs onto a DIB, black-on-white then + * inverting. GDI outputs gamma-corrected images so inverted black-on-white + * is very different from white-on-black. We favor the more common + * case where the final output is dark-on-light. + */ + cairo_win32_surface_t *tmp_surface; + cairo_surface_t *mask_surface; + cairo_surface_pattern_t mask; + cairo_bool_t use_subpixel_antialiasing = + scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing; + RECT r; + + tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); + if (tmp_surface->base.status) + return tmp_surface->base.status; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); + + status = _draw_glyphs_on_surface (tmp_surface, + scaled_font, RGB (0, 0, 0), + dest_x, dest_y, + glyphs, num_glyphs); + if (status) { + cairo_surface_destroy (&tmp_surface->base); + return status; + } + + if (use_subpixel_antialiasing) { + /* For ClearType, we need a 4-channel mask. If we are compositing on + * a surface with alpha, we need to compute the alpha channel of + * the mask (we just copy the green channel). But for a destination + * surface without alpha the alpha channel of the mask is ignored + */ + + if (surface->format != CAIRO_FORMAT_RGB24) + _compute_argb32_mask_alpha (tmp_surface); + else + _invert_argb32_mask (tmp_surface); + + mask_surface = &tmp_surface->base; + } else { + mask_surface = _compute_a8_mask (tmp_surface); + cairo_surface_destroy (&tmp_surface->base); + status = mask_surface->status; + if (status) + return status; + } + + /* For op == OVER, no-cleartype, a possible optimization here is to + * draw onto an intermediate ARGB32 surface and alpha-blend that with the + * destination + */ + _cairo_pattern_init_for_surface (&mask, mask_surface); + cairo_surface_destroy (mask_surface); + + if (use_subpixel_antialiasing) + mask.base.has_component_alpha = TRUE; + + status = _cairo_surface_composite (op, pattern, + &mask.base, + &surface->base, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height, + clip_region); + + _cairo_pattern_fini (&mask.base); + + return status; + } +} + +static cairo_int_status_t +_cairo_win32_scaled_font_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + HDC hdc; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + *length = GetFontData (hdc, tag, offset, buffer, *length); + if (*length == GDI_ERROR) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + GLYPHSET *glyph_set; + uint16_t *utf16 = NULL; + WORD *glyph_indices = NULL; + HDC hdc = NULL; + int res; + unsigned int i, j, num_glyphs; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + res = GetFontUnicodeRanges(hdc, NULL); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + glyph_set = malloc (res); + if (glyph_set == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + res = GetFontUnicodeRanges(hdc, glyph_set); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + *ucs4 = (uint32_t) -1; + for (i = 0; i < glyph_set->cRanges; i++) { + num_glyphs = glyph_set->ranges[i].cGlyphs; + + utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t)); + if (utf16 == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD)); + if (glyph_indices == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) + utf16[j] = glyph_set->ranges[i].wcLow + j; + utf16[j] = 0; + + if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) { + if (glyph_indices[j] == index) { + *ucs4 = utf16[j]; + goto exit2; + } + } + + free (glyph_indices); + glyph_indices = NULL; + free (utf16); + utf16 = NULL; + } + +exit2: + if (glyph_indices) + free (glyph_indices); + if (utf16) + free (utf16); + free (glyph_set); +exit1: + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return status; +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_status_t status; + cairo_glyph_t glyph; + cairo_win32_surface_t *surface; + cairo_t *cr; + cairo_surface_t *image; + int width, height; + int x1, y1, x2, y2; + + x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + width = x2 - x1; + height = y2 - y1; + + surface = (cairo_win32_surface_t *) + cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); + + cr = cairo_create (&surface->base); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + status = cairo_status (cr); + cairo_destroy(cr); + if (status) + goto FAIL; + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = -x1; + glyph.y = -y1; + status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0), + 0, 0, &glyph, 1); + if (status) + goto FAIL; + + GdiFlush(); + + image = _compute_a8_mask (surface); + status = image->status; + if (status) + goto FAIL; + + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image); + + FAIL: + cairo_surface_destroy (&surface->base); + + return status; +} + +static void +_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix, + FIXED Fx, FIXED Fy, + cairo_fixed_t *fx, cairo_fixed_t *fy) +{ + double x = Fx.value + Fx.fract / 65536.0; + double y = Fy.value + Fy.fract / 65536.0; + cairo_matrix_transform_point (matrix, &x, &y); + *fx = _cairo_fixed_from_double (x); + *fy = _cairo_fixed_from_double (y); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; + cairo_status_t status; + GLYPHMETRICS metrics; + HDC hdc; + DWORD bytesGlyph; + unsigned char *buffer, *ptr; + cairo_path_fixed_t *path; + cairo_matrix_t transform; + cairo_fixed_t x, y; + + if (scaled_font->is_bitmap) + return CAIRO_INT_STATUS_UNSUPPORTED; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) { + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + transform = scaled_font->base.scale; + cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square); + } else { + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + cairo_matrix_init_identity(&transform); + } + if (status) + goto CLEANUP_PATH; + + bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (bytesGlyph == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_FONT; + } + + ptr = buffer = malloc (bytesGlyph); + if (!buffer) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_FONT; + } + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_BUFFER; + } + + while (ptr < buffer + bytesGlyph) { + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; + unsigned char *endPoly = ptr + header->cb; + + ptr += sizeof (TTPOLYGONHEADER); + + _cairo_win32_transform_FIXED_to_fixed (&transform, + header->pfxStart.x, + header->pfxStart.y, + &x, &y); + status = _cairo_path_fixed_move_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + + while (ptr < endPoly) { + TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; + POINTFX *points = curve->apfx; + int i; + switch (curve->wType) { + case TT_PRIM_LINE: + for (i = 0; i < curve->cpfx; i++) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + status = _cairo_path_fixed_line_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_QSPLINE: + for (i = 0; i < curve->cpfx - 1; i++) { + cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; + if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y)) + goto CLEANUP_BUFFER; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &cx, &cy); + + if (i + 1 == curve->cpfx - 1) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &p2x, &p2y); + } else { + /* records with more than one curve use interpolation for + control points, per http://support.microsoft.com/kb/q87115/ */ + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x, &y); + p2x = (cx + x) / 2; + p2y = (cy + y) / 2; + } + + c1x = 2 * cx / 3 + p1x / 3; + c1y = 2 * cy / 3 + p1y / 3; + c2x = 2 * cx / 3 + p2x / 3; + c2y = 2 * cy / 3 + p2y / 3; + + status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_CSPLINE: + for (i = 0; i < curve->cpfx - 2; i += 2) { + cairo_fixed_t x1, y1, x2, y2; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x1, &y1); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 2].x, + points[i + 2].y, + &x2, &y2); + status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2); + if (status) + goto CLEANUP_BUFFER; + } + break; + } + ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); + } + status = _cairo_path_fixed_close_path (path); + if (status) + goto CLEANUP_BUFFER; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + + CLEANUP_BUFFER: + free (buffer); + + CLEANUP_FONT: + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + else + cairo_win32_scaled_font_done_font (&scaled_font->base); + + CLEANUP_PATH: + if (status != CAIRO_STATUS_SUCCESS) + _cairo_path_fixed_destroy (path); + + return status; +} + +const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_scaled_font_fini, + _cairo_win32_scaled_font_glyph_init, + NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ + _cairo_win32_scaled_font_ucs4_to_index, + _cairo_win32_scaled_font_show_glyphs, + _cairo_win32_scaled_font_load_truetype_table, + _cairo_win32_scaled_font_index_to_ucs4, +}; + +/* #cairo_win32_font_face_t */ + +typedef struct _cairo_win32_font_face cairo_win32_font_face_t; + +/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +struct _cairo_win32_font_face { + cairo_font_face_t base; + LOGFONTW logfont; + HFONT hfont; +}; + +/* implement the platform-specific interface */ + +static void +_cairo_win32_font_face_destroy (void *abstract_face); + +static cairo_bool_t +_is_scale (const cairo_matrix_t *matrix, double scale) +{ + return matrix->xx == scale && matrix->yy == scale && + matrix->xy == 0. && matrix->yx == 0. && + matrix->x0 == 0. && matrix->y0 == 0.; +} + +static cairo_status_t +_cairo_win32_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font) +{ + HFONT hfont = NULL; + + cairo_win32_font_face_t *font_face = abstract_face; + + if (font_face->hfont) { + /* Check whether it's OK to go ahead and use the font-face's HFONT. */ + if (_is_scale (ctm, 1.) && + _is_scale (font_matrix, -font_face->logfont.lfHeight)) { + hfont = font_face->hfont; + } + } + + return _win32_scaled_font_create (&font_face->logfont, + hfont, + &font_face->base, + font_matrix, ctm, options, + font); +} + +const cairo_font_face_backend_t _cairo_win32_font_face_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_font_face_create_for_toy, + _cairo_win32_font_face_destroy, + _cairo_win32_font_face_scaled_font_create +}; + +/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t. + * The primary purpose of this mapping is to provide unique + * #cairo_font_face_t values so that our cache and mapping from + * #cairo_font_face_t => #cairo_scaled_font_t works. Once the + * corresponding #cairo_font_face_t objects fall out of downstream + * caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_win32_font_face_mutex. + * + * Only #cairo_font_face_t values with null 'hfont' (no + * HFONT preallocated by caller) are stored in this table. We rely + * on callers to manage the lifetime of the HFONT, and they can't + * do that if we share #cairo_font_face_t values with other callers. + */ + +static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b); + +static void +_cairo_win32_font_face_hash_table_destroy (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * _cairo_win32_font_face_hash_table_lock simply to avoid creating + * the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + hash_table = cairo_win32_font_face_hash_table; + cairo_win32_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); +} + +static cairo_hash_table_t * +_cairo_win32_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) + { + cairo_win32_font_face_hash_table = + _cairo_hash_table_create (_cairo_win32_font_face_keys_equal); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) { + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + } + + return cairo_win32_font_face_hash_table; +} + +static void +_cairo_win32_font_face_hash_table_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); +} + +static void +_cairo_win32_font_face_init_key (cairo_win32_font_face_t *key, + LOGFONTW *logfont, + HFONT font) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + key->logfont = *logfont; + key->hfont = font; + + hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName)); + hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight)); + hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic)); + + key->base.hash_entry.hash = hash; +} + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_win32_font_face_t *face_a = key_a; + const cairo_win32_font_face_t *face_b = key_b; + + if (face_a->logfont.lfWeight == face_b->logfont.lfWeight && + face_a->logfont.lfItalic == face_b->logfont.lfItalic && + face_a->logfont.lfUnderline == face_b->logfont.lfUnderline && + face_a->logfont.lfStrikeOut == face_b->logfont.lfStrikeOut && + face_a->logfont.lfCharSet == face_b->logfont.lfCharSet && + face_a->logfont.lfOutPrecision == face_b->logfont.lfOutPrecision && + face_a->logfont.lfClipPrecision == face_b->logfont.lfClipPrecision && + face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily && + (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0)) + return TRUE; + else + return FALSE; +} + +static void +_cairo_win32_font_face_destroy (void *abstract_face) +{ + cairo_hash_table_t *hash_table; + cairo_win32_font_face_t *font_face = abstract_face; + + if (!font_face->hfont) { + hash_table = _cairo_win32_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) { + return; + } + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + _cairo_win32_font_face_hash_table_unlock (); + } +} + +/** + * cairo_win32_font_face_create_for_logfontw_hfont: + * @logfont: A #LOGFONTW structure specifying the font to use. + * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and + * lfEscapement must be zero. + * @font: An #HFONT that can be used when the font matrix is a scale by + * -lfHeight and the CTM is identity. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) +{ + cairo_win32_font_face_t *font_face, key; + cairo_hash_table_t *hash_table; + cairo_status_t status; + + if (!font) { + hash_table = _cairo_win32_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_win32_font_face_init_key (&key, logfont, font); + + /* Return existing unscaled font if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + cairo_font_face_reference (&font_face->base); + goto DONE; + } + } + + /* Otherwise create it and insert into hash table. */ + font_face = malloc (sizeof (cairo_win32_font_face_t)); + if (!font_face) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_win32_font_face_init_key (font_face, logfont, font); + _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); + + if (!font) { + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, + &font_face->base.hash_entry); + if (unlikely (status)) + goto FAIL; + } + +DONE: + if (!font) { + _cairo_win32_font_face_hash_table_unlock (); + } + + return &font_face->base; + +FAIL: + if (!font) { + _cairo_win32_font_face_hash_table_unlock (); + } + + return (cairo_font_face_t *)&_cairo_font_face_nil; +} + +/** + * cairo_win32_font_face_create_for_logfontw: + * @logfont: A #LOGFONTW structure specifying the font to use. + * The lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) +{ + return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL); +} + +/** + * cairo_win32_font_face_create_for_hfont: + * @font: An #HFONT structure specifying the font to use. + * + * Creates a new font for the Win32 font backend based on a + * #HFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_hfont (HFONT font) +{ + LOGFONTW logfont; + GetObjectW (font, sizeof(logfont), &logfont); + + if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 || + logfont.lfWidth != 0) { + /* We can't use this font because that optimization requires that + * lfEscapement, lfOrientation and lfWidth be zero. */ + font = NULL; + } + + return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font); +} + +static cairo_bool_t +_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &_cairo_win32_scaled_font_backend; +} + +/** + * cairo_win32_scaled_font_select_font: + * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an + * object can be created with cairo_win32_scaled_font_create_for_logfontw(). + * @hdc: a device context + * + * Selects the font into the given device context and changes the + * map mode and world transformation of the device context to match + * that of the font. This function is intended for use when using + * layout APIs such as Uniscribe to do text layout with the + * cairo font. After finishing using the device context, you must call + * cairo_win32_scaled_font_done_font() to release any resources allocated + * by this function. + * + * See cairo_win32_scaled_font_get_metrics_factor() for converting logical + * coordinates from the device context to font space. + * + * Normally, calls to SaveDC() and RestoreDC() would be made around + * the use of this function to preserve the original graphics state. + * + * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. + * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and + * the device context is unchanged. + **/ +cairo_status_t +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + int old_mode; + + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } + + if (scaled_font->status) + return scaled_font->status; + + status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); + + old_mode = SetGraphicsMode (hdc, GM_ADVANCED); + if (!old_mode) { + status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); + SelectObject (hdc, old_hfont); + return status; + } + + status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); + if (status) { + SetGraphicsMode (hdc, old_mode); + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_scaled_font_done_font: + * @scaled_font: A scaled font from the Win32 font backend. + * + * Releases any resources allocated by cairo_win32_scaled_font_select_font() + **/ +void +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } +} + +/** + * cairo_win32_scaled_font_get_metrics_factor: + * @scaled_font: a scaled font from the Win32 font backend + * + * Gets a scale factor between logical coordinates in the coordinate + * space used by cairo_win32_scaled_font_select_font() (that is, the + * coordinate system used by the Windows functions to return metrics) and + * font space coordinates. + * + * Return value: factor to multiply logical units by to get font space + * coordinates. + **/ +double +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return 1.; + } + return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; +} + +/** + * cairo_win32_scaled_font_get_logical_to_device: + * @scaled_font: a scaled font from the Win32 font backend + * @logical_to_device: matrix to return + * + * Gets the transformation mapping the logical space used by @scaled_font + * to device space. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *logical_to_device) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (logical_to_device); + return; + } + *logical_to_device = win_font->logical_to_device; +} + +/** + * cairo_win32_scaled_font_get_device_to_logical: + * @scaled_font: a scaled font from the Win32 font backend + * @device_to_logical: matrix to return + * + * Gets the transformation mapping device space to the logical space + * used by @scaled_font. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (device_to_logical); + return; + } + *device_to_logical = win_font->device_to_logical; +} + +void +_cairo_win32_font_reset_static_data (void) +{ + _cairo_win32_font_face_hash_table_destroy (); +} diff --git a/libs/cairo/src/cairo-win32-printing-surface.c b/libs/cairo/src/cairo-win32-printing-surface.c new file mode 100644 index 000000000..56cf3242b --- /dev/null +++ b/libs/cairo/src/cairo-win32-printing-surface.c @@ -0,0 +1,1956 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +/* We require at least Windows 7 features */ +#if !defined(WINVER) || (WINVER < 0x0601) +# define WINVER 0x0601 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) +# define _WIN32_WINNT 0x0601 +#endif + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-paginated-private.h" + +#include "cairo-clip-private.h" +#include "cairo-win32-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-image-info-private.h" +#include "cairo-surface-clipper-private.h" + +#include + +#if !defined(POSTSCRIPT_IDENTIFY) +# define POSTSCRIPT_IDENTIFY 0x1015 +#endif + +#if !defined(PSIDENT_GDICENTRIC) +# define PSIDENT_GDICENTRIC 0x0000 +#endif + +#if !defined(GET_PS_FEATURESETTING) +# define GET_PS_FEATURESETTING 0x1019 +#endif + +#if !defined(FEATURESETTING_PSLEVEL) +# define FEATURESETTING_PSLEVEL 0x0002 +#endif + +#if !defined(GRADIENT_FILL_RECT_H) +# define GRADIENT_FILL_RECT_H 0x00 +#endif + +#if !defined(CHECKJPEGFORMAT) +# define CHECKJPEGFORMAT 0x1017 +#endif + +#if !defined(CHECKPNGFORMAT) +# define CHECKPNGFORMAT 0x1018 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +static const cairo_surface_backend_t cairo_win32_printing_surface_backend; +static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend; + +static void +_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface) +{ + DWORD word; + INT ps_feature, ps_level; + + word = PSIDENT_GDICENTRIC; + if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) + return; + + ps_feature = FEATURESETTING_PSLEVEL; + if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT), + (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0) + return; + + if (ps_level >= 3) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; +} + +static void +_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface) +{ + DWORD word; + + word = CHECKJPEGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; + + word = CHECKPNGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; +} + +/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not + * work unless the GDI function GdiInitializeLanguagePack() has been + * called. + * + * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html + * + * The only information I could find on the how to use this + * undocumented function is the use in: + * + * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup + * + * to solve the same problem. The above code first checks if LPK.DLL + * is already loaded. If it is not it calls + * GdiInitializeLanguagePack() using the prototype + * BOOL GdiInitializeLanguagePack (int) + * and argument 0. + */ +static void +_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface) +{ + typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); + gdi_init_lang_pack_func_t gdi_init_lang_pack; + HMODULE module; + + if (GetModuleHandleW (L"LPK.DLL")) + return; + + module = GetModuleHandleW (L"GDI32.DLL"); + if (module) { + gdi_init_lang_pack = (gdi_init_lang_pack_func_t) + GetProcAddress (module, "GdiInitializeLanguagePack"); + if (gdi_init_lang_pack) + gdi_init_lang_pack (0); + } +} + +static cairo_int_status_t +analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; + cairo_image_transparency_t transparency; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &image_extra); + if (status) + return status; + + transparency = _cairo_image_analyze_transparency (image); + switch (transparency) { + case CAIRO_IMAGE_UNKNOWN: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_OPAQUE: + status = CAIRO_STATUS_SUCCESS; + break; + + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + case CAIRO_IMAGE_HAS_ALPHA: + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + break; + } + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (_cairo_surface_is_recording (pattern->surface)) + return TRUE; + + if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && + pattern->surface->backend->acquire_source_image == NULL) + { + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) + return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; + + return FALSE; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (! pattern_supported (surface, pattern)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!(op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_CLEAR)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if ( _cairo_surface_is_recording (surface_pattern->surface)) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + + if (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If + * the pattern contains transparency, we return + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transarency into the white + * background to convert the pattern to opaque. + */ + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + return analyze_surface_pattern_transparency (surface_pattern); + } + + if (_cairo_pattern_is_opaque (pattern, NULL)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; +} + +static cairo_bool_t +_cairo_win32_printing_surface_operation_supported (cairo_win32_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) + return TRUE; + else + return FALSE; +} + +static void +_cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface, + cairo_solid_pattern_t *color) +{ + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) + _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE); + else + _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK); +} + +static COLORREF +_cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surface, + const cairo_color_t *color) +{ + COLORREF c; + BYTE red, green, blue; + + red = color->red_short >> 8; + green = color->green_short >> 8; + blue = color->blue_short >> 8; + + if (!CAIRO_COLOR_IS_OPAQUE(color)) { + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + /* Blend into white */ + uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8); + + red = (color->red_short >> 8) + one_minus_alpha; + green = (color->green_short >> 8) + one_minus_alpha; + blue = (color->blue_short >> 8) + one_minus_alpha; + } else { + /* Blend into black */ + red = (color->red_short >> 8); + green = (color->green_short >> 8); + blue = (color->blue_short >> 8); + } + } + c = RGB (red, green, blue); + + return c; +} + +static cairo_status_t +_cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface, + const cairo_pattern_t *source) +{ + cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &pattern->color); + surface->brush = CreateSolidBrush (color); + if (!surface->brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); + surface->old_brush = SelectObject (surface->dc, surface->brush); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface) +{ + if (surface->old_brush) { + SelectObject (surface->dc, surface->old_brush); + DeleteObject (surface->brush); + surface->old_brush = NULL; + } +} + +static cairo_status_t +_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, + RECT *clip) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&surface->ctm, &xform); + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); + GetClipBox (surface->dc, clip); + + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface, + const cairo_pattern_t *pattern) +{ + RECT clip; + cairo_status_t status; + + GetClipBox (surface->dc, &clip); + status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); + if (status) + return status; + + FillRect (surface->dc, &clip, surface->brush); + _cairo_win32_printing_surface_done_solid_brush (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_content_t old_content; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_rectangle_int_t recording_extents; + cairo_status_t status; + cairo_extend_t extend; + cairo_matrix_t p2d; + XFORM xform; + int x_tile, y_tile, left, right, top, bottom; + RECT clip; + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; + cairo_box_t bbox; + + extend = cairo_pattern_get_extend (&pattern->base); + + p2d = pattern->base.matrix; + status = cairo_matrix_invert (&p2d); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + cairo_matrix_multiply (&p2d, &p2d, &surface->ctm); + surface->ctm = p2d; + SaveDC (surface->dc); + _cairo_matrix_to_win32_xform (&p2d, &xform); + + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); + if (status) + return status; + + _cairo_box_round_to_rectangle (&bbox, &recording_extents); + + status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); + if (status) + return status; + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + old_content = surface->content; + if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { + surface->content = CAIRO_CONTENT_COLOR; + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, + &_cairo_pattern_black.base); + if (status) + return status; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + cairo_matrix_t m; + double x, y; + + SaveDC (surface->dc); + m = p2d; + cairo_matrix_translate (&m, + x_tile*recording_extents.width, + y_tile*recording_extents.height); + if (extend == CAIRO_EXTEND_REFLECT) { + if (x_tile % 2) { + cairo_matrix_translate (&m, recording_extents.width, 0); + cairo_matrix_scale (&m, -1, 1); + } + if (y_tile % 2) { + cairo_matrix_translate (&m, 0, recording_extents.height); + cairo_matrix_scale (&m, 1, -1); + } + } + surface->ctm = m; + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + + /* Set clip path around bbox of the pattern. */ + BeginPath (surface->dc); + + x = 0; + y = 0; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + MoveToEx (surface->dc, (int) x, (int) y, NULL); + + x = recording_extents.width; + y = 0; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + x = recording_extents.width; + y = recording_extents.height; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + x = 0; + y = recording_extents.height; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + CloseFigure (surface->dc); + EndPath (surface->dc); + SelectClipPath (surface->dc, RGN_AND); + + SaveDC (surface->dc); /* Allow clip path to be reset during replay */ + status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + /* Restore both the clip save and our earlier path SaveDC */ + RestoreDC (surface->dc, -2); + + if (status) + return status; + } + } + + surface->content = old_content; + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + RestoreDC (surface->dc, -1); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_status_t status; + cairo_extend_t extend; + cairo_image_surface_t *image; + void *image_extra; + cairo_image_surface_t *opaque_image = NULL; + BITMAPINFO bi; + cairo_matrix_t m; + int oldmode; + XFORM xform; + int x_tile, y_tile, left, right, top, bottom; + RECT clip; + const cairo_color_t *background_color; + const unsigned char *mime_data; + unsigned long mime_size; + cairo_image_info_t mime_info; + cairo_bool_t use_mime; + DWORD mime_type; + cairo_bool_t axis_swap; + + /* If we can't use StretchDIBits with this surface, we can't do anything + * here. + */ + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) + background_color = CAIRO_COLOR_WHITE; + else + background_color = CAIRO_COLOR_BLACK; + + extend = cairo_pattern_get_extend (&pattern->base); + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); + if (status) + return status; + + if (image->base.status) { + status = image->base.status; + goto CLEANUP_IMAGE; + } + + if (image->width == 0 || image->height == 0) { + status = CAIRO_STATUS_SUCCESS; + goto CLEANUP_IMAGE; + } + + mime_type = BI_JPEG; + status = _cairo_win32_printing_surface_check_jpeg (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + mime_type = BI_PNG; + status = _cairo_win32_printing_surface_check_png (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + } + if (_cairo_status_is_error (status)) + return status; + + use_mime = (status == CAIRO_STATUS_SUCCESS); + + m = pattern->base.matrix; + status = cairo_matrix_invert (&m); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&m, &m, &surface->ctm); + cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); + /* Check if the matrix swaps the X and Y axes by checking if the diagonal + * is effectively zero. This can happen, for example, if it was composed + * with a rotation such as a landscape transform. Some printing devices + * don't support such transforms in StretchDIBits. + */ + axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1; + + if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) { + cairo_surface_t *opaque_surface; + cairo_surface_pattern_t image_pattern; + cairo_solid_pattern_t background_pattern; + int width = image->width, height = image->height; + + if (axis_swap) { + width = image->height; + height = image->width; + } + opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + width, + height); + if (opaque_surface->status) { + status = opaque_surface->status; + goto CLEANUP_OPAQUE_IMAGE; + } + + if (image->format != CAIRO_FORMAT_RGB24) { + _cairo_pattern_init_solid (&background_pattern, + background_color); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_SOURCE, + &background_pattern.base, + NULL); + if (status) + goto CLEANUP_OPAQUE_IMAGE; + } + + _cairo_pattern_init_for_surface (&image_pattern, &image->base); + if (axis_swap) { + /* swap the X and Y axes to undo the axis swap in the matrix */ + cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; + cairo_pattern_set_matrix (&image_pattern.base, &swap_xy); + cairo_matrix_multiply (&m, &swap_xy, &m); + } + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_OVER, + &image_pattern.base, + NULL); + _cairo_pattern_fini (&image_pattern.base); + if (status) + goto CLEANUP_OPAQUE_IMAGE; + + opaque_image = (cairo_image_surface_t *) opaque_surface; + } else { + opaque_image = image; + } + + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; + bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; + bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + SaveDC (surface->dc); + _cairo_matrix_to_win32_xform (&m, &xform); + + if (! SetWorldTransform (surface->dc, &xform)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); + goto CLEANUP_OPAQUE_IMAGE; + } + + oldmode = SetStretchBltMode(surface->dc, HALFTONE); + + GetClipBox (surface->dc, &clip); + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + left = floor ( clip.left / (double) opaque_image->width); + right = ceil (clip.right / (double) opaque_image->width); + top = floor (clip.top / (double) opaque_image->height); + bottom = ceil (clip.bottom / (double) opaque_image->height); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + if (!StretchDIBits (surface->dc, + x_tile*opaque_image->width, + y_tile*opaque_image->height, + opaque_image->width, + opaque_image->height, + 0, + 0, + use_mime ? mime_info.width : opaque_image->width, + use_mime ? mime_info.height : opaque_image->height, + use_mime ? mime_data : opaque_image->data, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); + goto CLEANUP_OPAQUE_IMAGE; + } + } + } + SetStretchBltMode(surface->dc, oldmode); + RestoreDC (surface->dc, -1); + +CLEANUP_OPAQUE_IMAGE: + if (opaque_image != image) + cairo_surface_destroy (&opaque_image->base); +CLEANUP_IMAGE: + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + if (_cairo_surface_is_recording (pattern->surface)) { + return _cairo_win32_printing_surface_paint_recording_pattern (surface, + pattern); + } else { + return _cairo_win32_printing_surface_paint_image_pattern (surface, + pattern); + } +} + +static void +vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) +{ + /* MSDN says that the range here is 0x0000 .. 0xff00; + * that may well be a typo, but just chop the low bits + * here. */ + vert->Alpha = 0xff00; + vert->Red = color->red_short & 0xff00; + vert->Green = color->green_short & 0xff00; + vert->Blue = color->blue_short & 0xff00; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ + TRIVERTEX *vert; + GRADIENT_RECT *rect; + RECT clip; + XFORM xform; + int i, num_stops; + cairo_matrix_t mat, rot; + double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs; + cairo_extend_t extend; + int range_start, range_stop, num_ranges, num_rects, stop; + int total_verts, total_rects; + cairo_status_t status; + + extend = cairo_pattern_get_extend (&pattern->base.base); + SaveDC (surface->dc); + + mat = pattern->base.base.matrix; + status = cairo_matrix_invert (&mat); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&mat, &surface->ctm, &mat); + + p1x = _cairo_fixed_to_double (pattern->p1.x); + p1y = _cairo_fixed_to_double (pattern->p1.y); + p2x = _cairo_fixed_to_double (pattern->p2.x); + p2y = _cairo_fixed_to_double (pattern->p2.y); + cairo_matrix_translate (&mat, p1x, p1y); + + xd = p2x - p1x; + yd = p2y - p1y; + d = sqrt (xd*xd + yd*yd); + sn = yd/d; + cs = xd/d; + cairo_matrix_init (&rot, + cs, sn, + -sn, cs, + 0, 0); + cairo_matrix_multiply (&mat, &rot, &mat); + + _cairo_matrix_to_win32_xform (&mat, &xform); + + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); + + GetClipBox (surface->dc, &clip); + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + range_start = floor (clip.left / d); + range_stop = ceil (clip.right / d); + } else { + range_start = 0; + range_stop = 1; + } + num_ranges = range_stop - range_start; + num_stops = pattern->base.n_stops; + num_rects = num_stops - 1; + + /* Add an extra four points and two rectangles for EXTEND_PAD */ + vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); + rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); + + for (i = 0; i < num_ranges*num_rects; i++) { + vert[i*2].y = (LONG) clip.top; + if (i%num_rects == 0) { + stop = 0; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects; + vert[i*2].x = (LONG)(d*(range_start + i/num_rects)); + vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color); + } else { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = vert[i*2-1].Alpha; + } + + stop = i%num_rects + 1; + vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset)); + vert[i*2+1].y = (LONG) clip.bottom; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects - stop; + vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color); + + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + } + total_verts = 2*num_ranges*num_rects; + total_rects = num_ranges*num_rects; + + if (extend == CAIRO_EXTEND_PAD) { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = clip.right; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[i*2-1].Red; + vert[i*2+1].Green = vert[i*2-1].Green; + vert[i*2+1].Blue = vert[i*2-1].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + i++; + + vert[i*2].x = clip.left; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[0].Red; + vert[i*2].Green = vert[0].Green; + vert[i*2].Blue = vert[0].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = vert[0].x; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[0].Red; + vert[i*2+1].Green = vert[0].Green; + vert[i*2+1].Blue = vert[0].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + total_verts += 4; + total_rects += 2; + } + + if (!GradientFill (surface->dc, + vert, total_verts, + rect, total_rects, + GRADIENT_FILL_RECT_H)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); + + free (rect); + free (vert); + RestoreDC (surface->dc, -1); + + return 0; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_win32_printing_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + return CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _win32_print_path_info { + cairo_win32_surface_t *surface; +} win32_path_info_t; + +static cairo_status_t +_cairo_win32_printing_surface_path_move_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL); + } else { + MoveToEx (path_info->surface->dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y), + NULL); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_line_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + LineTo (path_info->surface->dc, (int) x, (int) y); + } else { + LineTo (path_info->surface->dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + win32_path_info_t *path_info = closure; + POINT points[3]; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (b->x); + y = _cairo_fixed_to_double (b->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[0].x = (LONG) x; + points[0].y = (LONG) y; + + x = _cairo_fixed_to_double (c->x); + y = _cairo_fixed_to_double (c->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[1].x = (LONG) x; + points[1].y = (LONG) y; + + x = _cairo_fixed_to_double (d->x); + y = _cairo_fixed_to_double (d->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[2].x = (LONG) x; + points[2].y = (LONG) y; + } else { + points[0].x = _cairo_fixed_integer_part (b->x); + points[0].y = _cairo_fixed_integer_part (b->y); + points[1].x = _cairo_fixed_integer_part (c->x); + points[1].y = _cairo_fixed_integer_part (c->y); + points[2].x = _cairo_fixed_integer_part (d->x); + points[2].y = _cairo_fixed_integer_part (d->y); + } + PolyBezierTo (path_info->surface->dc, points, 3); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_close_path (void *closure) +{ + win32_path_info_t *path_info = closure; + + CloseFigure (path_info->surface->dc); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, + cairo_path_fixed_t *path) +{ + win32_path_info_t path_info; + + path_info.surface = surface; + return _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_win32_printing_surface_path_move_to, + _cairo_win32_printing_surface_path_line_to, + _cairo_win32_printing_surface_path_curve_to, + _cairo_win32_printing_surface_path_close_path, + &path_info); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_show_page (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + /* Undo both SaveDC's that we did in start_page */ + RestoreDC (surface->dc, -2); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_win32_surface_t *surface = cairo_container_of (clipper, + cairo_win32_surface_t, + clipper); + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + if (path == NULL) { + RestoreDC (surface->dc, -1); + SaveDC (surface->dc); + + return CAIRO_STATUS_SUCCESS; + } + + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + SelectClipPath (surface->dc, RGN_AND); + + return status; +} + +static void +_cairo_win32_printing_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_solid_pattern_t clear; + cairo_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + + return _cairo_win32_printing_surface_paint_pattern (surface, source); +} + +static int +_cairo_win32_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return PS_ENDCAP_FLAT; + case CAIRO_LINE_CAP_ROUND: + return PS_ENDCAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: + return PS_ENDCAP_SQUARE; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_win32_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return PS_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: + return PS_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: + return PS_JOIN_BEVEL; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static void +_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) +{ + double s; + + s = fabs (m->xx); + if (fabs (m->xy) > s) + s = fabs (m->xy); + if (fabs (m->yx) > s) + s = fabs (m->yx); + if (fabs (m->yy) > s) + s = fabs (m->yy); + *scale = s; + s = 1.0/s; + cairo_matrix_scale (m, s, s); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_int_status_t status; + HPEN pen; + LOGBRUSH brush; + COLORREF color; + XFORM xform; + DWORD pen_style; + DWORD pen_width; + DWORD *dash_array; + HGDIOBJ obj; + unsigned int i; + cairo_solid_pattern_t clear; + cairo_matrix_t mat; + double scale; + double scaled_width; + double major, minor; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* Win32 does not support a dash offset. */ + if (style->num_dashes > 0 && style->dash_offset != 0.0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); + + cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); + _cairo_matrix_factor_out_scale (&mat, &scale); + + pen_style = PS_GEOMETRIC; + dash_array = NULL; + if (style->num_dashes) { + pen_style |= PS_USERSTYLE; + dash_array = calloc (sizeof (DWORD), style->num_dashes); + for (i = 0; i < style->num_dashes; i++) { + DWORD dashes = (DWORD) (scale * style->dash[i]); + /* zero dash-lengths cause ExtCreatePen to fail. Make the dashes + * longer if necessary. + */ + dash_array[i] = MAX(1, dashes); + } + } else { + pen_style |= PS_SOLID; + } + + SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL); + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + } else { + /* Color not used as the pen will only be used by WidenPath() */ + color = RGB (0,0,0); + } + brush.lbStyle = BS_SOLID; + brush.lbColor = color; + brush.lbHatch = 0; + pen_style |= _cairo_win32_line_cap (style->line_cap); + pen_style |= _cairo_win32_line_join (style->line_join); + scaled_width = scale * style->line_width; + if (scaled_width == 0.0) + return status; + pen_width = (DWORD)scaled_width; + if (pen_width == 0) { + /* ExtCreatePen will fail if passed zero width. We have to choose + * between drawing something too wide, or drawing nothing at all. + * Let's draw something. + */ + pen_width = 1; + } + pen = ExtCreatePen(pen_style, + pen_width, + &brush, + style->num_dashes, + dash_array); + if (pen == NULL) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); + obj = SelectObject (surface->dc, pen); + if (obj == NULL) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); + + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + if (status) + return status; + + /* + * Switch to user space to set line parameters + */ + SaveDC (surface->dc); + + /* Some printers don't handle transformed strokes. Avoid the transform + * if not required for the pen shape. Use the SVD here to find the major + * and minor scales then check if they differ by more than 1 device unit. + * If the difference is smaller, then just treat the scaling as uniform. + * This check ignores rotations as the pen shape is symmetric before + * transformation. + */ + _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor); + if (fabs (major - minor) > 1) { + /* Check if the matrix swaps the X and Y axes such that the diagonal + * is nearly zero. This was observed to cause problems with XPS export. + */ + if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) { + /* swap the X and Y axes to undo the axis swap in the matrix */ + cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; + cairo_matrix_multiply (&mat, &swap_xy, &mat); + } + _cairo_matrix_to_win32_xform (&mat, &xform); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + StrokePath (surface->dc); + } else { + if (!WidenPath (surface->dc)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); + if (!SelectClipPath (surface->dc, RGN_AND)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); + + /* Return to device space to paint the pattern */ + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + } + RestoreDC (surface->dc, -1); + DeleteObject (pen); + if (dash_array) + free (dash_array); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_solid_pattern_t clear; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + + surface->path_empty = TRUE; + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_win32_printing_surface_select_solid_brush (surface, source); + if (status) + return status; + + FillPath (surface->dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else if (surface->path_empty == FALSE) { + SaveDC (surface->dc); + SelectClipPath (surface->dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + RestoreDC (surface->dc, -1); + } + + fflush(stderr); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_matrix_t ctm; + cairo_glyph_t *unicode_glyphs; + cairo_scaled_font_subsets_glyph_t subset_glyph; + int i, first; + cairo_bool_t sequence_is_unicode; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Where possible reverse the glyph indices back to unicode + * characters. Strings of glyphs that could not be reversed to + * unicode will be printed with ETO_GLYPH_INDEX. + * + * As _cairo_win32_scaled_font_index_to_ucs4() is a slow + * operation, the font subsetting function + * _cairo_scaled_font_subsets_map_glyph() is used to obtain + * the unicode value because it caches the reverse mapping in + * the subsets. + */ + + if (surface->has_ctm) { + for (i = 0; i < num_glyphs; i++) + cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); + cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); + scaled_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &ctm, + &scaled_font->options); + } + + unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unicode_glyphs == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, + scaled_font, + glyphs[i].index, + NULL, 0, + &subset_glyph); + if (status) + goto fail; + + unicode_glyphs[i].index = subset_glyph.unicode; + } + + i = 0; + first = 0; + sequence_is_unicode = unicode_glyphs[0].index <= 0xffff; + while (i < num_glyphs) { + if (i == num_glyphs - 1 || + ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode)) + { + status = _cairo_win32_surface_show_glyphs_internal ( + surface, + op, + source, + sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], + i - first + 1, + scaled_font, + clip, + remaining_glyphs, + ! sequence_is_unicode); + first = i + 1; + if (i < num_glyphs - 1) + sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff; + } + i++; + } + +fail: + if (surface->has_ctm) + cairo_scaled_font_destroy (scaled_font); + + free (unicode_glyphs); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + cairo_pattern_t *opaque = NULL; + int i; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_solid_pattern_t clear; + cairo_scaled_font_t *local_scaled_font = NULL; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* When printing bitmap fonts to a printer DC, Windows may + * substitute an outline font for bitmap font. As the win32 + * font backend always uses a screen DC when obtaining the + * font metrics the metrics of the substituted font will not + * match the metrics that the win32 font backend returns. + * + * If we are printing a bitmap font, use fallback images to + * ensure the font is not substituted. + */ +#if CAIRO_HAS_WIN32_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { + if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } +#endif + +#if CAIRO_HAS_DWRITE_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } +#endif + + /* For non win32 fonts we need to check that each glyph has a + * path available. If a path is not available, + * _cairo_scaled_glyph_lookup() will return + * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be + * used. + */ + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + return status; + } + + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, + GetGValue (color) / 255.0, + GetBValue (color) / 255.0); + if (opaque->status) + return opaque->status; + source = opaque; + } + +#if CAIRO_HAS_DWRITE_FONT + /* For a printer, the dwrite path is not desirable as it goes through the + * bitmap-blitting GDI interop route. Better to create a win32 (GDI) font + * so that ExtTextOut can be used, giving the printer driver the chance + * to do the right thing with the text. + */ + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { + status = _cairo_dwrite_scaled_font_create_win32_scaled_font (scaled_font, &local_scaled_font); + if (status == CAIRO_STATUS_SUCCESS) { + scaled_font = local_scaled_font; + } else { + /* Reset status; we'll fall back to drawing glyphs as paths */ + status = CAIRO_STATUS_SUCCESS; + } + } +#endif + +#if CAIRO_HAS_WIN32_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && + source->type == CAIRO_PATTERN_TYPE_SOLID) + { + status = _cairo_win32_printing_surface_emit_win32_glyphs (surface, + op, + source, + glyphs, + num_glyphs, + scaled_font, + clip, + remaining_glyphs); + goto FINISH; + } +#endif + + SaveDC (surface->dc); + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + surface->has_ctm = TRUE; + surface->path_empty = TRUE; + BeginPath (surface->dc); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + break; + surface->ctm = old_ctm; + cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); + status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); + } + EndPath (surface->dc); + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) { + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_win32_printing_surface_select_solid_brush (surface, source); + if (status) + goto FINISH; + + SetPolyFillMode (surface->dc, WINDING); + FillPath (surface->dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else { + SelectClipPath (surface->dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + } + } + RestoreDC (surface->dc, -1); + + if (opaque) + cairo_pattern_destroy (opaque); + +FINISH: + if (local_scaled_font) + cairo_scaled_font_destroy (local_scaled_font); + + return status; +} + +static cairo_surface_t * +_cairo_win32_printing_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_start_page (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + XFORM xform; + double x_res, y_res; + cairo_matrix_t inverse_ctm; + cairo_status_t status; + + SaveDC (surface->dc); /* Save application context first, before doing MWT */ + + /* As the logical coordinates used by GDI functions (eg LineTo) + * are integers we need to do some additional work to prevent + * rounding errors. For example the obvious way to paint a recording + * pattern is to: + * + * SaveDC() + * transform the device context DC by the pattern to device matrix + * replay the recording surface + * RestoreDC() + * + * The problem here is that if the pattern to device matrix is + * [100 0 0 100 0 0], coordinates in the recording pattern such as + * (1.56, 2.23) which correspond to (156, 223) in device space + * will be rounded to (100, 200) due to (1.56, 2.23) being + * truncated to integers. + * + * This is solved by saving the current GDI CTM in surface->ctm, + * switch the GDI CTM to identity, and transforming all + * coordinates by surface->ctm before passing them to GDI. When + * painting a recording pattern, surface->ctm is transformed by the + * pattern to device matrix. + * + * For printing device contexts where 1 unit is 1 dpi, switching + * the GDI CTM to identity maximises the possible resolution of + * coordinates. + * + * If the device context is an EMF file, using an identity + * transform often provides insufficent resolution. The workaround + * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] + * and scale the cairo CTM by [16 0 0 16 0 0]. The + * SetWorldTransform function call to scale the GDI CTM by 1.0/16 + * will be recorded in the EMF followed by all the graphics + * functions by their coordinateds multiplied by 16. + * + * To support allowing the user to set a GDI CTM with scale < 1, + * we avoid switching to an identity CTM if the CTM xx and yy is < 1. + */ + SetGraphicsMode (surface->dc, GM_ADVANCED); + GetWorldTransform(surface->dc, &xform); + if (xform.eM11 < 1 && xform.eM22 < 1) { + cairo_matrix_init_identity (&surface->ctm); + surface->gdi_ctm.xx = xform.eM11; + surface->gdi_ctm.xy = xform.eM21; + surface->gdi_ctm.yx = xform.eM12; + surface->gdi_ctm.yy = xform.eM22; + surface->gdi_ctm.x0 = xform.eDx; + surface->gdi_ctm.y0 = xform.eDy; + } else { + surface->ctm.xx = xform.eM11; + surface->ctm.xy = xform.eM21; + surface->ctm.yx = xform.eM12; + surface->ctm.yy = xform.eM22; + surface->ctm.x0 = xform.eDx; + surface->ctm.y0 = xform.eDy; + cairo_matrix_init_identity (&surface->gdi_ctm); + if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); + } + + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); + inverse_ctm = surface->ctm; + status = cairo_matrix_invert (&inverse_ctm); + if (status) + return status; + + x_res = GetDeviceCaps (surface->dc, LOGPIXELSX); + y_res = GetDeviceCaps (surface->dc, LOGPIXELSY); + cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); + _cairo_surface_set_resolution (&surface->base, x_res, y_res); + + SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_win32_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + +static cairo_bool_t +_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +/** + * cairo_win32_printing_surface_create: + * @hdc: the DC to create a surface for + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. The DC should be a printing DC; + * antialiasing will be ignored, and GDI will be used as much as + * possible to draw to the surface. + * + * The returned surface will be wrapped using the paginated surface to + * provide correct complex rendering behaviour; show_page() and + * associated methods must be used for correct output. + * + * Return value: the newly created surface + * + * Since: 1.6 + **/ +cairo_surface_t * +cairo_win32_printing_surface_create (HDC hdc) +{ + cairo_win32_surface_t *surface; + cairo_surface_t *paginated; + RECT rect; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_win32_printing_surface_clipper_intersect_clip_path); + + surface->image = NULL; + surface->format = CAIRO_FORMAT_RGB24; + surface->content = CAIRO_CONTENT_COLOR_ALPHA; + surface->d3d9surface = NULL; + + surface->dc = hdc; + surface->bitmap = NULL; + surface->is_dib = FALSE; + surface->saved_dc_bitmap = NULL; + surface->brush = NULL; + surface->old_brush = NULL; + surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); + if (surface->font_subsets == NULL) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + GetClipBox(hdc, &rect); + surface->extents.x = rect.left; + surface->extents.y = rect.top; + surface->extents.width = rect.right - rect.left; + surface->extents.height = rect.bottom - rect.top; + + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; + + _cairo_win32_printing_surface_init_ps_mode (surface); + _cairo_win32_printing_surface_init_image_support (surface); + _cairo_win32_printing_surface_init_language_pack (surface); + _cairo_surface_init (&surface->base, + &cairo_win32_printing_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + paginated = _cairo_paginated_surface_create (&surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_win32_surface_paginated_backend); + + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + + return paginated; +} + +cairo_bool_t +_cairo_surface_is_win32_printing (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_printing_surface_backend; +} + +static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + _cairo_win32_printing_surface_create_similar, + _cairo_win32_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + _cairo_win32_printing_surface_show_page, + _cairo_win32_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_win32_printing_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _cairo_win32_printing_surface_paint, + NULL, /* mask */ + _cairo_win32_printing_surface_stroke, + _cairo_win32_printing_surface_fill, + _cairo_win32_printing_surface_show_glyphs, + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ +}; + +static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { + _cairo_win32_printing_surface_start_page, + _cairo_win32_printing_surface_set_paginated_mode, + NULL, /* set_bounding_box */ + NULL, /* _cairo_win32_printing_surface_has_fallback_images, */ + _cairo_win32_printing_surface_supports_fine_grained_fallbacks, +}; diff --git a/libs/cairo/src/cairo-win32-private.h b/libs/cairo/src/cairo-win32-private.h new file mode 100644 index 000000000..571b7547c --- /dev/null +++ b/libs/cairo/src/cairo-win32-private.h @@ -0,0 +1,218 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_WIN32_PRIVATE_H +#define CAIRO_WIN32_PRIVATE_H + +#include "cairo-win32.h" +#include "cairoint.h" +#include "cairo-surface-clipper-private.h" + +#ifndef SHADEBLENDCAPS +#define SHADEBLENDCAPS 120 +#endif +#ifndef SB_NONE +#define SB_NONE 0 +#endif + +#define WIN32_FONT_LOGICAL_SCALE 1 + + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_win32_surface { + cairo_surface_t base; + + cairo_format_t format; + + HDC dc; + + struct IDirect3DSurface9 *d3d9surface; + + /* We create off-screen surfaces as DIBs or DDBs, based on what we created + * originally*/ + HBITMAP bitmap; + cairo_bool_t is_dib; + + /* Used to save the initial 1x1 monochrome bitmap for the DC to + * select back into the DC before deleting the DC and our + * bitmap. For Windows XP, this doesn't seem to be necessary + * ... we can just delete the DC and that automatically unselects + * out bitmap. But it's standard practice so apparently is needed + * on some versions of Windows. + */ + HBITMAP saved_dc_bitmap; + + cairo_surface_t *image; + + cairo_rectangle_int_t extents; + + /* Initial clip bits + * We need these kept around so that we maintain + * whatever clip was set on the original DC at creation + * time when cairo is asked to reset the surface clip. + */ + cairo_rectangle_int_t clip_rect; + HRGN initial_clip_rgn; + cairo_bool_t had_simple_clip; + cairo_region_t *clip_region; + + /* For path clipping to the printing-surface */ + cairo_surface_clipper_t clipper; + + /* Surface DC flags */ + uint32_t flags; + + /* printing surface bits */ + cairo_paginated_mode_t paginated_mode; + cairo_content_t content; + cairo_bool_t path_empty; + cairo_bool_t has_ctm; + cairo_matrix_t ctm; + cairo_bool_t has_gdi_ctm; + cairo_matrix_t gdi_ctm; + HBRUSH brush, old_brush; + cairo_scaled_font_subsets_t *font_subsets; +} cairo_win32_surface_t; + +/* Surface DC flag values */ +enum { + /* If this is a surface created for printing or not */ + CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0), + + /* Whether the DC is a display DC or not */ + CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1), + + /* Whether we can use BitBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2), + + /* Whether we can use AlphaBlend with this surface */ + CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3), + + /* Whether we can use StretchBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4), + + /* Whether we can use StretchDIBits with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5), + + /* Whether we can use GradientFill rectangles with this surface */ + CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), + + /* if this DDB surface can be converted to a DIB if necessary */ + CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<9), +}; + +cairo_status_t +_cairo_win32_print_gdi_error (const char *context); + +cairo_bool_t +_cairo_surface_is_win32 (cairo_surface_t *surface); + +cairo_bool_t +_cairo_surface_is_win32_printing (cairo_surface_t *surface); + +cairo_status_t +_cairo_win32_surface_finish (void *abstract_surface); + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +uint32_t +_cairo_win32_flags_for_dc (HDC dc); + +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region); + +cairo_int_status_t +_cairo_win32_surface_show_glyphs_internal (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs, + cairo_bool_t glyph_indices); + +cairo_int_status_t +_cairo_win32_surface_show_glyphs (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +cairo_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height); + +cairo_status_t +_cairo_win32_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_content_t content, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out); + +static inline void +_cairo_matrix_to_win32_xform (const cairo_matrix_t *m, + XFORM *xform) +{ + xform->eM11 = (FLOAT) m->xx; + xform->eM21 = (FLOAT) m->xy; + xform->eM12 = (FLOAT) m->yx; + xform->eM22 = (FLOAT) m->yy; + xform->eDx = (FLOAT) m->x0; + xform->eDy = (FLOAT) m->y0; +} + +cairo_int_status_t +_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface); + +cairo_int_status_t +_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface); + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); + +cairo_bool_t +_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); + +cairo_bool_t +_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); + +#ifdef CAIRO_HAS_DWRITE_FONT + +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_int_status_t +_cairo_dwrite_scaled_font_create_win32_scaled_font(cairo_scaled_font_t *scaled_font, + cairo_scaled_font_t **new_font); + +#endif /* CAIRO_HAS_DWRITE_FONT */ +CAIRO_END_DECLS +#endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-win32-refptr.h b/libs/cairo/src/cairo-win32-refptr.h new file mode 100644 index 000000000..8c0ec3338 --- /dev/null +++ b/libs/cairo/src/cairo-win32-refptr.h @@ -0,0 +1,147 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_WIN32_REFPTR_H +#define CAIRO_WIN32_REFPTR_H + +template class TemporaryRef; + +/** + * RefPtr points to a refcounted thing that has AddRef and Release + * methods to increase/decrease the refcount, respectively. After a + * RefPtr is assigned a T*, the T* can be used through the RefPtr + * as if it were a T*. + * + * A RefPtr can forget its underlying T*, which results in the T* + * being wrapped in a temporary object until the T* is either + * re-adopted from or released by the temporary. + */ +template +class RefPtr +{ + // To allow them to use unref() + friend class TemporaryRef; + + struct dontRef {}; + +public: + RefPtr() : ptr(0) { } + RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} + RefPtr(const TemporaryRef& o) : ptr(o.drop()) {} + RefPtr(T* t) : ptr(ref(t)) {} + + template + RefPtr(const RefPtr& o) : ptr(ref(o.get())) {} + + ~RefPtr() { unref(ptr); } + + RefPtr& operator=(const RefPtr& o) { + assign(ref(o.ptr)); + return *this; + } + RefPtr& operator=(const TemporaryRef& o) { + assign(o.drop()); + return *this; + } + RefPtr& operator=(T* t) { + assign(ref(t)); + return *this; + } + + template + RefPtr& operator=(const RefPtr& o) { + assign(ref(o.get())); + return *this; + } + + TemporaryRef forget() { + T* tmp = ptr; + ptr = 0; + return TemporaryRef(tmp, dontRef()); + } + + T* get() const { return ptr; } + operator T*() const { return ptr; } + T* operator->() const { return ptr; } + T& operator*() const { return *ptr; } + template + operator TemporaryRef() { return TemporaryRef(ptr); } + + /** + * WARNING for ease of use, passing a reference will release/clear out ptr! + * We null out the ptr before returning its address so people passing byref + * as input will most likely get functions returning errors rather than accessing + * freed memory. Further more accessing it after this point if it hasn't + * been set will produce a null pointer dereference. + */ + T** operator&() + { + if (ptr) { + ptr->Release(); + ptr = NULL; + } + return &ptr; + } + +private: + void assign(T* t) { + unref(ptr); + ptr = t; + } + + T* ptr; + + static inline T* ref(T* t) { + if (t) { + t->AddRef(); + } + return t; + } + + static inline void unref(T* t) { + if (t) { + t->Release(); + } + } +}; + +/** + * TemporaryRef represents an object that holds a temporary + * reference to a T. TemporaryRef objects can't be manually ref'd or + * unref'd (being temporaries, not lvalues), so can only relinquish + * references to other objects, or unref on destruction. + */ +template +class TemporaryRef +{ + // To allow it to construct TemporaryRef from a bare T* + friend class RefPtr; + + typedef typename RefPtr::dontRef dontRef; + +public: + TemporaryRef(T* t) : ptr(RefPtr::ref(t)) {} + TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} + + template + TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} + + ~TemporaryRef() { RefPtr::unref(ptr); } + + T* drop() const { + T* tmp = ptr; + ptr = 0; + return tmp; + } + +private: + TemporaryRef(T* t, const dontRef&) : ptr(t) {} + + mutable T* ptr; + + TemporaryRef(); + TemporaryRef& operator=(const TemporaryRef&); +}; + +#endif diff --git a/libs/cairo/src/cairo-win32-surface.c b/libs/cairo/src/cairo-win32-surface.c new file mode 100644 index 000000000..2d7395590 --- /dev/null +++ b/libs/cairo/src/cairo-win32-surface.c @@ -0,0 +1,4056 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +/* We require at least Windows 7 features */ +#if !defined(WINVER) || (WINVER < 0x0601) +# define WINVER 0x0601 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0601) +# define _WIN32_WINNT 0x0601 +#endif + +#include "cairoint.h" + +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-paginated-private.h" +#include "cairo-win32-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-gstate-private.h" +#include "cairo-private.h" +#include +#include +#include + +#if defined(__MINGW32__) && !defined(ETO_PDY) +# define ETO_PDY 0x2000 +#endif + +#undef DEBUG_COMPOSITE + +/* for older SDKs */ +#ifndef SHADEBLENDCAPS +#define SHADEBLENDCAPS 120 +#endif +#ifndef SB_NONE +#define SB_NONE 0x00000000 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +/** + * SECTION:cairo-win32 + * @Title: Win32 Surfaces + * @Short_Description: Microsoft Windows surface support + * @See_Also: #cairo_surface_t + * + * The Microsoft Windows surface is used to render cairo graphics to + * Microsoft Windows windows, bitmaps, and printing device contexts. + * + * The surface returned by cairo_win32_printing_surface_create() is of surface + * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface + * type. + * + * The surface returned by the other win32 constructors is of surface type + * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. + */ + +/** + * CAIRO_HAS_WIN32_SURFACE: + * + * Defined if the Microsoft Windows surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +static const cairo_surface_backend_t cairo_win32_surface_backend; + +/** + * _cairo_win32_print_gdi_error: + * @context: context string to display along with the error + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_gdi_error (const char *context) +{ + void *lpMsgBuf; + DWORD last_error = GetLastError (); + + if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown GDI error", context); + } else { + fwprintf (stderr, L"%s: %S", context, (wchar_t *)lpMsgBuf); + + LocalFree (lpMsgBuf); + } + fflush(stderr); + + /* We should switch off of last_status, but we'd either return + * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there + * is no CAIRO_STATUS_UNKNOWN_ERROR. + */ + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + +uint32_t +_cairo_win32_flags_for_dc (HDC dc) +{ + uint32_t flags = 0; + + if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) { + flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; + + /* These will always be possible, but the actual GetDeviceCaps + * calls will return whether they're accelerated or not. + * We may want to use our own (pixman) routines sometimes + * if they're eventually faster, but for now have GDI do + * everything. + */ + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; + } else { + int cap; + + cap = GetDeviceCaps(dc, SHADEBLENDCAPS); + if (cap != SB_NONE) + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + + cap = GetDeviceCaps(dc, RASTERCAPS); + if (cap & RC_BITBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + if (cap & RC_STRETCHBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + if (cap & RC_STRETCHDIB) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; + } + + return flags; +} + +static cairo_status_t +_create_dc_and_bitmap (cairo_win32_surface_t *surface, + HDC original_dc, + cairo_format_t format, + int width, + int height, + unsigned char **bits_out, + int *rowstride_out) +{ + cairo_status_t status; + + BITMAPINFO *bitmap_info = NULL; + struct { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[2]; + } bmi_stack; + void *bits; + + int num_palette = 0; /* Quiet GCC */ + int i; + + surface->dc = NULL; + surface->bitmap = NULL; + surface->is_dib = FALSE; + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + num_palette = 0; + break; + + case CAIRO_FORMAT_A8: + num_palette = 256; + break; + + case CAIRO_FORMAT_A1: + num_palette = 2; + break; + } + + if (num_palette > 2) { + bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); + if (!bitmap_info) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + bitmap_info = (BITMAPINFO *)&bmi_stack; + } + + bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; + bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ + bitmap_info->bmiHeader.biSizeImage = 0; + bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ + bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ + bitmap_info->bmiHeader.biPlanes = 1; + + switch (format) { + /* We can't create real RGB24 bitmaps because something seems to + * break if we do, especially if we don't set up an image + * fallback. It could be a bug with using a 24bpp pixman image + * (and creating one with masks). So treat them like 32bpp. + * Note: This causes problems when using BitBlt/AlphaBlend/etc! + * see end of file. + */ + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + bitmap_info->bmiHeader.biBitCount = 32; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ + bitmap_info->bmiHeader.biClrImportant = 0; + break; + + case CAIRO_FORMAT_A8: + bitmap_info->bmiHeader.biBitCount = 8; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 256; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 256; i++) { + bitmap_info->bmiColors[i].rgbBlue = i; + bitmap_info->bmiColors[i].rgbGreen = i; + bitmap_info->bmiColors[i].rgbRed = i; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + + break; + + case CAIRO_FORMAT_A1: + bitmap_info->bmiHeader.biBitCount = 1; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 2; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 2; i++) { + bitmap_info->bmiColors[i].rgbBlue = i * 255; + bitmap_info->bmiColors[i].rgbGreen = i * 255; + bitmap_info->bmiColors[i].rgbRed = i * 255; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + + break; + } + + surface->dc = CreateCompatibleDC (original_dc); + if (!surface->dc) + goto FAIL; + + surface->bitmap = CreateDIBSection (surface->dc, + bitmap_info, + DIB_RGB_COLORS, + &bits, + NULL, 0); + if (!surface->bitmap) + goto FAIL; + + surface->is_dib = TRUE; + + GdiFlush(); + + surface->saved_dc_bitmap = SelectObject (surface->dc, + surface->bitmap); + if (!surface->saved_dc_bitmap) + goto FAIL; + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (bits_out) + *bits_out = bits; + + if (rowstride_out) { + /* Windows bitmaps are padded to 32-bit (dword) boundaries */ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + *rowstride_out = 4 * width; + break; + + case CAIRO_FORMAT_A8: + *rowstride_out = (width + 3) & ~3; + break; + + case CAIRO_FORMAT_A1: + *rowstride_out = ((width + 31) & ~31) / 8; + break; + } + } + + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (surface->saved_dc_bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + surface->saved_dc_bitmap = NULL; + } + + if (surface->bitmap) { + DeleteObject (surface->bitmap); + surface->bitmap = NULL; + } + + if (surface->dc) { + DeleteDC (surface->dc); + surface->dc = NULL; + } + + return status; +} + +static cairo_surface_t * +_cairo_win32_surface_create_for_dc (HDC original_dc, + cairo_format_t format, + int width, + int height) +{ + cairo_status_t status; + cairo_win32_surface_t *surface; + unsigned char *bits; + int rowstride; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->clip_region = NULL; + + status = _create_dc_and_bitmap (surface, original_dc, format, + width, height, + &bits, &rowstride); + if (status) + goto FAIL; + + surface->image = cairo_image_surface_create_for_data (bits, format, + width, height, rowstride); + status = surface->image->status; + if (status) + goto FAIL; + + surface->format = format; + surface->d3d9surface = NULL; + + surface->clip_rect.x = 0; + surface->clip_rect.y = 0; + surface->clip_rect.width = width; + surface->clip_rect.height = height; + + surface->initial_clip_rgn = NULL; + surface->had_simple_clip = FALSE; + + surface->extents = surface->clip_rect; + surface->font_subsets = NULL; + + _cairo_surface_init (&surface->base, + &cairo_win32_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format)); + + return &surface->base; + + FAIL: + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + free (surface); + + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_win32_surface_create_similar_internal (void *abstract_src, + cairo_content_t content, + int width, + int height, + cairo_bool_t force_dib) +{ + cairo_win32_surface_t *src = abstract_src; + cairo_format_t format = _cairo_format_from_content (content); + cairo_surface_t *new_surf = NULL; + + /* We force a DIB always if: + * - we need alpha; or + * - the parent is a DIB; or + * - the parent is for printing (because we don't care about the bit depth at that point) + * + * We also might end up with a DIB even if a DDB is requested if DDB creation failed + * due to out of memory. + */ + if (src->is_dib || + (content & CAIRO_CONTENT_ALPHA) || + src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) + { + force_dib = TRUE; + } + + if (!force_dib) { + /* try to create a ddb */ + new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height); + + if (new_surf->status != CAIRO_STATUS_SUCCESS) + new_surf = NULL; + } + + if (new_surf == NULL) { + new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height); + } + + return new_surf; +} + +cairo_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE); +} + +cairo_status_t +_cairo_win32_surface_finish (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) + cairo_surface_destroy (surface->image); + + /* If we created the Bitmap and DC, destroy them */ + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } else { + _cairo_win32_restore_initial_clip (surface); + } + + if (surface->d3d9surface) { + IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); + IDirect3DSurface9_Release (surface->d3d9surface); + } + + if (surface->initial_clip_rgn) + DeleteObject (surface->initial_clip_rgn); + + if (surface->font_subsets != NULL) + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + + return CAIRO_STATUS_SUCCESS; +} + +static void +get_d3d9_dc_and_clear_clip (cairo_win32_surface_t *surface) +{ + IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc); + // The DC that we get back from the surface will not have + // a clip so clear surface->clip_region so that we don't think we have + // one when we don't. + _cairo_win32_surface_set_clip_region (surface, NULL); +} + +static cairo_status_t +_cairo_win32_surface_d3d9_lock_rect (cairo_win32_surface_t *surface, + int x, + int y, + int width, + int height, + cairo_image_surface_t **local_out) +{ + cairo_image_surface_t *local; + cairo_int_status_t status; + + RECT rectin = { x, y, x+width, y+height }; + D3DLOCKED_RECT rectout; + HRESULT hr; + hr = IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); + hr = IDirect3DSurface9_LockRect (surface->d3d9surface, + &rectout, &rectin, 0); + surface->dc = 0; // Don't use the DC when this is locked! + if (hr) { + get_d3d9_dc_and_clear_clip (surface); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + local = cairo_image_surface_create_for_data (rectout.pBits, + surface->format, + width, height, + rectout.Pitch); + if (local == NULL) { + IDirect3DSurface9_UnlockRect (surface->d3d9surface); + get_d3d9_dc_and_clear_clip (surface); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (local->base.status) { + IDirect3DSurface9_UnlockRect (surface->d3d9surface); + get_d3d9_dc_and_clear_clip (surface); + return local->base.status; + } + + *local_out = local; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, + int x, + int y, + int width, + int height, + cairo_win32_surface_t **local_out) +{ + cairo_win32_surface_t *local; + cairo_int_status_t status; + cairo_content_t content = _cairo_content_from_format (surface->format); + + local = + (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal + (surface, content, width, height, TRUE); + if (local == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (local->base.status) + return local->base.status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + + /* Only BitBlt if the source surface supports it. */ + if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) && + BitBlt (local->dc, + 0, 0, + width, height, + surface->dc, + x, y, + SRCCOPY)) + { + status = CAIRO_STATUS_SUCCESS; + } + + if (status) { + /* If we failed here, most likely the source or dest doesn't + * support BitBlt/AlphaBlend (e.g. a printer). + * You can't reliably get bits from a printer DC, so just fill in + * the surface as white (common case for printing). + */ + + RECT r; + r.left = r.top = 0; + r.right = width; + r.bottom = height; + FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); + } + + *local_out = local; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface) +{ + cairo_win32_surface_t *new_surface; + int width = surface->extents.width + surface->extents.x; + int height = surface->extents.height + surface->extents.y; + + BOOL ok; + HBITMAP oldbitmap; + + new_surface = (cairo_win32_surface_t*) + _cairo_win32_surface_create_for_dc (surface->dc, + surface->format, + width, + height); + + if (new_surface->base.status) + return; + + /* DDB can't be 32bpp, so BitBlt is safe */ + ok = BitBlt (new_surface->dc, + 0, 0, width, height, + surface->dc, + 0, 0, SRCCOPY); + + if (!ok) + goto out; + + /* Now swap around new_surface and surface's internal bitmap + * pointers. */ + DeleteDC (new_surface->dc); + new_surface->dc = NULL; + + oldbitmap = SelectObject (surface->dc, new_surface->bitmap); + DeleteObject (oldbitmap); + + surface->image = new_surface->image; + surface->is_dib = new_surface->is_dib; + surface->bitmap = new_surface->bitmap; + + new_surface->bitmap = NULL; + new_surface->image = NULL; + + /* Finally update flags */ + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + + out: + cairo_surface_destroy ((cairo_surface_t*)new_surface); +} + +static cairo_status_t +_cairo_win32_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->image && !surface->is_dib && surface->bitmap && + (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0) + { + /* This is a DDB, and we're being asked to use it as a source for + * something that we couldn't support natively. So turn it into + * a DIB, so that we have an equivalent image surface, as long + * as we're allowed to via flags. + */ + _cairo_win32_convert_ddb_to_dib (surface); + } + + if (surface->image) { + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + if (surface->d3d9surface) { + cairo_image_surface_t *local; + status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, 0, 0, + surface->extents.width, + surface->extents.height, &local); + if (status) + return status; + + *image_out = local; + *image_extra = surface; + } else { + cairo_win32_surface_t *local; + status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, + surface->extents.width, + surface->extents.height, &local); + if (status) + return status; + + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + } + // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points + // to the original surface to get back the d3d9surface and properly unlock. + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = image_extra; + + if (local && local->d3d9surface) { + IDirect3DSurface9_UnlockRect (local->d3d9surface); + get_d3d9_dc_and_clear_clip (surface); + cairo_surface_destroy ((cairo_surface_t *)image); + } else { + cairo_surface_destroy ((cairo_surface_t *)local); + } +} + +static cairo_status_t +_cairo_win32_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->image) { + GdiFlush(); + + *image_out = (cairo_image_surface_t *) surface->image; + *image_extra = NULL; + *image_rect = surface->extents; + return CAIRO_STATUS_SUCCESS; + } + + if (surface->d3d9surface) { + cairo_image_surface_t *local = NULL; + status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, + interest_rect->x, + interest_rect->y, + interest_rect->width, + interest_rect->height, &local); + + if (status) + return status; + + *image_out = local; + *image_extra = surface; + } else { + cairo_win32_surface_t *local = NULL; + status = _cairo_win32_surface_get_subimage (abstract_surface, + interest_rect->x, + interest_rect->y, + interest_rect->width, + interest_rect->height, &local); + + if (status) + return status; + + *image_out = (cairo_image_surface_t *) local->image; + *image_extra = local; + } + // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points + // to the original surface to get back the d3d9surface and properly unlock. + + *image_rect = *interest_rect; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = image_extra; + + if (!local) + return; + + if (local->d3d9surface) { + IDirect3DSurface9_UnlockRect (local->d3d9surface); + get_d3d9_dc_and_clear_clip (surface); + cairo_surface_destroy ((cairo_surface_t *)image); + } else { + + /* clear any clip that's currently set on the surface + so that we can blit uninhibited. */ + _cairo_win32_surface_set_clip_region (surface, NULL); + + if (!BitBlt (surface->dc, + image_rect->x, image_rect->y, + image_rect->width, image_rect->height, + local->dc, + 0, 0, + SRCCOPY)) + _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); + + cairo_surface_destroy ((cairo_surface_t *)local); + } + +} + +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + /* The semantics we want is that any clip set by cairo combines + * is intersected with the clip on device context that the + * surface was created for. To implement this, we need to + * save the original clip when first setting a clip on surface. + */ + + /* Clear any clip set by cairo, return to the original first */ + status = _cairo_win32_restore_initial_clip (surface); + + /* Then combine any new region with it */ + if (region) { + cairo_rectangle_int_t extents; + int num_rects; + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + + /* Create a GDI region for the cairo region */ + + cairo_region_get_extents (region, &extents); + num_rects = cairo_region_num_rectangles (region); + /* XXX see notes in _cairo_win32_save_initial_clip -- + * this code will interact badly with a HDC which had an initial + * world transform -- we should probably manually transform the + * region rects, because SelectClipRgn takes device units, not + * logical units (unlike IntersectClipRect). + */ + + data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); + data = malloc (data_size); + if (!data) + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + rects = (RECT *)data->Buffer; + + data->rdh.dwSize = sizeof (RGNDATAHEADER); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = num_rects; + data->rdh.nRgnSize = num_rects * sizeof (RECT); + data->rdh.rcBound.left = extents.x; + data->rdh.rcBound.top = extents.y; + data->rdh.rcBound.right = extents.x + extents.width; + data->rdh.rcBound.bottom = extents.y + extents.height; + + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].left = rect.x; + rects[i].top = rect.y; + rects[i].right = rect.x + rect.width; + rects[i].bottom = rect.y + rect.height; + } + + gdi_region = ExtCreateRegion (NULL, data_size, data); + free (data); + + if (!gdi_region) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* AND the new region into our DC */ + if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + + DeleteObject (gdi_region); + } + + return status; +} + +#if !defined(AC_SRC_OVER) +#define AC_SRC_OVER 0x00 +#pragma pack(1) +typedef struct { + BYTE BlendOp; + BYTE BlendFlags; + BYTE SourceConstantAlpha; + BYTE AlphaFormat; +}BLENDFUNCTION; +#pragma pack() +#endif + +/* for compatibility with VC++ 6 */ +#ifndef AC_SRC_ALPHA +#define AC_SRC_ALPHA 0x01 +#endif + +typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest, + int nXOriginDest, + int nYOriginDest, + int nWidthDest, + int hHeightDest, + HDC hdcSrc, + int nXOriginSrc, + int nYOriginSrc, + int nWidthSrc, + int nHeightSrc, + BLENDFUNCTION blendFunction); + +static cairo_int_status_t +_composite_alpha_blend (cairo_win32_surface_t *dst, + cairo_win32_surface_t *src, + int alpha, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ + static unsigned alpha_blend_checked = FALSE; + static cairo_alpha_blend_func_t alpha_blend = NULL; + + BLENDFUNCTION blend_function; + + /* Check for AlphaBlend dynamically */ + if (!alpha_blend_checked) { + HMODULE msimg32_dll = LoadLibraryW (L"msimg32"); + + if (msimg32_dll != NULL) + alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll, + "AlphaBlend"); + alpha_blend_checked = TRUE; + } + + if (alpha_blend == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + blend_function.BlendOp = AC_SRC_OVER; + blend_function.BlendFlags = 0; + blend_function.SourceConstantAlpha = alpha; + blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; + + if (!alpha_blend (dst->dc, + dst_x, dst_y, + dst_w, dst_h, + src->dc, + src_x, src_y, + src_w, src_h, + blend_function)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)"); + + return CAIRO_STATUS_SUCCESS; +} + +/* makes the alpha channel in a RGB24 surface 0xff */ +static void +make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r) +{ + int x; int y; + for (y = 0; y < src_r.height; y++) { + for (x = 0; x < src_r.width; x++) { + image->data[(src_r.y + y) * image->stride + (src_r.x + x)*4 + 3] = 0xff; + } + } +} + +static cairo_int_status_t +_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src, + cairo_image_surface_t *src_image, + cairo_win32_surface_t *dst, + cairo_rectangle_int_t src_extents, + cairo_rectangle_int_t src_r, + cairo_rectangle_int_t dst_r, + int alpha, + cairo_bool_t needs_alpha, + cairo_bool_t needs_scale) +{ + /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */ + if (src_image) { + if (needs_alpha || needs_scale) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { + BITMAPINFO bi; + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = src_image->width; + bi.bmiHeader.biHeight = - src_image->height; + bi.bmiHeader.biSizeImage = 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + /* StretchDIBits is broken with top-down dibs; you need to do some + * special munging to make the coordinate space work (basically, + * need to address everything based on the bottom left, instead of top left, + * and need to tell it to flip the resulting image. + * + * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments. + */ + if (!StretchDIBits (dst->dc, + /* dst x,y,w,h */ + dst_r.x, dst_r.y + dst_r.height - 1, + dst_r.width, - (int) dst_r.height, + /* src x,y,w,h */ + src_r.x, src_extents.height - src_r.y + 1, + src_r.width, - (int) src_r.height, + src_image->data, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)"); + } + } else if (!needs_alpha) { + if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) { + /* Because we store RGB24 & ARGB32 in the same way GDI has no way + * to ignore the alpha channel from a RGB24 source. Therefore, we set + * the alpha channel in our RGB24 source to opaque so that we can treat + * it like ARGB32. */ + GdiFlush(); + make_opaque(src->image, src_r); + } + /* BitBlt or StretchBlt? */ + if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) { + if (!BitBlt (dst->dc, + dst_r.x, dst_r.y, + dst_r.width, dst_r.height, + src->dc, + src_r.x, src_r.y, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)"); + } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { + /* StretchBlt? */ + /* XXX check if we want HALFTONE, based on the src filter */ + BOOL success; + int oldmode = SetStretchBltMode(dst->dc, HALFTONE); + success = StretchBlt(dst->dc, + dst_r.x, dst_r.y, + dst_r.width, dst_r.height, + src->dc, + src_r.x, src_r.y, + src_r.width, src_r.height, + SRCCOPY); + SetStretchBltMode(dst->dc, oldmode); + + if (!success) + return _cairo_win32_print_gdi_error ("StretchBlt"); + } + } else if (needs_alpha && !needs_scale) { + RECT r = {0, 0, 5000, 5000}; + //FillRect(dst->dc, &r, GetStockObject(DKGRAY_BRUSH)); + return _composite_alpha_blend (dst, src, alpha, + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* from pixman-private.h */ +#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) + +static cairo_int_status_t +_cairo_win32_surface_composite (cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_win32_surface_t *dst = abstract_dst; + cairo_win32_surface_t *src; + cairo_surface_pattern_t *src_surface_pattern; + int alpha; + double scalex, scaley; + cairo_fixed_t x0_fixed, y0_fixed; + cairo_int_status_t status; + + cairo_bool_t needs_alpha, needs_scale, needs_repeat, needs_pad; + cairo_image_surface_t *src_image = NULL; + + cairo_format_t src_format; + cairo_rectangle_int_t src_extents; + + cairo_rectangle_int_t src_r = { src_x, src_y, width, height }; + cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height }; + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n", + op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); +#endif + + /* If the destination can't do any of these, then + * we may as well give up, since this is what we'll + * look to for optimization. + */ + if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT | + CAIRO_WIN32_SURFACE_CAN_ALPHABLEND | + CAIRO_WIN32_SURFACE_CAN_STRETCHBLT | + CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + == 0) + { + goto UNSUPPORTED; + } + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + goto UNSUPPORTED; + + if (pattern->extend != CAIRO_EXTEND_NONE && + pattern->extend != CAIRO_EXTEND_REPEAT && + pattern->extend != CAIRO_EXTEND_PAD) + goto UNSUPPORTED; + + if (mask_pattern) { + /* FIXME: When we fully support RENDER style 4-channel + * masks we need to check r/g/b != 1.0. + */ + if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; + } else { + alpha = 255; + } + + src_surface_pattern = (cairo_surface_pattern_t *)pattern; + src = (cairo_win32_surface_t *)src_surface_pattern->surface; + + if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE && + dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + { + /* In some very limited cases, we can use StretchDIBits to draw + * an image surface directly: + * - source is CAIRO_FORMAT_ARGB32 + * - dest is CAIRO_FORMAT_ARGB32 + * - alpha is 255 + * - operator is SOURCE or OVER + * - image stride is 4*width + */ + src_image = (cairo_image_surface_t*) src; + + if (src_image->format != CAIRO_FORMAT_RGB24 || + dst->format != CAIRO_FORMAT_RGB24 || + alpha != 255 || + (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || + src_image->stride != (src_image->width * 4)) + { + goto UNSUPPORTED; + } + + src_format = src_image->format; + src_extents.x = 0; + src_extents.y = 0; + src_extents.width = src_image->width; + src_extents.height = src_image->height; + } else if (src->base.backend != dst->base.backend) { + goto UNSUPPORTED; + } else { + src_format = src->format; + src_extents = src->extents; + } + + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n", + src_extents.width, src_extents.height, + src_x, src_y, + dst_x, dst_y, width, height, + pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy); +#endif + + /* We can only use GDI functions if the source and destination rectangles + * are on integer pixel boundaries. Figure that out here. + */ + x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx); + y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy); + + if (pattern->matrix.yx != 0.0 || + pattern->matrix.xy != 0.0 || + !_cairo_fixed_is_integer(x0_fixed) || + !_cairo_fixed_is_integer(y0_fixed)) + { + goto UNSUPPORTED; + } + + scalex = pattern->matrix.xx; + scaley = pattern->matrix.yy; + + src_r.x += _cairo_fixed_integer_part(x0_fixed); + src_r.y += _cairo_fixed_integer_part(y0_fixed); + + /* Success, right? */ + if (scalex == 0.0 || scaley == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (scalex != 1.0 || scaley != 1.0) + goto UNSUPPORTED; + + /* If the src coordinates are outside of the source surface bounds, + * we have to fix them up, because this is an error for the GDI + * functions. + */ + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n", + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + fflush (stderr); +#endif + + /* If the src rectangle doesn't wholly lie within the src extents, + * fudge things. We really need to do fixup on the unpainted + * region -- e.g. the SOURCE operator is broken for areas outside + * of the extents, because it won't clear that area to transparent + * black. + */ + + needs_pad = FALSE; + if (pattern->extend != CAIRO_EXTEND_REPEAT) { + needs_repeat = FALSE; + + /* If the src rect and the extents of the source image don't overlap at all, + * we can't do anything useful here. + */ + if (src_r.x > src_extents.width || src_r.y > src_extents.height || + (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0) + { + if (op == CAIRO_OPERATOR_OVER) + return CAIRO_STATUS_SUCCESS; + goto UNSUPPORTED; + } + + if (src_r.x < 0) { + src_r.width += src_r.x; + + dst_r.width += src_r.x; + dst_r.x -= src_r.x; + + src_r.x = 0; + needs_pad = TRUE; + } + + if (src_r.y < 0) { + src_r.height += src_r.y; + + dst_r.height += src_r.y; + dst_r.y -= src_r.y; + + src_r.y = 0; + needs_pad = TRUE; + } + + if (src_r.x + src_r.width > src_extents.width) { + src_r.width = src_extents.width - src_r.x; + dst_r.width = src_r.width; + needs_pad = TRUE; + } + + if (src_r.y + src_r.height > src_extents.height) { + src_r.height = src_extents.height - src_r.y; + dst_r.height = src_r.height; + needs_pad = TRUE; + } + } else { + needs_repeat = TRUE; + } + + if (pattern->extend == CAIRO_EXTEND_PAD && needs_pad) { + goto UNSUPPORTED; + } + + /* + * Operations that we can do: + * + * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0) + * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0)) + * This turns into Dst.Alpha = Src.Alpha when SCA = 255. + * (http://msdn.microsoft.com/en-us/library/aa921335.aspx) + * + * RGB OVER RGB -> BitBlt (same as SOURCE) + * RGB OVER ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB + * by setting the alpha values of the source to 255. + * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA + * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte + * + * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA + * RGB OVER ARGB + mask -> Partially supported, We convert this operation into a ARGB OVER ARGB + mask + * by setting the alpha values of the source to 255. + * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA + * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte + * + * RGB SOURCE RGB -> BitBlt + * RGB SOURCE ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB + * by setting the alpha values of the source to 255. + * ARGB SOURCE ARGB -> BitBlt + * ARGB SOURCE RGB -> BitBlt + * + * RGB SOURCE RGB + mask -> unsupported + * RGB SOURCE ARGB + mask -> unsupported + * ARGB SOURCE ARGB + mask -> unsupported + * ARGB SOURCE RGB + mask -> unsupported + */ + + /* + * Figure out what action to take. + */ + if (op == CAIRO_OPERATOR_OVER) { + if (alpha == 0) + return CAIRO_STATUS_SUCCESS; + + if (src_format == dst->format) { + if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) { + needs_alpha = FALSE; + } else { + needs_alpha = TRUE; + } + } else if (src_format == CAIRO_FORMAT_ARGB32 && + dst->format == CAIRO_FORMAT_RGB24) + { + needs_alpha = TRUE; + } else if (src_format == CAIRO_FORMAT_RGB24 && + dst->format == CAIRO_FORMAT_ARGB32 && + src->image) + { + if (alpha == 255) { + needs_alpha = FALSE; + } else { + needs_alpha = TRUE; + } + } else { + goto UNSUPPORTED; + } + } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) { + if ((src_format == dst->format) || + (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24) || + (src_format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32 && src->image)) + { + needs_alpha = FALSE; + } else { + goto UNSUPPORTED; + } + } else { + goto UNSUPPORTED; + } + + if (scalex == 1.0 && scaley == 1.0) { + needs_scale = FALSE; + } else { + /* Should never be reached until we turn StretchBlt back on */ + needs_scale = TRUE; + } + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n", + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + fflush (stderr); +#endif + + status = _cairo_win32_surface_set_clip_region (dst, clip_region); + if (status) + return status; + + /* If we need to repeat, we turn the repeated blit into + * a bunch of piece-by-piece blits. + */ + if (needs_repeat) { + cairo_rectangle_int_t piece_src_r, piece_dst_r; + uint32_t rendered_width = 0, rendered_height = 0; + uint32_t to_render_height, to_render_width; + int32_t piece_x, piece_y; + int32_t src_start_x = MOD(src_r.x, src_extents.width); + int32_t src_start_y = MOD(src_r.y, src_extents.height); + + if (needs_scale) + goto UNSUPPORTED; + + /* If both the src and dest have an image, we may as well fall + * back, because it will be faster than our separate blits. + * Our blit code will be fastest when the src is a DDB and the + * destination is a DDB. + */ + if ((src_image || src->image) && dst->image) + goto UNSUPPORTED; + + /* If the src is not a bitmap but an on-screen (or unknown) + * DC, chances are that fallback will be faster. + */ + if (src->bitmap == NULL) + goto UNSUPPORTED; + + /* If we can use PatBlt, just do so */ + if (!src_image && !needs_alpha) + { + HBRUSH brush; + HGDIOBJ old_brush; + POINT old_brush_origin; + + /* Set up the brush with our bitmap */ + brush = CreatePatternBrush (src->bitmap); + + /* SetBrushOrgEx sets the coordinates in the destination DC of where the + * pattern should start. + */ + SetBrushOrgEx (dst->dc, dst_r.x - src_start_x, + dst_r.y - src_start_y, &old_brush_origin); + + old_brush = SelectObject (dst->dc, brush); + + PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY); + + /* Restore the old brush and pen */ + SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL); + SelectObject (dst->dc, old_brush); + DeleteObject (brush); + + return CAIRO_STATUS_SUCCESS; + } + + /* If we were not able to use PatBlt, then manually expand out the blit */ + + /* Arbitrary factor; we think that going through + * fallback will be faster if we have to do more + * than this amount of blits in either direction. + */ + if (dst_r.width / src_extents.width > 5 || + dst_r.height / src_extents.height > 5) + goto UNSUPPORTED; + + for (rendered_height = 0; + rendered_height < dst_r.height; + rendered_height += to_render_height) + { + piece_y = (src_start_y + rendered_height) % src_extents.height; + to_render_height = src_extents.height - piece_y; + + if (rendered_height + to_render_height > dst_r.height) + to_render_height = dst_r.height - rendered_height; + + for (rendered_width = 0; + rendered_width < dst_r.width; + rendered_width += to_render_width) + { + piece_x = (src_start_x + rendered_width) % src_extents.width; + to_render_width = src_extents.width - piece_x; + + if (rendered_width + to_render_width > dst_r.width) + to_render_width = dst_r.width - rendered_width; + + piece_src_r.x = piece_x; + piece_src_r.y = piece_y; + piece_src_r.width = to_render_width; + piece_src_r.height = to_render_height; + + piece_dst_r.x = dst_r.x + rendered_width; + piece_dst_r.y = dst_r.y + rendered_height; + piece_dst_r.width = to_render_width; + piece_dst_r.height = to_render_height; + + status = _cairo_win32_surface_composite_inner (src, src_image, dst, + src_extents, piece_src_r, piece_dst_r, + alpha, needs_alpha, needs_scale); + if (status != CAIRO_STATUS_SUCCESS) { + /* Uh oh. If something failed, and it's the first + * piece, then we can jump to UNSUPPORTED. + * Otherwise, this is bad times, because part of the + * rendering was already done. */ + if (rendered_width == 0 && + rendered_height == 0) + { + goto UNSUPPORTED; + } + + return status; + } + } + } + } else { + status = _cairo_win32_surface_composite_inner (src, src_image, dst, + src_extents, src_r, dst_r, + alpha, needs_alpha, needs_scale); + } + + if (status == CAIRO_STATUS_SUCCESS) + return status; + +UNSUPPORTED: + /* Fall back to image surface directly, if this is a DIB surface */ + if (dst->image) { + GdiFlush(); + + return dst->image->backend->composite (op, pattern, mask_pattern, + dst->image, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height, + clip_region); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* This big function tells us how to optimize operators for the + * case of solid destination and constant-alpha source + * + * Note: This function needs revisiting if we add support for + * super-luminescent colors (a == 0, r,g,b > 0) + */ +static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED } +categorize_solid_dest_operator (cairo_operator_t op, + unsigned short alpha) +{ + enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source; + + if (alpha >= 0xff00) + source = SOURCE_SOLID; + else if (alpha < 0x100) + source = SOURCE_TRANSPARENT; + else + source = SOURCE_OTHER; + + switch (op) { + case CAIRO_OPERATOR_CLEAR: /* 0 0 */ + case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */ + return DO_CLEAR; + break; + + case CAIRO_OPERATOR_SOURCE: /* 1 0 */ + case CAIRO_OPERATOR_IN: /* Ab 0 */ + return DO_SOURCE; + break; + + case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */ + case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_SOURCE; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */ + case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_CLEAR; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST: /* 0 1 */ + case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */ + case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */ + return DO_NOTHING; + break; + + case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */ + case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */ + if (source == SOURCE_SOLID) + return DO_NOTHING; + else if (source == SOURCE_TRANSPARENT) + return DO_CLEAR; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_ADD: /* 1 1 */ + if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + } + + ASSERT_NOT_REACHED; + return DO_UNSUPPORTED; +} + +static cairo_status_t +_cairo_win32_surface_fill_rectangles_stretchdib (HDC dc, + const cairo_color_t *color, + cairo_rectangle_int_t *rect, + int num_rects) +{ + BITMAPINFO bi; + int pixel = ((color->alpha_short >> 8) << 24) | + ((color->red_short >> 8) << 16) | + ((color->green_short >> 8) << 8) | + (color->blue_short >> 8); + int i; + + /* Experiments suggest that it's impossible to use FillRect to set the alpha value + of a Win32 HDC for a transparent window. So instead we use StretchDIBits of a single + pixel, which does work. */ + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = 1; + bi.bmiHeader.biHeight = 1; + bi.bmiHeader.biSizeImage = 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + for (i = 0; i < num_rects; i++) { + if (!StretchDIBits (dc, + /* dst x,y,w,h */ + rect[i].x, rect[i].y, rect[i].width, rect[i].height, + /* src x,y,w,h */ + 0, 0, 1, 1, + &pixel, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles_stretchdib(StretchDIBits)"); + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + COLORREF new_color; + HBRUSH new_brush; + int i; + + status = _cairo_win32_surface_set_clip_region (surface, NULL); + if (status) + return status; + + if (surface->format == CAIRO_FORMAT_ARGB32 && + (surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) && + (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && color->alpha_short >= 0xff00))) { + return _cairo_win32_surface_fill_rectangles_stretchdib (surface->dc, + color, rects, num_rects); + } + if (surface->format != CAIRO_FORMAT_RGB24) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all + * surfaces with alpha.) + */ + switch (categorize_solid_dest_operator (op, color->alpha_short)) { + case DO_CLEAR: + new_color = RGB (0, 0, 0); + break; + case DO_SOURCE: + new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); + break; + case DO_NOTHING: + return CAIRO_STATUS_SUCCESS; + case DO_UNSUPPORTED: + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + new_brush = CreateSolidBrush (new_color); + if (!new_brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + for (i = 0; i < num_rects; i++) { + RECT rect; + + rect.left = rects[i].x; + rect.top = rects[i].y; + rect.right = rects[i].x + rects[i].width; + rect.bottom = rects[i].y + rects[i].height; + + if (!FillRect (surface->dc, &rect, new_brush)) + goto FAIL; + } + + DeleteObject (new_brush); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + DeleteObject (new_brush); + + return status; +} + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_win32_surface_t *surface = abstract_surface; + + *rectangle = surface->extents; + return TRUE; +} + +static cairo_status_t +_cairo_win32_surface_flush (void *abstract_surface) +{ + return _cairo_win32_surface_set_clip_region (abstract_surface, NULL); +} + +#define STACK_GLYPH_SIZE 256 + +cairo_int_status_t +_cairo_win32_surface_show_glyphs_internal (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs, + cairo_bool_t glyph_indexing) +{ +#ifdef CAIRO_HAS_WIN32_FONT + if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { +#ifdef CAIRO_HAS_DWRITE_FONT + return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); +#endif + } else { + cairo_win32_surface_t *dst = surface; + + WORD glyph_buf_stack[STACK_GLYPH_SIZE]; + WORD *glyph_buf = glyph_buf_stack; + int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; + int *dxy_buf = dxy_buf_stack; + + BOOL win_result = 0; + int i, j; + + cairo_solid_pattern_t *solid_pattern; + COLORREF color; + + cairo_matrix_t device_to_logical; + + int start_x, start_y; + double user_x, user_y; + int logical_x, logical_y; + unsigned int glyph_index_option; + + /* We can only handle win32 fonts */ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle opaque solid color sources */ + if (!_cairo_pattern_is_opaque_solid(source)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle operator SOURCE or OVER with the destination + * having no alpha */ + if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || + (dst->format != CAIRO_FORMAT_RGB24)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path, but only if we're not + * doing this for printing */ + if (clip != NULL) { + if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { + cairo_region_t *clip_region; + cairo_status_t status; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + + _cairo_win32_surface_set_clip_region (surface, clip_region); + } + } else { + _cairo_win32_surface_set_clip_region (surface, NULL); + } + + solid_pattern = (cairo_solid_pattern_t *)source; + color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); + + SaveDC(dst->dc); + + cairo_win32_scaled_font_select_font(scaled_font, dst->dc); + SetTextColor(dst->dc, color); + SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); + SetBkMode(dst->dc, TRANSPARENT); + + if (num_glyphs > STACK_GLYPH_SIZE) { + glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); + dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); + } + + /* It is vital that dx values for dxy_buf are calculated from the delta of + * _logical_ x coordinates (not user x coordinates) or else the sum of all + * previous dx values may start to diverge from the current glyph's x + * coordinate due to accumulated rounding error. As a result strings could + * be painted shorter or longer than expected. */ + + user_x = glyphs[0].x; + user_y = glyphs[0].y; + + cairo_matrix_transform_point(&device_to_logical, + &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + start_x = logical_x; + start_y = logical_y; + + for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { + glyph_buf[i] = (WORD) glyphs[i].index; + if (i == num_glyphs - 1) { + dxy_buf[j] = 0; + dxy_buf[j+1] = 0; + } else { + double next_user_x = glyphs[i+1].x; + double next_user_y = glyphs[i+1].y; + int next_logical_x, next_logical_y; + + cairo_matrix_transform_point(&device_to_logical, + &next_user_x, &next_user_y); + + next_logical_x = _cairo_lround (next_user_x); + next_logical_y = _cairo_lround (next_user_y); + + dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); + dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y); + /* note that GDI coordinate system is inverted */ + + logical_x = next_logical_x; + logical_y = next_logical_y; + } + } + + if (glyph_indexing) + glyph_index_option = ETO_GLYPH_INDEX; + else + glyph_index_option = 0; + + win_result = ExtTextOutW(dst->dc, + start_x, + start_y, + glyph_index_option | ETO_PDY, + NULL, + glyph_buf, + num_glyphs, + dxy_buf); + if (!win_result) { + _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); + } + + RestoreDC(dst->dc, -1); + + if (glyph_buf != glyph_buf_stack) { + free(glyph_buf); + free(dxy_buf); + } + return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; + } +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +#undef STACK_GLYPH_SIZE + +cairo_int_status_t +_cairo_win32_surface_show_glyphs (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + return _cairo_win32_surface_show_glyphs_internal (surface, + op, + source, + glyphs, + num_glyphs, + scaled_font, + clip, + remaining_glyphs, + TRUE); +} + +static cairo_surface_t * +cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format) +{ + cairo_win32_surface_t *surface; + + RECT rect; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->clip_region = NULL; + surface->image = NULL; + surface->format = format; + + surface->d3d9surface = NULL; + surface->dc = hdc; + surface->bitmap = NULL; + surface->is_dib = FALSE; + surface->saved_dc_bitmap = NULL; + surface->brush = NULL; + surface->old_brush = NULL; + surface->font_subsets = NULL; + + GetClipBox(hdc, &rect); + surface->extents.x = rect.left; + surface->extents.y = rect.top; + surface->extents.width = rect.right - rect.left; + surface->extents.height = rect.bottom - rect.top; + + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + + _cairo_surface_init (&surface->base, + &cairo_win32_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format)); + + return &surface->base; +} + +/** + * cairo_win32_surface_create: + * @hdc: the DC to create a surface for + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. The resulting surface will always be of + * format %CAIRO_FORMAT_RGB24; should you need another surface format, + * you will need to create one through + * cairo_win32_surface_create_with_dib() or call + * cairo_win32_surface_create_with_alpha. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_win32_surface_create (HDC hdc) +{ + /* Assume everything comes in as RGB24 */ + return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_RGB24); +} + +/** + * cairo_win32_surface_create_with_alpha: + * @hdc: the DC to create a surface for + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. The resulting surface will always be of + * format %CAIRO_FORMAT_ARGB32; this format is used when drawing into + * transparent windows. + * + * Return value: the newly created surface + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_alpha (HDC hdc) +{ + return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_ARGB32); +} + +/** + * cairo_win32_surface_create_with_dib: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-independent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be uninitialized. + * + * Return value: the newly created surface + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_dib (cairo_format_t format, + int width, + int height) +{ + return _cairo_win32_surface_create_for_dc (NULL, format, width, height); +} + +/** + * cairo_win32_surface_create_with_ddb: + * @hdc: the DC to create a surface for + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-independent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be uninitialized. + * + * Return value: the newly created surface + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_ddb (HDC hdc, + cairo_format_t format, + int width, + int height) +{ + cairo_win32_surface_t *new_surf; + HBITMAP ddb; + HDC screen_dc, ddb_dc; + HBITMAP saved_dc_bitmap; + + if (format != CAIRO_FORMAT_RGB24) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); +/* XXX handle these eventually + format != CAIRO_FORMAT_A8 || + format != CAIRO_FORMAT_A1) +*/ + + if (!hdc) { + screen_dc = GetDC (NULL); + hdc = screen_dc; + } else { + screen_dc = NULL; + } + + ddb_dc = CreateCompatibleDC (hdc); + if (ddb_dc == NULL) { + new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto FINISH; + } + + ddb = CreateCompatibleBitmap (hdc, width, height); + if (ddb == NULL) { + DeleteDC (ddb_dc); + + /* Note that if an app actually does hit this out of memory + * condition, it's going to have lots of other issues, as + * video memory is probably exhausted. However, it can often + * continue using DIBs instead of DDBs. + */ + new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto FINISH; + } + + saved_dc_bitmap = SelectObject (ddb_dc, ddb); + + new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc); + new_surf->bitmap = ddb; + new_surf->saved_dc_bitmap = saved_dc_bitmap; + new_surf->is_dib = FALSE; + +FINISH: + if (screen_dc) + ReleaseDC (NULL, screen_dc); + + return (cairo_surface_t*) new_surf; +} + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_d3dsurface9 (IDirect3DSurface9 *surface) +{ + HDC dc; + cairo_surface_t *win_surface; + + IDirect3DSurface9_AddRef (surface); + IDirect3DSurface9_GetDC (surface, &dc); + win_surface = cairo_win32_surface_create_internal(dc, CAIRO_FORMAT_RGB24); + if (likely(win_surface->status == CAIRO_STATUS_SUCCESS)) { + ((cairo_win32_surface_t*)win_surface)->d3d9surface = surface; + } + return win_surface; + +} +/** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a win32 surface. This will + * return False if this is a win32 printing surface; use + * _cairo_surface_is_win32_printing() to check for that. + * + * Return value: True if the surface is an win32 surface + **/ +int +_cairo_surface_is_win32 (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_surface_backend; +} + +/** + * cairo_win32_surface_get_dc + * @surface: a #cairo_surface_t + * + * Returns the HDC associated with this surface, or %NULL if none. + * Also returns %NULL if the surface is not a win32 surface. + * + * Return value: HDC or %NULL if no HDC available. + * + * Since: 1.2 + **/ +HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface) +{ + cairo_win32_surface_t *winsurf; + + if (_cairo_surface_is_win32 (surface)){ + winsurf = (cairo_win32_surface_t *) surface; + + return winsurf->dc; + } + + if (_cairo_surface_is_paginated (surface)) { + cairo_surface_t *target; + + target = _cairo_paginated_surface_get_target (surface); + +#ifndef CAIRO_OMIT_WIN32_PRINTING + if (_cairo_surface_is_win32_printing (target)) { + winsurf = (cairo_win32_surface_t *) target; + return winsurf->dc; + } +#endif + } + + return NULL; +} + + +HDC +cairo_win32_get_dc_with_clip (cairo_t *cr) +{ + cairo_surface_t *surface = cr->gstate->target; + cairo_clip_t clip; + _cairo_clip_init_copy(&clip, &cr->gstate->clip); + + if (_cairo_surface_is_win32 (surface)){ + cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) surface; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + if (clip.path) { + status = _cairo_clip_get_region (&clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) { + _cairo_clip_fini(&clip); + return NULL; + } + } + _cairo_win32_surface_set_clip_region (winsurf, clip_region); + + _cairo_clip_fini(&clip); + return winsurf->dc; + } + + if (_cairo_surface_is_paginated (surface)) { + cairo_surface_t *target; + + target = _cairo_paginated_surface_get_target (surface); + +#ifndef CAIRO_OMIT_WIN32_PRINTING + if (_cairo_surface_is_win32_printing (target)) { + cairo_status_t status; + cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) target; + + status = _cairo_surface_clipper_set_clip (&winsurf->clipper, &clip); + + _cairo_clip_fini(&clip); + + if (status) + return NULL; + + return winsurf->dc; + } +#endif + } + + _cairo_clip_fini(&clip); + return NULL; +} + + + +/** + * cairo_win32_surface_get_image + * @surface: a #cairo_surface_t + * + * Returns a #cairo_surface_t image surface that refers to the same bits + * as the DIB of the Win32 surface. If the passed-in win32 surface + * is not a DIB surface, %NULL is returned. + * + * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), + * or %NULL if the win32 surface is not a DIB. + * + * Since: 1.4 + */ +cairo_surface_t * +cairo_win32_surface_get_image (cairo_surface_t *surface) +{ + if (!_cairo_surface_is_win32(surface)) + return NULL; + + return ((cairo_win32_surface_t*)surface)->image; +} + +static cairo_bool_t +_cairo_win32_surface_is_similar (void *surface_a, + void *surface_b) +{ + cairo_win32_surface_t *a = surface_a; + cairo_win32_surface_t *b = surface_b; + + return a->dc == b->dc; +} + +typedef struct _cairo_win32_surface_span_renderer { + cairo_span_renderer_t base; + + cairo_operator_t op; + const cairo_pattern_t *pattern; + cairo_antialias_t antialias; + + uint8_t *mask_data; + uint32_t mask_stride; + + cairo_image_surface_t *mask; + cairo_win32_surface_t *dst; + cairo_region_t *clip_region; + + cairo_composite_rectangles_t composite_rectangles; +} cairo_win32_surface_span_renderer_t; + +static cairo_status_t +_cairo_win32_surface_span_renderer_render_rows ( + void *abstract_renderer, + int y, + int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; + while (height--) + _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_surface_span_renderer_destroy (void *abstract_renderer) +{ + cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; + if (!renderer) return; + + if (renderer->mask != NULL) + cairo_surface_destroy (&renderer->mask->base); + + free (renderer); +} + +static cairo_status_t +_cairo_win32_surface_span_renderer_finish (void *abstract_renderer) +{ + cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (renderer->pattern == NULL || renderer->mask == NULL) + return CAIRO_STATUS_SUCCESS; + + status = cairo_surface_status (&renderer->mask->base); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; + cairo_win32_surface_t *dst = renderer->dst; + cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&renderer->mask->base); + /* composite onto the image surface directly if we can */ + if (dst->image) { + GdiFlush(); /* XXX: I'm not sure if this needed or not */ + + status = dst->image->backend->composite (renderer->op, + renderer->pattern, mask_pattern, dst->image, + rects->bounded.x, rects->bounded.y, + 0, 0, /* mask.x, mask.y */ + rects->bounded.x, rects->bounded.y, + rects->bounded.width, rects->bounded.height, + renderer->clip_region); + } else { + /* otherwise go through the fallback_composite path which + * will do the appropriate surface acquisition */ + status = _cairo_surface_fallback_composite ( + renderer->op, + renderer->pattern, mask_pattern, &dst->base, + rects->bounded.x, rects->bounded.y, + 0, 0, /* mask.x, mask.y */ + rects->bounded.x, rects->bounded.y, + rects->bounded.width, rects->bounded.height, + renderer->clip_region); + } + cairo_pattern_destroy (mask_pattern); + + } + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_span_renderer_set_error (abstract_renderer, + status); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) { + return _cairo_surface_paint (surface->image, op, source, clip); + } + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) { + return _cairo_surface_mask (surface->image, op, source, mask, clip); + } + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) { + return _cairo_surface_stroke (surface->image, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + + } + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) { + return _cairo_surface_fill (surface->image, op, source, + path, fill_rule, + tolerance, antialias, + clip); + } + return CAIRO_INT_STATUS_UNSUPPORTED; +} + + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-spans-private.h" +#include "cairo-surface-fallback-private.h" + +typedef struct { + cairo_surface_t *dst; + cairo_rectangle_int_t extents; + cairo_image_surface_t *image; + cairo_rectangle_int_t image_rect; + void *image_extra; +} fallback_state_t; + +/** + * _fallback_init: + * + * Acquire destination image surface needed for an image-based + * fallback. + * + * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not + * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all + * went well, or some error status otherwise. + **/ +static cairo_int_status_t +_fallback_init (fallback_state_t *state, + cairo_surface_t *dst, + int x, + int y, + int width, + int height) +{ + cairo_status_t status; + + state->extents.x = x; + state->extents.y = y; + state->extents.width = width; + state->extents.height = height; + + state->dst = dst; + + status = _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, + &state->image_extra); + if (unlikely (status)) + return status; + + + /* XXX: This NULL value tucked away in state->image is a rather + * ugly interface. Cleaner would be to push the + * CAIRO_INT_STATUS_NOTHING_TO_DO value down into + * _cairo_surface_acquire_dest_image and its backend + * counterparts. */ + assert (state->image != NULL); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_fallback_fini (fallback_state_t *state) +{ + _cairo_surface_release_dest_image (state->dst, &state->extents, + state->image, &state->image_rect, + state->image_extra); +} + +typedef cairo_status_t +(*cairo_draw_func_t) (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region); + +static cairo_status_t +_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, + cairo_clip_t *clip, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *mask; + cairo_region_t *clip_region = NULL, *fallback_region = NULL; + cairo_status_t status; + cairo_bool_t clip_surface = FALSE; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with + * a mask (as called via _cairo_surface_mask) triggers assertion failures. + */ + mask = _cairo_surface_create_similar_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + if (unlikely (mask->status)) + return mask->status; + + if (clip_region && (extents->x || extents->y)) { + fallback_region = cairo_region_copy (clip_region); + status = fallback_region->status; + if (unlikely (status)) + goto CLEANUP_SURFACE; + + cairo_region_translate (fallback_region, + -extents->x, + -extents->y); + clip_region = fallback_region; + } + + status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, mask, + extents->x, extents->y, + extents, + clip_region); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + if (clip_surface) + status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); + + _cairo_pattern_init_for_surface (mask_pattern, mask); + + CLEANUP_SURFACE: + if (fallback_region) + cairo_region_destroy (fallback_region); + cairo_surface_destroy (mask); + + return status; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t mask_pattern; + cairo_status_t status; + + status = _create_composite_mask_pattern (&mask_pattern, + clip, + draw_func, draw_closure, + dst, extents); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _cairo_surface_composite (op, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + + _cairo_pattern_fini (&mask_pattern.base); + } + + return status; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *intermediate; + cairo_surface_pattern_t pattern; + cairo_surface_pattern_t clip_pattern; + cairo_surface_t *clip_surface; + int clip_x, clip_y; + cairo_status_t status; + + /* We'd be better off here creating a surface identical in format + * to dst, but we have no way of getting that information. Instead + * we ask the backend to create a similar surface of identical content, + * in the belief that the backend will do something useful - like use + * an identical format. For example, the xlib backend will endeavor to + * use a compatible depth to enable core protocol routines. + */ + intermediate = + _cairo_surface_create_similar_scratch (dst, dst->content, + extents->width, + extents->height); + if (intermediate == NULL) { + intermediate = + _cairo_image_surface_create_with_content (dst->content, + extents->width, + extents->width); + } + if (unlikely (intermediate->status)) + return intermediate->status; + + /* Initialize the intermediate surface from the destination surface */ + _cairo_pattern_init_for_surface (&pattern, dst); + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL, intermediate, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + status = (*draw_func) (draw_closure, op, + src, intermediate, + extents->x, extents->y, + extents, + NULL); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + assert (clip->path != NULL); + clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + goto CLEANUP_SURFACE; + + _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); + + /* Combine that with the clip */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, + &clip_pattern.base, NULL, intermediate, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + 0, 0, + extents->width, extents->height, + NULL); + if (unlikely (status)) + goto CLEANUP_CLIP; + + /* Punch the clip out of the destination */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &clip_pattern.base, NULL, dst, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + if (unlikely (status)) + goto CLEANUP_CLIP; + + /* Now add the two results together */ + _cairo_pattern_init_for_surface (&pattern, intermediate); + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &pattern.base, NULL, dst, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + NULL); + _cairo_pattern_fini (&pattern.base); + + CLEANUP_CLIP: + _cairo_pattern_fini (&clip_pattern.base); + CLEANUP_SURFACE: + cairo_surface_destroy (intermediate); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t mask_pattern; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + } + + /* Create a surface that is mask IN clip */ + status = _create_composite_mask_pattern (&mask_pattern, + clip, + draw_func, draw_closure, + dst, extents); + if (unlikely (status)) + return status; + + /* Compute dest' = dest OUT (mask IN clip) */ + status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, + &mask_pattern.base, NULL, dst, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + clip_region); + + if (unlikely (status)) + goto CLEANUP_MASK_PATTERN; + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + src, &mask_pattern.base, dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height, + clip_region); + + CLEANUP_MASK_PATTERN: + _cairo_pattern_fini (&mask_pattern.base); + return status; +} + +static int +_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) +{ + return rect->width == 0 || rect->height == 0; +} + +/** + * _clip_and_composite: + * @clip: a #cairo_clip_t + * @op: the operator to draw with + * @src: source pattern + * @draw_func: function that can be called to draw with the mask onto a surface. + * @draw_closure: data to pass to @draw_func. + * @dst: destination surface + * @extents: rectangle holding a bounding box for the operation; this + * rectangle will be used as the size for the temporary + * surface. + * + * When there is a surface clip, we typically need to create an intermediate + * surface. This function handles the logic of creating a temporary surface + * drawing to it, then compositing the result onto the target surface. + * + * @draw_func is to called to draw the mask; it will be called no more + * than once. + * + * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. + **/ +static cairo_status_t +_clip_and_composite (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_draw_func_t draw_func, + void *draw_closure, + cairo_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + + if (_cairo_rectangle_empty (extents)) + /* Nothing to do */ + return CAIRO_STATUS_SUCCESS; + + if (op == CAIRO_OPERATOR_CLEAR) { + src = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (clip, + src, + draw_func, draw_closure, + dst, extents); + } else { + cairo_bool_t clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + return status; + } + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip_surface) { + if (_cairo_operator_bounded_by_mask (op)) { + status = _clip_and_composite_with_mask (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } else { + status = _clip_and_composite_combine (clip, op, + src, + draw_func, draw_closure, + dst, extents); + } + } else { + status = draw_func (draw_closure, op, + src, dst, + 0, 0, + extents, + clip_region); + } + } + + return status; +} + +/* Composites a region representing a set of trapezoids. + */ +static cairo_status_t +_composite_trap_region (cairo_clip_t *clip, + const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + const cairo_rectangle_int_t *extents) +{ + cairo_status_t status; + cairo_surface_pattern_t mask_pattern; + cairo_pattern_t *mask = NULL; + int mask_x = 0, mask_y =0; + + if (clip != NULL) { + cairo_surface_t *clip_surface = NULL; + int clip_x, clip_y; + + clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); + if (unlikely (clip_surface->status)) + return clip_surface->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + src = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); + mask_x = extents->x - clip_x; + mask_y = extents->y - clip_y; + mask = &mask_pattern.base; + } + + status = _cairo_surface_composite (op, src, mask, dst, + extents->x, extents->y, + mask_x, mask_y, + extents->x, extents->y, + extents->width, extents->height, + trap_region); + + if (mask != NULL) + _cairo_pattern_fini (mask); + + return status; +} + +typedef struct { + cairo_traps_t *traps; + cairo_antialias_t antialias; +} cairo_composite_traps_info_t; + +static cairo_status_t +_composite_traps_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_composite_traps_info_t *info = closure; + cairo_status_t status; + cairo_region_t *extents_region = NULL; + + if (dst_x != 0 || dst_y != 0) + _cairo_traps_translate (info->traps, - dst_x, - dst_y); + + if (clip_region == NULL && + !_cairo_operator_bounded_by_source (op)) { + extents_region = cairo_region_create_rectangle (extents); + if (unlikely (extents_region->status)) + return extents_region->status; + cairo_region_translate (extents_region, -dst_x, -dst_y); + clip_region = extents_region; + } + + status = _cairo_surface_composite_trapezoids (op, + src, dst, info->antialias, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + info->traps->traps, + info->traps->num_traps, + clip_region); + + if (extents_region) + cairo_region_destroy (extents_region); + + return status; +} + +enum { + HAS_CLEAR_REGION = 0x1, +}; + +static cairo_status_t +_clip_and_composite_region (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_region_t *trap_region, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_region_t clear_region; + unsigned int has_region = 0; + cairo_status_t status; + + if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { + /* If we optimize drawing with an unbounded operator to + * _cairo_surface_fill_rectangles() or to drawing with a + * clip region, then we have an additional region to clear. + */ + _cairo_region_init_rectangle (&clear_region, extents); + status = cairo_region_subtract (&clear_region, trap_region); + if (unlikely (status)) + return status; + + if (! cairo_region_is_empty (&clear_region)) + has_region |= HAS_CLEAR_REGION; + } + + if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && + clip == NULL) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + /* Solid rectangles special case */ + status = _cairo_surface_fill_region (dst, op, color, trap_region); + } else { + /* For a simple rectangle, we can just use composite(), for more + * rectangles, we have to set a clip region. The cost of rasterizing + * trapezoids is pretty high for most backends currently, so it's + * worthwhile even if a region is needed. + * + * If we have a clip surface, we set it as the mask; this only works + * for bounded operators other than SOURCE; for unbounded operators, + * clip and mask cannot be interchanged. For SOURCE, the operator + * as implemented by the backends is different in its handling + * of the mask then what we want. + * + * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has + * more than rectangle and the destination doesn't support clip + * regions. In that case, we fall through. + */ + status = _composite_trap_region (clip, src, op, dst, + trap_region, extents); + } + + if (has_region & HAS_CLEAR_REGION) { + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_surface_fill_region (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear_region); + } + _cairo_region_fini (&clear_region); + } + + return status; +} + +/* avoid using region code to re-validate boxes */ +static cairo_status_t +_fill_rectangles (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + const cairo_color_t *color; + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_status_t status; + int i; + + if (! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: convert clip region to geometric boxes? */ + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX: fallback for the region_subtract() operation */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->has_intersections) { + if (traps->is_rectangular) { + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + } else { + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + } + if (unlikely (status)) + return status; + } + + for (i = 0; i < traps->num_traps; i++) { + if (! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (traps->num_traps, + sizeof (cairo_rectangle_int_t)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < traps->num_traps; i++) { + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + int y1 = _cairo_fixed_integer_part (traps->traps[i].top); + int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + + rects[i].x = x1; + rects[i].y = y1; + rects[i].width = x2 - x1; + rects[i].height = y2 - y1; + } + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *)src)->color; + + status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); + + if (rects != stack_rects) + free (rects); + + return status; +} + +/* fast-path for very common composite of a single rectangle */ +static cairo_status_t +_composite_rectangle (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_traps_t *traps, + cairo_clip_t *clip) +{ + cairo_rectangle_int_t rect; + + if (clip != NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_fixed_is_integer (traps->traps[0].top) || + ! _cairo_fixed_is_integer (traps->traps[0].bottom) || + ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) + { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); + rect.y = _cairo_fixed_integer_part (traps->traps[0].top); + rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; + rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; + + return _cairo_surface_composite (op, src, NULL, dst, + rect.x, rect.y, + 0, 0, + rect.x, rect.y, + rect.width, rect.height, + NULL); +} + +/* Warning: This call modifies the coordinates of traps */ +static cairo_status_t +_clip_and_composite_trapezoids (const cairo_pattern_t *src, + cairo_operator_t op, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_composite_traps_info_t traps_info; + cairo_region_t *clip_region = NULL; + cairo_bool_t clip_surface = FALSE; + cairo_status_t status; + + if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) + return CAIRO_STATUS_SUCCESS; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status))) + return status; + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_STATUS_SUCCESS; + + clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (! clip_surface || + (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) + { + cairo_region_t *trap_region = NULL; + + if (_cairo_operator_bounded_by_source (op)) { + status = _fill_rectangles (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _composite_rectangle (dst, op, src, traps, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_traps_extract_region (traps, &trap_region); + if (unlikely (_cairo_status_is_error (status))) + return status; + + if (trap_region != NULL) { + status = cairo_region_intersect_rectangle (trap_region, extents); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + + if (clip_region != NULL) { + status = cairo_region_intersect (trap_region, clip_region); + if (unlikely (status)) { + cairo_region_destroy (trap_region); + return status; + } + } + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t trap_extents; + + cairo_region_get_extents (trap_region, &trap_extents); + if (! _cairo_rectangle_intersect (extents, &trap_extents)) { + cairo_region_destroy (trap_region); + return CAIRO_STATUS_SUCCESS; + } + } + + status = _clip_and_composite_region (src, op, dst, + trap_region, + clip_surface ? clip : NULL, + extents); + cairo_region_destroy (trap_region); + + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + return status; + } + } + + /* No fast path, exclude self-intersections and clip trapezoids. */ + if (traps->has_intersections) { + if (traps->is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); + else if (traps->is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + return status; + } + + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + traps_info.traps = traps; + traps_info.antialias = antialias; + + return _clip_and_composite (clip, op, src, + _composite_traps_draw_func, + &traps_info, dst, extents); +} + +typedef struct { + cairo_polygon_t *polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} cairo_composite_spans_info_t; + +static cairo_status_t +_composite_spans_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_composite_rectangles_t rects; + cairo_composite_spans_info_t *info = closure; + + rects.source = *extents; + rects.mask = *extents; + rects.bounded = *extents; + /* The incoming dst_x/y are where we're pretending the origin of + * the dst surface is -- *not* the offset of a rectangle where + * we'd like to place the result. */ + rects.bounded.x -= dst_x; + rects.bounded.y -= dst_y; + rects.unbounded = rects.bounded; + rects.is_bounded = _cairo_operator_bounded_by_either (op); + + return _cairo_surface_composite_polygon (dst, op, src, + info->fill_rule, + info->antialias, + &rects, + info->polygon, + clip_region); +} + +static cairo_status_t +_cairo_win32_surface_span_renderer_composite + (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_image_surface_t *mask, + cairo_win32_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (src == NULL || mask == NULL) + return CAIRO_STATUS_SUCCESS; + + status = cairo_surface_status (&mask->base); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&mask->base); + /* composite onto the image surface directly if we can */ + if (dst->image) { + GdiFlush(); /* XXX: I'm not sure if this needed or not */ + + status = dst->image->backend->composite (op, + src, mask_pattern, dst->image, + extents->x, extents->y, + 0, 0, /* mask.x, mask.y */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } else { + /* otherwise go through the fallback_composite path which + * will do the appropriate surface acquisition */ + status = _cairo_surface_fallback_composite ( + op, + src, mask_pattern, &dst->base, + extents->x, extents->y, + 0, 0, /* mask.x, mask.y */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } + cairo_pattern_destroy (mask_pattern); + + } + return status; +} + +typedef struct _cairo_image_surface_span_renderer { + cairo_span_renderer_t base; + + uint8_t *mask_data; + uint32_t mask_stride; +} cairo_image_surface_span_renderer_t; + +cairo_status_t +_cairo_image_surface_span (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans); + +static cairo_status_t +_composite_spans (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_composite_spans_info_t *info = closure; + cairo_image_surface_span_renderer_t renderer; + cairo_scan_converter_t *converter; + cairo_image_surface_t *mask; + cairo_status_t status; + + converter = _cairo_tor_scan_converter_create (extents->x, extents->y, + extents->x + extents->width, + extents->y + extents->height, + info->fill_rule); + status = converter->add_polygon (converter, info->polygon); + if (unlikely (status)) + goto CLEANUP_CONVERTER; + + /* TODO: support rendering to A1 surfaces (or: go add span + * compositing to pixman.) */ + + { + mask = cairo_image_surface_create (CAIRO_FORMAT_A8, + extents->width, + extents->height); + + if (cairo_surface_status(&mask->base)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_CONVERTER; + } + } + + renderer.base.render_rows = _cairo_image_surface_span; + renderer.mask_stride = cairo_image_surface_get_stride (mask); + renderer.mask_data = cairo_image_surface_get_data (mask); + renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; + + status = converter->generate (converter, &renderer.base); + if (unlikely (status)) + goto CLEANUP_RENDERER; + + _cairo_win32_surface_span_renderer_composite + (closure, + op, + src, + mask, + (cairo_win32_surface_t*)dst, // ewwww + dst_x, + dst_y, + extents, + clip_region); +#if 0 + { + pixman_image_t *src; + int src_x, src_y; + + src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_RENDERER; + } + + pixman_image_composite32 (_pixman_operator (op), src, mask, dst, + extents->x + src_x, extents->y + src_y, + 0, 0, /* mask.x, mask.y */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + pixman_image_unref (src); + } +#endif + CLEANUP_RENDERER: + cairo_surface_destroy (mask); + CLEANUP_CONVERTER: + converter->destroy (converter); + return status; +} + + + +cairo_status_t +_cairo_win32_surface_fallback_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_clip_path_t *clip_path = clip ? clip->path : NULL; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_boxes_t boxes; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + cairo_traps_t traps; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &rect, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && clip_path->prev == NULL && + _cairo_operator_bounded_by_mask (op)) + { + return _cairo_surface_fill (surface, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL); + } + + /* meh, surface-fallback is dying anyway... */ + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _cairo_traps_init_boxes (&traps, &boxes); + if (unlikely (status)) + goto CLEANUP_BOXES; + + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, CAIRO_ANTIALIAS_DEFAULT, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + _cairo_traps_fini (&traps); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +static cairo_status_t +_cairo_surface_mask_draw_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_region_t *clip_region) +{ + cairo_pattern_t *mask = closure; + cairo_status_t status; + cairo_region_t *extents_region = NULL; + + if (clip_region == NULL && + !_cairo_operator_bounded_by_source (op)) { + extents_region = cairo_region_create_rectangle (extents); + if (unlikely (extents_region->status)) + return extents_region->status; + cairo_region_translate (extents_region, -dst_x, -dst_y); + clip_region = extents_region; + } + + if (src) { + status = _cairo_surface_composite (op, + src, mask, dst, + extents->x, extents->y, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } else { + status = _cairo_surface_composite (op, + mask, NULL, dst, + extents->x, extents->y, + 0, 0, /* unused */ + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + clip_region); + } + + if (extents_region) + cairo_region_destroy (extents_region); + + return status; +} + +cairo_status_t +_cairo_win32_surface_fallback_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &rect, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + } + + return _clip_and_composite (clip, op, source, + _cairo_surface_mask_draw_func, + (void *) mask, + surface, + extents.is_bounded ? &extents.bounded : &extents.unbounded); +} + +cairo_status_t +_cairo_win32_surface_fallback_stroke (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &rect, + op, source, + path, stroke_style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, clip_boxes, num_boxes); + + if (path->is_rectilinear) { + status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, + stroke_style, + ctm, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) + goto DO_TRAPS; + + if (_cairo_operator_bounded_by_mask (op)) { + _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + goto CLEANUP; + } + + if (antialias != CAIRO_ANTIALIAS_NONE) { + cairo_composite_spans_info_t info; + + info.polygon = &polygon; + info.fill_rule = CAIRO_FILL_RULE_WINDING; + info.antialias = antialias; + + status = _clip_and_composite (clip, op, source, + _composite_spans, + &info, surface, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + CLEANUP: + _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +cairo_status_t +_cairo_win32_surface_fallback_fill (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_polygon_t polygon; + cairo_traps_t traps; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_bool_t is_rectilinear; + cairo_composite_rectangles_t extents; + cairo_rectangle_int_t rect; + cairo_status_t status; + + if (!_cairo_surface_get_extents (surface, &rect)) + ASSERT_NOT_REACHED; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &rect, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) + return status; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, clip_boxes, num_boxes); + + _cairo_polygon_init (&polygon); + _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); + + if (_cairo_path_fixed_fill_is_empty (path)) + goto DO_TRAPS; + + is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); + if (is_rectilinear) { + status = _cairo_path_fixed_fill_rectilinear_to_traps (path, + fill_rule, + &traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (_cairo_status_is_error (status)) + goto CLEANUP; + } + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (unlikely (status)) + goto CLEANUP; + + if (polygon.num_edges == 0) + goto DO_TRAPS; + + if (_cairo_operator_bounded_by_mask (op)) { + _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); + if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) + goto CLEANUP; + } + + if (is_rectilinear) { + status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, + &polygon, + fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) + goto DO_TRAPS; + + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP; + } + + + if (antialias != CAIRO_ANTIALIAS_NONE) { + cairo_composite_spans_info_t info; + + info.polygon = &polygon; + info.fill_rule = fill_rule; + info.antialias = antialias; + + status = _clip_and_composite (clip, op, source, + _composite_spans, + &info, surface, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + goto CLEANUP; + } + + /* Fall back to trapezoid fills. */ + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + fill_rule); + if (unlikely (status)) + goto CLEANUP; + + DO_TRAPS: + status = _clip_and_composite_trapezoids (source, op, surface, + &traps, antialias, + clip, + extents.is_bounded ? &extents.bounded : &extents.unbounded); + CLEANUP: + _cairo_traps_fini (&traps); + _cairo_polygon_fini (&polygon); + if (clip_boxes != boxes_stack) + free (clip_boxes); + + return status; +} + +static const cairo_surface_backend_t cairo_win32_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32, + _cairo_win32_surface_create_similar, + _cairo_win32_surface_finish, + _cairo_win32_surface_acquire_source_image, + _cairo_win32_surface_release_source_image, + _cairo_win32_surface_acquire_dest_image, + _cairo_win32_surface_release_dest_image, + NULL, /* clone similar */ + _cairo_win32_surface_composite, + _cairo_win32_surface_fill_rectangles, + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_win32_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + _cairo_win32_surface_flush, + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + _cairo_win32_surface_fallback_paint, + _cairo_win32_surface_fallback_mask, + _cairo_win32_surface_fallback_stroke, + _cairo_win32_surface_fallback_fill, + _cairo_win32_surface_show_glyphs, + + NULL, /* snapshot */ + _cairo_win32_surface_is_similar, +}; + +/* Notes: + * + * Win32 alpha-understanding functions + * + * BitBlt - will copy full 32 bits from a 32bpp DIB to result + * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) + * (but not safe going RGB24->ARGB32, if RGB24 is also represented + * as a 32bpp DIB, since the alpha isn't discarded!) + * + * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, + * it will still copy over the src alpha, because the SCA value (255) will be + * multiplied by all the src components. + */ + + +cairo_int_status_t +_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface) +{ + RECT rect; + int clipBoxType; + int gm; + XFORM saved_xform; + + /* GetClipBox/GetClipRgn and friends interact badly with a world transform + * set. GetClipBox returns values in logical (transformed) coordinates; + * it's unclear what GetClipRgn returns, because the region is empty in the + * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. + * Similarily, IntersectClipRect works in logical units, whereas SelectClipRgn + * works in device units. + * + * So, avoid the whole mess and get rid of the world transform + * while we store our initial data and when we restore initial coordinates. + * + * XXX we may need to modify x/y by the ViewportOrg or WindowOrg + * here in GM_COMPATIBLE; unclear. + */ + gm = GetGraphicsMode (hdc); + if (gm == GM_ADVANCED) { + GetWorldTransform (hdc, &saved_xform); + ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); + } + + clipBoxType = GetClipBox (hdc, &rect); + if (clipBoxType == ERROR) { + _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); + SetGraphicsMode (hdc, gm); + /* XXX: Can we make a more reasonable guess at the error cause here? */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + surface->clip_rect.x = rect.left; + surface->clip_rect.y = rect.top; + surface->clip_rect.width = rect.right - rect.left; + surface->clip_rect.height = rect.bottom - rect.top; + + surface->initial_clip_rgn = NULL; + surface->had_simple_clip = FALSE; + + if (clipBoxType == COMPLEXREGION) { + surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); + if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { + DeleteObject(surface->initial_clip_rgn); + surface->initial_clip_rgn = NULL; + } + } else if (clipBoxType == SIMPLEREGION) { + surface->had_simple_clip = TRUE; + } + + if (gm == GM_ADVANCED) + SetWorldTransform (hdc, &saved_xform); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + XFORM saved_xform; + int gm = GetGraphicsMode (surface->dc); + if (gm == GM_ADVANCED) { + GetWorldTransform (surface->dc, &saved_xform); + ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY); + } + + /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ + SelectClipRgn (surface->dc, surface->initial_clip_rgn); + + if (surface->had_simple_clip) { + /* then if we had a simple clip, intersect */ + IntersectClipRect (surface->dc, + surface->clip_rect.x, + surface->clip_rect.y, + surface->clip_rect.x + surface->clip_rect.width, + surface->clip_rect.y + surface->clip_rect.height); + } + + if (gm == GM_ADVANCED) + SetWorldTransform (surface->dc, &saved_xform); + + return status; +} + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) +{ + RGNDATA *rd; + unsigned int z; + + if (header) + fprintf (stderr, "%s\n", header); + + if (rgn == NULL) { + fprintf (stderr, " NULL\n"); + } + + z = GetRegionData(rgn, 0, NULL); + rd = (RGNDATA*) malloc(z); + z = GetRegionData(rgn, z, rd); + + fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", + rd->rdh.nCount, + rd->rdh.rcBound.left, + rd->rdh.rcBound.top, + rd->rdh.rcBound.right - rd->rdh.rcBound.left, + rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); + + for (z = 0; z < rd->rdh.nCount; z++) { + RECT r = ((RECT*)rd->Buffer)[z]; + fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", + z, r.left, r.top, r.right - r.left, r.bottom - r.top); + } + + free(rd); + fflush (stderr); +} + +/** + * cairo_win32_surface_set_can_convert_to_dib + * @surface: a #cairo_surface_t + * @can_convert: a #cairo_bool_t indicating whether this surface can + * be coverted to a DIB if necessary + * + * A DDB surface with this flag set can be converted to a DIB if it's + * used as a source in a way that GDI can't natively handle; for + * example, drawing a RGB24 DDB onto an ARGB32 DIB. Doing this + * conversion results in a significant speed optimization, because we + * can call on pixman to perform the operation natively, instead of + * reading the data from the DC each time. + * + * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully + * changed, or an error otherwise. + * + */ +cairo_status_t +cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert) +{ + cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; + if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + if (surface->bitmap) { + if (can_convert) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; + else + surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_surface_get_can_convert_to_dib + * @surface: a #cairo_surface_t + * @can_convert: a #cairo_bool_t* that receives the return value + * + * Returns the value of the flag indicating whether the surface can be + * converted to a DIB if necessary, as set by + * cairo_win32_surface_set_can_convert_to_dib. + * + * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully + * retreived, or an error otherwise. + * + */ +cairo_status_t +cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert) +{ + cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; + if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0); + return CAIRO_STATUS_SUCCESS; +} + +int +cairo_win32_surface_get_width (cairo_surface_t *asurface) +{ + cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; + if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + return surface->extents.width; +} + +int +cairo_win32_surface_get_height (cairo_surface_t *asurface) +{ + cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; + if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + return surface->extents.height; +} diff --git a/libs/cairo/src/cairo-win32.h b/libs/cairo/src/cairo-win32.h new file mode 100644 index 000000000..a1d45c19a --- /dev/null +++ b/libs/cairo/src/cairo-win32.h @@ -0,0 +1,307 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CAIRO_WIN32_H_ +#define _CAIRO_WIN32_H_ + +#include "cairo.h" + +#if CAIRO_HAS_WIN32_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_win32_surface_create (HDC hdc); + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_alpha (HDC hdc); + +cairo_public cairo_surface_t * +cairo_win32_printing_surface_create (HDC hdc); + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_ddb (HDC hdc, + cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_dib (cairo_format_t format, + int width, + int height); +cairo_public HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface); + +cairo_public HDC +cairo_win32_get_dc_with_clip (cairo_t *cr); + +cairo_public cairo_surface_t * +cairo_win32_surface_get_image (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert); + +cairo_public cairo_status_t +cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert); + +BYTE cairo_win32_get_system_text_quality (void); + +cairo_public int +cairo_win32_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_win32_surface_get_height (cairo_surface_t *surface); + +#if CAIRO_HAS_WIN32_FONT + +/* + * Win32 font support + */ + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_hfont (HFONT font); + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font); + +cairo_public cairo_status_t +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc); + +cairo_public void +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font); + +cairo_public double +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *logical_to_device); + +cairo_public void +cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical); + +#endif /* CAIRO_HAS_WIN32_FONT */ + +#if CAIRO_HAS_DWRITE_FONT + +/* + * Win32 DirectWrite font support + */ +cairo_public cairo_font_face_t * +cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrite_font_face); + +void +cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed); + +void +cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force); + +cairo_bool_t +cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font); + +void +cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode); + +int +cairo_dwrite_get_cleartype_rendering_mode(); + +#endif /* CAIRO_HAS_DWRITE_FONT */ + +struct IDirect3DSurface9; +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_d3dsurface9 (struct IDirect3DSurface9 *surface); + + +#if CAIRO_HAS_D2D_SURFACE + +struct _cairo_device +{ + int type; + int refcount; +}; + +/** + * Create a D2D device + * + * \return New D2D device, NULL if creation failed. + */ +cairo_device_t * +cairo_d2d_create_device(); + +cairo_device_t * +cairo_d2d_create_device_from_d3d10device(struct ID3D10Device1 *device); + +/** + * Releases a D2D device. + * + * \return References left to the device + */ +int +cairo_release_device(cairo_device_t *device); + +/** + * Addrefs a D2D device. + * + * \return References to the device + */ +int +cairo_addref_device(cairo_device_t *device); + +/** + * Flushes a D3D device. In most cases the surface backend will do this + * internally, but when using a surfaces created from a shared handle this + * should be executed manually when a different device is going to be accessing + * the same surface data. This will also block until the device is finished + * processing all work. + */ +void +cairo_d2d_finish_device(cairo_device_t *device); + +/** + * Gets the D3D10 device used by a certain cairo_device_t. + */ +struct ID3D10Device1* +cairo_d2d_device_get_device(cairo_device_t *device); + +/** + * Create a D2D surface for an HWND + * + * \param device Device used to create the surface + * \param wnd Handle for the window + * \param content Content of the window, should be COLOR_ALPHA for transparent windows + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content); + +/** + * Create a D2D surface of a certain size. + * + * \param device Device used to create the surface + * \param format Cairo format of the surface + * \param width Width of the surface + * \param height Height of the surface + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, + int width, + int height); + +/** + * Create a D3D surface from a Texture SharedHandle, this is obtained from a + * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format + * or an A8 format, the treatment of the alpha channel can be indicated using + * the content parameter. + * + * \param device Device used to create the surface + * \param handle Shared handle to the texture we want to wrap + * \param content Content of the texture, COLOR_ALPHA for ARGB + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content); + +/** + * Create a D3D surface from an ID3D10Texture2D texture, this is obtained from a + * CreateTexture2D call on a D3D10 device. This has to be an A8R8G8B8 format + * or an A8 format, the treatment of the alpha channel can be indicated using + * the content parameter. + * + * \param device Device used to create the surface + * \param texture Texture that we want to wrap + * \param content Content of the texture + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_texture(cairo_device_t *device, + struct ID3D10Texture2D *texture, + cairo_content_t content); + +/** + * Get the ID3D10Texture2D used for a surface. + */ +cairo_public struct ID3D10Texture2D *cairo_d2d_surface_get_texture(cairo_surface_t *surf); + +/** + * Present the backbuffer for a surface create for an HWND. This needs + * to be called when the owner of the original window surface wants to + * actually present the executed drawing operations to the screen. + * + * \param surface D2D surface. + */ +void cairo_d2d_present_backbuffer(cairo_surface_t *surface); + +/** + * Scroll the surface, this only moves the surface graphics, it does not + * actually scroll child windows or anything like that. Nor does it invalidate + * that area of the window. + * + * \param surface The d2d surface this operation should apply to. + * \param x The x delta for the movement + * \param y The y delta for the movement + * \param clip The clip rectangle, the is the 'part' of the surface that needs + * scrolling. + */ +void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); + +/** + * Get a DC for the current render target. When selecting the retention option this + * call can be relatively slow, since it may require reading back contents from the + * hardware surface. + * + * \note This must be matched by a call to ReleaseDC! + * + * \param retain_contents If true the current contents of the RT is copied to the DC, + * otherwise the DC is initialized to transparent black. + */ +HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents); + +/** + * Release the DC acquired through GetDC(). Optionally an update region may be specified + * + * \param updated_rect The area of the DC that was updated, if null the entire dc will + * be updated. + */ +void cairo_d2d_release_dc(cairo_surface_t *surcace, const cairo_rectangle_int_t *updated_rect); + +/** + * Get an estimate of the amount of (video) RAM which is currently in use by the D2D + * internal image surface cache. + */ +int cairo_d2d_get_image_surface_cache_usage(); + +/** + * Get an estimate of the amount of VRAM which is currently used by the d2d + * surfaces for a device. This does -not- include the internal image surface + * cache. + */ +int cairo_d2d_get_surface_vram_usage(cairo_device_t *device); + +/** + * Get the width of the surface. + */ +int cairo_d2d_surface_get_width(cairo_surface_t *surface); + +/** + * Get the height of the surface. + */ +int cairo_d2d_surface_get_height(cairo_surface_t *surface); +#endif + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_WIN32_SURFACE */ +# error Cairo was not compiled with support for the win32 backend +#endif /* CAIRO_HAS_WIN32_SURFACE */ + +#endif /* _CAIRO_WIN32_H_ */ diff --git a/libs/cairo/src/cairo-xcb-surface.c b/libs/cairo/src/cairo-xcb-surface.c new file mode 100644 index 000000000..7f53e630f --- /dev/null +++ b/libs/cairo/src/cairo-xcb-surface.c @@ -0,0 +1,1332 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-xcb.h" +#include "cairo-xcb-private.h" + +#if CAIRO_HAS_XCB_DRM_FUNCTIONS +#include +#endif + +#define AllPlanes ((unsigned) -1) +#define CAIRO_ASSUME_PIXMAP 20 +#define XLIB_COORD_MAX 32767 + +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_proto (cairo_xcb_surface_create); +slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); +slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); +#endif + +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS +#include "drm/cairo-drm-private.h" +#endif + +/** + * SECTION:cairo-xcb + * @Title: XCB Surfaces + * @Short_Description: X Window System rendering using the XCB library + * @See_Also: #cairo_surface_t + * + * The XCB surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XCB library. + * + * Note that the XCB surface automatically takes advantage of the X render + * extension if it is available. + */ + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +static cairo_status_t +_cairo_xcb_surface_create_similar_shm (cairo_xcb_surface_t *other, + pixman_format_code_t pixman_format, + int width, int height, + cairo_surface_t **out) +{ + size_t size, stride; + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; + cairo_surface_t *image; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); + size = stride * height; + if (size < CAIRO_XCB_SHM_SMALL_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_xcb_connection_allocate_shm_info (other->connection, + size, &shm_info); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + status = image->status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + status = _cairo_user_data_array_set_data (&image->user_data, + (const cairo_user_data_key_t *) other->connection, + shm_info, + (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); + if (unlikely (status)) { + cairo_surface_destroy (image); + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + *out = image; + return CAIRO_STATUS_SUCCESS; +} +#endif + +cairo_surface_t * +_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other, + cairo_content_t content, + int width, int height) +{ + cairo_surface_t *image = NULL; + pixman_format_code_t pixman_format; + + /* XXX choose pixman_format from connection->image_formats */ + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + break; + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + break; + } + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + if (other->flags & CAIRO_XCB_HAS_SHM) { + cairo_status_t status; + + status = _cairo_xcb_surface_create_similar_shm (other, + pixman_format, + width, height, + &image); + if (_cairo_status_is_error (status)) + return _cairo_surface_create_in_error (status); + } +#endif + + if (image == NULL) { + image = _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + width, height, + 0); + } + + return image; +} + +cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + cairo_xcb_surface_t *other = abstract_other; + cairo_xcb_surface_t *surface; + cairo_xcb_connection_t *connection; + xcb_pixmap_t pixmap; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return NULL; + + if (width <= 0 || height <= 0) + return NULL; + +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS + if (other->drm != NULL) { + cairo_surface_t *drm; + + drm = _cairo_drm_surface_create_similar (other->drm, content, width, height); + if (drm != NULL) + return drm; + } +#endif + + if ((other->flags & CAIRO_XCB_HAS_RENDER) == 0) + return _cairo_xcb_surface_create_similar_image (other, content, width, height); + + connection = other->connection; + status = _cairo_xcb_connection_acquire (connection); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + status =_cairo_xcb_connection_take_socket (connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (connection); + return _cairo_surface_create_in_error (status); + } + + if (content == other->base.content) { + pixmap = _cairo_xcb_connection_create_pixmap (connection, + other->depth, + other->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + other->pixman_format, + other->xrender_format, + width, height); + } else { + cairo_format_t format; + pixman_format_code_t pixman_format; + + /* XXX find a compatible xrender format */ + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + format = CAIRO_FORMAT_A8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + format = CAIRO_FORMAT_RGB24; + break; + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + format = CAIRO_FORMAT_ARGB32; + break; + } + + pixmap = _cairo_xcb_connection_create_pixmap (connection, + PIXMAN_FORMAT_DEPTH (pixman_format), + other->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + pixman_format, + connection->standard_formats[format], + width, height); + } + + if (unlikely (surface->base.status)) + _cairo_xcb_connection_free_pixmap (connection, pixmap); + + _cairo_xcb_connection_release (connection); + + return &surface->base; +} + +static cairo_status_t +_cairo_xcb_surface_finish (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->fallback != NULL) { + cairo_surface_finish (surface->fallback); + cairo_surface_destroy (surface->fallback); + } + + cairo_list_del (&surface->link); + +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS + if (surface->drm != NULL) { + cairo_surface_finish (surface->drm); + cairo_surface_destroy (surface->drm); + + xcb_dri2_destroy_drawable (surface->connection->xcb_connection, + surface->drawable); + } +#endif + + status = _cairo_xcb_connection_acquire (surface->connection); + if (status == CAIRO_STATUS_SUCCESS) { + if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { + if (surface->picture != XCB_NONE) { + _cairo_xcb_connection_render_free_picture (surface->connection, + surface->picture); + } + + if (surface->owns_pixmap) + _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); + } + _cairo_xcb_connection_release (surface->connection); + } + + _cairo_xcb_connection_destroy (surface->connection); + + return status; +} + +static void +_destroy_image (pixman_image_t *image, void *data) +{ + free (data); +} + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +static cairo_int_status_t +_cairo_xcb_surface_create_shm_image (cairo_xcb_surface_t *target, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; + size_t size, stride; + + if ((target->flags & CAIRO_XCB_HAS_SHM) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (target->width, + PIXMAN_FORMAT_BPP (target->pixman_format)); + size = stride * target->height; + if (size < CAIRO_XCB_SHM_SMALL_IMAGE) { + target->flags &= ~CAIRO_XCB_HAS_SHM; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_xcb_connection_allocate_shm_info (target->screen->connection, + size, &shm_info); + if (unlikely (status)) + return status; + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (shm_info->mem, + target->pixman_format, + target->width, + target->height, + stride); + status = image->base.status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + status = _cairo_user_data_array_set_data (&image->base.user_data, + (const cairo_user_data_key_t *) target->connection, + shm_info, + (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + *image_out = image; + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_status_t +_get_shm_image (cairo_xcb_surface_t *surface, + cairo_image_surface_t **image_out) +{ +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_status_t status; + + status = _cairo_xcb_surface_create_shm_image (surface, &image, &shm_info); + if (unlikely (status)) + return status; + + if (! surface->base.is_clear) { + status = _cairo_xcb_connection_shm_get_image (surface->connection, + surface->drawable, + 0, 0, + surface->width, + surface->height, + shm_info->shm, + shm_info->offset); + if (unlikely (status)) + return status; + } else { + memset (image->data, 0, image->stride * image->height); + } + + *image_out = image; + return CAIRO_STATUS_SUCCESS; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_status_t +_get_image (cairo_xcb_surface_t *surface, + cairo_bool_t use_shm, + cairo_image_surface_t **image_out) +{ + cairo_image_surface_t *image; + cairo_xcb_connection_t *connection; + xcb_get_image_reply_t *reply; + cairo_status_t status; + + if (surface->base.is_clear || surface->deferred_clear) { + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + surface->pixman_format, + surface->width, + surface->height, + 0); + *image_out = image; + return image->base.status; + } + + connection = surface->connection; + + status = _cairo_xcb_connection_acquire (connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (connection); + if (unlikely (status)) + goto FAIL; + + if (use_shm) { + status = _get_shm_image (surface, image_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FAIL; + } + + if (surface->use_pixmap == 0) { + status = _cairo_xcb_connection_get_image (connection, + surface->drawable, + 0, 0, + surface->width, + surface->height, + &reply); + if (unlikely (status)) + goto FAIL; + } else { + surface->use_pixmap--; + reply = NULL; + } + + if (reply == NULL && ! surface->owns_pixmap) { + /* xcb_get_image_t from a window is dangerous because it can + * produce errors if the window is unmapped or partially + * outside the screen. We could check for errors and + * retry, but to keep things simple, we just create a + * temporary pixmap + */ + xcb_pixmap_t pixmap; + xcb_gcontext_t gc; + + gc = _cairo_xcb_screen_get_gc (surface->screen, + surface->drawable, + surface->depth); + pixmap = _cairo_xcb_connection_create_pixmap (connection, + surface->depth, + surface->drawable, + surface->width, + surface->height); + + /* XXX IncludeInferiors? */ + _cairo_xcb_connection_copy_area (connection, + surface->drawable, + pixmap, gc, + 0, 0, + 0, 0, + surface->width, + surface->height); + + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + + status = _cairo_xcb_connection_get_image (connection, + pixmap, + 0, 0, + surface->width, + surface->height, + &reply); + _cairo_xcb_connection_free_pixmap (connection, pixmap); + + if (unlikely (status)) + goto FAIL; + } + + if (unlikely (reply == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + /* XXX byte swap */ + /* XXX format conversion */ + assert (reply->depth == surface->depth); + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format + (xcb_get_image_data (reply), + surface->pixman_format, + surface->width, + surface->height, + CAIRO_STRIDE_FOR_WIDTH_BPP (surface->width, + PIXMAN_FORMAT_BPP (surface->pixman_format))); + status = image->base.status; + if (unlikely (status)) { + free (reply); + goto FAIL; + } + + assert (xcb_get_image_data_length (reply) == image->height * image->stride); + + pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply); + + _cairo_xcb_connection_release (connection); + + /* synchronisation point */ + surface->marked_dirty = FALSE; + + *image_out = image; + return CAIRO_STATUS_SUCCESS; + +FAIL: + _cairo_xcb_connection_release (connection); + return status; +} + +static cairo_status_t +_cairo_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_acquire_source_image (surface->drm, + image_out, image_extra); + } + + if (surface->fallback != NULL) { + image = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback); + goto DONE; + } + + image = (cairo_image_surface_t *) + _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); + if (image != NULL) { + image = (cairo_image_surface_t *) cairo_surface_reference (&image->base); + goto DONE; + } + + status = _get_image (surface, FALSE, &image); + if (unlikely (status)) + return status; + + cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); + +DONE: + *image_out = image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_release_source_image (surface->drm, + image, image_extra); + } + + cairo_surface_destroy (&image->base); +} + +static cairo_bool_t +_cairo_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + return TRUE; +} + +static void +_cairo_xcb_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + /* XXX copy from xlib */ + _cairo_font_options_init_default (options); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); +} + +static cairo_status_t +_put_shm_image (cairo_xcb_surface_t *surface, + xcb_gcontext_t gc, + cairo_image_surface_t *image) +{ +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + cairo_xcb_shm_info_t *shm_info; + + shm_info = _cairo_user_data_array_get_data (&image->base.user_data, + (const cairo_user_data_key_t *) surface->connection); + if (shm_info == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + shm_info->seqno = + _cairo_xcb_connection_shm_put_image (surface->connection, + surface->drawable, + gc, + surface->width, surface->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); + + return CAIRO_STATUS_SUCCESS; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_status_t +_put_image (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* XXX track damaged region? */ + + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + status = _cairo_xcb_connection_take_socket (surface->connection); + if (unlikely (status)) { + _cairo_xcb_connection_release (surface->connection); + return status; + } + + if (image->pixman_format == surface->pixman_format) { + xcb_gcontext_t gc; + + assert (image->width == surface->width); + assert (image->height == surface->height); + assert (image->depth == surface->depth); + assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); + + gc = _cairo_xcb_screen_get_gc (surface->screen, + surface->drawable, + surface->depth); + + status = _put_shm_image (surface, gc, image); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_xcb_connection_put_image (surface->connection, + surface->drawable, gc, + image->width, image->height, + 0, 0, + image->depth, + image->stride, + image->data); + status = CAIRO_STATUS_SUCCESS; + } + + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + } else { + ASSERT_NOT_REACHED; + } + + _cairo_xcb_connection_release (surface->connection); + return status; +} + +static cairo_status_t +_cairo_xcb_surface_flush (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) + return surface->drm->backend->flush (surface->drm); + + if (likely (surface->fallback == NULL)) { + status = CAIRO_STATUS_SUCCESS; + if (! surface->base.finished && surface->deferred_clear) + status = _cairo_xcb_surface_clear (surface); + + return status; + } + + status = surface->base.status; + if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) { + status = cairo_surface_status (surface->fallback); + + if (status == CAIRO_STATUS_SUCCESS) { + status = _put_image (surface, + (cairo_image_surface_t *) surface->fallback); + } + + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_attach_snapshot (&surface->base, + surface->fallback, + cairo_surface_finish); + } + } + + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_xcb_surface_t *surface = abstract_surface; + surface->marked_dirty = TRUE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_xcb_surface_map_to_image (cairo_xcb_surface_t *surface) +{ + cairo_status_t status; + cairo_image_surface_t *image; + + status = _get_image (surface, TRUE, &image); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + +static cairo_int_status_t +_cairo_xcb_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) + return _cairo_surface_paint (surface->drm, op, source, clip); + + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_xcb_surface_render_paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } + + return _cairo_surface_paint (surface->fallback, op, source, clip); +} + +static cairo_int_status_t +_cairo_xcb_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) + return _cairo_surface_mask (surface->drm, op, source, mask, clip); + + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_mask (surface, + op, source, mask, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_xcb_surface_render_mask (surface, + op, source, mask, clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } + + return _cairo_surface_mask (surface->fallback, + op, source, mask, + clip); +} + +static cairo_int_status_t +_cairo_xcb_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_stroke (surface->drm, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + } + + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_stroke (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_xcb_surface_render_stroke (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } + + return _cairo_surface_stroke (surface->fallback, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_xcb_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_fill (surface->drm, + op, source, + path, fill_rule, + tolerance, antialias, + clip); + } + + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_xcb_surface_render_fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } + + return _cairo_surface_fill (surface->fallback, + op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_xcb_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *num_remaining) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + *num_remaining = 0; + + if (surface->drm != NULL && ! surface->marked_dirty) { + return _cairo_surface_show_text_glyphs (surface->drm, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); + } + + if (surface->fallback == NULL) { + status = _cairo_xcb_surface_cairo_glyphs (surface, + op, source, + scaled_font, glyphs, num_glyphs, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_xcb_surface_render_glyphs (surface, + op, source, + scaled_font, glyphs, num_glyphs, + clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + surface->fallback = _cairo_xcb_surface_map_to_image (surface); + } + + return _cairo_surface_show_text_glyphs (surface->fallback, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); +} + +const cairo_surface_backend_t _cairo_xcb_surface_backend = { + CAIRO_SURFACE_TYPE_XCB, + + _cairo_xcb_surface_create_similar, + _cairo_xcb_surface_finish, + _cairo_xcb_surface_acquire_source_image, + _cairo_xcb_surface_release_source_image, + NULL, NULL, NULL, /* dest acquire/release/clone */ + + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_xcb_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_xcb_surface_get_font_options, + + _cairo_xcb_surface_flush, + _cairo_xcb_surface_mark_dirty, + _cairo_xcb_surface_scaled_font_fini, + _cairo_xcb_surface_scaled_glyph_fini, + + _cairo_xcb_surface_paint, + _cairo_xcb_surface_mask, + _cairo_xcb_surface_stroke, + _cairo_xcb_surface_fill, + _cairo_xcb_surface_glyphs, +}; + +#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS +static cairo_surface_t * +_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, + cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + pixman_format_code_t pixman_format, + int width, int height) +{ + uint32_t attachments[] = { XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT }; + xcb_dri2_get_buffers_reply_t *buffers; + xcb_dri2_dri2_buffer_t *buffer; + cairo_surface_t *surface; + + if (! _cairo_drm_size_is_valid (screen->device, width, height)) + return NULL; + + xcb_dri2_create_drawable (connection->xcb_connection, + drawable); + + buffers = xcb_dri2_get_buffers_reply (connection->xcb_connection, + xcb_dri2_get_buffers (connection->xcb_connection, + drawable, 1, + ARRAY_LENGTH (attachments), + attachments), + 0); + if (buffers == NULL) { + xcb_dri2_destroy_drawable (connection->xcb_connection, + drawable); + return NULL; + } + + /* If the drawable is a window, we expect to receive an extra fake front, + * which would involve copying on each flush - contrary to the user + * expectations. But that is likely to be preferable to mixing drm/xcb + * operations. + */ + buffer = xcb_dri2_get_buffers_buffers (buffers); + if (buffers->count == 1 && buffer[0].attachment == XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT) { + assert (buffer[0].cpp == PIXMAN_FORMAT_BPP (pixman_format) / 8); + surface = cairo_drm_surface_create_for_name (screen->device, + buffer[0].name, + _cairo_format_from_pixman_format (pixman_format), + width, height, + buffer[0].pitch); + } else { + xcb_dri2_destroy_drawable (connection->xcb_connection, + drawable); + surface = NULL; + } + free (buffers); + + return surface; +} + +#else + +static cairo_surface_t * +_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, + cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + pixman_format_code_t pixman_format, + int width, int height) +{ + return NULL; +} + +#endif + +cairo_surface_t * +_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + cairo_bool_t owns_pixmap, + pixman_format_code_t pixman_format, + xcb_render_pictformat_t xrender_format, + int width, + int height) +{ + cairo_xcb_surface_t *surface; + + surface = malloc (sizeof (cairo_xcb_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_surface_backend, + &screen->connection->device, + _cairo_content_from_pixman_format (pixman_format)); + + surface->connection = _cairo_xcb_connection_reference (screen->connection); + surface->screen = screen; + cairo_list_add (&surface->link, &screen->surfaces); + + surface->fallback = NULL; + + surface->drawable = drawable; + surface->owns_pixmap = owns_pixmap; + surface->use_pixmap = 0; + + surface->deferred_clear = FALSE; + + surface->width = width; + surface->height = height; + surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); + + surface->picture = XCB_NONE; + + surface->pixman_format = pixman_format; + surface->xrender_format = xrender_format; + + surface->flags = screen->connection->flags; + + surface->marked_dirty = FALSE; + surface->drm = NULL; + if (screen->device != NULL) { + surface->drm = _xcb_drm_create_surface_for_drawable (surface->connection, + surface->screen, + drawable, + pixman_format, + width, height); + } + + return &surface->base; +} + +static xcb_screen_t * +_cairo_xcb_screen_from_visual (xcb_connection_t *connection, + xcb_visualtype_t *visual, + int *depth) +{ + xcb_depth_iterator_t d; + xcb_screen_iterator_t s; + + s = xcb_setup_roots_iterator (xcb_get_setup (connection)); + for (; s.rem; xcb_screen_next (&s)) { + if (s.data->root_visual == visual->visual_id) { + *depth = s.data->root_depth; + return s.data; + } + + d = xcb_screen_allowed_depths_iterator(s.data); + for (; d.rem; xcb_depth_next (&d)) { + xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); + + for (; v.rem; xcb_visualtype_next (&v)) { + if (v.data->visual_id == visual->visual_id) { + *depth = d.data->depth; + return s.data; + } + } + } + } + + return NULL; +} + +cairo_surface_t * +cairo_xcb_surface_create (xcb_connection_t *xcb_connection, + xcb_drawable_t drawable, + xcb_visualtype_t *visual, + int width, + int height) +{ + cairo_xcb_screen_t *screen; + xcb_screen_t *xcb_screen; + cairo_format_masks_t image_masks; + pixman_format_code_t pixman_format; + xcb_render_pictformat_t xrender_format; + int depth; + + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth); + if (unlikely (xcb_screen == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_VISUAL); + + image_masks.alpha_mask = 0; + image_masks.red_mask = visual->red_mask; + image_masks.green_mask = visual->green_mask; + image_masks.blue_mask = visual->blue_mask; + if (depth > 16) + image_masks.bpp = 32; + else if (depth > 8) + image_masks.bpp = 16; + else if (depth > 1) + image_masks.bpp = 8; + else + image_masks.bpp = 1; + + if (! _pixman_format_from_masks (&image_masks, &pixman_format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + xrender_format = + _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection, + visual->visual_id); + + return _cairo_xcb_surface_create_internal (screen, drawable, FALSE, + pixman_format, + xrender_format, + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_create); +#endif + +cairo_surface_t * +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen, + xcb_pixmap_t bitmap, + int width, + int height) +{ + cairo_xcb_screen_t *screen; + + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE, + PIXMAN_a1, + screen->connection->standard_formats[CAIRO_FORMAT_A1], + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_create_for_bitmap); +#endif + +/** + * cairo_xcb_surface_create_with_xrender_format: + * @connection: an XCB connection + * @drawable: an XCB drawable + * @screen: the XCB screen associated with @drawable + * @format: the picture format to use for drawing to @drawable. The + * depth of @format mush match the depth of the drawable. + * @width: the current width of @drawable + * @height: the current height of @drawable + * + * Creates an XCB surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided picture format. + * + * Note: If @drawable is a Window, then the function + * cairo_xcb_surface_set_size() must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface. + **/ +cairo_surface_t * +cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen, + xcb_drawable_t drawable, + xcb_render_pictforminfo_t *format, + int width, + int height) +{ + cairo_xcb_screen_t *screen; + cairo_format_masks_t image_masks; + pixman_format_code_t pixman_format; + + if (xcb_connection_has_error (xcb_connection)) + return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + image_masks.alpha_mask = + (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift; + image_masks.red_mask = + (unsigned long) format->direct.red_mask << format->direct.red_shift; + image_masks.green_mask = + (unsigned long) format->direct.green_mask << format->direct.green_shift; + image_masks.blue_mask = + (unsigned long) format->direct.blue_mask << format->direct.blue_shift; +#if 0 + image_masks.bpp = format->depth; +#else + if (format->depth > 16) + image_masks.bpp = 32; + else if (format->depth > 8) + image_masks.bpp = 16; + else if (format->depth > 1) + image_masks.bpp = 8; + else + image_masks.bpp = 1; +#endif + + if (! _pixman_format_from_masks (&image_masks, &pixman_format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _cairo_xcb_surface_create_internal (screen, + drawable, + FALSE, + pixman_format, + format->id, + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); +#endif + +/** + * cairo_xcb_surface_set_size: + * @surface: a #cairo_surface_t for the XCB backend + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the XCB drawable underlying the + * surface. For a surface created for a window (rather than a pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * A pixmap can never change size, so it is never necessary to call + * this function on a surface created for a pixmap. + **/ +void +cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_xcb_surface_t *surface; + cairo_status_t status_ignored; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status_ignored = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + + if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { + status_ignored = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status_ignored = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } + + surface = (cairo_xcb_surface_t *) abstract_surface; + surface->width = width; + surface->height = height; +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_set_size); +#endif diff --git a/libs/cairo/src/cairo-xcb-xrender.h b/libs/cairo/src/cairo-xcb-xrender.h new file mode 100644 index 000000000..ff81706c4 --- /dev/null +++ b/libs/cairo/src/cairo-xcb-xrender.h @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XCB_XRENDER_H +#define CAIRO_XCB_XRENDER_H + +#include "cairo.h" + +#if CAIRO_HAS_XCB_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c, + xcb_drawable_t drawable, + xcb_screen_t *screen, + xcb_render_pictforminfo_t *format, + int width, + int height); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_XCB_SURFACE */ +# error Cairo was not compiled with support for the xcb backend +#endif /* CAIRO_HAS_XCB_SURFACE */ + +#endif /* CAIRO_XCB_XRENDER_H */ diff --git a/libs/cairo/src/cairo-xcb.h b/libs/cairo/src/cairo-xcb.h new file mode 100644 index 000000000..de72b4f04 --- /dev/null +++ b/libs/cairo/src/cairo-xcb.h @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XCB_H +#define CAIRO_XCB_H + +#include "cairo.h" + +#if CAIRO_HAS_XCB_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xcb_surface_create (xcb_connection_t *connection, + xcb_drawable_t drawable, + xcb_visualtype_t *visual, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, + xcb_screen_t *screen, + xcb_pixmap_t bitmap, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, + xcb_screen_t *screen, + xcb_drawable_t drawable, + xcb_render_pictforminfo_t *format, + int width, + int height); + +cairo_public void +cairo_xcb_surface_set_size (cairo_surface_t *surface, + int width, + int height); + +/* debug interface */ + +cairo_public void +cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, + int major_version, + int minor_version); + +cairo_public void +cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_XCB_SURFACE */ +# error Cairo was not compiled with support for the xcb backend +#endif /* CAIRO_HAS_XCB_SURFACE */ + +#endif /* CAIRO_XCB_H */ diff --git a/libs/cairo/src/cairo-xlib-display.c b/libs/cairo/src/cairo-xlib-display.c new file mode 100644 index 000000000..7967d6027 --- /dev/null +++ b/libs/cairo/src/cairo-xlib-display.c @@ -0,0 +1,638 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-xlib-private.h" +#include "cairo-xlib-xrender-private.h" +#include "cairo-freelist-private.h" +#include "cairo-error-private.h" + +#include /* For XESetCloseDisplay */ + +typedef int (*cairo_xlib_error_func_t) (Display *display, + XErrorEvent *event); + +struct _cairo_xlib_job { + cairo_xlib_job_t *next; + enum { + RESOURCE, + WORK + } type; + union { + struct { + cairo_xlib_notify_resource_func notify; + XID xid; + } resource; + struct { + cairo_xlib_notify_func notify; + void *data; + void (*destroy) (void *); + } work; + } func; +}; + +static cairo_xlib_display_t *_cairo_xlib_display_list; + +static void +_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, + cairo_xlib_hook_t *hook); + +static void +_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) +{ + cairo_xlib_screen_t *screen; + cairo_xlib_hook_t *hook; + + cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link) + _cairo_xlib_screen_close_display (display, screen); + + while (TRUE) { + hook = display->close_display_hooks; + if (hook == NULL) + break; + + _cairo_xlib_remove_close_display_hook_internal (display, hook); + + hook->func (display, hook); + } + display->closed = TRUE; +} + +static void +_cairo_xlib_display_finish (void *abstract_display) +{ + cairo_xlib_display_t *display = abstract_display; + + display->display = NULL; +} + +static void +_cairo_xlib_display_destroy (void *abstract_display) +{ + cairo_xlib_display_t *display = abstract_display; + + /* destroy all outstanding notifies */ + while (display->workqueue != NULL) { + cairo_xlib_job_t *job = display->workqueue; + display->workqueue = job->next; + + if (job->type == WORK && job->func.work.destroy != NULL) + job->func.work.destroy (job->func.work.data); + + _cairo_freelist_free (&display->wq_freelist, job); + } + _cairo_freelist_fini (&display->wq_freelist); + + while (! cairo_list_is_empty (&display->screens)) { + _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens, + cairo_xlib_screen_t, + link)); + } + + free (display); +} + +static int +_noop_error_handler (Display *display, + XErrorEvent *event) +{ + return False; /* return value is ignored */ +} + +static void +_cairo_xlib_display_notify (cairo_xlib_display_t *display) +{ + cairo_xlib_job_t *jobs, *job, *freelist; + Display *dpy = display->display; + + /* Optimistic atomic pointer read -- don't care if it is wrong due to + * contention as we will check again very shortly. + */ + if (display->workqueue == NULL) + return; + + jobs = display->workqueue; + while (jobs != NULL) { + display->workqueue = NULL; + + /* reverse the list to obtain FIFO order */ + job = NULL; + do { + cairo_xlib_job_t *next = jobs->next; + jobs->next = job; + job = jobs; + jobs = next; + } while (jobs != NULL); + freelist = jobs = job; + + do { + job = jobs; + jobs = job->next; + + switch (job->type){ + case WORK: + job->func.work.notify (dpy, job->func.work.data); + if (job->func.work.destroy != NULL) + job->func.work.destroy (job->func.work.data); + break; + + case RESOURCE: + job->func.resource.notify (dpy, job->func.resource.xid); + break; + } + } while (jobs != NULL); + + do { + job = freelist; + freelist = job->next; + _cairo_freelist_free (&display->wq_freelist, job); + } while (freelist != NULL); + + jobs = display->workqueue; + } +} + +static int +_cairo_xlib_close_display (Display *dpy, XExtCodes *codes) +{ + cairo_xlib_display_t *display, **prev, *next; + cairo_xlib_error_func_t old_handler; + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + for (display = _cairo_xlib_display_list; display; display = display->next) + if (display->display == dpy) + break; + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + if (display == NULL) + return 0; + + if (! cairo_device_acquire (&display->base)) { + /* protect the notifies from triggering XErrors */ + XSync (dpy, False); + old_handler = XSetErrorHandler (_noop_error_handler); + + _cairo_xlib_display_notify (display); + _cairo_xlib_call_close_display_hooks (display); + + /* catch any that arrived before marking the display as closed */ + _cairo_xlib_display_notify (display); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + + cairo_device_release (&display->base); + } + + /* + * Unhook from the global list + */ + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + prev = &_cairo_xlib_display_list; + for (display = _cairo_xlib_display_list; display; display = next) { + next = display->next; + if (display->display == dpy) { + *prev = next; + break; + } else + prev = &display->next; + } + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + assert (display != NULL); + + cairo_device_finish (&display->base); + cairo_device_destroy (&display->base); + + /* Return value in accordance with requirements of + * XESetCloseDisplay */ + return 0; +} + +static const cairo_device_backend_t _cairo_xlib_device_backend = { + CAIRO_DEVICE_TYPE_XLIB, + + NULL, + NULL, + + NULL, /* flush */ + _cairo_xlib_display_finish, + _cairo_xlib_display_destroy, +}; + +/** + * cairo_xlib_device_create: + * @dpy: the display to create the device for + * + * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. + * + * Returns: the device belonging to @dpy + **/ +cairo_device_t * +_cairo_xlib_device_create (Display *dpy) +{ + cairo_xlib_display_t *display; + cairo_xlib_display_t **prev; + cairo_device_t *device; + XExtCodes *codes; + const char *env; + + static int buggy_repeat_force = -1; + + CAIRO_MUTEX_INITIALIZE (); + + /* There is an apparent deadlock between this mutex and the + * mutex for the display, but it's actually safe. For the + * app to call XCloseDisplay() while any other thread is + * inside this function would be an error in the logic + * app, and the CloseDisplay hook is the only other place we + * acquire this mutex. + */ + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + + for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next) + { + if (display->display == dpy) { + /* + * MRU the list + */ + if (prev != &_cairo_xlib_display_list) { + *prev = display->next; + display->next = _cairo_xlib_display_list; + _cairo_xlib_display_list = display; + } + device = cairo_device_reference (&display->base); + goto UNLOCK; + } + } + + display = malloc (sizeof (cairo_xlib_display_t)); + if (unlikely (display == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + goto UNLOCK; + } + + /* Xlib calls out to the extension close_display hooks in LIFO + * order. So we have to ensure that all extensions that we depend + * on in our close_display hook are properly initialized before we + * add our hook. For now, that means Render, so we call into its + * QueryVersion function to ensure it gets initialized. + */ + display->render_major = display->render_minor = -1; + XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); + env = getenv ("CAIRO_DEBUG"); + if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) { + int max_render_major, max_render_minor; + + env += sizeof ("xrender-version=") - 1; + if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) + max_render_major = max_render_minor = -1; + + if (max_render_major < display->render_major || + (max_render_major == display->render_major && + max_render_minor < display->render_minor)) + { + display->render_major = max_render_major; + display->render_minor = max_render_minor; + } + } + + codes = XAddExtension (dpy); + if (unlikely (codes == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + free (display); + goto UNLOCK; + } + + _cairo_device_init (&display->base, &_cairo_xlib_device_backend); + + XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); + + _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t)); + + cairo_device_reference (&display->base); /* add one for the CloseDisplay */ + display->display = dpy; + cairo_list_init (&display->screens); + display->workqueue = NULL; + display->close_display_hooks = NULL; + display->closed = FALSE; + + memset (display->cached_xrender_formats, 0, + sizeof (display->cached_xrender_formats)); + + /* Prior to Render 0.10, there is no protocol support for gradients and + * we call function stubs instead, which would silently consume the drawing. + */ +#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 + display->buggy_gradients = TRUE; +#else + display->buggy_gradients = FALSE; +#endif + display->buggy_pad_reflect = FALSE; + display->buggy_repeat = FALSE; + + /* This buggy_repeat condition is very complicated because there + * are multiple X server code bases (with multiple versioning + * schemes within a code base), and multiple bugs. + * + * The X servers: + * + * 1. The Vendor=="XFree86" code base with release numbers such + * as 4.7.0 (VendorRelease==40700000). + * + * 2. The Vendor=="X.Org" code base (a descendant of the + * XFree86 code base). It originally had things like + * VendorRelease==60700000 for release 6.7.0 but then changed + * its versioning scheme so that, for example, + * VendorRelease==10400000 for the 1.4.0 X server within the + * X.Org 7.3 release. + * + * The bugs: + * + * 1. The original bug that led to the buggy_repeat + * workaround. This was a bug that Owen Taylor investigated, + * understood well, and characterized against carious X + * servers. Confirmed X servers with this bug include: + * + * "XFree86" <= 40500000 + * "X.Org" <= 60802000 (only with old numbering >= 60700000) + * + * 2. A separate bug resulting in a crash of the X server when + * using cairo's extend-reflect test case, (which, surprisingly + * enough was not passing RepeatReflect to the X server, but + * instead using RepeatNormal in a workaround). Nobody to date + * has understood the bug well, but it appears to be gone as of + * the X.Org 1.4.0 server. This bug is coincidentally avoided + * by using the same buggy_repeat workaround. Confirmed X + * servers with this bug include: + * + * "X.org" == 60900000 (old versioning scheme) + * "X.org" < 10400000 (new numbering scheme) + * + * For the old-versioning-scheme X servers we don't know + * exactly when second the bug started, but since bug 1 is + * present through 6.8.2 and bug 2 is present in 6.9.0 it seems + * safest to just blacklist all old-versioning-scheme X servers, + * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. + */ + if (strstr (ServerVendor (dpy), "X.Org") != NULL) { + if (VendorRelease (dpy) >= 60700000) { + if (VendorRelease (dpy) < 70000000) + display->buggy_repeat = TRUE; + + /* We know that gradients simply do not work in early Xorg servers */ + if (VendorRelease (dpy) < 70200000) + display->buggy_gradients = TRUE; + + /* And the extended repeat modes were not fixed until much later */ + display->buggy_pad_reflect = TRUE; + } else { + if (VendorRelease (dpy) < 10400000) + display->buggy_repeat = TRUE; + + /* Too many bugs in the early drivers */ + if (VendorRelease (dpy) < 10699000) + display->buggy_pad_reflect = TRUE; + } + } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { + if (VendorRelease (dpy) <= 40500000) + display->buggy_repeat = TRUE; + + display->buggy_gradients = TRUE; + display->buggy_pad_reflect = TRUE; + } + + /* gradients don't seem to work */ + display->buggy_gradients = TRUE; + + + /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ + /* If buggy_repeat_force == -1, then initialize. + * - set to -2, meaning "nothing was specified", and we trust the above detection. + * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off + * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on + */ + if (buggy_repeat_force == -1) { + const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT"); + + buggy_repeat_force = -2; + + if (flag && flag[0] == '0') + buggy_repeat_force = 0; + else if (flag && flag[0] == '1') + buggy_repeat_force = 1; + } + + if (buggy_repeat_force != -2) + display->buggy_repeat = (buggy_repeat_force == 1); + + display->next = _cairo_xlib_display_list; + _cairo_xlib_display_list = display; + + device = &display->base; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + return device; +} + +void +_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, + cairo_xlib_hook_t *hook) +{ + hook->prev = NULL; + hook->next = display->close_display_hooks; + if (hook->next != NULL) + hook->next->prev = hook; + display->close_display_hooks = hook; +} + +static void +_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, + cairo_xlib_hook_t *hook) +{ + if (display->close_display_hooks == hook) + display->close_display_hooks = hook->next; + else if (hook->prev != NULL) + hook->prev->next = hook->next; + + if (hook->next != NULL) + hook->next->prev = hook->prev; + + hook->prev = NULL; + hook->next = NULL; +} + +void +_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, + cairo_xlib_hook_t *hook) +{ + _cairo_xlib_remove_close_display_hook_internal (display, hook); +} + +cairo_status_t +_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, + cairo_xlib_notify_resource_func notify, + XID xid) +{ + cairo_xlib_job_t *job; + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + + if (display->closed == FALSE) { + job = _cairo_freelist_alloc (&display->wq_freelist); + if (job != NULL) { + job->type = RESOURCE; + job->func.resource.xid = xid; + job->func.resource.notify = notify; + + job->next = display->workqueue; + display->workqueue = job; + + status = CAIRO_STATUS_SUCCESS; + } + } + + return status; +} + +cairo_status_t +_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, + cairo_xlib_notify_func notify, + void *data, + void (*destroy) (void *)) +{ + cairo_xlib_job_t *job; + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + + if (display->closed == FALSE) { + job = _cairo_freelist_alloc (&display->wq_freelist); + if (job != NULL) { + job->type = WORK; + job->func.work.data = data; + job->func.work.notify = notify; + job->func.work.destroy = destroy; + + job->next = display->workqueue; + display->workqueue = job; + + status = CAIRO_STATUS_SUCCESS; + } + } + + return status; +} + +cairo_status_t +_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) +{ + cairo_status_t status; + + status = cairo_device_acquire (device); + if (status) + return status; + + *display = (cairo_xlib_display_t *) device; + _cairo_xlib_display_notify (*display); + return status; +} + +XRenderPictFormat * +_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, + cairo_format_t format) +{ + XRenderPictFormat *xrender_format; + +#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER + xrender_format = display->cached_xrender_formats[format]; + if (likely (xrender_format != NULL)) + return xrender_format; +#endif + + xrender_format = display->cached_xrender_formats[format]; + if (xrender_format == NULL) { + int pict_format; + + switch (format) { + case CAIRO_FORMAT_A1: + pict_format = PictStandardA1; break; + case CAIRO_FORMAT_A8: + pict_format = PictStandardA8; break; + case CAIRO_FORMAT_RGB24: + pict_format = PictStandardRGB24; break; + case CAIRO_FORMAT_RGB16_565: { + Visual *visual = NULL; + Screen *screen = DefaultScreenOfDisplay(display->display); + int j; + for (j = 0; j < screen->ndepths; j++) { + Depth *d = &screen->depths[j]; + if (d->depth == 16 && d->nvisuals && &d->visuals[0]) { + if (d->visuals[0].red_mask == 0xf800 && + d->visuals[0].green_mask == 0x7e0 && + d->visuals[0].blue_mask == 0x1f) + visual = &d->visuals[0]; + break; + } + } + if (!visual) + return NULL; + xrender_format = XRenderFindVisualFormat(display->display, visual); + break; + } + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_ARGB32: + pict_format = PictStandardARGB32; break; + } + if (!xrender_format) + xrender_format = XRenderFindStandardFormat (display->display, + pict_format); + display->cached_xrender_formats[format] = xrender_format; + } + + return xrender_format; +} + +cairo_xlib_screen_t * +_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, + Screen *screen) +{ + cairo_xlib_screen_t *info; + + cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) { + if (info->screen == screen) { + if (display->screens.next != &info->link) + cairo_list_move (&info->link, &display->screens); + return info; + } + } + + return NULL; +} + +void +_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, + int *major, int *minor) +{ + *major = display->render_major; + *minor = display->render_minor; +} + +cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_device_t *device) +{ + return ! ((cairo_xlib_display_t *) device)->buggy_repeat; +} + +cairo_bool_t +_cairo_xlib_display_has_reflect (cairo_device_t *device) +{ + return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect; +} + +cairo_bool_t +_cairo_xlib_display_has_gradients (cairo_device_t *device) +{ + return ! ((cairo_xlib_display_t *) device)->buggy_gradients; +} diff --git a/libs/cairo/src/cairo-xlib-private.h b/libs/cairo/src/cairo-xlib-private.h new file mode 100644 index 000000000..e575b2704 --- /dev/null +++ b/libs/cairo/src/cairo-xlib-private.h @@ -0,0 +1,168 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XLIB_PRIVATE_H +#define CAIRO_XLIB_PRIVATE_H + +#include "cairo-xlib.h" +#include "cairo-xlib-xrender-private.h" + +#include "cairo-compiler-private.h" +#include "cairo-device-private.h" +#include "cairo-freelist-type-private.h" +#include "cairo-list-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-types-private.h" + +typedef struct _cairo_xlib_display cairo_xlib_display_t; +typedef struct _cairo_xlib_screen cairo_xlib_screen_t; + +typedef struct _cairo_xlib_hook cairo_xlib_hook_t; +typedef struct _cairo_xlib_job cairo_xlib_job_t; +typedef void (*cairo_xlib_notify_func) (Display *, void *); +typedef void (*cairo_xlib_notify_resource_func) (Display *, XID); + +struct _cairo_xlib_hook { + cairo_xlib_hook_t *prev, *next; /* private */ + void (*func) (cairo_xlib_display_t *display, void *data); +}; + +/* size of color cube */ +#define CUBE_SIZE 6 +/* size of gray ramp */ +#define RAMP_SIZE 16 + +struct _cairo_xlib_display { + cairo_device_t base; + + cairo_xlib_display_t *next; + + Display *display; + cairo_list_t screens; + + int render_major; + int render_minor; + XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1]; + + cairo_xlib_job_t *workqueue; + cairo_freelist_t wq_freelist; + + cairo_xlib_hook_t *close_display_hooks; + unsigned int buggy_gradients :1; + unsigned int buggy_pad_reflect :1; + unsigned int buggy_repeat :1; + unsigned int closed :1; +}; + +typedef struct _cairo_xlib_visual_info { + cairo_list_t link; + VisualID visualid; + struct { uint8_t a, r, g, b; } colors[256]; + uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE]; + uint8_t field8_to_cube[256]; + int8_t dither8_to_cube[256]; + uint8_t gray8_to_pseudocolor[256]; +} cairo_xlib_visual_info_t; + +struct _cairo_xlib_screen { + cairo_list_t link; + + cairo_device_t *device; + Screen *screen; + + cairo_bool_t has_font_options; + cairo_font_options_t font_options; + + GC gc[4]; + cairo_atomic_int_t gc_depths; /* 4 x uint8_t */ + + cairo_list_t visuals; +}; + +cairo_private cairo_device_t * +_cairo_xlib_device_create (Display *display); + +cairo_private cairo_xlib_screen_t * +_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, + Screen *screen); + +cairo_private void +_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); + +cairo_private void +_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); + +cairo_private cairo_status_t +_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, + cairo_xlib_notify_func notify, + void *data, + void (*destroy)(void *)); +cairo_private cairo_status_t +_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, + cairo_xlib_notify_resource_func notify, + XID resource); +cairo_private cairo_status_t +_cairo_xlib_display_acquire (cairo_device_t *device, + cairo_xlib_display_t **display); + +cairo_private void +_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, + int *major, int *minor); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_device_t *device); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_reflect (cairo_device_t *device); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_gradients (cairo_device_t *device); + +cairo_private XRenderPictFormat * +_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, + cairo_format_t format); + +cairo_private cairo_status_t +_cairo_xlib_screen_get (Display *dpy, + Screen *screen, + cairo_xlib_screen_t **out); + +cairo_private void +_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info); + +cairo_private void +_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info); + +cairo_private GC +_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + Drawable drawable); + +cairo_private void +_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + GC gc); + +cairo_private cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info); + +cairo_private cairo_status_t +_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + Visual *visual, + cairo_xlib_visual_info_t **out); + +cairo_private cairo_status_t +_cairo_xlib_visual_info_create (Display *dpy, + int screen, + VisualID visualid, + cairo_xlib_visual_info_t **out); + +cairo_private void +_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info); + +#endif /* CAIRO_XLIB_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-xlib-screen.c b/libs/cairo/src/cairo-xlib-screen.c new file mode 100644 index 000000000..9663ddb7b --- /dev/null +++ b/libs/cairo/src/cairo-xlib-screen.c @@ -0,0 +1,439 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Partially based on code from xftdpy.c, original code licensed under: + * + * Copyright © 2000 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cairoint.h" + +#include "cairo-xlib-private.h" +#include "cairo-xlib-xrender-private.h" + +#include "cairo-xlib-surface-private.h" +#include "cairo-error-private.h" + +#include "cairo-fontconfig-private.h" + +static int +parse_boolean (const char *v) +{ + char c0, c1; + + c0 = *v; + if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') + return 1; + if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') + return 0; + if (c0 == 'o') + { + c1 = v[1]; + if (c1 == 'n' || c1 == 'N') + return 1; + if (c1 == 'f' || c1 == 'F') + return 0; + } + + return -1; +} + +static cairo_bool_t +get_boolean_default (Display *dpy, + const char *option, + cairo_bool_t *value) +{ + char *v; + int i; + + v = XGetDefault (dpy, "Xft", option); + if (v) { + i = parse_boolean (v); + if (i >= 0) { + *value = i; + return TRUE; + } + } + + return FALSE; +} + +static cairo_bool_t +get_integer_default (Display *dpy, + const char *option, + int *value) +{ + char *v, *e; + + v = XGetDefault (dpy, "Xft", option); + if (v) { +#if CAIRO_HAS_FC_FONT + if (FcNameConstant ((FcChar8 *) v, value)) + return TRUE; +#endif + + *value = strtol (v, &e, 0); + if (e != v) + return TRUE; + } + + return FALSE; +} + +static void +_cairo_xlib_init_screen_font_options (Display *dpy, + cairo_xlib_screen_t *info) +{ + cairo_bool_t xft_hinting; + cairo_bool_t xft_antialias; + int xft_hintstyle; + int xft_rgba; + int xft_lcdfilter; + cairo_antialias_t antialias; + cairo_subpixel_order_t subpixel_order; + cairo_lcd_filter_t lcd_filter; + cairo_hint_style_t hint_style; + + if (!get_boolean_default (dpy, "antialias", &xft_antialias)) + xft_antialias = TRUE; + + if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) { + /* -1 is an non-existant Fontconfig constant used to differentiate + * the case when no lcdfilter property is available. + */ + xft_lcdfilter = -1; + } + + if (!get_boolean_default (dpy, "hinting", &xft_hinting)) + xft_hinting = TRUE; + + if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle)) + xft_hintstyle = FC_HINT_FULL; + + if (!get_integer_default (dpy, "rgba", &xft_rgba)) + { + cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device; + + xft_rgba = FC_RGBA_UNKNOWN; + +#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6 + if (display->render_major > 0 || display->render_minor >= 6) { + int render_order = XRenderQuerySubpixelOrder (dpy, + XScreenNumberOfScreen (info->screen)); + + switch (render_order) { + default: + case SubPixelUnknown: + xft_rgba = FC_RGBA_UNKNOWN; + break; + case SubPixelHorizontalRGB: + xft_rgba = FC_RGBA_RGB; + break; + case SubPixelHorizontalBGR: + xft_rgba = FC_RGBA_BGR; + break; + case SubPixelVerticalRGB: + xft_rgba = FC_RGBA_VRGB; + break; + case SubPixelVerticalBGR: + xft_rgba = FC_RGBA_VBGR; + break; + case SubPixelNone: + xft_rgba = FC_RGBA_NONE; + break; + } + } +#endif + } + + if (xft_hinting) { + switch (xft_hintstyle) { + case FC_HINT_NONE: + hint_style = CAIRO_HINT_STYLE_NONE; + break; + case FC_HINT_SLIGHT: + hint_style = CAIRO_HINT_STYLE_SLIGHT; + break; + case FC_HINT_MEDIUM: + hint_style = CAIRO_HINT_STYLE_MEDIUM; + break; + case FC_HINT_FULL: + hint_style = CAIRO_HINT_STYLE_FULL; + break; + default: + hint_style = CAIRO_HINT_STYLE_DEFAULT; + } + } else { + hint_style = CAIRO_HINT_STYLE_NONE; + } + + switch (xft_rgba) { + case FC_RGBA_RGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; + break; + case FC_RGBA_BGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; + break; + case FC_RGBA_VRGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; + break; + case FC_RGBA_VBGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; + break; + case FC_RGBA_UNKNOWN: + case FC_RGBA_NONE: + default: + subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + } + + switch (xft_lcdfilter) { + case FC_LCD_NONE: + lcd_filter = CAIRO_LCD_FILTER_NONE; + break; + case FC_LCD_DEFAULT: + lcd_filter = CAIRO_LCD_FILTER_FIR5; + break; + case FC_LCD_LIGHT: + lcd_filter = CAIRO_LCD_FILTER_FIR3; + break; + case FC_LCD_LEGACY: + lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; + break; + default: + lcd_filter = CAIRO_LCD_FILTER_DEFAULT; + break; + } + + if (xft_antialias) { + if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) + antialias = CAIRO_ANTIALIAS_GRAY; + else + antialias = CAIRO_ANTIALIAS_SUBPIXEL; + } else { + antialias = CAIRO_ANTIALIAS_NONE; + } + + cairo_font_options_set_hint_style (&info->font_options, hint_style); + cairo_font_options_set_antialias (&info->font_options, antialias); + cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order); + _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter); + cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); +} + +void +_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info) +{ + Display *dpy; + int i; + + dpy = display->display; + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if ((info->gc_depths >> (8*i)) & 0xff) + XFreeGC (dpy, info->gc[i]); + } + info->gc_depths = 0; +} + +void +_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) +{ + while (! cairo_list_is_empty (&info->visuals)) { + _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, + cairo_xlib_visual_info_t, + link)); + } + + cairo_list_del (&info->link); + + free (info); +} + +cairo_status_t +_cairo_xlib_screen_get (Display *dpy, + Screen *screen, + cairo_xlib_screen_t **out) +{ + cairo_xlib_display_t *display; + cairo_device_t *device; + cairo_xlib_screen_t *info; + cairo_status_t status; + + device = _cairo_xlib_device_create (dpy); + status = device->status; + if (unlikely (status)) + goto CLEANUP_DEVICE; + + status = _cairo_xlib_display_acquire (device, &display); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + info = _cairo_xlib_display_get_screen (display, screen); + if (info != NULL) { + *out = info; + goto CLEANUP_DISPLAY; + } + + info = malloc (sizeof (cairo_xlib_screen_t)); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DISPLAY; + } + + info->device = device; + info->screen = screen; + info->has_font_options = FALSE; + info->gc_depths = 0; + memset (info->gc, 0, sizeof (info->gc)); + + cairo_list_init (&info->visuals); + cairo_list_add (&info->link, &display->screens); + + *out = info; + + CLEANUP_DISPLAY: + cairo_device_release (&display->base); + + CLEANUP_DEVICE: + cairo_device_destroy (device); + return status; +} + +GC +_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + Drawable drawable) +{ + GC gc = NULL; + int i; + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0xff) == depth) { + info->gc_depths &= ~(0xff << (8*i)); + gc = info->gc[i]; + break; + } + } + + if (gc == NULL) { + XGCValues gcv; + + gcv.graphics_exposures = False; + gcv.fill_style = FillTiled; + gc = XCreateGC (display->display, + drawable, + GCGraphicsExposures | GCFillStyle, &gcv); + } + + return gc; +} + +void +_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + GC gc) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0xff) == 0) + break; + } + + if (i == ARRAY_LENGTH (info->gc)) { + cairo_status_t status; + + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (info->gc); + status = + _cairo_xlib_display_queue_work (display, + (cairo_xlib_notify_func) XFreeGC, + info->gc[i], + NULL); + if (unlikely (status)) { + /* leak the server side resource... */ + XFree ((char *) info->gc[i]); + } + } + + info->gc[i] = gc; + info->gc_depths &= ~(0xff << (8*i)); + info->gc_depths |= depth << (8*i); +} + +cairo_status_t +_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + Visual *v, + cairo_xlib_visual_info_t **out) +{ + cairo_xlib_visual_info_t *visual; + cairo_status_t status; + + cairo_list_foreach_entry (visual, + cairo_xlib_visual_info_t, + &info->visuals, + link) + { + if (visual->visualid == v->visualid) { + *out = visual; + return CAIRO_STATUS_SUCCESS; + } + } + + status = _cairo_xlib_visual_info_create (display->display, + XScreenNumberOfScreen (info->screen), + v->visualid, + &visual); + if (unlikely (status)) + return status; + + cairo_list_add (&visual->link, &info->visuals); + *out = visual; + return CAIRO_STATUS_SUCCESS; +} + +cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) +{ + if (! info->has_font_options) { + _cairo_font_options_init_default (&info->font_options); + _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON); + + if (info->screen != NULL) { + cairo_xlib_display_t *display; + + if (! _cairo_xlib_display_acquire (info->device, &display)) { + _cairo_xlib_init_screen_font_options (display->display, + info); + cairo_device_release (&display->base); + } + } + + info->has_font_options = TRUE; + } + + return &info->font_options; +} diff --git a/libs/cairo/src/cairo-xlib-surface-private.h b/libs/cairo/src/cairo-xlib-surface-private.h new file mode 100644 index 000000000..cd7e79d69 --- /dev/null +++ b/libs/cairo/src/cairo-xlib-surface-private.h @@ -0,0 +1,84 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XLIB_SURFACE_PRIVATE_H +#define CAIRO_XLIB_SURFACE_PRIVATE_H + +#include "cairo-xlib.h" +#include "cairo-xlib-private.h" +#include "cairo-xlib-xrender-private.h" + +#include "cairo-surface-private.h" + +typedef struct _cairo_xlib_surface cairo_xlib_surface_t; + +struct _cairo_xlib_surface { + cairo_surface_t base; + + cairo_xlib_screen_t *screen; + cairo_xlib_hook_t close_display_hook; + + Drawable drawable; + cairo_bool_t owns_pixmap; + Visual *visual; + + int use_pixmap; + + int render_major; + int render_minor; + + /* TRUE if the server has a bug with repeating pictures + * + * https://bugs.freedesktop.org/show_bug.cgi?id=3566 + * + * We can't test for this because it depends on whether the + * picture is in video memory or not. + * + * We also use this variable as a guard against a second + * independent bug with transformed repeating pictures: + * + * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html + * + * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so + * we can reuse the test for now. + */ + unsigned int buggy_gradients : 1; + unsigned int buggy_pad_reflect : 1; + unsigned int buggy_repeat : 1; +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1 +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1 +#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1 + + int width; + int height; + int depth; + + Picture dst_picture, src_picture; + + unsigned int clip_dirty; + XRectangle embedded_clip_rects[8]; + XRectangle *clip_rects; + int num_clip_rects; + cairo_region_t *clip_region; + + XRenderPictFormat *xrender_format; + cairo_filter_t filter; + cairo_extend_t extend; + cairo_bool_t has_component_alpha; + int precision; + XTransform xtransform; + + uint32_t a_mask; + uint32_t r_mask; + uint32_t g_mask; + uint32_t b_mask; +}; + +enum { + CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC = 0x01, + CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02, + CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL = 0x03 +}; + +#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-xlib-surface.c b/libs/cairo/src/cairo-xlib-surface.c new file mode 100644 index 000000000..36b696eaa --- /dev/null +++ b/libs/cairo/src/cairo-xlib-surface.c @@ -0,0 +1,4896 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Heed well the words of Owen Taylor: + * "Any patch that works around a render bug, or claims to, without a + * specific reference to the bug filed in bugzilla.freedesktop.org will + * never pass approval." + */ + +#include "cairoint.h" + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-region-private.h" +#include "cairo-xlib-xrender-private.h" + +#include /* for XDestroyImage */ +#include /* for access to XDisplay's innards */ + +#define XLIB_COORD_MAX 32767 + +#define DEBUG 0 + +#if DEBUG +#define UNSUPPORTED(reason) \ + fprintf (stderr, \ + "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason), \ + CAIRO_INT_STATUS_UNSUPPORTED +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +#if DEBUG +static void CAIRO_PRINTF_FORMAT (2, 3) +_x_bread_crumb (Display *dpy, + const char *fmt, + ...) +{ + xReq *req; + char buf[2048]; + unsigned int len, len_dwords; + va_list ap; + + va_start (ap, fmt); + len = vsnprintf (buf, sizeof (buf), fmt, ap); + va_end (ap); + + buf[len++] = '\0'; + while (len & 3) + buf[len++] = '\0'; + + LockDisplay (dpy); + GetEmptyReq (NoOperation, req); + + len_dwords = len >> 2; + SetReqLen (req, len_dwords, len_dwords); + Data (dpy, buf, len); + + UnlockDisplay (dpy); + SyncHandle (); +} +#define X_DEBUG(x) _x_bread_crumb x +#else +#define X_DEBUG(x) +#endif + +/** + * SECTION:cairo-xlib + * @Title: XLib Surfaces + * @Short_Description: X Window System rendering using XLib + * @See_Also: #cairo_surface_t + * + * The XLib surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XLib library. + * + * Note that the XLib surface automatically takes advantage of X render extension + * if it is available. + */ + +/** + * CAIRO_HAS_XLIB_SURFACE: + * + * Defined if the Xlib surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +/** + * SECTION:cairo-xlib-xrender + * @Title: XLib/XRender Backend + * @Short_Description: X Window System rendering using XLib and the X Render extension + * @See_Also: #cairo_surface_t + * + * The XLib surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XLib and Xrender libraries. + * + * Note that the XLib surface automatically takes advantage of X Render extension + * if it is available. + */ + +/** + * CAIRO_HAS_XLIB_XRENDER_SURFACE: + * + * Defined if the XLib/XRender surface functions are available. + * This macro can be used to conditionally compile backend-specific code. + */ + +/* Xlib doesn't define a typedef, so define one ourselves */ +typedef int (*cairo_xlib_error_func_t) (Display *display, + XErrorEvent *event); + +static cairo_surface_t * +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + Visual *visual, + XRenderPictFormat *xrender_format, + int width, + int height, + int depth); + +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface); + +static cairo_bool_t +_native_byte_order_lsb (void); + +static cairo_int_status_t +_cairo_xlib_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + +/* + * Instead of taking two round trips for each blending request, + * assume that if a particular drawable fails GetImage that it will + * fail for a "while"; use temporary pixmaps to avoid the errors + */ + +#define CAIRO_ASSUME_PIXMAP 20 + +static const XTransform identity = { { + { 1 << 16, 0x00000, 0x00000 }, + { 0x00000, 1 << 16, 0x00000 }, + { 0x00000, 0x00000, 1 << 16 }, +} }; + +#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ + (((surface)->render_major > major) || \ + (((surface)->render_major == major) && ((surface)->render_minor >= minor))) + +#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) + +#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) + +#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) +#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) + +#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) + +#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) + +#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) +#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) + +#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) + +#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ + ((op) <= CAIRO_OPERATOR_SATURATE || \ + (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ + (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) + +static Visual * +_visual_for_xrender_format(Screen *screen, + XRenderPictFormat *xrender_format) +{ + int d, v; + + /* XXX Consider searching through the list of known cairo_visual_t for + * the reverse mapping. + */ + + for (d = 0; d < screen->ndepths; d++) { + Depth *d_info = &screen->depths[d]; + + if (d_info->depth != xrender_format->depth) + continue; + + for (v = 0; v < d_info->nvisuals; v++) { + Visual *visual = &d_info->visuals[v]; + + switch (visual->class) { + case TrueColor: + if (xrender_format->type != PictTypeDirect) + continue; + break; + + case DirectColor: + /* Prefer TrueColor to DirectColor. + * (XRenderFindVisualFormat considers both TrueColor and DirectColor + * Visuals to match the same PictFormat.) + */ + continue; + + case StaticGray: + case GrayScale: + case StaticColor: + case PseudoColor: + if (xrender_format->type != PictTypeIndexed) + continue; + break; + } + + if (xrender_format == + XRenderFindVisualFormat (DisplayOfScreen(screen), visual)) + return visual; + } + } + + return NULL; +} + +static cairo_status_t +_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, + cairo_region_t *region) +{ + cairo_bool_t had_clip_rects = surface->clip_region != NULL; + + if (had_clip_rects == FALSE && region == NULL) + return CAIRO_STATUS_SUCCESS; + + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; + + if (cairo_region_equal (surface->clip_region, region)) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + if (surface->clip_rects != surface->embedded_clip_rects) { + free (surface->clip_rects); + surface->clip_rects = surface->embedded_clip_rects; + } + surface->num_clip_rects = 0; + + if (region != NULL) { + XRectangle *rects = NULL; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (region); + if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + rects = surface->embedded_clip_rects; + } + + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + + surface->clip_rects = rects; + surface->num_clip_rects = n_rects; + } + + surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_content_t +_xrender_format_to_content (XRenderPictFormat *xrender_format) +{ + cairo_bool_t xrender_format_has_alpha; + cairo_bool_t xrender_format_has_color; + + /* This only happens when using a non-Render server. Let's punt + * and say there's no alpha here. */ + if (xrender_format == NULL) + return CAIRO_CONTENT_COLOR; + + xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0); + xrender_format_has_color = (xrender_format->direct.redMask != 0 || + xrender_format->direct.greenMask != 0 || + xrender_format->direct.blueMask != 0); + + if (xrender_format_has_alpha) + if (xrender_format_has_color) + return CAIRO_CONTENT_COLOR_ALPHA; + else + return CAIRO_CONTENT_ALPHA; + else + return CAIRO_CONTENT_COLOR; +} + +static cairo_surface_t * +_cairo_xlib_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_xlib_surface_t *src = abstract_src; + XRenderPictFormat *xrender_format; + cairo_xlib_surface_t *surface; + cairo_xlib_display_t *display; + Pixmap pix; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return NULL; + + if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) + return NULL; + + if (_cairo_xlib_display_acquire (src->base.device, &display)) + return NULL; + + /* If we never found an XRenderFormat or if it isn't compatible + * with the content being requested, then we fallback to just + * constructing a cairo_format_t instead, (which will fairly + * arbitrarily pick a visual/depth for the similar surface. + */ + xrender_format = src->xrender_format; + if ((xrender_format != NULL && + _xrender_format_to_content (xrender_format) == content) || + (xrender_format = + _cairo_xlib_display_get_xrender_format (display, + _cairo_format_from_content (content)))) + { + Visual *visual; + + /* We've got a compatible XRenderFormat now, which means the + * similar surface will match the existing surface as closely in + * visual/depth etc. as possible. */ + pix = XCreatePixmap (display->display, src->drawable, + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + xrender_format->depth); + + if (xrender_format == src->xrender_format) + visual = src->visual; + else + visual = _visual_for_xrender_format(src->screen->screen, + xrender_format); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + visual, + xrender_format, + width, height, + xrender_format->depth); + } + else + { +#ifdef DEBUG_FORCE_FALLBACKS + Screen *screen = src->screen->screen; + int depth; + + /* No compatabile XRenderFormat, see if we can make an ordinary pixmap, + * so that we can still accelerate blits with XCopyArea(). */ + if (content != CAIRO_CONTENT_COLOR) { + cairo_device_release (&display->base); + return NULL; + } + + depth = DefaultDepthOfScreen (screen); + + pix = XCreatePixmap (display->display, RootWindowOfScreen (screen), + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + depth); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + DefaultVisualOfScreen (screen), + NULL, + width, height, depth); +#else + /* No compatabile XRenderFormat, just say no. */ + cairo_device_release (&display->base); + return NULL; +#endif + } + + if (unlikely (surface->base.status)) { + XFreePixmap (display->display, pix); + cairo_device_release (&display->base); + return &surface->base; + } + + surface->owns_pixmap = TRUE; + + cairo_device_release (&display->base); + + return &surface->base; +} + +static cairo_status_t +_cairo_xlib_surface_finish (void *abstract_surface) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_xlib_display_t *display; + + X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + if (surface->owns_pixmap) { + cairo_status_t status2; + + if (surface->dst_picture != None) { + status2 = _cairo_xlib_display_queue_resource (display, + XRenderFreePicture, + surface->dst_picture); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + + if (surface->src_picture != None) { + status2 = _cairo_xlib_display_queue_resource (display, + XRenderFreePicture, + surface->src_picture); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + + status2 = _cairo_xlib_display_queue_resource (display, + (cairo_xlib_notify_resource_func) XFreePixmap, + surface->drawable); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } else { + if (surface->dst_picture != None) + XRenderFreePicture (display->display, surface->dst_picture); + + if (surface->src_picture != None) + XRenderFreePicture (display->display, surface->src_picture); + } + + if (surface->clip_rects != surface->embedded_clip_rects) + free (surface->clip_rects); + + if (display->display != NULL) + _cairo_xlib_remove_close_display_hook (display, + &surface->close_display_hook); + + cairo_device_release (&display->base); + + cairo_region_destroy (surface->clip_region); + + return status; +} + +static cairo_status_t +_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + GC *gc) +{ + *gc = _cairo_xlib_screen_get_gc (display, + surface->screen, + surface->depth, + surface->drawable); + if (unlikely (*gc == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + GC gc) +{ + _cairo_xlib_screen_put_gc (display, + surface->screen, + surface->depth, + gc); +} + +static int +_noop_error_handler (Display *display, + XErrorEvent *event) +{ + return False; /* return value is ignored */ +} + +static void +_swap_ximage_2bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint16_t *p = (uint16_t *) line; + for (i = ximage->width; i; i--) { + *p = bswap_16 (*p); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_3bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint8_t *p = (uint8_t *) line; + for (i = ximage->width; i; i--) { + uint8_t tmp; + tmp = p[2]; + p[2] = p[0]; + p[0] = tmp; + p += 3; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_4bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint32_t *p = (uint32_t *) line; + for (i = ximage->width; i; i--) { + *p = bswap_32 (*p); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_nibbles (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint8_t *p = (uint8_t *) line; + for (i = (ximage->width + 1) / 2; i; i--) { + *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_bits (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + int unit = ximage->bitmap_unit; + int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8; + + for (j = ximage->height; j; j--) { + char *p = line; + + for (i = line_bytes; i; i--) { + char b = *p; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *p = b; + + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_to_native (XImage *ximage) +{ + int unit_bytes = 0; + int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + + if (ximage->bits_per_pixel == 1 && + ximage->bitmap_bit_order != native_byte_order) + { + _swap_ximage_bits (ximage); + if (ximage->bitmap_bit_order == ximage->byte_order) + return; + } + + if (ximage->byte_order == native_byte_order) + return; + + switch (ximage->bits_per_pixel) { + case 1: + unit_bytes = ximage->bitmap_unit / 8; + break; + case 4: + _swap_ximage_nibbles (ximage); + /* fall-through */ + case 8: + case 16: + case 20: + case 24: + case 28: + case 30: + case 32: + unit_bytes = (ximage->bits_per_pixel + 7) / 8; + break; + default: + /* This could be hit on some rare but possible cases. */ + ASSERT_NOT_REACHED; + } + + switch (unit_bytes) { + case 1: + break; + case 2: + _swap_ximage_2bytes (ximage); + break; + case 3: + _swap_ximage_3bytes (ximage); + break; + case 4: + _swap_ximage_4bytes (ximage); + break; + default: + ASSERT_NOT_REACHED; + } +} + + +/* Given a mask, (with a single sequence of contiguous 1 bits), return + * the number of 1 bits in 'width' and the number of 0 bits to its + * right in 'shift'. */ +static void +_characterize_field (uint32_t mask, int *width, int *shift) +{ + *width = _cairo_popcount (mask); + /* The final '& 31' is to force a 0 mask to result in 0 shift. */ + *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; +} + + +/* Convert a field of 'width' bits to 'new_width' bits with correct + * rounding. */ +static inline uint32_t +_resize_field (uint32_t field, int width, int new_width) +{ + if (width == 0) + return 0; + + if (width >= new_width) { + return field >> (width - new_width); + } else { + uint32_t result = field << (new_width - width); + + while (width < new_width) { + result |= result >> width; + width <<= 1; + } + return result; + } +} + +static inline uint32_t +_adjust_field (uint32_t field, int adjustment) +{ + return MIN (255, MAX(0, (int)field + adjustment)); +} + +/* Given a shifted field value, (described by 'width' and 'shift), + * resize it 8-bits and return that value. + * + * Note that the original field value must not have any non-field bits + * set. + */ +static inline uint32_t +_field_to_8 (uint32_t field, int width, int shift) +{ + return _resize_field (field >> shift, width, 8); +} + +static inline uint32_t +_field_to_8_undither (uint32_t field, int width, int shift, + int dither_adjustment) +{ + return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width); +} + +/* Given an 8-bit value, convert it to a field of 'width', shift it up + * to 'shift, and return it. */ +static inline uint32_t +_field_from_8 (uint32_t field, int width, int shift) +{ + return _resize_field (field, 8, width) << shift; +} + +static inline uint32_t +_field_from_8_dither (uint32_t field, int width, int shift, + int8_t dither_adjustment) +{ + return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift); +} + +static inline uint32_t +_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info, + uint32_t r, uint32_t g, uint32_t b, + int8_t dither_adjustment) +{ + if (r == g && g == b) { + dither_adjustment /= RAMP_SIZE; + return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)]; + } else { + dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128]; + return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]] + [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]] + [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]]; + } +} + +static inline uint32_t +_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, + uint32_t pixel) +{ + uint32_t r, g, b; + pixel &= 0xff; + r = visual_info->colors[pixel].r; + g = visual_info->colors[pixel].g; + b = visual_info->colors[pixel].b; + return (r << 16) | + (g << 8) | + (b ); +} + + +/* should range from -128 to 127 */ +#define X 16 +static const int8_t dither_pattern[4][4] = { + {-8*X, +0*X, -6*X, +2*X}, + {+4*X, -4*X, +6*X, -2*X}, + {-5*X, +4*X, -7*X, +1*X}, + {+7*X, -1*X, +5*X, -3*X} +}; +#undef X + + +static cairo_status_t +_get_image_surface (cairo_xlib_surface_t *surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect) +{ + cairo_int_status_t status; + cairo_image_surface_t *image = NULL; + XImage *ximage; + cairo_rectangle_int_t extents; + pixman_format_code_t pixman_format; + cairo_format_masks_t xlib_masks; + cairo_xlib_display_t *display; + + extents.x = 0; + extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + + if (interest_rect) { + if (! _cairo_rectangle_intersect (&extents, interest_rect)) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (status) + return status; + + if (image_rect) + *image_rect = extents; + + /* XXX: This should try to use the XShm extension if available */ + + if (surface->use_pixmap == 0) + { + cairo_xlib_error_func_t old_handler; + + old_handler = XSetErrorHandler (_noop_error_handler); + + ximage = XGetImage (display->display, + surface->drawable, + extents.x, extents.y, + extents.width, extents.height, + AllPlanes, ZPixmap); + + XSetErrorHandler (old_handler); + + /* If we get an error, the surface must have been a window, + * so retry with the safe code path. + */ + if (!ximage) + surface->use_pixmap = CAIRO_ASSUME_PIXMAP; + } + else + { + surface->use_pixmap--; + ximage = NULL; + } + + if (ximage == NULL) { + /* XGetImage from a window is dangerous because it can + * produce errors if the window is unmapped or partially + * outside the screen. We could check for errors and + * retry, but to keep things simple, we just create a + * temporary pixmap + */ + Pixmap pixmap; + GC gc; + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto BAIL; + + pixmap = XCreatePixmap (display->display, + surface->drawable, + extents.width <= 0 ? 1 : extents.width, + extents.height <= 0 ? 1 : extents.height, + surface->depth); + if (pixmap) { + XCopyArea (display->display, surface->drawable, pixmap, gc, + extents.x, extents.y, + extents.width, extents.height, + 0, 0); + + ximage = XGetImage (display->display, + pixmap, + 0, 0, + extents.width <= 0 ? 1 : extents.width, + extents.height <= 0 ? 1 : extents.height, + AllPlanes, ZPixmap); + + XFreePixmap (display->display, pixmap); + } + + _cairo_xlib_surface_put_gc (display, surface, gc); + + if (ximage == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + _swap_ximage_to_native (ximage); + + xlib_masks.bpp = ximage->bits_per_pixel; + xlib_masks.alpha_mask = surface->a_mask; + xlib_masks.red_mask = surface->r_mask; + xlib_masks.green_mask = surface->g_mask; + xlib_masks.blue_mask = surface->b_mask; + + /* We can't use pixman to simply write to image if: + * (a) the pixels are not appropriately aligned, + * (b) pixman does not the pixel format, or + * (c) if the image is palettized and we need to convert. + */ + if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && + _pixman_format_from_masks (&xlib_masks, &pixman_format) && + (surface->visual == NULL || surface->visual->class == TrueColor)) + { + image = (cairo_image_surface_t*) + _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, + pixman_format, + ximage->width, + ximage->height, + ximage->bytes_per_line); + status = image->base.status; + if (unlikely (status)) + goto BAIL; + + /* Let the surface take ownership of the data */ + _cairo_image_surface_assume_ownership_of_data (image); + ximage->data = NULL; + } else { + /* The visual we are dealing with is not supported by the + * standard pixman formats. So we must first convert the data + * to a supported format. */ + + cairo_format_t format; + unsigned char *data; + uint32_t *row; + uint32_t in_pixel, out_pixel; + unsigned int rowstride; + uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0; + int a_width=0, r_width=0, g_width=0, b_width=0; + int a_shift=0, r_shift=0, g_shift=0, b_shift=0; + int x, y, x0, y0, x_off, y_off; + cairo_xlib_visual_info_t *visual_info = NULL; + + if (surface->visual == NULL || surface->visual->class == TrueColor) { + cairo_bool_t has_alpha; + cairo_bool_t has_color; + + has_alpha = surface->a_mask; + has_color = (surface->r_mask || + surface->g_mask || + surface->b_mask); + + if (has_color) { + if (has_alpha) { + format = CAIRO_FORMAT_ARGB32; + } else { + format = CAIRO_FORMAT_RGB24; + } + } else { + /* XXX: Using CAIRO_FORMAT_A8 here would be more + * efficient, but would require slightly different code in + * the image conversion to put the alpha channel values + * into the right place. */ + format = CAIRO_FORMAT_ARGB32; + } + + a_mask = surface->a_mask; + r_mask = surface->r_mask; + g_mask = surface->g_mask; + b_mask = surface->b_mask; + + _characterize_field (a_mask, &a_width, &a_shift); + _characterize_field (r_mask, &r_width, &r_shift); + _characterize_field (g_mask, &g_width, &g_shift); + _characterize_field (b_mask, &b_width, &b_shift); + + } else { + format = CAIRO_FORMAT_RGB24; + + status = _cairo_xlib_screen_get_visual_info (display, + surface->screen, + surface->visual, + &visual_info); + if (unlikely (status)) + goto BAIL; + } + + image = (cairo_image_surface_t *) cairo_image_surface_create + (format, ximage->width, ximage->height); + status = image->base.status; + if (unlikely (status)) + goto BAIL; + + data = cairo_image_surface_get_data (&image->base); + rowstride = cairo_image_surface_get_stride (&image->base) >> 2; + row = (uint32_t *) data; + x0 = extents.x + surface->base.device_transform.x0; + y0 = extents.y + surface->base.device_transform.y0; + for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); + y < ximage->height; + y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { + const int8_t *dither_row = dither_pattern[y_off]; + for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); + x < ximage->width; + x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { + int dither_adjustment = dither_row[x_off]; + + in_pixel = XGetPixel (ximage, x, y); + if (visual_info == NULL) { + out_pixel = ( + _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | + _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | + _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 | + _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment)); + } else { + /* Undithering pseudocolor does not look better */ + out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel); + } + row[x] = out_pixel; + } + row += rowstride; + } + cairo_surface_mark_dirty (&image->base); + } + + BAIL: + if (ximage) + XDestroyImage (ximage); + + cairo_device_release (&display->base); + + if (unlikely (status)) { + if (image) { + cairo_surface_destroy (&image->base); + image = NULL; + } + } + *image_out = image; + return status; +} + +static void +_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface) +{ + if (!surface->src_picture) { + XRenderPictureAttributes pa; + int mask = 0; + + pa.subwindow_mode = IncludeInferiors; + mask |= CPSubwindowMode; + + surface->src_picture = XRenderCreatePicture (display->display, + surface->drawable, + surface->xrender_format, + mask, &pa); + } +} + +static void +_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface) +{ + if (surface->clip_region != NULL) { + XRenderSetPictureClipRectangles (display->display, surface->dst_picture, + 0, 0, + surface->clip_rects, + surface->num_clip_rects); + } else { + XRenderPictureAttributes pa; + pa.clip_mask = None; + XRenderChangePicture (display->display, surface->dst_picture, + CPClipMask, &pa); + } + + surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; +} + +static void +_cairo_xlib_surface_set_precision (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + cairo_antialias_t antialias) +{ + int precision; + + switch (antialias) { + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + precision = PolyModeImprecise; + break; + case CAIRO_ANTIALIAS_NONE: + case CAIRO_ANTIALIAS_SUBPIXEL: + precision = PolyModePrecise; + break; + } + + /* NVidia's driver version 190.42 is much slower when using PolyModeInprecise */ + precision = PolyModePrecise; + + if (surface->precision != precision) { + XRenderPictureAttributes pa; + + pa.poly_mode = precision; + XRenderChangePicture (display->display, surface->dst_picture, + CPPolyMode, &pa); + + surface->precision = precision; + } +} + +static void +_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface) +{ + if (!surface->dst_picture) { + surface->dst_picture = XRenderCreatePicture (display->display, + surface->drawable, + surface->xrender_format, + 0, NULL); + } + + if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) + _cairo_xlib_surface_set_picture_clip_rects (display, surface); +} + +static cairo_status_t +_draw_image_surface (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image, + int src_x, + int src_y, + int width, + int height, + int dst_x, + int dst_y) +{ + cairo_xlib_display_t *display; + XImage ximage; + cairo_format_masks_t image_masks; + int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + pixman_image_t *pixman_image = NULL; + cairo_status_t status; + cairo_bool_t own_data; + GC gc; + + ximage.width = image->width; + ximage.height = image->height; + ximage.format = ZPixmap; + ximage.byte_order = native_byte_order; + ximage.bitmap_unit = 32; /* always for libpixman */ + ximage.bitmap_bit_order = native_byte_order; + ximage.bitmap_pad = 32; /* always for libpixman */ + ximage.depth = surface->depth; + ximage.red_mask = surface->r_mask; + ximage.green_mask = surface->g_mask; + ximage.blue_mask = surface->b_mask; + ximage.xoffset = 0; + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) + { + pixman_format_code_t intermediate_format; + int ret; + + image_masks.alpha_mask = surface->a_mask; + image_masks.red_mask = surface->r_mask; + image_masks.green_mask = surface->g_mask; + image_masks.blue_mask = surface->b_mask; + image_masks.bpp = surface->depth; + ret = _pixman_format_from_masks (&image_masks, &intermediate_format); + assert (ret); + + own_data = FALSE; + + pixman_image = pixman_image_create_bits (intermediate_format, + image->width, + image->height, + NULL, + 0); + if (pixman_image == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + + ximage.bits_per_pixel = image_masks.bpp; + ximage.data = (char *) pixman_image_get_data (pixman_image); + ximage.bytes_per_line = pixman_image_get_stride (pixman_image); + + ret = XInitImage (&ximage); + assert (ret != 0); + } + else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && + (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && + (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && + (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) + { + int ret; + + ximage.bits_per_pixel = image_masks.bpp; + ximage.bytes_per_line = image->stride; + ximage.data = (char *)image->data; + own_data = FALSE; + + ret = XInitImage (&ximage); + assert (ret != 0); + } + else + { + unsigned int stride, rowstride; + int x, y, x0, y0, x_off, y_off; + uint32_t in_pixel, out_pixel, *row; + int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0; + int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0; + int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0; + int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0; + cairo_xlib_visual_info_t *visual_info = NULL; + cairo_bool_t true_color; + int ret; + + if (surface->depth > 16) + ximage.bits_per_pixel = 32; + else if (surface->depth > 8) + ximage.bits_per_pixel = 16; + else if (surface->depth > 1) + ximage.bits_per_pixel = 8; + else + ximage.bits_per_pixel = 1; + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, + ximage.bits_per_pixel); + ximage.bytes_per_line = stride; + ximage.data = _cairo_malloc_ab (stride, ximage.height); + if (unlikely (ximage.data == NULL)) { + own_data = FALSE; + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + own_data = TRUE; + + ret = XInitImage (&ximage); + assert (ret != 0); + + _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift); + _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift); + _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift); + _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift); + + true_color = surface->visual == NULL || + surface->visual->class == TrueColor; + if (true_color) { + _characterize_field (surface->a_mask, &o_a_width, &o_a_shift); + _characterize_field (surface->r_mask, &o_r_width, &o_r_shift); + _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); + _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); + } else { + status = _cairo_xlib_screen_get_visual_info (display, + surface->screen, + surface->visual, + &visual_info); + if (unlikely (status)) + goto BAIL; + } + + rowstride = image->stride >> 2; + row = (uint32_t *) image->data; + x0 = dst_x + surface->base.device_transform.x0; + y0 = dst_y + surface->base.device_transform.y0; + for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); + y < ximage.height; + y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) + { + const int8_t *dither_row = dither_pattern[y_off]; + + for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); + x < ximage.width; + x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) + { + int dither_adjustment = dither_row[x_off]; + int a, r, g, b; + + if (image_masks.bpp == 1) + in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); + else if (image_masks.bpp <= 8) + in_pixel = ((uint8_t*)row)[x]; + else if (image_masks.bpp <= 16) + in_pixel = ((uint16_t*)row)[x]; + else if (image_masks.bpp <= 24) +#ifdef WORDS_BIGENDIAN + in_pixel = ((uint8_t*)row)[3 * x] << 16 | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2]; +#else + in_pixel = ((uint8_t*)row)[3 * x] | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2] << 16; +#endif + else + in_pixel = row[x]; + + /* If the incoming image has no alpha channel, then the input + * is opaque and the output should have the maximum alpha value. + * For all other channels, their absence implies 0. + */ + if (image_masks.alpha_mask == 0x0) + a = 0xff; + else + a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift); + r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift); + g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift); + b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift); + + if (true_color) { + out_pixel = _field_from_8 (a, o_a_width, o_a_shift) | + _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) | + _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) | + _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment); + } else { + out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment); + } + + XPutPixel (&ximage, x, y, out_pixel); + } + + row += rowstride; + } + } + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto BAIL; + + XPutImage (display->display, surface->drawable, gc, + &ximage, src_x, src_y, dst_x, dst_y, + width, height); + + _cairo_xlib_surface_put_gc (display, surface, gc); + + BAIL: + + cairo_device_release (&display->base); + + if (own_data) + free (ximage.data); + if (pixman_image) + pixman_image_unref (pixman_image); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xlib_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (unlikely (status)) + return status; + + *image_out = image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_xlib_surface_snapshot (void *abstract_surface) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + +static void +_cairo_xlib_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, interest_rect, &image, image_rect_out); + if (unlikely (status)) + return status; + + *image_out = image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xlib_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _draw_image_surface (surface, image, + 0, 0, image->width, image->height, + image_rect->x, image_rect->y); + status = _cairo_surface_set_error (&surface->base, status); + + cairo_surface_destroy (&image->base); +} + +/* + * Return whether two xlib surfaces share the same + * screen. Both core and Render drawing require this + * when using multiple drawables in an operation. + */ +static inline cairo_bool_t +_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, + cairo_xlib_surface_t *src) +{ + return dst->screen == src->screen; +} + +static cairo_status_t +_cairo_xlib_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_xlib_surface_t *clone; + cairo_status_t status; + + if (src->backend == surface->base.backend ) { + cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; + + if (_cairo_xlib_surface_same_screen (surface, xlib_src)) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return UNSUPPORTED ("roi too large for xlib"); + + clone = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_similar (surface, + image_src->base.content, + width, height); + if (clone == NULL) + return UNSUPPORTED ("unhandled image format, no similar surface"); + + if (unlikely (clone->base.status)) + return clone->base.status; + + status = _draw_image_surface (clone, image_src, + src_x, src_y, + width, height, + 0, 0); + if (unlikely (status)) { + cairo_surface_destroy (&clone->base); + return status; + } + + *clone_offset_x = src_x; + *clone_offset_y = src_y; + *clone_out = &clone->base; + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_surface_t * +_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, + const cairo_solid_pattern_t *solid_pattern) +{ + /* This function's only responsibility is to create a proper surface + * for when XRender is not available. The proper surface is a xlib + * surface (as opposed to image surface which is what create_similar + * returns in those cases) and the size of the dithering pattern, not + * 1x1. This surface can then be used in + * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid" + * fills using core protocol */ + + cairo_xlib_surface_t *other = abstract_surface; + cairo_image_surface_t *image; + cairo_xlib_surface_t *surface = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_xlib_display_t *display; + + int width = ARRAY_LENGTH (dither_pattern[0]); + int height = ARRAY_LENGTH (dither_pattern); + + Pixmap pixmap = None; + + if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) + return NULL; + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_with_content (_cairo_color_get_content (&solid_pattern->color), + width, height); + status = image->base.status; + if (unlikely (status)) + goto BAIL; + + status = _cairo_xlib_display_acquire (other->base.device, &display); + if (unlikely (status)) + goto BAIL; + + pixmap = XCreatePixmap (display->display, + other->drawable, + width, height, + other->depth); + cairo_device_release (&display->base); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (other->screen, + pixmap, + other->visual, + other->xrender_format, + width, height, + other->depth); + status = surface->base.status; + if (unlikely (status)) + goto BAIL; + + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + &solid_pattern->base, + NULL); + if (unlikely (status)) + goto BAIL; + + status = _draw_image_surface (surface, image, + 0, 0, + width, height, + 0, 0); + if (unlikely (status)) + goto BAIL; + + BAIL: + cairo_surface_destroy (&image->base); + + if (status) { + if (pixmap != None) { + if (!_cairo_xlib_display_acquire (other->base.device, &display)) { + XFreePixmap (display->display, pixmap); + cairo_device_release (&display->base); + } + } + cairo_surface_destroy (&surface->base); + + return _cairo_surface_create_in_error (status); + } + + surface->owns_pixmap = TRUE; + return &surface->base; +} + +static cairo_bool_t +_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, + const cairo_solid_pattern_t *solid_pattern) +{ + cairo_xlib_surface_t *other = abstract_surface; + return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); +} + +static cairo_status_t +_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + const cairo_matrix_t *matrix, + double xc, + double yc) +{ + XTransform xtransform; + + /* Casting between pixman_transform_t and XTransform is safe because + * they happen to be the exact same type. + */ + _cairo_matrix_to_pixman_matrix (matrix, + (pixman_transform_t *) &xtransform, + xc, yc); + + if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) + return CAIRO_STATUS_SUCCESS; + + if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + return UNSUPPORTED ("XRender does not support picture transforms"); + + XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform); + surface->xtransform = xtransform; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + cairo_filter_t filter) +{ + const char *render_filter; + + if (surface->filter == filter) + return CAIRO_STATUS_SUCCESS; + + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + return CAIRO_STATUS_SUCCESS; + + return UNSUPPORTED ("XRender does not support filter"); + } + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = FilterFast; + break; + case CAIRO_FILTER_GOOD: + render_filter = FilterGood; + break; + case CAIRO_FILTER_BEST: + render_filter = FilterBest; + break; + case CAIRO_FILTER_NEAREST: + render_filter = FilterNearest; + break; + case CAIRO_FILTER_BILINEAR: + render_filter = FilterBilinear; + break; + case CAIRO_FILTER_GAUSSIAN: + /* XXX: The GAUSSIAN value has no implementation in cairo + * whatsoever, so it was really a mistake to have it in the + * API. We could fix this by officially deprecating it, or + * else inventing semantics and providing an actual + * implementation for it. */ + default: + render_filter = FilterBest; + break; + } + + XRenderSetPictureFilter (display->display, surface->src_picture, + (char *) render_filter, NULL, 0); + surface->filter = filter; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, + cairo_extend_t extend, + unsigned long *mask, + XRenderPictureAttributes *pa) +{ + int repeat; + + if (surface->extend == extend) + return CAIRO_STATUS_SUCCESS; + + switch (extend) { + case CAIRO_EXTEND_NONE: + repeat = RepeatNone; + break; + case CAIRO_EXTEND_REPEAT: + repeat = RepeatNormal; + break; + case CAIRO_EXTEND_REFLECT: + if (surface->buggy_pad_reflect) + return UNSUPPORTED ("buggy reflect"); + + repeat = RepeatReflect; + break; + case CAIRO_EXTEND_PAD: + if (surface->buggy_pad_reflect) + return UNSUPPORTED ("buggy pad"); + + repeat = RepeatPad; + break; + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *mask |= CPRepeat; + pa->repeat = repeat; + + surface->extend = extend; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, + cairo_bool_t ca, + unsigned long *mask, + XRenderPictureAttributes *pa) +{ + if (surface->has_component_alpha == ca) + return CAIRO_STATUS_SUCCESS; + + *mask |= CPComponentAlpha; + pa->component_alpha = ca; + + surface->has_component_alpha = ca; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + const cairo_surface_attributes_t *attributes, + double xc, + double yc) +{ + cairo_int_status_t status; + XRenderPictureAttributes pa; + unsigned long mask = 0; + + _cairo_xlib_surface_ensure_src_picture (display, surface); + + status = _cairo_xlib_surface_set_matrix (display, surface, + &attributes->matrix, xc, yc); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_set_repeat (surface, attributes->extend, + &mask, &pa); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_set_component_alpha (surface, + attributes->has_component_alpha, + &mask, &pa); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_set_filter (display, surface, attributes->filter); + if (unlikely (status)) + return status; + + if (mask) + XRenderChangePicture (display->display, surface->src_picture, mask, &pa); + + return CAIRO_STATUS_SUCCESS; +} + +/* Checks whether we can can directly draw from src to dst with + * the core protocol: either with CopyArea or using src as a + * a tile in a GC. + */ +static cairo_bool_t +_surfaces_compatible (cairo_xlib_surface_t *dst, + cairo_xlib_surface_t *src) +{ + /* same screen */ + if (! _cairo_xlib_surface_same_screen (dst, src)) + return FALSE; + + /* same depth (for core) */ + if (src->depth != dst->depth) + return FALSE; + + /* if Render is supported, match picture formats */ + if (src->xrender_format != dst->xrender_format) + return FALSE; + else if (src->xrender_format != NULL) + return TRUE; + + /* Without Render, match visuals instead */ + if (src->visual == dst->visual) + return TRUE; + + return FALSE; +} + +static cairo_bool_t +_surface_has_alpha (cairo_xlib_surface_t *surface) +{ + if (surface->xrender_format) { + if (surface->xrender_format->type == PictTypeDirect && + surface->xrender_format->direct.alphaMask != 0) + return TRUE; + else + return FALSE; + } else { + /* In the no-render case, we never have alpha */ + return FALSE; + } +} + +/* Returns true if the given operator and alpha combination requires alpha + * compositing to complete on source and destination surfaces with the same + * format. i.e. if a simple bitwise copy is not appropriate. + */ +static cairo_bool_t +_operator_needs_alpha_composite (cairo_operator_t op, + cairo_bool_t surfaces_have_alpha) +{ + if (op == CAIRO_OPERATOR_SOURCE) + return FALSE; + + if (op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_IN || + op == CAIRO_OPERATOR_ATOP) + return surfaces_have_alpha; + + return TRUE; +} + +/* There is a bug in most older X servers with compositing using a + * untransformed repeating source pattern when the source is in off-screen + * video memory, and another with repeated transformed images using a + * general transform matrix. When these bugs could be triggered, we need a + * fallback: in the common case where we have no transformation and the + * source and destination have the same format/visual, we can do the + * operation using the core protocol for the first bug, otherwise, we need + * a software fallback. + * + * We can also often optimize a compositing operation by calling XCopyArea + * for some common cases where there is no alpha compositing to be done. + * We figure that out here as well. + */ +typedef enum { + DO_RENDER, /* use render */ + DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ + DO_XTILE, /* core protocol XSetTile optimization/fallback */ + DO_UNSUPPORTED /* software fallback */ +} composite_operation_t; + +/* Initial check for the render bugs; we need to recheck for the + * offscreen-memory bug after we turn patterns into surfaces, since that + * may introduce a repeating pattern for gradient patterns. We don't need + * to check for the repeat+transform bug because gradient surfaces aren't + * transformed. + * + * All we do here is reject cases where we *know* are going to + * hit the bug and won't be able to use a core protocol fallback. + */ +static composite_operation_t +_categorize_composite_operation (cairo_xlib_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_bool_t have_mask) + +{ + if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) + return DO_UNSUPPORTED; + + if (! dst->buggy_repeat) + return DO_RENDER; + + if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && + src_pattern->extend == CAIRO_EXTEND_REPEAT) + { + /* Check for the bug with repeat patterns nad general transforms. */ + if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, + NULL, NULL)) + { + return DO_UNSUPPORTED; + } + + if (have_mask || + !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) + { + return DO_UNSUPPORTED; + } + + if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; + + /* This is the case where we have the bug involving + * untransformed repeating source patterns with off-screen + * video memory; reject some cases where a core protocol + * fallback is impossible. + */ + if (_cairo_surface_is_xlib (surface_pattern->surface)) { + cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; + + if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) + return DO_UNSUPPORTED; + + /* If these are on the same screen but otherwise incompatible, + * make a copy as core drawing can't cross depths and doesn't + * work right across visuals of the same depth + */ + if (_cairo_xlib_surface_same_screen (dst, src) && + !_surfaces_compatible (dst, src)) + { + return DO_UNSUPPORTED; + } + } + } + } + + return DO_RENDER; +} + +/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces + * If we end up returning DO_UNSUPPORTED here, we're throwing away work we + * did to turn gradients into a pattern, but most of the time we can handle + * that case with core protocol fallback. + * + * Also check here if we can just use XCopyArea, instead of going through + * Render. + */ +static composite_operation_t +_recategorize_composite_operation (cairo_xlib_surface_t *dst, + cairo_operator_t op, + cairo_xlib_surface_t *src, + cairo_surface_attributes_t *src_attr, + cairo_bool_t have_mask) +{ + /* Can we use the core protocol? */ + if (! have_mask && + _surfaces_compatible (src, dst) && + _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + ! _operator_needs_alpha_composite (op, _surface_has_alpha (dst))) + { + if (src_attr->extend == CAIRO_EXTEND_NONE) + return DO_XCOPYAREA; + + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) + return DO_XTILE; + } + + if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT && + (src->width != 1 || src->height != 1)) + return DO_UNSUPPORTED; + + if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) + return DO_UNSUPPORTED; + + if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) + return DO_UNSUPPORTED; + + return DO_RENDER; +} + +static int +_render_operator (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return PictOpClear; + + case CAIRO_OPERATOR_SOURCE: + return PictOpSrc; + case CAIRO_OPERATOR_OVER: + return PictOpOver; + case CAIRO_OPERATOR_IN: + return PictOpIn; + case CAIRO_OPERATOR_OUT: + return PictOpOut; + case CAIRO_OPERATOR_ATOP: + return PictOpAtop; + + case CAIRO_OPERATOR_DEST: + return PictOpDst; + case CAIRO_OPERATOR_DEST_OVER: + return PictOpOverReverse; + case CAIRO_OPERATOR_DEST_IN: + return PictOpInReverse; + case CAIRO_OPERATOR_DEST_OUT: + return PictOpOutReverse; + case CAIRO_OPERATOR_DEST_ATOP: + return PictOpAtopReverse; + + case CAIRO_OPERATOR_XOR: + return PictOpXor; + case CAIRO_OPERATOR_ADD: + return PictOpAdd; + case CAIRO_OPERATOR_SATURATE: + return PictOpSaturate; + + case CAIRO_OPERATOR_MULTIPLY: + return PictOpMultiply; + case CAIRO_OPERATOR_SCREEN: + return PictOpScreen; + case CAIRO_OPERATOR_OVERLAY: + return PictOpOverlay; + case CAIRO_OPERATOR_DARKEN: + return PictOpDarken; + case CAIRO_OPERATOR_LIGHTEN: + return PictOpLighten; + case CAIRO_OPERATOR_COLOR_DODGE: + return PictOpColorDodge; + case CAIRO_OPERATOR_COLOR_BURN: + return PictOpColorBurn; + case CAIRO_OPERATOR_HARD_LIGHT: + return PictOpHardLight; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PictOpSoftLight; + case CAIRO_OPERATOR_DIFFERENCE: + return PictOpDifference; + case CAIRO_OPERATOR_EXCLUSION: + return PictOpExclusion; + case CAIRO_OPERATOR_HSL_HUE: + return PictOpHSLHue; + case CAIRO_OPERATOR_HSL_SATURATION: + return PictOpHSLSaturation; + case CAIRO_OPERATOR_HSL_COLOR: + return PictOpHSLColor; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PictOpHSLLuminosity; + + default: + ASSERT_NOT_REACHED; + return PictOpOver; + } +} + +static cairo_int_status_t +_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display, + cairo_xlib_surface_t *dst, + const cairo_pattern_t *pattern, + int x, int y, + int width, int height, + cairo_xlib_surface_t **surface_out, + cairo_surface_attributes_t *attributes) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + cairo_matrix_t matrix = pattern->matrix; + cairo_xlib_surface_t *surface; + char buf[CAIRO_STACK_BUFFER_SIZE]; + XFixed *stops; + XRenderColor *colors; + XRenderPictFormat *format; + Picture picture; + unsigned int i; + + if (dst->buggy_gradients) + break; + + if (gradient->n_stops < 2) /* becomes a solid */ + break; + + if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) + { + stops = (XFixed *) buf; + } + else + { + stops = + _cairo_malloc_ab (gradient->n_stops, + sizeof (XFixed) + sizeof (XRenderColor)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + colors = (XRenderColor *) (stops + gradient->n_stops); + for (i = 0; i < gradient->n_stops; i++) { + stops[i] = + _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + + colors[i].red = gradient->stops[i].color.red_short; + colors[i].green = gradient->stops[i].color.green_short; + colors[i].blue = gradient->stops[i].color.blue_short; + colors[i].alpha = gradient->stops[i].color.alpha_short; + } + +#if 0 + /* For some weird reason the X server is sometimes getting + * CreateGradient requests with bad length. So far I've only seen + * XRenderCreateLinearGradient request with 4 stops sometime end up + * with length field matching 0 stops at the server side. I've + * looked at the libXrender code and I can't see anything that + * could cause this behavior. However, for some reason having a + * XSync call here seems to avoid the issue so I'll keep it here + * until it's solved. + */ + XSync (display->display, False); +#endif + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + XLinearGradient grad; + + cairo_fixed_t xdim, ydim; + + xdim = linear->p2.x - linear->p1.x; + ydim = linear->p2.y - linear->p1.y; + + /* + * Transform the matrix to avoid overflow when converting between + * cairo_fixed_t and pixman_fixed_t (without incurring performance + * loss when the transformation is unnecessary). + * + * XXX: Consider converting out-of-range co-ordinates and transforms. + * Having a function to compute the required transformation to + * "normalize" a given bounding box would be generally useful - + * cf linear patterns, gradient patterns, surface patterns... + */ +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || + _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) + { + double sf; + + if (xdim > ydim) + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); + else + sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); + + grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); + grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); + grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); + grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); + + cairo_matrix_scale (&matrix, sf, sf); + } + else + { + grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x); + grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y); + grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x); + grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y); + } + + picture = XRenderCreateLinearGradient (display->display, &grad, + stops, colors, + gradient->n_stops); + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + XRadialGradient grad; + + grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x); + grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y); + grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1); + + grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x); + grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y); + grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2); + + picture = XRenderCreateRadialGradient (display->display, &grad, + stops, colors, + gradient->n_stops); + + } + + if (stops != (XFixed *) buf) + free (stops); + + if (unlikely (picture == None)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* Wrap the remote Picture in an xlib surface. */ + format = _cairo_xlib_display_get_xrender_format (display, + CAIRO_FORMAT_ARGB32); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (dst->screen, None, + NULL, format, + /* what could possibly go wrong? */ + XLIB_COORD_MAX, XLIB_COORD_MAX, 32); + if (unlikely (surface->base.status)) { + XRenderFreePicture (display->display, picture); + return surface->base.status; + } + + surface->src_picture = picture; + + attributes->matrix = matrix; + attributes->extend = pattern->extend; + attributes->filter = CAIRO_FILTER_NEAREST; + attributes->x_offset = 0; + attributes->y_offset = 0; + attributes->has_component_alpha = FALSE; + + *surface_out = surface; + return CAIRO_STATUS_SUCCESS; + } + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_SURFACE: + break; + } + + return _cairo_pattern_acquire_surface (pattern, &dst->base, + x, y, width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) surface_out, + attributes); +} + +static cairo_int_status_t +_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display, + cairo_xlib_surface_t *dst, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_xlib_surface_t **src_out, + cairo_xlib_surface_t **mask_out, + cairo_surface_attributes_t *src_attr, + cairo_surface_attributes_t *mask_attr) +{ + if (! dst->buggy_gradients && + (src->type == CAIRO_PATTERN_TYPE_LINEAR || + src->type == CAIRO_PATTERN_TYPE_RADIAL || + (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || + mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) + { + cairo_int_status_t status; + + status = _cairo_xlib_surface_acquire_pattern_surface (display, + dst, src, + src_x, src_y, + width, height, + src_out, + src_attr); + if (unlikely (status)) + return status; + + if (mask) { + status = _cairo_xlib_surface_acquire_pattern_surface (display, + dst, mask, + mask_x, + mask_y, + width, + height, + mask_out, + mask_attr); + if (unlikely (status)) { + _cairo_pattern_release_surface (src, &(*src_out)->base, + src_attr); + return status; + } + } else { + *mask_out = NULL; + } + + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_pattern_acquire_surfaces (src, mask, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) src_out, + (cairo_surface_t **) mask_out, + src_attr, mask_attr); +} + +static cairo_int_status_t +_cairo_xlib_surface_upload(cairo_xlib_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int src_x, int src_y, + int dst_x, int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_image_surface_t *image; + cairo_rectangle_int_t extents; + cairo_status_t status; + int tx, ty; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) pattern)->surface; + if (image->base.type != CAIRO_SURFACE_TYPE_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && + (image->base.content & CAIRO_CONTENT_ALPHA) == 0))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) { + if (image->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target; + extents.x = extents.y = 0; + extents.width = image->width; + extents.height = image->height; + } else if (image->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) image; + image = (cairo_image_surface_t *) sub->target; + src_x += sub->extents.x; + src_y += sub->extents.y; + extents = sub->extents; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else { + extents.x = extents.y = 0; + extents.width = image->width; + extents.height = image->height; + } + + if (image->format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (image->depth != surface->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + src_x += tx; + src_y += ty; + + /* XXX for EXTEND_NONE perform unbounded fixups? */ + if (src_x < extents.x || + src_y < extents.y || + src_x + width > (unsigned) extents.width || + src_y + height > (unsigned) extents.height) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + if (clip_region != NULL) { + int n, num_rect; + + src_x -= dst_x; + src_y -= dst_y; + + num_rect = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rect; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + status = _draw_image_surface (surface, image, + rect.x + src_x, rect.y + src_y, + rect.width, rect.height, + rect.x, rect.y); + if (unlikely (status)) + break; + } + } else { + status = _draw_image_surface (surface, image, + src_x, src_y, + width, height, + dst_x, dst_y); + } + + cairo_device_release (surface->base.device); + + return status; +} + +static cairo_int_status_t +_cairo_xlib_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_surface_attributes_t src_attr, mask_attr; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_xlib_surface_t *mask; + cairo_xlib_display_t *display; + cairo_int_status_t status; + composite_operation_t operation; + int itx, ity; + cairo_bool_t is_integer_translation; + GC gc; + + if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) + return UNSUPPORTED ("no support for masks"); + + operation = _categorize_composite_operation (dst, op, src_pattern, + mask_pattern != NULL); + if (operation == DO_UNSUPPORTED) + return UNSUPPORTED ("unsupported operation"); + + X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); + + if (mask_pattern == NULL) { + /* Can we do a simple upload in-place? */ + status = _cairo_xlib_surface_upload(dst, op, src_pattern, + src_x, src_y, + dst_x, dst_y, + width, height, + clip_region); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_xlib_display_acquire (dst-> base.device, &display); + if (unlikely (status)) + return status; + + status = + _cairo_xlib_surface_acquire_pattern_surfaces (display, dst, + src_pattern, mask_pattern, + src_x, src_y, + mask_x, mask_y, + width, height, + &src, &mask, + &src_attr, &mask_attr); + if (unlikely (status)) + goto BAIL0; + + /* check for fallback surfaces that we cannot handle ... */ + assert (_cairo_surface_is_xlib (&src->base)); + assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); + + if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { + status = UNSUPPORTED ("unsupported mask"); + goto BAIL; + } + + operation = _recategorize_composite_operation (dst, op, src, &src_attr, + mask_pattern != NULL); + if (operation == DO_UNSUPPORTED) { + status = UNSUPPORTED ("unsupported operation"); + goto BAIL; + } + + switch (operation) + { + case DO_RENDER: + status = _cairo_xlib_surface_set_attributes (display, + src, &src_attr, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) + goto BAIL; + + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + + _cairo_xlib_surface_ensure_dst_picture (display, dst); + if (mask) { + status = _cairo_xlib_surface_set_attributes (display, + mask, &mask_attr, + dst_x + width / 2., + dst_y + height/ 2.); + if (unlikely (status)) + goto BAIL; + + XRenderComposite (display->display, + _render_operator (op), + src->src_picture, + mask->src_picture, + dst->dst_picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } else { + XRenderComposite (display->display, + _render_operator (op), + src->src_picture, + 0, + dst->dst_picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } + + break; + + case DO_XCOPYAREA: + status = _cairo_xlib_surface_get_gc (display, dst, &gc); + if (unlikely (status)) + goto BAIL; + + is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); + /* This is a pre-condition for DO_XCOPYAREA. */ + assert (is_integer_translation); + + if (clip_region == NULL) { + XCopyArea (display->display, src->drawable, dst->drawable, gc, + src_x + src_attr.x_offset + itx, + src_y + src_attr.y_offset + ity, + width, height, + dst_x, dst_y); + } else { + int n, num_rects, x, y; + + x = src_x + src_attr.x_offset + itx - dst_x; + y = src_y + src_attr.y_offset + ity - dst_y; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + XCopyArea (display->display, src->drawable, dst->drawable, gc, + rect.x + x, rect.y + y, + rect.width, rect.height, + rect.x, rect.y); + } + } + + _cairo_xlib_surface_put_gc (display, dst, gc); + break; + + case DO_XTILE: + /* This case is only used for bug fallbacks, though we also use it for + * the case where we don't have the RENDER extension, by forcing + * buggy_repeat to TRUE. + * + * We've checked that we have a repeating unscaled source in + * _recategorize_composite_operation. + */ + + status = _cairo_xlib_surface_get_gc (display, dst, &gc); + if (unlikely (status)) + goto BAIL; + + is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); + /* This is a pre-condition for DO_XTILE. */ + assert (is_integer_translation); + + XSetTSOrigin (display->display, gc, + - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); + XSetTile (display->display, gc, src->drawable); + + if (clip_region == NULL) { + XFillRectangle (display->display, dst->drawable, gc, + dst_x, dst_y, width, height); + } else { + int n, num_rects; + + num_rects = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rects; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + XFillRectangle (display->display, dst->drawable, gc, + rect.x, rect.y, rect.width, rect.height); + } + } + + _cairo_xlib_surface_put_gc (display, dst, gc); + break; + + case DO_UNSUPPORTED: + default: + ASSERT_NOT_REACHED; + } + + if (!_cairo_operator_bounded_by_source (op)) + status = _cairo_surface_composite_fixup_unbounded (&dst->base, + &src_attr, src->width, src->height, + mask ? &mask_attr : NULL, + mask ? mask->width : 0, + mask ? mask->height : 0, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, width, height, + clip_region); + + BAIL: + if (mask) + _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); + + BAIL0: + cairo_device_release (&display->base); + + return status; +} + +/* XXX move this out of core and into acquire_pattern_surface() above. */ +static cairo_int_status_t +_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_status_t status; + cairo_solid_pattern_t solid; + cairo_surface_t *solid_surface = NULL; + cairo_surface_attributes_t attrs; + cairo_xlib_display_t *display; + GC gc; + int i; + + _cairo_pattern_init_solid (&solid, color); + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + return status; + + X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + + status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, + 0, 0, + ARRAY_LENGTH (dither_pattern[0]), + ARRAY_LENGTH (dither_pattern), + CAIRO_PATTERN_ACQUIRE_NONE, + &solid_surface, + &attrs); + if (unlikely (status)) { + _cairo_xlib_surface_put_gc (display, surface, gc); + cairo_device_release (&display->base); + return status; + } + + assert (_cairo_surface_is_xlib (solid_surface)); + + XSetTSOrigin (display->display, gc, + - (surface->base.device_transform.x0 + attrs.x_offset), + - (surface->base.device_transform.y0 + attrs.y_offset)); + XSetTile (display->display, gc, + ((cairo_xlib_surface_t *) solid_surface)->drawable); + + for (i = 0; i < num_rects; i++) { + XFillRectangle (display->display, surface->drawable, gc, + rects[i].x, rects[i].y, + rects[i].width, rects[i].height); + } + + _cairo_xlib_surface_put_gc (display, surface, gc); + + _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); + + cairo_device_release (&display->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xlib_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_xlib_display_t *display; + XRenderColor render_color; + cairo_status_t status; + int i; + + if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { + if (op == CAIRO_OPERATOR_CLEAR || + ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && + CAIRO_COLOR_IS_OPAQUE (color))) + { + return _cairo_xlib_surface_solid_fill_rectangles (surface, color, + rects, num_rects); + } + + return UNSUPPORTED ("no support for FillRectangles with this op"); + } + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + status = _cairo_xlib_surface_set_clip_region (surface, NULL); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_xlib_surface_ensure_dst_picture (display, surface); + if (num_rects == 1) { + /* Take advantage of the protocol compaction that libXrender performs + * to amalgamate sequences of XRenderFillRectangle(). + */ + XRenderFillRectangle (display->display, + _render_operator (op), + surface->dst_picture, + &render_color, + rects->x, + rects->y, + rects->width, + rects->height); + } else { + XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *xrects = static_xrects; + + if (num_rects > ARRAY_LENGTH (static_xrects)) { + xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); + if (unlikely (xrects == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + for (i = 0; i < num_rects; i++) { + xrects[i].x = rects[i].x; + xrects[i].y = rects[i].y; + xrects[i].width = rects[i].width; + xrects[i].height = rects[i].height; + } + + XRenderFillRectangles (display->display, + _render_operator (op), + surface->dst_picture, + &render_color, xrects, num_rects); + + if (xrects != static_xrects) + free (xrects); + } + +BAIL: + cairo_device_release (&display->base); + return status; +} + +#define CAIRO_FIXED_16_16_MIN -32768 +#define CAIRO_FIXED_16_16_MAX 32767 + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); +} + +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + XLineFixed *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + +static cairo_int_status_t +_cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *clip_region) +{ + cairo_surface_attributes_t attributes; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_xlib_display_t *display; + cairo_int_status_t status; + composite_operation_t operation; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; + XRenderPictFormat *pict_format; + XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; + XTrapezoid *xtraps = xtraps_stack; + int i; + + if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) + return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); + + operation = _categorize_composite_operation (dst, op, pattern, TRUE); + if (operation == DO_UNSUPPORTED) + return UNSUPPORTED ("unsupported operation"); + + status = _cairo_xlib_display_acquire (dst->base.device, &display); + if (unlikely (status)) + return status; + + X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); + + status = _cairo_xlib_surface_acquire_pattern_surface (display, + dst, + pattern, + src_x, src_y, + width, height, + &src, &attributes); + if (unlikely (status)) + goto BAIL0; + + operation = _recategorize_composite_operation (dst, op, src, + &attributes, TRUE); + if (operation == DO_UNSUPPORTED) { + status = UNSUPPORTED ("unsupported operation"); + goto BAIL; + } + + switch (antialias) { + case CAIRO_ANTIALIAS_NONE: + pict_format = + _cairo_xlib_display_get_xrender_format (display, + CAIRO_FORMAT_A1); + break; + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_DEFAULT: + default: + pict_format = + _cairo_xlib_display_get_xrender_format (display, + CAIRO_FORMAT_A8); + break; + } + + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL; + + _cairo_xlib_surface_ensure_dst_picture (display, dst); + _cairo_xlib_surface_set_precision (display, dst, antialias); + + status = _cairo_xlib_surface_set_attributes (display, + src, &attributes, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) + goto BAIL; + + if (num_traps > ARRAY_LENGTH (xtraps_stack)) { + xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); + if (unlikely (xtraps == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + for (i = 0; i < num_traps; i++) { + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); + xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (_line_exceeds_16_16 (&traps[i].left))) { + _project_line_x_onto_16_16 (&traps[i].left, + traps[i].top, + traps[i].bottom, + &xtraps[i].left); + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; + } else { + xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); + xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); + xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); + xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); + } + + if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { + _project_line_x_onto_16_16 (&traps[i].right, + traps[i].top, + traps[i].bottom, + &xtraps[i].right); + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); + xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); + xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); + xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); + } + } + + if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { + render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); + render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); + render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); + } + + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; + + XRenderCompositeTrapezoids (display->display, + _render_operator (op), + src->src_picture, dst->dst_picture, + pict_format, + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + xtraps, num_traps); + + if (xtraps != xtraps_stack) + free (xtraps); + + if (! _cairo_operator_bounded_by_mask (op)) { + cairo_traps_t _traps; + cairo_box_t box; + cairo_rectangle_int_t extents; + + /* XRenderCompositeTrapezoids() creates a mask only large enough for the + * trapezoids themselves, but if the operator is unbounded, then we need + * to actually composite all the way out to the bounds. + */ + /* XXX: update the interface to pass composite rects */ + _traps.traps = traps; + _traps.num_traps = num_traps; + _cairo_traps_extents (&_traps, &box); + _cairo_box_round_to_rectangle (&box, &extents); + + status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, + &attributes, + src->width, src->height, + extents.width, extents.height, + src_x, src_y, + -extents.x + dst_x, -extents.y + dst_y, + dst_x, dst_y, + width, height, + clip_region); + } + + BAIL: + _cairo_pattern_release_surface (pattern, &src->base, &attributes); + BAIL0: + cairo_device_release (&display->base); + + return status; +} + +static cairo_bool_t +_cairo_xlib_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_xlib_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + *options = *_cairo_xlib_screen_get_font_options (surface->screen); +} + +static void +_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +static cairo_bool_t +_cairo_xlib_surface_is_similar (void *surface_a, + void *surface_b) +{ + return _cairo_xlib_surface_same_screen (surface_a, surface_b); +} + +static const cairo_surface_backend_t cairo_xlib_surface_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_surface_create_similar, + _cairo_xlib_surface_finish, + _cairo_xlib_surface_acquire_source_image, + _cairo_xlib_surface_release_source_image, + _cairo_xlib_surface_acquire_dest_image, + _cairo_xlib_surface_release_dest_image, + _cairo_xlib_surface_clone_similar, + _cairo_xlib_surface_composite, + _cairo_xlib_surface_fill_rectangles, + _cairo_xlib_surface_composite_trapezoids, + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_xlib_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_xlib_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_xlib_surface_scaled_font_fini, + _cairo_xlib_surface_scaled_glyph_fini, + + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + _cairo_xlib_surface_show_glyphs, + + _cairo_xlib_surface_snapshot, + _cairo_xlib_surface_is_similar, + + NULL, /* fill_stroke */ + + _cairo_xlib_surface_create_solid_pattern_surface, + _cairo_xlib_surface_can_repaint_solid_pattern_surface +}; + +/** + * _cairo_surface_is_xlib: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_xlib_surface_t + * + * Return value: True if the surface is an xlib surface + **/ +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface) +{ + return surface->backend == &cairo_xlib_surface_backend; +} + +/* callback from CloseDisplay */ +static void +_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) +{ + cairo_xlib_surface_t *surface = cairo_container_of (data, + cairo_xlib_surface_t, + close_display_hook); + Display *dpy; + + dpy = display->display; + + X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); + + if (surface->dst_picture != None) { + XRenderFreePicture (dpy, surface->dst_picture); + surface->dst_picture = None; + } + + if (surface->src_picture != None) { + XRenderFreePicture (dpy, surface->src_picture); + surface->src_picture = None; + } + + if (surface->owns_pixmap) { + XFreePixmap (dpy, surface->drawable); + surface->drawable = None; + surface->owns_pixmap = FALSE; + } +} + +static cairo_surface_t * +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + Visual *visual, + XRenderPictFormat *xrender_format, + int width, + int height, + int depth) +{ + cairo_xlib_surface_t *surface; + cairo_xlib_display_t *display; + cairo_status_t status; + + if (depth == 0) { + if (xrender_format) { + depth = xrender_format->depth; + + /* XXX find matching visual for core/dithering fallbacks? */ + } else if (visual) { + Screen *scr = screen->screen; + + if (visual == DefaultVisualOfScreen (scr)) { + depth = DefaultDepthOfScreen (scr); + } else { + int j, k; + + /* This is ugly, but we have to walk over all visuals + * for the display to find the correct depth. + */ + depth = 0; + for (j = 0; j < scr->ndepths; j++) { + Depth *d = &scr->depths[j]; + for (k = 0; k < d->nvisuals; k++) { + if (&d->visuals[k] == visual) { + depth = d->depth; + goto found; + } + } + } + } + } + + if (depth == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + +found: + ; + } + + surface = malloc (sizeof (cairo_xlib_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + status = _cairo_xlib_display_acquire (screen->device, &display); + if (unlikely (status)) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (status)); + } + + _cairo_xlib_display_get_xrender_version (display, + &surface->render_major, + &surface->render_minor); + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { + if (!xrender_format) { + if (visual) { + xrender_format = XRenderFindVisualFormat (display->display, visual); + } else if (depth == 1) { + xrender_format = + _cairo_xlib_display_get_xrender_format (display, + CAIRO_FORMAT_A1); + } + } + } else { + /* we cannot use XRender for this surface, so ensure we don't try */ + surface->render_major = -1; + surface->render_minor = -1; + } + + /* initialize and hook into the CloseDisplay callback */ + surface->close_display_hook.func = _cairo_xlib_surface_detach_display; + _cairo_xlib_add_close_display_hook (display, + &surface->close_display_hook); + + cairo_device_release (&display->base); + + _cairo_surface_init (&surface->base, + &cairo_xlib_surface_backend, + screen->device, + _xrender_format_to_content (xrender_format)); + + surface->screen = screen; + + surface->drawable = drawable; + surface->owns_pixmap = FALSE; + surface->use_pixmap = 0; + surface->width = width; + surface->height = height; + + surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device); + if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { + /* so we can use the XTile fallback */ + surface->buggy_repeat = TRUE; + } + + surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device); + if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) + surface->buggy_pad_reflect = TRUE; + + surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device); + if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) + surface->buggy_gradients = TRUE; + + surface->dst_picture = None; + surface->src_picture = None; + + surface->visual = visual; + surface->xrender_format = xrender_format; + surface->depth = depth; + surface->filter = CAIRO_FILTER_NEAREST; + surface->extend = CAIRO_EXTEND_NONE; + surface->has_component_alpha = FALSE; + surface->precision = PolyModePrecise; + surface->xtransform = identity; + + surface->clip_region = NULL; + surface->clip_rects = surface->embedded_clip_rects; + surface->num_clip_rects = 0; + surface->clip_dirty = 0; + + /* + * Compute the pixel format masks from either a XrenderFormat or + * else from a visual; failing that we assume the drawable is an + * alpha-only pixmap as it could only have been created that way + * through the cairo_xlib_surface_create_for_bitmap function. + */ + if (xrender_format) { + surface->a_mask = (unsigned long) + surface->xrender_format->direct.alphaMask + << surface->xrender_format->direct.alpha; + surface->r_mask = (unsigned long) + surface->xrender_format->direct.redMask + << surface->xrender_format->direct.red; + surface->g_mask = (unsigned long) + surface->xrender_format->direct.greenMask + << surface->xrender_format->direct.green; + surface->b_mask = (unsigned long) + surface->xrender_format->direct.blueMask + << surface->xrender_format->direct.blue; + } else if (visual) { + surface->a_mask = 0; + surface->r_mask = visual->red_mask; + surface->g_mask = visual->green_mask; + surface->b_mask = visual->blue_mask; + } else { + if (depth < 32) + surface->a_mask = (1 << depth) - 1; + else + surface->a_mask = 0xffffffff; + surface->r_mask = 0; + surface->g_mask = 0; + surface->b_mask = 0; + } + + return &surface->base; +} + +static Screen * +_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) +{ + int s, d, v; + + for (s = 0; s < ScreenCount (dpy); s++) { + Screen *screen; + + screen = ScreenOfDisplay (dpy, s); + if (visual == DefaultVisualOfScreen (screen)) + return screen; + + for (d = 0; d < screen->ndepths; d++) { + Depth *depth; + + depth = &screen->depths[d]; + for (v = 0; v < depth->nvisuals; v++) + if (visual == &depth->visuals[v]) + return screen; + } + } + + return NULL; +} + +/** + * cairo_xlib_surface_create: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * Note: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size() must be called whenever the size of the + * window changes. + * + * When @drawable is a Window containing child windows then drawing to + * the created surface will be clipped by those child windows. When + * the created surface is used as a source, the contents of the + * children will be included. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) +{ + Screen *scr; + cairo_xlib_screen_t *screen; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + /* you're lying, and you know it! */ + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + } + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); + + return _cairo_xlib_surface_create_internal (screen, drawable, + visual, NULL, + width, height, 0); +} + +/** + * cairo_xlib_surface_create_for_bitmap: + * @dpy: an X Display + * @bitmap: an X Drawable, (a depth-1 Pixmap) + * @screen: the X Screen associated with @bitmap + * @width: the current width of @bitmap. + * @height: the current height of @bitmap. + * + * Creates an Xlib surface that draws to the given bitmap. + * This will be drawn to as a %CAIRO_FORMAT_A1 object. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *scr, + int width, + int height) +{ + cairo_xlib_screen_t *screen; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); + + return _cairo_xlib_surface_create_internal (screen, bitmap, + NULL, NULL, + width, height, 1); +} + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE +/** + * cairo_xlib_surface_create_with_xrender_format: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @screen: the X Screen associated with @drawable + * @format: the picture format to use for drawing to @drawable. The depth + * of @format must match the depth of the drawable. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided picture format. + * + * Note: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size() must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *scr, + XRenderPictFormat *format, + int width, + int height) +{ + cairo_xlib_screen_t *screen; + cairo_status_t status; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); + + return _cairo_xlib_surface_create_internal (screen, drawable, + _visual_for_xrender_format (scr, format), + format, width, height, 0); +} + +/** + * cairo_xlib_surface_get_xrender_format: + * @surface: an xlib surface + * + * Gets the X Render picture format that @surface uses for rendering with the + * X Render extension. If the surface was created by + * cairo_xlib_surface_create_with_xrender_format() originally, the return + * value is the format passed to that constructor. + * + * Return value: the XRenderPictFormat* associated with @surface, + * or %NULL if the surface is not an xlib surface + * or if the X Render extension is not available. + * + * Since: 1.6 + **/ +XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) +{ + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; + + /* Throw an error for a non-xlib surface */ + if (! _cairo_surface_is_xlib (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->xrender_format; +} +#endif + +/** + * cairo_xlib_surface_set_size: + * @surface: a #cairo_surface_t for the XLib backend + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the X Drawable underlying the + * surface. For a surface created for a Window (rather than a Pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * A Pixmap can never change size, so it is never necessary to call + * this function on a surface created for a Pixmap. + **/ +void +cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_xlib (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } + + surface->width = width; + surface->height = height; +} +/** + * cairo_xlib_surface_set_drawable: + * @surface: a #cairo_surface_t for the XLib backend + * @drawable: the new drawable for the surface + * @width: the width of the new drawable + * @height: the height of the new drawable + * + * Informs cairo of a new X Drawable underlying the + * surface. The drawable must match the display, screen + * and format of the existing drawable or the application + * will get X protocol errors and will probably terminate. + * No checks are done by this function to ensure this + * compatibility. + **/ +void +cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, + Drawable drawable, + int width, + int height) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_xlib (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } + + /* XXX: and what about this case? */ + if (surface->owns_pixmap) + return; + + if (surface->drawable != drawable) { + cairo_xlib_display_t *display; + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return; + + X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); + + if (surface->dst_picture != None) { + status = _cairo_xlib_display_queue_resource ( + display, + XRenderFreePicture, + surface->dst_picture); + if (unlikely (status)) { + status = _cairo_surface_set_error (&surface->base, status); + return; + } + + surface->dst_picture = None; + } + + if (surface->src_picture != None) { + status = _cairo_xlib_display_queue_resource ( + display, + XRenderFreePicture, + surface->src_picture); + if (unlikely (status)) { + status = _cairo_surface_set_error (&surface->base, status); + return; + } + + surface->src_picture = None; + } + + cairo_device_release (&display->base); + + surface->drawable = drawable; + } + surface->width = width; + surface->height = height; +} + +/** + * cairo_xlib_surface_get_display: + * @surface: a #cairo_xlib_surface_t + * + * Get the X Display for the underlying X Drawable. + * + * Return value: the display. + * + * Since: 1.2 + **/ +Display * +cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) +{ + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return ((cairo_xlib_display_t *) abstract_surface->device)->display; +} + +/** + * cairo_xlib_surface_get_drawable: + * @surface: a #cairo_xlib_surface_t + * + * Get the underlying X Drawable used for the surface. + * + * Return value: the drawable. + * + * Since: 1.2 + **/ +Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->drawable; +} + +/** + * cairo_xlib_surface_get_screen: + * @surface: a #cairo_xlib_surface_t + * + * Get the X Screen for the underlying X Drawable. + * + * Return value: the screen. + * + * Since: 1.2 + **/ +Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->screen->screen; +} + +/** + * cairo_xlib_surface_get_visual: + * @surface: a #cairo_xlib_surface_t + * + * Gets the X Visual associated with @surface, suitable for use with the + * underlying X Drawable. If @surface was created by + * cairo_xlib_surface_create(), the return value is the Visual passed to that + * constructor. + * + * Return value: the Visual or %NULL if there is no appropriate Visual for + * @surface. + * + * Since: 1.2 + **/ +Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *surface) +{ + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; + + if (! _cairo_surface_is_xlib (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->visual; +} + +/** + * cairo_xlib_surface_get_depth: + * @surface: a #cairo_xlib_surface_t + * + * Get the number of bits used to represent each pixel value. + * + * Return value: the depth of the surface in bits. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->depth; +} + +/** + * cairo_xlib_surface_get_width: + * @surface: a #cairo_xlib_surface_t + * + * Get the width of the X Drawable underlying the surface in pixels. + * + * Return value: the width of the surface in pixels. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->width; +} + +/** + * cairo_xlib_surface_get_height: + * @surface: a #cairo_xlib_surface_t + * + * Get the height of the X Drawable underlying the surface in pixels. + * + * Return value: the height of the surface in pixels. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->height; +} + +enum { + GLYPHSET_INDEX_ARGB32, + GLYPHSET_INDEX_A8, + GLYPHSET_INDEX_A1, + NUM_GLYPHSETS +}; + +typedef struct _cairo_xlib_font_glyphset_free_glyphs { + GlyphSet glyphset; + int glyph_count; + unsigned long glyph_indices[128]; +} cairo_xlib_font_glyphset_free_glyphs_t; + +typedef struct _cairo_xlib_font_glyphset_info { + GlyphSet glyphset; + cairo_format_t format; + XRenderPictFormat *xrender_format; + cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; +} cairo_xlib_font_glyphset_info_t; + +typedef struct _cairo_xlib_surface_font_private { + cairo_scaled_font_t *scaled_font; + cairo_scaled_font_t *grayscale_font; + cairo_xlib_hook_t close_display_hook; + cairo_device_t *device; + cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; +} cairo_xlib_surface_font_private_t; + +/* callback from CloseDisplay */ +static void +_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, + void *data) +{ + cairo_xlib_surface_font_private_t *font_private; + cairo_scaled_font_t *scaled_font; + + font_private = cairo_container_of (data, + cairo_xlib_surface_font_private_t, + close_display_hook); + scaled_font = font_private->scaled_font; + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + font_private = scaled_font->surface_private; + scaled_font->surface_private = NULL; + + _cairo_scaled_font_reset_cache (scaled_font); + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (font_private != NULL) { + int i; + + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info; + + glyphset_info = &font_private->glyphset_info[i]; + if (glyphset_info->glyphset) + XRenderFreeGlyphSet (display->display, glyphset_info->glyphset); + + if (glyphset_info->pending_free_glyphs != NULL) + free (glyphset_info->pending_free_glyphs); + } + + cairo_device_destroy (font_private->device); + free (font_private); + } +} + +static cairo_status_t +_cairo_xlib_surface_font_init (cairo_xlib_display_t *display, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private; + int i; + + font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->scaled_font = scaled_font; + font_private->grayscale_font = NULL; + font_private->device = cairo_device_reference (&display->base); + + /* initialize and hook into the CloseDisplay callback */ + font_private->close_display_hook.func = + _cairo_xlib_surface_remove_scaled_font; + _cairo_xlib_add_close_display_hook (display, + &font_private->close_display_hook); + + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; + switch (i) { + case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; + case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; + case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; + default: ASSERT_NOT_REACHED; break; + } + glyphset_info->xrender_format = NULL; + glyphset_info->glyphset = None; + glyphset_info->pending_free_glyphs = NULL; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &cairo_xlib_surface_backend; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private; + cairo_status_t status; + + font_private = scaled_font->surface_private; + if (font_private != NULL) { + cairo_xlib_display_t *display; + int i; + + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + status = _cairo_xlib_display_acquire (font_private->device, &display); + if (status) + goto BAIL; + + _cairo_xlib_remove_close_display_hook (display, + &font_private->close_display_hook); + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_info_t *glyphset_info; + + glyphset_info = &font_private->glyphset_info[i]; + + if (glyphset_info->pending_free_glyphs != NULL) + free (glyphset_info->pending_free_glyphs); + + if (glyphset_info->glyphset) { + status = _cairo_xlib_display_queue_resource (display, + XRenderFreeGlyphSet, + glyphset_info->glyphset); + (void) status; /* XXX cannot propagate failure */ + } + } + + cairo_device_release (&display->base); +BAIL: + cairo_device_destroy (&display->base); + free (font_private); + } +} + +static void +_cairo_xlib_render_free_glyphs (Display *dpy, + cairo_xlib_font_glyphset_free_glyphs_t *to_free) +{ + XRenderFreeGlyphs (dpy, + to_free->glyphset, + to_free->glyph_indices, + to_free->glyph_count); +} + +static cairo_xlib_font_glyphset_info_t * +_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) +{ + return scaled_glyph->surface_private; +} + +static void +_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, + cairo_xlib_font_glyphset_info_t *glyphset_info) +{ + scaled_glyph->surface_private = glyphset_info; +} + +static void +_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private; + cairo_xlib_font_glyphset_info_t *glyphset_info; + + if (scaled_font->finished) + return; + + font_private = scaled_font->surface_private; + glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); + if (font_private != NULL && glyphset_info != NULL) { + cairo_xlib_font_glyphset_free_glyphs_t *to_free; + cairo_status_t status; + + to_free = glyphset_info->pending_free_glyphs; + if (to_free != NULL && + to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) + { + cairo_xlib_display_t *display; + + status = _cairo_xlib_display_acquire (font_private->device, &display); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_xlib_display_queue_work (display, + (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, + to_free, + free); + cairo_device_release (&display->base); + } + /* XXX cannot propagate failure */ + if (unlikely (status)) + free (to_free); + + to_free = glyphset_info->pending_free_glyphs = NULL; + } + + if (to_free == NULL) { + to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); + if (unlikely (to_free == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return; /* XXX cannot propagate failure */ + } + + to_free->glyphset = glyphset_info->glyphset; + to_free->glyph_count = 0; + glyphset_info->pending_free_glyphs = to_free; + } + + to_free->glyph_indices[to_free->glyph_count++] = + _cairo_scaled_glyph_index (scaled_glyph); + } +} + +static cairo_bool_t +_native_byte_order_lsb (void) +{ + int x = 1; + + return *((char *) &x) == 1; +} + +static int +_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) +{ + if (format == CAIRO_FORMAT_A8) + return GLYPHSET_INDEX_A8; + if (format == CAIRO_FORMAT_A1) + return GLYPHSET_INDEX_A1; + + assert (format == CAIRO_FORMAT_ARGB32); + return GLYPHSET_INDEX_ARGB32; +} + +static cairo_xlib_font_glyphset_info_t * +_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, + cairo_format_t format) +{ + cairo_xlib_surface_font_private_t *font_private; + cairo_xlib_font_glyphset_info_t *glyphset_info; + int glyphset_index; + + glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); + font_private = scaled_font->surface_private; + glyphset_info = &font_private->glyphset_info[glyphset_index]; + if (glyphset_info->glyphset == None) { + cairo_xlib_display_t *display; + + if (_cairo_xlib_display_acquire (font_private->device, &display)) + return NULL; + + glyphset_info->xrender_format = + _cairo_xlib_display_get_xrender_format (display, + glyphset_info->format); + glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, + glyphset_info->xrender_format); + + cairo_device_release (&display->base); + } + + return glyphset_info; +} + +static cairo_bool_t +_cairo_xlib_glyphset_info_has_pending_free_glyph ( + cairo_xlib_font_glyphset_info_t *glyphset_info, + unsigned long glyph_index) +{ + if (glyphset_info->pending_free_glyphs != NULL) { + cairo_xlib_font_glyphset_free_glyphs_t *to_free; + int i; + + to_free = glyphset_info->pending_free_glyphs; + for (i = 0; i < to_free->glyph_count; i++) { + if (to_free->glyph_indices[i] == glyph_index) { + to_free->glyph_count--; + memmove (&to_free->glyph_indices[i], + &to_free->glyph_indices[i+1], + (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); + return TRUE; + } + } + } + + return FALSE; +} + +static cairo_xlib_font_glyphset_info_t * +_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph ( + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_image_surface_t *surface) +{ + cairo_xlib_surface_font_private_t *font_private; + int i; + + font_private = scaled_font->surface_private; + if (font_private == NULL) + return NULL; + + if (surface != NULL) { + i = _cairo_xlib_get_glyphset_index_for_format (surface->format); + if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( + &font_private->glyphset_info[i], + glyph_index)) + { + return &font_private->glyphset_info[i]; + } + } else { + for (i = 0; i < NUM_GLYPHSETS; i++) { + if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( + &font_private->glyphset_info[i], + glyph_index)) + { + return &font_private->glyphset_info[i]; + } + } + } + + return NULL; +} + +static cairo_status_t +_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **pscaled_glyph) +{ + XGlyphInfo glyph_info; + unsigned long glyph_index; + unsigned char *data; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_bool_t already_had_glyph_surface; + cairo_xlib_font_glyphset_info_t *glyphset_info; + + glyph_index = _cairo_scaled_glyph_index (scaled_glyph); + + /* check to see if we have a pending XRenderFreeGlyph for this glyph */ + glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); + if (glyphset_info != NULL) { + _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + return CAIRO_STATUS_SUCCESS; + } + + if (!glyph_surface) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + pscaled_glyph); + if (unlikely (status)) + return status; + + scaled_glyph = *pscaled_glyph; + glyph_surface = scaled_glyph->surface; + already_had_glyph_surface = FALSE; + } else { + already_had_glyph_surface = TRUE; + } + + if (scaled_font->surface_private == NULL) { + status = _cairo_xlib_surface_font_init (display, scaled_font); + if (unlikely (status)) + return status; + } + + glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, + glyph_surface->format); + + /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */ + { + int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height; + int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) + : XMaxRequestSize (display->display)) * 4 - + sz_xRenderAddGlyphsReq - + sz_xGlyphInfo - + 8; + if (len >= max_request_size) + return UNSUPPORTED ("glyph too large for XRequest"); + } + + /* If the glyph surface has zero height or width, we create + * a clear 1x1 surface, to avoid various X server bugs. + */ + if (glyph_surface->width == 0 || glyph_surface->height == 0) { + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + } + + /* If the glyph format does not match the font format, then we + * create a temporary surface for the glyph image with the font's + * format. + */ + if (glyph_surface->format != glyphset_info->format) { + cairo_surface_pattern_t pattern; + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (glyphset_info->format, + glyph_surface->width, + glyph_surface->height); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); + status = _cairo_surface_paint (tmp_surface, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + + if (unlikely (status)) + goto BAIL; + } + + /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ + glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); + glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); + glyph_info.width = glyph_surface->width; + glyph_info.height = glyph_surface->height; + glyph_info.xOff = scaled_glyph->x_advance; + glyph_info.yOff = scaled_glyph->y_advance; + + data = glyph_surface->data; + + /* flip formats around */ + switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) { + case GLYPHSET_INDEX_A1: + /* local bitmaps are always stored with bit == byte */ + if (_native_byte_order_lsb() != (BitmapBitOrder (display->display) == LSBFirst)) { + int c = glyph_surface->stride * glyph_surface->height; + unsigned char *d; + unsigned char *new, *n; + + new = malloc (c); + if (!new) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + n = new; + d = data; + do { + char b = *d++; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *n++ = b; + } while (--c); + data = new; + } + break; + case GLYPHSET_INDEX_A8: + break; + case GLYPHSET_INDEX_ARGB32: + if (_native_byte_order_lsb() != (ImageByteOrder (display->display) == LSBFirst)) { + unsigned int c = glyph_surface->stride * glyph_surface->height / 4; + const uint32_t *d; + uint32_t *new, *n; + + new = malloc (4 * c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + n = new; + d = (uint32_t *) data; + do { + *n++ = bswap_32 (*d); + d++; + } while (--c); + data = (uint8_t *) new; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } + /* XXX assume X server wants pixman padding. Xft assumes this as well */ + + struct _XDisplay *dpy = (struct _XDisplay *) display->display; + int req_length = sz_xRenderAddGlyphsReq + 4; + if (req_length & 3) + req_length += 4 - (req_length & 3); + if (dpy->bufptr + req_length > dpy->bufmax) + XFlush (display->display); + + XRenderAddGlyphs (display->display, glyphset_info->glyphset, + &glyph_index, &glyph_info, 1, + (char *) data, + glyph_surface->stride * glyph_surface->height); + + if (data != glyph_surface->data) + free (data); + + _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + + BAIL: + if (glyph_surface != scaled_glyph->surface) + cairo_surface_destroy (&glyph_surface->base); + + /* if the scaled glyph didn't already have a surface attached + * to it, release the created surface now that we have it + * uploaded to the X server. If the surface has already been + * there (eg. because image backend requested it), leave it in + * the cache + */ + if (!already_had_glyph_surface) + _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); + + return status; +} + +typedef void (*cairo_xrender_composite_text_func_t) + (Display *dpy, + int op, + Picture src, + Picture dst, + _Xconst XRenderPictFormat *maskFormat, + int xSrc, + int ySrc, + int xDst, + int yDst, + _Xconst XGlyphElt8 *elts, + int nelt); + +/* Build a struct of the same size of #cairo_glyph_t that can be used both as + * an input glyph with double coordinates, and as "working" glyph with + * integer from-current-point offsets. */ +typedef union { + cairo_glyph_t d; + unsigned long index; + struct { + unsigned long index; + int x; + int y; + } i; +} cairo_xlib_glyph_t; + +/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ +COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); + +/* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs + * (Xrender limits each element to 252 glyphs, we limit them to 128) + * + * These same conditions need to be mirrored between + * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks + */ +#define _start_new_glyph_elt(count, glyph) \ + (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) + +static cairo_status_t +_emit_glyphs_chunk (cairo_xlib_display_t *display, + cairo_xlib_surface_t *dst, + cairo_xlib_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + cairo_xlib_surface_t *src, + cairo_surface_attributes_t *attributes, + /* info for this chunk */ + int num_elts, + int width, + cairo_xlib_font_glyphset_info_t *glyphset_info) +{ + /* Which XRenderCompositeText function to use */ + cairo_xrender_composite_text_func_t composite_text_func; + int size; + + /* Element buffer stuff */ + XGlyphElt8 *elts; + XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; + + /* Reuse the input glyph array for output char generation */ + char *char8 = (char *) glyphs; + unsigned short *char16 = (unsigned short *) glyphs; + unsigned int *char32 = (unsigned int *) glyphs; + + int i; + int nelt; /* Element index */ + int n; /* Num output glyphs in current element */ + int j; /* Num output glyphs so far */ + + switch (width) { + case 1: + /* don't cast the 8-variant, to catch possible mismatches */ + composite_text_func = XRenderCompositeText8; + size = sizeof (char); + break; + case 2: + composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; + size = sizeof (unsigned short); + break; + default: + case 4: + composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; + size = sizeof (unsigned int); + } + + /* Allocate element array */ + if (num_elts <= ARRAY_LENGTH (stack_elts)) { + elts = stack_elts; + } else { + elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); + if (unlikely (elts == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Fill them in */ + nelt = 0; + n = 0; + j = 0; + for (i = 0; i < num_glyphs; i++) { + + /* Start a new element for first output glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() + */ + if (_start_new_glyph_elt (j, &glyphs[i])) { + if (j) { + elts[nelt].nchars = n; + nelt++; + n = 0; + } + elts[nelt].chars = char8 + size * j; + elts[nelt].glyphset = glyphset_info->glyphset; + elts[nelt].xOff = glyphs[i].i.x; + elts[nelt].yOff = glyphs[i].i.y; + } + + switch (width) { + case 1: char8 [j] = (char) glyphs[i].index; break; + case 2: char16[j] = (unsigned short) glyphs[i].index; break; + default: + case 4: char32[j] = (unsigned int) glyphs[i].index; break; + } + + n++; + j++; + } + + if (n) { + elts[nelt].nchars = n; + nelt++; + } + + /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the + * expected number of xGlyphElts. */ + assert (nelt == num_elts); + + composite_text_func (display->display, + _render_operator (op), + src->src_picture, + dst->dst_picture, + glyphset_info->xrender_format, + attributes->x_offset + elts[0].xOff, + attributes->y_offset + elts[0].yOff, + elts[0].xOff, elts[0].yOff, + (XGlyphElt8 *) elts, nelt); + + if (elts != stack_elts) + free (elts); + + return CAIRO_STATUS_SUCCESS; +} + + +/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have + * enough room for padding */ +#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) + +static cairo_status_t +_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display, + cairo_xlib_surface_t *dst, + cairo_xlib_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + cairo_xlib_surface_t *src, + cairo_surface_attributes_t *attributes, + int *remaining_glyphs) +{ + int i; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + cairo_fixed_t x = 0, y = 0; + cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; + + unsigned long max_index = 0; + int width = 1; + int num_elts = 0; + int num_out_glyphs = 0; + + int max_request_size = XMaxRequestSize (display->display) * 4 + - MAX (sz_xRenderCompositeGlyphs8Req, + MAX(sz_xRenderCompositeGlyphs16Req, + sz_xRenderCompositeGlyphs32Req)); + int request_size = 0; + + _cairo_xlib_surface_ensure_dst_picture (display, dst); + + for (i = 0; i < num_glyphs; i++) { + int this_x, this_y; + int old_width; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + this_x = _cairo_lround (glyphs[i].d.x); + this_y = _cairo_lround (glyphs[i].d.y); + + /* Glyph skipping: + * + * We skip any glyphs that have troublesome coordinates. We want + * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in + * a signed 16bit integer, otherwise it will overflow in the render + * protocol. + * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in + * a signed 15bit integer. The trivial option would be to allow + * coordinates -8192..8192, but that's kinda dull. It probably will + * take a decade or so to get monitors 8192x4096 or something. A + * negative value of -8192 on the other hand, is absolutely useless. + * Note that we do want to allow some negative positions. The glyph + * may start off the screen but part of it make it to the screen. + * Anyway, we will allow positions in the range -4096..122887. That + * will buy us a few more years before this stops working. + * + * Update: upon seeing weird glyphs, we just return and let fallback + * code do the job. + */ + if (((this_x+4096)|(this_y+4096))&~0x3fffu) + break; + + /* Send unsent glyphs to the server */ + if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { + status = _cairo_xlib_surface_add_glyph (display, + scaled_font, + &scaled_glyph); + if (unlikely (status)) { + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + /* Break so we flush glyphs so far and let fallback code + * handle the rest */ + break; + + return status; + } + } + + this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); + if (!glyphset_info) + glyphset_info = this_glyphset_info; + + /* The invariant here is that we can always flush the glyphs + * accumulated before this one, using old_width, and they + * would fit in the request. + */ + old_width = width; + + /* Update max glyph index */ + if (glyphs[i].index > max_index) { + max_index = glyphs[i].index; + if (max_index >= 65536) + width = 4; + else if (max_index >= 256) + width = 2; + if (width != old_width) + request_size += (width - old_width) * num_out_glyphs; + } + + /* If we will pass the max request size by adding this glyph, + * flush current glyphs. Note that we account for a + * possible element being added below. + * + * Also flush if changing glyphsets, as Xrender limits one mask + * format per request, so we can either break up, or use a + * wide-enough mask format. We do the former. One reason to + * prefer the latter is the fact that Xserver ADDs all glyphs + * to the mask first, and then composes that to final surface, + * though it's not a big deal. + */ + if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || + (this_glyphset_info != glyphset_info)) { + status = _emit_glyphs_chunk (display, dst, glyphs, i, + scaled_font, op, src, attributes, + num_elts, old_width, glyphset_info); + if (unlikely (status)) + return status; + + glyphs += i; + num_glyphs -= i; + i = 0; + max_index = glyphs[i].index; + width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; + request_size = 0; + num_elts = 0; + num_out_glyphs = 0; + x = y = 0; + glyphset_info = this_glyphset_info; + } + + /* Convert absolute glyph position to relative-to-current-point + * position */ + glyphs[i].i.x = this_x - x; + glyphs[i].i.y = this_y - y; + + /* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _emit_glyphs_chunk(). + */ + if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { + num_elts++; + request_size += _cairo_sz_xGlyphElt; + } + + /* adjust current-position */ + x = this_x + scaled_glyph->x_advance; + y = this_y + scaled_glyph->y_advance; + + num_out_glyphs++; + request_size += width; + } + + if (num_elts) { + status = _emit_glyphs_chunk (display, dst, glyphs, i, + scaled_font, op, src, attributes, + num_elts, width, glyphset_info); + } + + *remaining_glyphs = num_glyphs - i; + if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + return status; +} + +static cairo_bool_t +_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if ((scaled_font->surface_backend != NULL && + scaled_font->surface_backend != &cairo_xlib_surface_backend) || + (font_private != NULL && font_private->device != dst->base.device)) + { + return FALSE; + } + + return TRUE; +} + +/* Gets a grayscale version of scaled_font. The grayscale version is cached + * in our surface_private data. + */ +static cairo_scaled_font_t * +_cairo_xlib_get_grayscale_font (cairo_xlib_display_t *display, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + cairo_bool_t needs_font; + + if (font_private == NULL) { + cairo_status_t status = _cairo_xlib_surface_font_init (display, scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + font_private = scaled_font->surface_private; + } + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + needs_font = !font_private->grayscale_font; + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (needs_font) { + cairo_font_options_t options; + cairo_scaled_font_t *new_font; + + options = scaled_font->options; + options.antialias = CAIRO_ANTIALIAS_GRAY; + new_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, &options); + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + if (!font_private->grayscale_font) { + font_private->grayscale_font = new_font; + new_font = NULL; + } + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (new_font) { + cairo_scaled_font_destroy (new_font); + } + } + + return font_private->grayscale_font; +} + +static cairo_int_status_t +_cairo_xlib_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; + composite_operation_t operation; + cairo_surface_attributes_t attributes; + cairo_xlib_surface_t *src = NULL; + cairo_region_t *clip_region = NULL; + cairo_xlib_display_t *display; + + if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) + return UNSUPPORTED ("XRender does not support CompositeText"); + + /* Just let unbounded operators go through the fallback code + * instead of trying to do the fixups here */ + if (! _cairo_operator_bounded_by_mask (op)) + return UNSUPPORTED ("unsupported unbounded op"); + + /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- + * the solid source seems to be multiplied by the glyph mask, and + * then the entire thing is copied to the destination surface, + * including the fully transparent "background" of the rectangular + * glyph surface. */ + if (op == CAIRO_OPERATOR_SOURCE && + ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) + { + return UNSUPPORTED ("known bug in Render"); + } + + /* We can only use our code if we either have no clip or + * have a real native clip region set. If we're using + * fallback clip masking, we have to go through the full + * fallback path. + */ + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); + if (status) + return status; + } + + operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); + if (operation == DO_UNSUPPORTED) + return UNSUPPORTED ("unsupported op"); + + if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) + return UNSUPPORTED ("unowned font"); + + + status = _cairo_xlib_display_acquire (dst->base.device, &display); + if (unlikely (status)) + return status; + + if (!dst->base.permit_subpixel_antialiasing && + scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + scaled_font = _cairo_xlib_get_grayscale_font (display, scaled_font); + } + + X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); + + if (clip_region != NULL && + cairo_region_num_rectangles (clip_region) == 1) + { + cairo_rectangle_int_t glyph_extents; + cairo_rectangle_int_t clip_extents; + + /* Can we do without the clip? + * Around 50% of the time the clip is redundant (firefox). + */ + _cairo_scaled_font_glyph_approximate_extents (scaled_font, + glyphs, num_glyphs, + &glyph_extents); + + cairo_region_get_extents(clip_region, &clip_extents); + if (clip_extents.x <= glyph_extents.x && + clip_extents.y <= glyph_extents.y && + clip_extents.x + clip_extents.width >= glyph_extents.x + glyph_extents.width && + clip_extents.y + clip_extents.height >= glyph_extents.y + glyph_extents.height) + { + clip_region = NULL; + } + } + + status = _cairo_xlib_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + goto BAIL0; + + /* After passing all those tests, we're now committed to rendering + * these glyphs or to fail trying. We first upload any glyphs to + * the X server that it doesn't have already, then we draw + * them. + */ + + /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore + * the mask (the glyphs). This code below was executed as a side effect + * of going through the _clip_and_composite fallback code for old_show_glyphs, + * so PictOpClear was never used with CompositeText before. + */ + if (op == CAIRO_OPERATOR_CLEAR) { + src_pattern = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + 0, 0, 1, 1, + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) &src, + &attributes); + if (unlikely (status)) + goto BAIL0; + } else { + cairo_rectangle_int_t glyph_extents; + + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + goto BAIL0; + + if (clip != NULL) { + if (! _cairo_rectangle_intersect (&glyph_extents, + _cairo_clip_get_extents (clip))) + { + goto BAIL0; + } + } + + status = _cairo_xlib_surface_acquire_pattern_surface (display, + dst, src_pattern, + glyph_extents.x, + glyph_extents.y, + glyph_extents.width, + glyph_extents.height, + &src, &attributes); + if (unlikely (status)) + goto BAIL0; + } + + operation = _recategorize_composite_operation (dst, op, src, + &attributes, TRUE); + if (operation == DO_UNSUPPORTED) { + status = UNSUPPORTED ("unsupported op"); + goto BAIL1; + } + + status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0); + if (unlikely (status)) + goto BAIL1; + + _cairo_scaled_font_freeze_cache (scaled_font); + if (_cairo_xlib_surface_owns_font (dst, scaled_font)) { + status = _cairo_xlib_surface_emit_glyphs (display, + dst, + (cairo_xlib_glyph_t *) glyphs, + num_glyphs, + scaled_font, + op, + src, + &attributes, + remaining_glyphs); + } else { + status = UNSUPPORTED ("unowned font"); + } + _cairo_scaled_font_thaw_cache (scaled_font); + + BAIL1: + if (src) + _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); + BAIL0: + cairo_device_release (&display->base); + + return status; +} diff --git a/libs/cairo/src/cairo-xlib-visual.c b/libs/cairo/src/cairo-xlib-visual.c new file mode 100644 index 000000000..f70db3a77 --- /dev/null +++ b/libs/cairo/src/cairo-xlib-visual.c @@ -0,0 +1,156 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" + +#include "cairo-xlib-private.h" + +#include "cairo-error-private.h" + +/* A perceptual distance metric between two colors. No sqrt needed + * since the square of the distance is still a valid metric. */ + +/* XXX: This is currently using linear distance in RGB space which is + * decidedly not perceptually linear. If someone cared a lot about the + * quality, they might choose something else here. Then again, they + * might also choose not to use a PseudoColor visual... */ +static inline int +_color_distance (unsigned short r1, unsigned short g1, unsigned short b1, + unsigned short r2, unsigned short g2, unsigned short b2) +{ + r1 >>= 8; g1 >>= 8; b1 >>= 8; + r2 >>= 8; g2 >>= 8; b2 >>= 8; + + return ((r2 - r1) * (r2 - r1) + + (g2 - g1) * (g2 - g1) + + (b2 - b1) * (b2 - b1)); +} + +cairo_status_t +_cairo_xlib_visual_info_create (Display *dpy, + int screen, + VisualID visualid, + cairo_xlib_visual_info_t **out) +{ + cairo_xlib_visual_info_t *info; + Colormap colormap = DefaultColormap (dpy, screen); + XColor color; + int gray, red, green, blue; + int i, j, distance, min_distance = 0; + XColor colors[256]; + unsigned short cube_index_to_short[CUBE_SIZE]; + unsigned short ramp_index_to_short[RAMP_SIZE]; + unsigned char gray_to_pseudocolor[RAMP_SIZE]; + + for (i = 0; i < CUBE_SIZE; i++) + cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1); + for (i = 0; i < RAMP_SIZE; i++) + ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); + + info = malloc (sizeof (cairo_xlib_visual_info_t)); + if (unlikely (info == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + info->visualid = visualid; + + /* Allocate a gray ramp and a color cube. + * Give up as soon as failures start. */ + + for (gray = 0; gray < RAMP_SIZE; gray++) { + color.red = color.green = color.blue = ramp_index_to_short[gray]; + if (! XAllocColor (dpy, colormap, &color)) + goto DONE_ALLOCATE; + } + + /* XXX: Could do this in a more clever order to have the best + * possible results from early failure. Could also choose a cube + * uniformly distributed in a better space than RGB. */ + for (red = 0; red < CUBE_SIZE; red++) { + for (green = 0; green < CUBE_SIZE; green++) { + for (blue = 0; blue < CUBE_SIZE; blue++) { + color.red = cube_index_to_short[red]; + color.green = cube_index_to_short[green]; + color.blue = cube_index_to_short[blue]; + color.pixel = 0; + color.flags = 0; + color.pad = 0; + if (! XAllocColor (dpy, colormap, &color)) + goto DONE_ALLOCATE; + } + } + } + DONE_ALLOCATE: + + for (i = 0; i < ARRAY_LENGTH (colors); i++) + colors[i].pixel = i; + XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors)); + + /* Search for nearest colors within allocated colormap. */ + for (gray = 0; gray < RAMP_SIZE; gray++) { + for (i = 0; i < 256; i++) { + distance = _color_distance (ramp_index_to_short[gray], + ramp_index_to_short[gray], + ramp_index_to_short[gray], + colors[i].red, + colors[i].green, + colors[i].blue); + if (i == 0 || distance < min_distance) { + gray_to_pseudocolor[gray] = colors[i].pixel; + min_distance = distance; + if (!min_distance) + break; + } + } + } + for (red = 0; red < CUBE_SIZE; red++) { + for (green = 0; green < CUBE_SIZE; green++) { + for (blue = 0; blue < CUBE_SIZE; blue++) { + for (i = 0; i < 256; i++) { + distance = _color_distance (cube_index_to_short[red], + cube_index_to_short[green], + cube_index_to_short[blue], + colors[i].red, + colors[i].green, + colors[i].blue); + if (i == 0 || distance < min_distance) { + info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel; + min_distance = distance; + if (!min_distance) + break; + } + } + } + } + } + + for (i = 0, j = 0; i < 256; i++) { + if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i))) + j++; + info->field8_to_cube[i] = j; + + info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1); + } + for (i = 0, j = 0; i < 256; i++) { + if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i))) + j++; + info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j]; + } + + for (i = 0; i < 256; i++) { + info->colors[i].a = 0xff; + info->colors[i].r = colors[i].red >> 8; + info->colors[i].g = colors[i].green >> 8; + info->colors[i].b = colors[i].blue >> 8; + } + + *out = info; + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info) +{ + /* No need for XFreeColors() whilst using DefaultColormap */ + free (info); +} diff --git a/libs/cairo/src/cairo-xlib-xrender-private.h b/libs/cairo/src/cairo-xlib-xrender-private.h new file mode 100644 index 000000000..6372787a0 --- /dev/null +++ b/libs/cairo/src/cairo-xlib-xrender-private.h @@ -0,0 +1,1138 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Part of this file is subject to a different license. See below. */ + +#ifndef CAIRO_XLIB_XRENDER_PRIVATE_H +#define CAIRO_XLIB_XRENDER_PRIVATE_H + +#include "cairo-features.h" +#include "cairo-compiler-private.h" + +#include + +/* These prototypes are used when defining interfaces missing from the + * render headers. As it happens, it is the case that all libxrender + * functions take a pointer as first argument. */ + +__attribute__((__unused__)) static void _void_consume (void *p, ...) { } +__attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } +__attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } +__attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } + + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE + +#include "cairo-xlib-xrender.h" + +#include +#include + +/* We require Render >= 0.6. The following defines were only added in + * 0.10. Make sure they are defined. + */ + +/* Filters included in 0.10 */ +#ifndef FilterConvolution +#define FilterConvolution "convolution" +#endif + +/* Extended repeat attributes included in 0.10 */ +#ifndef RepeatNone +#define RepeatNone 0 +#define RepeatNormal 1 +#define RepeatPad 2 +#define RepeatReflect 3 +#endif + + +#ifndef PictOptBlendMinimum +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e +#endif + +/* There doesn't appear to be a simple #define that we can conditionalize + * on. Instead, use the version; gradients were introdiced in 0.10. */ +#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 +#define XRenderCreateLinearGradient _int_consume +#define XRenderCreateRadialGradient _int_consume +#define XRenderCreateConicalGradient _int_consume +typedef struct _XCircle { + XFixed x; + XFixed y; + XFixed radius; +} XCircle; +typedef struct _XLinearGradient { + XPointFixed p1; + XPointFixed p2; +} XLinearGradient; + +typedef struct _XRadialGradient { + XCircle inner; + XCircle outer; +} XRadialGradient; + +typedef struct _XConicalGradient { + XPointFixed center; + XFixed angle; /* in degrees */ +} XConicalGradient; +#endif + + +#else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +/* Provide dummy symbols and macros to get it compile and take the fallback + * route, just like as if Xrender is not available in the server at run-time. */ + + +/* Functions */ + +#define XRenderQueryExtension _int_consume +#define XRenderQueryVersion _int_consume +#define XRenderQueryFormats _int_consume +#define XRenderQuerySubpixelOrder _int_consume +#define XRenderSetSubpixelOrder _int_consume +#define XRenderFindVisualFormat _voidp_consume +#define XRenderFindFormat _voidp_consume +#define XRenderFindStandardFormat _voidp_consume +#define XRenderQueryPictIndexValues _voidp_consume +#define XRenderCreatePicture _int_consume +#define XRenderChangePicture _void_consume +#define XRenderSetPictureClipRectangles _void_consume +#define XRenderSetPictureClipRegion _void_consume +#define XRenderSetPictureTransform _void_consume +#define XRenderFreePicture _void_consume_free +#define XRenderComposite _void_consume +#define XRenderCreateGlyphSet _int_consume +#define XRenderReferenceGlyphSet _int_consume +#define XRenderFreeGlyphSet _void_consume_free +#define XRenderAddGlyphs _void_consume +#define XRenderFreeGlyphs _void_consume +#define XRenderCompositeString8 _void_consume +#define XRenderCompositeString16 _void_consume +#define XRenderCompositeString32 _void_consume +#define XRenderCompositeText8 (cairo_xrender_composite_text_func_t) _void_consume +#define XRenderCompositeText16 _void_consume +#define XRenderCompositeText32 _void_consume +#define XRenderFillRectangle _void_consume +#define XRenderFillRectangles _void_consume +#define XRenderCompositeTrapezoids _void_consume +#define XRenderCompositeTriangles _void_consume +#define XRenderCompositeTriStrip _void_consume +#define XRenderCompositeTriFan _void_consume +#define XRenderCompositeDoublePoly _void_consume +#define XRenderParseColor _int_consume +#define XRenderCreateCursor _int_consume +#define XRenderQueryFilters _voidp_consume +#define XRenderSetPictureFilter _void_consume +#define XRenderCreateAnimCursor _int_consume +#define XRenderAddTraps _void_consume +#define XRenderCreateSolidFill _int_consume +#define XRenderCreateLinearGradient _int_consume +#define XRenderCreateRadialGradient _int_consume +#define XRenderCreateConicalGradient _int_consume + +#define cairo_xlib_surface_create_with_xrender_format _voidp_consume + + + +/* The rest of this file is copied from various Xrender header files, with + * the following copyright/license information: + * + * Copyright (C) 2000 SuSE, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Keith Packard, SuSE, Inc. + */ + + +/* Copied from X11/extensions/render.h */ + +typedef unsigned long Glyph; +typedef unsigned long GlyphSet; +typedef unsigned long Picture; +typedef unsigned long PictFormat; + +#define BadPictFormat 0 +#define BadPicture 1 +#define BadPictOp 2 +#define BadGlyphSet 3 +#define BadGlyph 4 +#define RenderNumberErrors (BadGlyph+1) + +#define PictTypeIndexed 0 +#define PictTypeDirect 1 + +#define PictOpMinimum 0 +#define PictOpClear 0 +#define PictOpSrc 1 +#define PictOpDst 2 +#define PictOpOver 3 +#define PictOpOverReverse 4 +#define PictOpIn 5 +#define PictOpInReverse 6 +#define PictOpOut 7 +#define PictOpOutReverse 8 +#define PictOpAtop 9 +#define PictOpAtopReverse 10 +#define PictOpXor 11 +#define PictOpAdd 12 +#define PictOpSaturate 13 +#define PictOpMaximum 13 + +/* + * Operators only available in version 0.2 + */ +#define PictOpDisjointMinimum 0x10 +#define PictOpDisjointClear 0x10 +#define PictOpDisjointSrc 0x11 +#define PictOpDisjointDst 0x12 +#define PictOpDisjointOver 0x13 +#define PictOpDisjointOverReverse 0x14 +#define PictOpDisjointIn 0x15 +#define PictOpDisjointInReverse 0x16 +#define PictOpDisjointOut 0x17 +#define PictOpDisjointOutReverse 0x18 +#define PictOpDisjointAtop 0x19 +#define PictOpDisjointAtopReverse 0x1a +#define PictOpDisjointXor 0x1b +#define PictOpDisjointMaximum 0x1b + +#define PictOpConjointMinimum 0x20 +#define PictOpConjointClear 0x20 +#define PictOpConjointSrc 0x21 +#define PictOpConjointDst 0x22 +#define PictOpConjointOver 0x23 +#define PictOpConjointOverReverse 0x24 +#define PictOpConjointIn 0x25 +#define PictOpConjointInReverse 0x26 +#define PictOpConjointOut 0x27 +#define PictOpConjointOutReverse 0x28 +#define PictOpConjointAtop 0x29 +#define PictOpConjointAtopReverse 0x2a +#define PictOpConjointXor 0x2b +#define PictOpConjointMaximum 0x2b + +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e + +#define PolyEdgeSharp 0 +#define PolyEdgeSmooth 1 + +#define PolyModePrecise 0 +#define PolyModeImprecise 1 + +#define CPRepeat (1 << 0) +#define CPAlphaMap (1 << 1) +#define CPAlphaXOrigin (1 << 2) +#define CPAlphaYOrigin (1 << 3) +#define CPClipXOrigin (1 << 4) +#define CPClipYOrigin (1 << 5) +#define CPClipMask (1 << 6) +#define CPGraphicsExposure (1 << 7) +#define CPSubwindowMode (1 << 8) +#define CPPolyEdge (1 << 9) +#define CPPolyMode (1 << 10) +#define CPDither (1 << 11) +#define CPComponentAlpha (1 << 12) +#define CPLastBit 12 + +/* Filters included in 0.6 */ +#define FilterNearest "nearest" +#define FilterBilinear "bilinear" +/* Filters included in 0.10 */ +#define FilterConvolution "convolution" + +#define FilterFast "fast" +#define FilterGood "good" +#define FilterBest "best" + +#define FilterAliasNone -1 + +/* Subpixel orders included in 0.6 */ +#define SubPixelUnknown 0 +#define SubPixelHorizontalRGB 1 +#define SubPixelHorizontalBGR 2 +#define SubPixelVerticalRGB 3 +#define SubPixelVerticalBGR 4 +#define SubPixelNone 5 + +/* Extended repeat attributes included in 0.10 */ +#define RepeatNone 0 +#define RepeatNormal 1 +#define RepeatPad 2 +#define RepeatReflect 3 + + + +/* Copied from X11/extensions/Xrender.h */ + +typedef struct { + short red; + short redMask; + short green; + short greenMask; + short blue; + short blueMask; + short alpha; + short alphaMask; +} XRenderDirectFormat; + +typedef struct { + PictFormat id; + int type; + int depth; + XRenderDirectFormat direct; + Colormap colormap; +} XRenderPictFormat; + +#define PictFormatID (1 << 0) +#define PictFormatType (1 << 1) +#define PictFormatDepth (1 << 2) +#define PictFormatRed (1 << 3) +#define PictFormatRedMask (1 << 4) +#define PictFormatGreen (1 << 5) +#define PictFormatGreenMask (1 << 6) +#define PictFormatBlue (1 << 7) +#define PictFormatBlueMask (1 << 8) +#define PictFormatAlpha (1 << 9) +#define PictFormatAlphaMask (1 << 10) +#define PictFormatColormap (1 << 11) + +typedef struct _XRenderPictureAttributes { + int repeat; + Picture alpha_map; + int alpha_x_origin; + int alpha_y_origin; + int clip_x_origin; + int clip_y_origin; + Pixmap clip_mask; + Bool graphics_exposures; + int subwindow_mode; + int poly_edge; + int poly_mode; + Atom dither; + Bool component_alpha; +} XRenderPictureAttributes; + +typedef struct { + unsigned short red; + unsigned short green; + unsigned short blue; + unsigned short alpha; +} XRenderColor; + +typedef struct _XGlyphInfo { + unsigned short width; + unsigned short height; + short x; + short y; + short xOff; + short yOff; +} XGlyphInfo; + +typedef struct _XGlyphElt8 { + GlyphSet glyphset; + _Xconst char *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt8; + +typedef struct _XGlyphElt16 { + GlyphSet glyphset; + _Xconst unsigned short *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt16; + +typedef struct _XGlyphElt32 { + GlyphSet glyphset; + _Xconst unsigned int *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt32; + +typedef double XDouble; + +typedef struct _XPointDouble { + XDouble x, y; +} XPointDouble; + +#define XDoubleToFixed(f) ((XFixed) ((f) * 65536)) +#define XFixedToDouble(f) (((XDouble) (f)) / 65536) + +typedef int XFixed; + +typedef struct _XPointFixed { + XFixed x, y; +} XPointFixed; + +typedef struct _XLineFixed { + XPointFixed p1, p2; +} XLineFixed; + +typedef struct _XTriangle { + XPointFixed p1, p2, p3; +} XTriangle; + +typedef struct _XCircle { + XFixed x; + XFixed y; + XFixed radius; +} XCircle; + +typedef struct _XTrapezoid { + XFixed top, bottom; + XLineFixed left, right; +} XTrapezoid; + +typedef struct _XTransform { + XFixed matrix[3][3]; +} XTransform; + +typedef struct _XFilters { + int nfilter; + char **filter; + int nalias; + short *alias; +} XFilters; + +typedef struct _XIndexValue { + unsigned long pixel; + unsigned short red, green, blue, alpha; +} XIndexValue; + +typedef struct _XAnimCursor { + Cursor cursor; + unsigned long delay; +} XAnimCursor; + +typedef struct _XSpanFix { + XFixed left, right, y; +} XSpanFix; + +typedef struct _XTrap { + XSpanFix top, bottom; +} XTrap; + +typedef struct _XLinearGradient { + XPointFixed p1; + XPointFixed p2; +} XLinearGradient; + +typedef struct _XRadialGradient { + XCircle inner; + XCircle outer; +} XRadialGradient; + +typedef struct _XConicalGradient { + XPointFixed center; + XFixed angle; /* in degrees */ +} XConicalGradient; + +#define PictStandardARGB32 0 +#define PictStandardRGB24 1 +#define PictStandardA8 2 +#define PictStandardA4 3 +#define PictStandardA1 4 +#define PictStandardNUM 5 + + + +/* Copied from X11/extensions/renderproto.h */ + +#include + +#define Window CARD32 +#define Drawable CARD32 +#define Font CARD32 +#define Pixmap CARD32 +#define Cursor CARD32 +#define Colormap CARD32 +#define GContext CARD32 +#define Atom CARD32 +#define VisualID CARD32 +#define Time CARD32 +#define KeyCode CARD8 +#define KeySym CARD32 + +#define Picture CARD32 +#define PictFormat CARD32 +#define Fixed INT32 +#define Glyphset CARD32 +#define Glyph CARD32 + +/* + * data structures + */ + +typedef struct { + CARD16 red B16; + CARD16 redMask B16; + CARD16 green B16; + CARD16 greenMask B16; + CARD16 blue B16; + CARD16 blueMask B16; + CARD16 alpha B16; + CARD16 alphaMask B16; +} xDirectFormat; + +#define sz_xDirectFormat 16 + +typedef struct { + PictFormat id B32; + CARD8 type; + CARD8 depth; + CARD16 pad1 B16; + xDirectFormat direct; + Colormap colormap; +} xPictFormInfo; + +#define sz_xPictFormInfo 28 + +typedef struct { + VisualID visual; + PictFormat format; +} xPictVisual; + +#define sz_xPictVisual 8 + +typedef struct { + CARD8 depth; + CARD8 pad1; + CARD16 nPictVisuals B16; + CARD32 pad2 B32; +} xPictDepth; + +#define sz_xPictDepth 8 + +typedef struct { + CARD32 nDepth B32; + PictFormat fallback B32; +} xPictScreen; + +#define sz_xPictScreen 8 + +typedef struct { + CARD32 pixel B32; + CARD16 red B16; + CARD16 green B16; + CARD16 blue B16; + CARD16 alpha B16; +} xIndexValue; + +#define sz_xIndexValue 12 + +typedef struct { + CARD16 red B16; + CARD16 green B16; + CARD16 blue B16; + CARD16 alpha B16; +} xRenderColor; + +#define sz_xRenderColor 8 + +typedef struct { + Fixed x B32; + Fixed y B32; +} xPointFixed; + +#define sz_xPointFixed 8 + +typedef struct { + xPointFixed p1; + xPointFixed p2; +} xLineFixed; + +#define sz_xLineFixed 16 + +typedef struct { + xPointFixed p1, p2, p3; +} xTriangle; + +#define sz_xTriangle 24 + +typedef struct { + Fixed top B32; + Fixed bottom B32; + xLineFixed left; + xLineFixed right; +} xTrapezoid; + +#define sz_xTrapezoid 40 + +typedef struct { + CARD16 width B16; + CARD16 height B16; + INT16 x B16; + INT16 y B16; + INT16 xOff B16; + INT16 yOff B16; +} xGlyphInfo; + +#define sz_xGlyphInfo 12 + +typedef struct { + CARD8 len; + CARD8 pad1; + CARD16 pad2; + INT16 deltax; + INT16 deltay; +} xGlyphElt; + +#define sz_xGlyphElt 8 + +typedef struct { + Fixed l, r, y; +} xSpanFix; + +#define sz_xSpanFix 12 + +typedef struct { + xSpanFix top, bot; +} xTrap; + +#define sz_xTrap 24 + +/* + * requests and replies + */ +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD32 majorVersion B32; + CARD32 minorVersion B32; +} xRenderQueryVersionReq; + +#define sz_xRenderQueryVersionReq 12 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 majorVersion B32; + CARD32 minorVersion B32; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xRenderQueryVersionReply; + +#define sz_xRenderQueryVersionReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; +} xRenderQueryPictFormatsReq; + +#define sz_xRenderQueryPictFormatsReq 4 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numFormats B32; + CARD32 numScreens B32; + CARD32 numDepths B32; + CARD32 numVisuals B32; + CARD32 numSubpixel B32; /* Version 0.6 */ + CARD32 pad5 B32; +} xRenderQueryPictFormatsReply; + +#define sz_xRenderQueryPictFormatsReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + PictFormat format B32; +} xRenderQueryPictIndexValuesReq; + +#define sz_xRenderQueryPictIndexValuesReq 8 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numIndexValues; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; +} xRenderQueryPictIndexValuesReply; + +#define sz_xRenderQueryPictIndexValuesReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + Drawable drawable B32; + PictFormat format B32; + CARD32 mask B32; +} xRenderCreatePictureReq; + +#define sz_xRenderCreatePictureReq 20 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + CARD32 mask B32; +} xRenderChangePictureReq; + +#define sz_xRenderChangePictureReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + INT16 xOrigin B16; + INT16 yOrigin B16; +} xRenderSetPictureClipRectanglesReq; + +#define sz_xRenderSetPictureClipRectanglesReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; +} xRenderFreePictureReq; + +#define sz_xRenderFreePictureReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture mask B32; + Picture dst B32; + INT16 xSrc B16; + INT16 ySrc B16; + INT16 xMask B16; + INT16 yMask B16; + INT16 xDst B16; + INT16 yDst B16; + CARD16 width B16; + CARD16 height B16; +} xRenderCompositeReq; + +#define sz_xRenderCompositeReq 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture src B32; + Picture dst B32; + CARD32 colorScale B32; + CARD32 alphaScale B32; + INT16 xSrc B16; + INT16 ySrc B16; + INT16 xDst B16; + INT16 yDst B16; + CARD16 width B16; + CARD16 height B16; +} xRenderScaleReq; + +#define sz_xRenderScaleReq 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTrapezoidsReq; + +#define sz_xRenderTrapezoidsReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTrianglesReq; + +#define sz_xRenderTrianglesReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTriStripReq; + +#define sz_xRenderTriStripReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTriFanReq; + +#define sz_xRenderTriFanReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset gsid B32; + PictFormat format B32; +} xRenderCreateGlyphSetReq; + +#define sz_xRenderCreateGlyphSetReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset gsid B32; + Glyphset existing B32; +} xRenderReferenceGlyphSetReq; + +#define sz_xRenderReferenceGlyphSetReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; +} xRenderFreeGlyphSetReq; + +#define sz_xRenderFreeGlyphSetReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; + CARD32 nglyphs; +} xRenderAddGlyphsReq; + +#define sz_xRenderAddGlyphsReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; +} xRenderFreeGlyphsReq; + +#define sz_xRenderFreeGlyphsReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + Glyphset glyphset B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderCompositeGlyphsReq, xRenderCompositeGlyphs8Req, +xRenderCompositeGlyphs16Req, xRenderCompositeGlyphs32Req; + +#define sz_xRenderCompositeGlyphs8Req 28 +#define sz_xRenderCompositeGlyphs16Req 28 +#define sz_xRenderCompositeGlyphs32Req 28 + +/* 0.1 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture dst B32; + xRenderColor color; +} xRenderFillRectanglesReq; + +#define sz_xRenderFillRectanglesReq 20 + +/* 0.5 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Cursor cid B32; + Picture src B32; + CARD16 x B16; + CARD16 y B16; +} xRenderCreateCursorReq; + +#define sz_xRenderCreateCursorReq 16 + +/* 0.6 and higher */ + +/* + * This can't use an array because 32-bit values may be in bitfields + */ +typedef struct { + Fixed matrix11 B32; + Fixed matrix12 B32; + Fixed matrix13 B32; + Fixed matrix21 B32; + Fixed matrix22 B32; + Fixed matrix23 B32; + Fixed matrix31 B32; + Fixed matrix32 B32; + Fixed matrix33 B32; +} xRenderTransform; + +#define sz_xRenderTransform 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + xRenderTransform transform; +} xRenderSetPictureTransformReq; + +#define sz_xRenderSetPictureTransformReq 44 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Drawable drawable B32; +} xRenderQueryFiltersReq; + +#define sz_xRenderQueryFiltersReq 8 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numAliases B32; /* LISTofCARD16 */ + CARD32 numFilters B32; /* LISTofSTRING8 */ + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xRenderQueryFiltersReply; + +#define sz_xRenderQueryFiltersReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + CARD16 nbytes B16; /* number of bytes in name */ + CARD16 pad B16; +} xRenderSetPictureFilterReq; + +#define sz_xRenderSetPictureFilterReq 12 + +/* 0.8 and higher */ + +typedef struct { + Cursor cursor B32; + CARD32 delay B32; +} xAnimCursorElt; + +#define sz_xAnimCursorElt 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Cursor cid B32; +} xRenderCreateAnimCursorReq; + +#define sz_xRenderCreateAnimCursorReq 8 + +/* 0.9 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture; + INT16 xOff B16; + INT16 yOff B16; +} xRenderAddTrapsReq; + +#define sz_xRenderAddTrapsReq 12 + +/* 0.10 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xRenderColor color; +} xRenderCreateSolidFillReq; + +#define sz_xRenderCreateSolidFillReq 16 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed p1; + xPointFixed p2; + CARD32 nStops; +} xRenderCreateLinearGradientReq; + +#define sz_xRenderCreateLinearGradientReq 28 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed inner; + xPointFixed outer; + Fixed inner_radius; + Fixed outer_radius; + CARD32 nStops; +} xRenderCreateRadialGradientReq; + +#define sz_xRenderCreateRadialGradientReq 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed center; + Fixed angle; /* in degrees */ + CARD32 nStops; +} xRenderCreateConicalGradientReq; + +#define sz_xRenderCreateConicalGradientReq 24 + +#undef Window +#undef Drawable +#undef Font +#undef Pixmap +#undef Cursor +#undef Colormap +#undef GContext +#undef Atom +#undef VisualID +#undef Time +#undef KeyCode +#undef KeySym + +#undef Picture +#undef PictFormat +#undef Fixed +#undef Glyphset +#undef Glyph + + +#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +#endif /* CAIRO_XLIB_XRENDER_PRIVATE_H */ diff --git a/libs/cairo/src/cairo-xlib-xrender.h b/libs/cairo/src/cairo-xlib-xrender.h new file mode 100644 index 000000000..996983e4e --- /dev/null +++ b/libs/cairo/src/cairo-xlib-xrender.h @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XLIB_XRENDER_H +#define CAIRO_XLIB_XRENDER_H + +#include "cairo.h" + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *screen, + XRenderPictFormat *format, + int width, + int height); + +cairo_public XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ +# error Cairo was not compiled with support for the xlib XRender backend +#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +#endif /* CAIRO_XLIB_XRENDER_H */ diff --git a/libs/cairo/src/cairo-xlib.h b/libs/cairo/src/cairo-xlib.h new file mode 100644 index 000000000..fdcff769c --- /dev/null +++ b/libs/cairo/src/cairo-xlib.h @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_XLIB_H +#define CAIRO_XLIB_H + +#include "cairo.h" + +#if CAIRO_HAS_XLIB_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *screen, + int width, + int height); + +cairo_public void +cairo_xlib_surface_set_size (cairo_surface_t *surface, + int width, + int height); + +cairo_public void +cairo_xlib_surface_set_drawable (cairo_surface_t *surface, + Drawable drawable, + int width, + int height); + +cairo_public Display * +cairo_xlib_surface_get_display (cairo_surface_t *surface); + +cairo_public Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *surface); + +cairo_public Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *surface); + +cairo_public Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *surface); + +cairo_public int +cairo_xlib_surface_get_depth (cairo_surface_t *surface); + +cairo_public int +cairo_xlib_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_xlib_surface_get_height (cairo_surface_t *surface); + +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/libs/cairo/src/cairo-xml-surface.c b/libs/cairo/src/cairo-xml-surface.c new file mode 100644 index 000000000..f1c3c3ed6 --- /dev/null +++ b/libs/cairo/src/cairo-xml-surface.c @@ -0,0 +1,1120 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This surface is intended to produce a verbose, hierarchical, DAG XML file + * representing a single surface. It is intended to be used by debuggers, + * such as cairo-sphinx, or by application test-suites that what a log of + * operations. + */ + +#include "cairoint.h" + +#include "cairo-xml.h" + +#include "cairo-clip-private.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-recording-surface-private.h" + +#define static cairo_warn static + +typedef struct _cairo_xml_surface cairo_xml_surface_t; + +typedef struct _cairo_xml { + cairo_device_t base; + + cairo_output_stream_t *stream; + int indent; +} cairo_xml_t; + +struct _cairo_xml_surface { + cairo_surface_t base; + + double width, height; +}; + +slim_hidden_proto (cairo_xml_for_recording_surface); + +static const cairo_surface_backend_t _cairo_xml_surface_backend; + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ + "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static const char * +_format_to_string (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: return "ARGB32"; + case CAIRO_FORMAT_RGB24: return "RGB24"; + case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; + case CAIRO_FORMAT_A8: return "A8"; + case CAIRO_FORMAT_A1: return "A1"; + case CAIRO_FORMAT_INVALID: return "INVALID"; + } + ASSERT_NOT_REACHED; + return "INVALID"; +} + +static cairo_status_t +_device_flush (void *abstract_device) +{ + cairo_xml_t *xml = abstract_device; + cairo_status_t status; + + status = _cairo_output_stream_flush (xml->stream); + + return status; +} + +static void +_device_destroy (void *abstract_device) +{ + cairo_xml_t *xml = abstract_device; + cairo_status_t status; + + status = _cairo_output_stream_destroy (xml->stream); + + free (xml); +} + +static const cairo_device_backend_t _cairo_xml_device_backend = { + CAIRO_DEVICE_TYPE_XML, + + NULL, NULL, /* lock, unlock */ + + _device_flush, + NULL, /* finish */ + _device_destroy +}; + +static cairo_device_t * +_cairo_xml_create_internal (cairo_output_stream_t *stream) +{ + cairo_xml_t *xml; + + xml = malloc (sizeof (cairo_xml_t)); + if (unlikely (xml == NULL)) + return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + memset (xml, 0, sizeof (cairo_xml_t)); + + _cairo_device_init (&xml->base, &_cairo_xml_device_backend); + + xml->indent = 0; + xml->stream = stream; + + return &xml->base; +} + +static void +_cairo_xml_indent (cairo_xml_t *xml, int indent) +{ + xml->indent += indent; + assert (xml->indent >= 0); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) +{ + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static cairo_surface_t * +_cairo_xml_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + + return cairo_recording_surface_create (content, &extents); +} + +static cairo_bool_t +_cairo_xml_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_xml_surface_t *surface = abstract_surface; + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static cairo_status_t +_cairo_xml_move_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f m", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_line_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f l", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_close_path (void *closure) +{ + _cairo_xml_printf_continue (closure, " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_path (cairo_xml_t *xml, + cairo_path_fixed_t *path) +{ + cairo_status_t status; + + _cairo_xml_printf_start (xml, ""); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_xml_move_to, + _cairo_xml_line_to, + _cairo_xml_curve_to, + _cairo_xml_close_path, + xml); + assert (status == CAIRO_STATUS_SUCCESS); + _cairo_xml_printf_end (xml, ""); +} + +static void +_cairo_xml_emit_string (cairo_xml_t *xml, + const char *node, + const char *data) +{ + _cairo_xml_printf (xml, "<%s>%s", node, data, node); +} + +static void +_cairo_xml_emit_double (cairo_xml_t *xml, + const char *node, + double data) +{ + _cairo_xml_printf (xml, "<%s>%f", node, data, node); +} + +static cairo_xml_t * +to_xml (cairo_xml_surface_t *surface) +{ + return (cairo_xml_t *) surface->base.device; +} + +static cairo_status_t +_cairo_xml_surface_emit_clip_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, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_path (xml, &clip_path->path); + _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); + _cairo_xml_emit_string (xml, "antialias", + _antialias_to_string (clip_path->antialias)); + _cairo_xml_emit_string (xml, "fill-rule", + _fill_rule_to_string (clip_path->fill_rule)); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, + 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, "%f %f %f %f", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_matrix (cairo_xml_t *xml, + const cairo_matrix_t *matrix) +{ + if (! _cairo_matrix_is_identity (matrix)) { + _cairo_xml_printf (xml, "%f %f %f %f %f %f", + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0); + } +} + +static void +_cairo_xml_emit_gradient (cairo_xml_t *xml, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int i; + + for (i = 0; i < gradient->n_stops; i++) { + _cairo_xml_printf (xml, + "%f %f %f %f %f", + gradient->stops[i].offset, + gradient->stops[i].color.red, + gradient->stops[i].color.green, + gradient->stops[i].color.blue, + gradient->stops[i].color.alpha); + } +} + +static cairo_status_t +_cairo_xml_emit_linear (cairo_xml_t *xml, + const cairo_linear_pattern_t *linear) +{ + _cairo_xml_printf (xml, + "", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &linear->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + 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, + "", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &radial->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_func (void *closure, const unsigned char *data, unsigned len) +{ + _cairo_output_stream_write (closure, data, len); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_image (cairo_xml_t *xml, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + _cairo_xml_printf_start (xml, + "", + image->width, image->height, + _format_to_string (image->format)); + + stream = _cairo_base64_stream_create (xml->stream); + status = cairo_surface_write_to_png_stream (&image->base, + _write_func, stream); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_output_stream_destroy (stream); + if (unlikely (status)) + return status; + + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_surface (cairo_xml_t *xml, + const cairo_surface_pattern_t *pattern) +{ + cairo_surface_t *source = pattern->surface; + cairo_status_t status; + + if (_cairo_surface_is_recording (source)) { + status = cairo_xml_for_recording_surface (&xml->base, source); + } else { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (source, + &image, &image_extra); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_image (xml, image); + + _cairo_surface_release_source_image (source, image, image_extra); + } + + return status; +} + +static cairo_status_t +_cairo_xml_emit_pattern (cairo_xml_t *xml, + const char *source_or_mask, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); + _cairo_xml_indent (xml, 2); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); + break; + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_xml_emit_matrix (xml, &pattern->matrix); + _cairo_xml_printf (xml, + "%s", + _extend_to_string (pattern->extend)); + _cairo_xml_printf (xml, + "%s", + _filter_to_string (pattern->filter)); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, "", source_or_mask); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "mask", mask); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + _cairo_xml_emit_double (xml, "line-width", style->line_width); + _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); + _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); + _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + if (style->num_dashes) { + unsigned int i; + + _cairo_xml_printf_start (xml, "", + style->dash_offset); + for (i = 0; i < style->num_dashes; i++) + _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); + + _cairo_xml_printf_end (xml, ""); + } + + _cairo_xml_emit_path (xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + + _cairo_xml_emit_matrix (xml, ctm); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_emit_path (xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +#if CAIRO_HAS_FT_FONT +#include "cairo-ft-private.h" +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_output_stream_t *base64_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); + if (unlikely (status)) { + free (buf); + return status; + } + + _cairo_xml_printf_start (xml, "", + _cairo_ft_scaled_font_get_load_flags (scaled_font)); + + + base64_stream = _cairo_base64_stream_create (xml->stream); + len = size; + _cairo_output_stream_write (base64_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base64_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base64_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_xml_printf_end (xml, ""); + + return status; +} +#else +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} +#endif + +static cairo_status_t +_cairo_xml_emit_type3_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + _cairo_xml_printf_start (xml, ""); + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_scaled_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + status = _cairo_xml_emit_type42_font (xml, scaled_font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_xml_emit_type3_font (xml, scaled_font, + glyphs, num_glyphs); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + int i; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + _cairo_xml_printf (xml, "%f %f", + glyphs[i].index, + glyphs[i].x, + glyphs[i].y); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + *remaining_glyphs = 0; + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t +_cairo_xml_surface_backend = { + CAIRO_SURFACE_TYPE_XML, + _cairo_xml_surface_create_similar, + NULL, + NULL, NULL, /* source image */ + NULL, NULL, /* dst image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, NULL, /* copy/show page */ + _cairo_xml_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* font fini */ + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_xml_surface_paint, + _cairo_xml_surface_mask, + _cairo_xml_surface_stroke, + _cairo_xml_surface_fill, + _cairo_xml_surface_glyphs, + + NULL, /* snapshot */ + + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + /* The alternate high-level text operation */ + NULL, NULL, /* has, show_text_glyphs */ +}; + +static cairo_surface_t * +_cairo_xml_surface_create_internal (cairo_device_t *device, + cairo_content_t content, + double width, + double height) +{ + cairo_xml_surface_t *surface; + + surface = malloc (sizeof (cairo_xml_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xml_surface_backend, + device, + content); + + surface->width = width; + surface->height = height; + + return &surface->base; +} + +cairo_device_t * +cairo_xml_create (const char *filename) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_filename (filename); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_xml_create_internal (stream); +} + +cairo_device_t * +cairo_xml_create_for_stream (cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_xml_create_internal (stream); +} + +cairo_surface_t * +cairo_xml_surface_create (cairo_device_t *device, + cairo_content_t content, + double width, double height) +{ + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + return _cairo_xml_surface_create_internal (device, content, width, height); +} + +cairo_status_t +cairo_xml_for_recording_surface (cairo_device_t *device, + cairo_surface_t *recording_surface) +{ + cairo_box_t bbox; + cairo_rectangle_int_t extents; + cairo_surface_t *surface; + cairo_xml_t *xml; + cairo_status_t status; + + if (unlikely (device->status)) + return device->status; + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) + return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + surface = _cairo_xml_surface_create_internal (device, + recording_surface->content, + extents.width, + extents.height); + if (unlikely (surface->status)) + return surface->status; + + xml = (cairo_xml_t *) device; + + _cairo_xml_printf (xml, + "", + _content_to_string (recording_surface->content), + extents.width, extents.height); + _cairo_xml_indent (xml, 2); + + cairo_surface_set_device_offset (surface, -extents.x, -extents.y); + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} +slim_hidden_def (cairo_xml_for_recording_surface); diff --git a/libs/cairo/src/cairo-xml.h b/libs/cairo/src/cairo-xml.h new file mode 100644 index 000000000..0367076a3 --- /dev/null +++ b/libs/cairo/src/cairo-xml.h @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/cairo/src/cairo.c b/libs/cairo/src/cairo.c new file mode 100644 index 000000000..e4a90b57f --- /dev/null +++ b/libs/cairo/src/cairo.c @@ -0,0 +1,4167 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cairoint.h" +#include "cairo-private.h" + +#include "cairo-arc-private.h" +#include "cairo-error-private.h" +#include "cairo-path-private.h" + +/** + * SECTION:cairo + * @Title: cairo_t + * @Short_Description: The cairo drawing context + * @See_Also: #cairo_surface_t + * + * #cairo_t is the main object used when drawing with cairo. To + * draw with cairo, you create a #cairo_t, set the target surface, + * and drawing options for the #cairo_t, create shapes with + * functions like cairo_move_to() and cairo_line_to(), and then + * draw shapes with cairo_stroke() or cairo_fill(). + * + * #cairo_t's can be pushed to a stack via cairo_save(). + * They may then safely be changed, without loosing the current state. + * Use cairo_restore() to restore to the saved state. + */ + +/** + * SECTION:cairo-text + * @Title: text + * @Short_Description: Rendering text and glyphs + * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(), + * cairo_glyph_path() + * + * The functions with text in their name form cairo's + * toy text API. The toy API takes UTF-8 encoded + * text and is limited in its functionality to rendering simple + * left-to-right text with no advanced features. That means for example + * that most complex scripts like Hebrew, Arabic, and Indic scripts are + * out of question. No kerning or correct positioning of diacritical marks + * either. The font selection is pretty limited too and doesn't handle the + * case that the selected font does not cover the characters in the text. + * This set of functions are really that, a toy text API, for testing and + * demonstration purposes. Any serious application should avoid them. + * + * The functions with glyphs in their name form cairo's + * low-level text API. The low-level API relies on + * the user to convert text to a set of glyph indexes and positions. This + * is a very hard problem and is best handled by external libraries, like + * the pangocairo that is part of the Pango text layout and rendering library. + * Pango is available from http://www.pango.org/. + */ + +/** + * SECTION:cairo-transforms + * @Title: Transformations + * @Short_Description: Manipulating the current transformation matrix + * @See_Also: #cairo_matrix_t + * + * The current transformation matrix, ctm, is a + * two-dimensional affine transformation that maps all coordinates and other + * drawing instruments from the user space into the + * surface's canonical coordinate system, also known as the device + * space. + */ + +#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) + +#if !defined(INFINITY) +#define INFINITY HUGE_VAL +#endif + +static const cairo_t _cairo_nil = { + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NO_MEMORY, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + NULL, /* gstate */ + {{ 0 }, { 0 }}, /* gstate_tail */ + NULL, /* gstate_freelist */ + {{ /* path */ + { 0, 0 }, /* last_move_point */ + { 0, 0 }, /* current point */ + FALSE, /* has_current_point */ + FALSE, /* has_last_move_point */ + FALSE, /* has_curve_to */ + FALSE, /* is_box */ + FALSE, /* maybe_fill_region */ + TRUE, /* is_empty_fill */ + { {0, 0}, {0, 0}}, /* extents */ + {{{NULL,NULL}}} /* link */ + }} +}; + +static const cairo_t _cairo_nil__null_pointer = { + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NULL_POINTER, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + NULL, /* gstate */ + {{ 0 }, { 0 }}, /* gstate_tail */ + NULL, /* gstate_freelist */ + {{ /* path */ + { 0, 0 }, /* last_move_point */ + { 0, 0 }, /* current point */ + FALSE, /* has_current_point */ + FALSE, /* has_last_move_point */ + FALSE, /* has_curve_to */ + FALSE, /* is_box */ + FALSE, /* maybe_fill_region */ + TRUE, /* is_empty_fill */ + { {0, 0}, {0, 0}}, /* extents */ + {{{NULL,NULL}}} /* link */ + }} +}; +#include + +/** + * _cairo_error: + * @status: a status value indicating an error, (eg. not + * %CAIRO_STATUS_SUCCESS) + * + * Checks that status is an error status, but does nothing else. + * + * All assignments of an error status to any user-visible object + * within the cairo application should result in a call to + * _cairo_error(). + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_status_t +_cairo_error (cairo_status_t status) +{ + CAIRO_ENSURE_UNIQUE; + assert (_cairo_status_is_error (status)); + +#ifdef MOZILLA_VERSION + static int abort_on_error = -1; + if (abort_on_error < 0) { + abort_on_error = (getenv("MOZ_CAIRO_ERROR_ABORT") != NULL) ? 1 : 0; + } + if (abort_on_error) { + abort(); + } +#endif + return status; +} + +/** + * _cairo_set_error: + * @cr: a cairo context + * @status: a status value indicating an error + * + * Atomically sets cr->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to cr->status should happen + * through _cairo_set_error(). Note that due to the nature of the atomic + * operation, it is not safe to call this function on the nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +static void +_cairo_set_error (cairo_t *cr, cairo_status_t status) +{ + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&cr->status, _cairo_error (status)); +} + +/* We keep a small stash of contexts to reduce malloc pressure */ +#define CAIRO_STASH_SIZE 4 +#if CAIRO_NO_MUTEX +static struct { + cairo_t pool[CAIRO_STASH_SIZE]; + int occupied; +} _context_stash; + +static cairo_t * +_context_get (void) +{ + int avail; + + avail = ffs (~_context_stash.occupied) - 1; + if (avail >= CAIRO_STASH_SIZE) + return malloc (sizeof (cairo_t)); + + _context_stash.occupied |= 1 << avail; + return &_context_stash.pool[avail]; +} + +static void +_context_put (cairo_t *cr) +{ + if (cr < &_context_stash.pool[0] || + cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) + { + free (cr); + return; + } + + _context_stash.occupied &= ~(1 << (cr - &_context_stash.pool[0])); +} +#elif HAS_ATOMIC_OPS +static struct { + cairo_t pool[CAIRO_STASH_SIZE]; + cairo_atomic_int_t occupied; +} _context_stash; + +static cairo_t * +_context_get (void) +{ + cairo_atomic_int_t avail, old, new; + + do { + old = _cairo_atomic_int_get (&_context_stash.occupied); + avail = ffs (~old) - 1; + if (avail >= CAIRO_STASH_SIZE) + return malloc (sizeof (cairo_t)); + + new = old | (1 << avail); + } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); + + return &_context_stash.pool[avail]; +} + +static void +_context_put (cairo_t *cr) +{ + cairo_atomic_int_t old, new, avail; + + if (cr < &_context_stash.pool[0] || + cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) + { + free (cr); + return; + } + + avail = ~(1 << (cr - &_context_stash.pool[0])); + do { + old = _cairo_atomic_int_get (&_context_stash.occupied); + new = old & avail; + } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); +} +#else +#define _context_get() malloc (sizeof (cairo_t)) +#define _context_put(cr) free (cr) +#endif + +/* XXX This should disappear in favour of a common pool of error objects. */ +static cairo_t *_cairo_nil__objects[CAIRO_STATUS_LAST_STATUS + 1]; + +static cairo_t * +_cairo_create_in_error (cairo_status_t status) +{ + cairo_t *cr; + + assert (status != CAIRO_STATUS_SUCCESS); + + /* Sanity check */ + if (status < 0 || status > CAIRO_STATUS_LAST_STATUS) { + abort(); + } + + /* special case OOM in order to avoid another allocation */ + switch ((int) status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_t *) &_cairo_nil; + case CAIRO_STATUS_NULL_POINTER: + return (cairo_t *) &_cairo_nil__null_pointer; + } + + CAIRO_MUTEX_LOCK (_cairo_error_mutex); + cr = _cairo_nil__objects[status]; + if (cr == NULL) { + cr = malloc (sizeof (cairo_t)); + if (unlikely (cr == NULL)) { + CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_t *) &_cairo_nil; + } + + *cr = _cairo_nil; + cr->status = status; + _cairo_nil__objects[status] = cr; + } + CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); + + return cr; +} + +void +_cairo_reset_static_data (void) +{ + int status; + + CAIRO_MUTEX_LOCK (_cairo_error_mutex); + for (status = CAIRO_STATUS_SUCCESS; + status <= CAIRO_STATUS_LAST_STATUS; + status++) + { + if (_cairo_nil__objects[status] != NULL) { + free (_cairo_nil__objects[status]); + _cairo_nil__objects[status] = NULL; + } + } + CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); +} + +/** + * cairo_create: + * @target: target surface for the context + * + * Creates a new #cairo_t with all graphics state parameters set to + * default values and with @target as a target surface. The target + * surface should be constructed with a backend-specific function such + * as cairo_image_surface_create() (or any other + * cairo_backend_surface_create() variant). + * + * This function references @target, so you can immediately + * call cairo_surface_destroy() on it if you don't need to + * maintain a separate reference to it. + * + * Return value: a newly allocated #cairo_t with a reference + * count of 1. The initial reference count should be released + * with cairo_destroy() when you are done using the #cairo_t. + * This function never returns %NULL. If memory cannot be + * allocated, a special #cairo_t object will be returned on + * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. + * You can use this object normally, but no drawing will + * be done. + **/ +cairo_t * +cairo_create (cairo_surface_t *target) +{ + cairo_t *cr; + cairo_status_t status; + + if (unlikely (target == NULL)) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + if (unlikely (target->status)) + return _cairo_create_in_error (target->status); + + cr = _context_get (); + if (unlikely (cr == NULL)) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1); + + cr->status = CAIRO_STATUS_SUCCESS; + + _cairo_user_data_array_init (&cr->user_data); + _cairo_path_fixed_init (cr->path); + + cr->gstate = &cr->gstate_tail[0]; + cr->gstate_freelist = &cr->gstate_tail[1]; + cr->gstate_tail[1].next = NULL; + + status = _cairo_gstate_init (cr->gstate, target); + if (unlikely (status)) { + _context_put (cr); + cr = _cairo_create_in_error (status); + } + + return cr; +} +slim_hidden_def (cairo_create); + +/** + * cairo_reference: + * @cr: a #cairo_t + * + * Increases the reference count on @cr by one. This prevents + * @cr from being destroyed until a matching call to cairo_destroy() + * is made. + * + * The number of references to a #cairo_t can be get using + * cairo_get_reference_count(). + * + * Return value: the referenced #cairo_t. + **/ +cairo_t * +cairo_reference (cairo_t *cr) +{ + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return cr; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); + + _cairo_reference_count_inc (&cr->ref_count); + + return cr; +} + +/** + * cairo_destroy: + * @cr: a #cairo_t + * + * Decreases the reference count on @cr by one. If the result + * is zero, then @cr and all associated resources are freed. + * See cairo_reference(). + **/ +void +cairo_destroy (cairo_t *cr) +{ + cairo_surface_t *surface; + + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) + return; + + while (cr->gstate != &cr->gstate_tail[0]) { + if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) + break; + } + + /* The context is expected (>99% of all use cases) to be held for the + * duration of a single expose event/sequence of graphic operations. + * Therefore, on destroy we explicitly flush the Cairo pipeline of any + * pending operations. + */ + surface = _cairo_gstate_get_original_target (cr->gstate); + if (surface != NULL) + cairo_surface_flush (surface); + + _cairo_gstate_fini (cr->gstate); + cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ + while (cr->gstate_freelist != NULL) { + cairo_gstate_t *gstate = cr->gstate_freelist; + cr->gstate_freelist = gstate->next; + free (gstate); + } + + _cairo_path_fixed_fini (cr->path); + + _cairo_user_data_array_fini (&cr->user_data); + + /* mark the context as invalid to protect against misuse */ + cr->status = CAIRO_STATUS_NULL_POINTER; + + _context_put (cr); +} +slim_hidden_def (cairo_destroy); + +/** + * cairo_get_user_data: + * @cr: a #cairo_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @cr using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +void * +cairo_get_user_data (cairo_t *cr, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&cr->user_data, + key); +} + +/** + * cairo_set_user_data: + * @cr: a #cairo_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @cr. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_set_user_data (cairo_t *cr, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return cr->status; + + return _cairo_user_data_array_set_data (&cr->user_data, + key, user_data, destroy); +} + +/** + * cairo_get_reference_count: + * @cr: a #cairo_t + * + * Returns the current reference count of @cr. + * + * Return value: the current reference count of @cr. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_get_reference_count (cairo_t *cr) +{ + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count); +} + +/** + * cairo_save: + * @cr: a #cairo_t + * + * Makes a copy of the current state of @cr and saves it + * on an internal stack of saved states for @cr. When + * cairo_restore() is called, @cr will be restored to + * the saved state. Multiple calls to cairo_save() and + * cairo_restore() can be nested; each call to cairo_restore() + * restores the state from the matching paired cairo_save(). + * + * It isn't necessary to clear all saved states before + * a #cairo_t is freed. If the reference count of a #cairo_t + * drops to zero in response to a call to cairo_destroy(), + * any saved states will be freed along with the #cairo_t. + **/ +void +cairo_save (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_save); + +/** + * cairo_restore: + * @cr: a #cairo_t + * + * Restores @cr to the state saved by a preceding call to + * cairo_save() and removes that state from the stack of + * saved states. + **/ +void +cairo_restore (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_restore); + +/** + * cairo_push_group: + * @cr: a cairo context + * + * Temporarily redirects drawing to an intermediate surface known as a + * group. The redirection lasts until the group is completed by a call + * to cairo_pop_group() or cairo_pop_group_to_source(). These calls + * provide the result of any drawing to the group as a pattern, + * (either as an explicit object, or set as the source pattern). + * + * This group functionality can be convenient for performing + * intermediate compositing. One common use of a group is to render + * objects as opaque within the group, (so that they occlude each + * other), and then blend the result with translucence onto the + * destination. + * + * Groups can be nested arbitrarily deep by making balanced calls to + * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new + * target group onto/from a stack. + * + * The cairo_push_group() function calls cairo_save() so that any + * changes to the graphics state will not be visible outside the + * group, (the pop_group functions call cairo_restore()). + * + * By default the intermediate group will have a content type of + * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for + * the group by using cairo_push_group_with_content() instead. + * + * As an example, here is how one might fill and stroke a path with + * translucence, but without any portion of the fill being visible + * under the stroke: + * + * + * cairo_push_group (cr); + * cairo_set_source (cr, fill_pattern); + * cairo_fill_preserve (cr); + * cairo_set_source (cr, stroke_pattern); + * cairo_stroke (cr); + * cairo_pop_group_to_source (cr); + * cairo_paint_with_alpha (cr, alpha); + * + * + * Since: 1.2 + */ +void +cairo_push_group (cairo_t *cr) +{ + cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); +} + +/** + * cairo_push_group_with_content: + * @cr: a cairo context + * @content: a #cairo_content_t indicating the type of group that + * will be created + * + * Temporarily redirects drawing to an intermediate surface known as a + * group. The redirection lasts until the group is completed by a call + * to cairo_pop_group() or cairo_pop_group_to_source(). These calls + * provide the result of any drawing to the group as a pattern, + * (either as an explicit object, or set as the source pattern). + * + * The group will have a content type of @content. The ability to + * control this content type is the only distinction between this + * function and cairo_push_group() which you should see for a more + * detailed description of group rendering. + * + * Since: 1.2 + */ +void +cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) +{ + cairo_surface_t *group_surface; + cairo_clip_t *clip; + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + clip = _cairo_gstate_get_clip (cr->gstate); + if (clip->all_clipped) { + group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); + status = group_surface->status; + if (unlikely (status)) + goto bail; + } else { + cairo_surface_t *parent_surface; + const cairo_rectangle_int_t *clip_extents; + cairo_rectangle_int_t extents; + cairo_matrix_t matrix; + cairo_bool_t is_empty; + + parent_surface = _cairo_gstate_get_target (cr->gstate); + + /* Get the extents that we'll use in creating our new group surface */ + is_empty = _cairo_surface_get_extents (parent_surface, &extents); + clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate)); + if (clip_extents != NULL) + is_empty = _cairo_rectangle_intersect (&extents, clip_extents); + + group_surface = _cairo_surface_create_similar_solid (parent_surface, + content, + extents.width, + extents.height, + CAIRO_COLOR_TRANSPARENT, + TRUE); + status = group_surface->status; + if (unlikely (status)) + goto bail; + + /* Set device offsets on the new surface so that logically it appears at + * the same location on the parent surface -- when we pop_group this, + * the source pattern will get fixed up for the appropriate target surface + * device offsets, so we want to set our own surface offsets from /that/, + * and not from the device origin. */ + cairo_surface_set_device_offset (group_surface, + parent_surface->device_transform.x0 - extents.x, + parent_surface->device_transform.y0 - extents.y); + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just applied. */ + cairo_matrix_init_translate (&matrix, -extents.x, -extents.y); + _cairo_path_fixed_transform (cr->path, &matrix); + } + + /* create a new gstate for the redirect */ + cairo_save (cr); + if (unlikely (cr->status)) + goto bail; + + status = _cairo_gstate_redirect_target (cr->gstate, group_surface); + +bail: + cairo_surface_destroy (group_surface); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_push_group_with_content); + +/** + * cairo_pop_group: + * @cr: a cairo context + * + * Terminates the redirection begun by a call to cairo_push_group() or + * cairo_push_group_with_content() and returns a new pattern + * containing the results of all drawing operations performed to the + * group. + * + * The cairo_pop_group() function calls cairo_restore(), (balancing a + * call to cairo_save() by the push_group function), so that any + * changes to the graphics state will not be visible outside the + * group. + * + * Return value: a newly created (surface) pattern containing the + * results of all drawing operations performed to the group. The + * caller owns the returned object and should call + * cairo_pattern_destroy() when finished with it. + * + * Since: 1.2 + **/ +cairo_pattern_t * +cairo_pop_group (cairo_t *cr) +{ + cairo_surface_t *group_surface, *parent_target; + cairo_pattern_t *group_pattern; + cairo_matrix_t group_matrix, device_transform_matrix; + cairo_status_t status; + + if (unlikely (cr->status)) + return _cairo_pattern_create_in_error (cr->status); + + /* Grab the active surfaces */ + group_surface = _cairo_gstate_get_target (cr->gstate); + parent_target = _cairo_gstate_get_parent_target (cr->gstate); + + /* Verify that we are at the right nesting level */ + if (parent_target == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_INVALID_POP_GROUP); + return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP); + } + + /* We need to save group_surface before we restore; we don't need + * to reference parent_target and original_target, since the + * gstate will still hold refs to them once we restore. */ + group_surface = cairo_surface_reference (group_surface); + + cairo_restore (cr); + + if (unlikely (cr->status)) { + group_pattern = _cairo_pattern_create_in_error (cr->status); + goto done; + } + + group_pattern = cairo_pattern_create_for_surface (group_surface); + status = group_pattern->status; + if (unlikely (status)) { + _cairo_set_error (cr, status); + goto done; + } + + _cairo_gstate_get_matrix (cr->gstate, &group_matrix); + /* Transform by group_matrix centered around device_transform so that when + * we call _cairo_gstate_copy_transformed_pattern the result is a pattern + * with a matrix equivalent to the device_transform of group_surface. */ + if (_cairo_surface_has_device_transform (group_surface)) { + cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); + _cairo_pattern_transform (group_pattern, &group_matrix); + _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); + } else { + cairo_pattern_set_matrix (group_pattern, &group_matrix); + } + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just removed. */ + cairo_matrix_multiply (&device_transform_matrix, + &_cairo_gstate_get_target (cr->gstate)->device_transform, + &group_surface->device_transform_inverse); + _cairo_path_fixed_transform (cr->path, &device_transform_matrix); + +done: + cairo_surface_destroy (group_surface); + + return group_pattern; +} +slim_hidden_def(cairo_pop_group); + +/** + * cairo_pop_group_to_source: + * @cr: a cairo context + * + * Terminates the redirection begun by a call to cairo_push_group() or + * cairo_push_group_with_content() and installs the resulting pattern + * as the source pattern in the given cairo context. + * + * The behavior of this function is equivalent to the sequence of + * operations: + * + * + * #cairo_pattern_t *group = cairo_pop_group (cr); + * cairo_set_source (cr, group); + * cairo_pattern_destroy (group); + * + * + * but is more convenient as their is no need for a variable to store + * the short-lived pointer to the pattern. + * + * The cairo_pop_group() function calls cairo_restore(), (balancing a + * call to cairo_save() by the push_group function), so that any + * changes to the graphics state will not be visible outside the + * group. + * + * Since: 1.2 + **/ +void +cairo_pop_group_to_source (cairo_t *cr) +{ + cairo_pattern_t *group_pattern; + + group_pattern = cairo_pop_group (cr); + cairo_set_source (cr, group_pattern); + cairo_pattern_destroy (group_pattern); +} + +/** + * cairo_set_operator: + * @cr: a #cairo_t + * @op: a compositing operator, specified as a #cairo_operator_t + * + * Sets the compositing operator to be used for all drawing + * operations. See #cairo_operator_t for details on the semantics of + * each available compositing operator. + * + * The default operator is %CAIRO_OPERATOR_OVER. + **/ +void +cairo_set_operator (cairo_t *cr, cairo_operator_t op) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_operator (cr->gstate, op); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_operator); + + +static cairo_bool_t +_current_source_matches_solid (cairo_t *cr, + double red, + double green, + double blue, + double alpha) +{ + const cairo_pattern_t *current; + cairo_color_t color; + + current = cr->gstate->source; + if (current->type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + return _cairo_color_equal (&color, + &((cairo_solid_pattern_t *) current)->color); +} +/** + * cairo_set_source_rgb + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Sets the source pattern within @cr to an opaque color. This opaque + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. + * + * The default source pattern is opaque black, (that is, it is + * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)). + **/ +void +cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) +{ + cairo_pattern_t *pattern; + + if (unlikely (cr->status)) + return; + + if (_current_source_matches_solid (cr, red, green, blue, 1.)) + return; + + /* push the current pattern to the freed lists */ + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + + pattern = cairo_pattern_create_rgb (red, green, blue); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} +slim_hidden_def (cairo_set_source_rgb); + +/** + * cairo_set_source_rgba: + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Sets the source pattern within @cr to a translucent color. This + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color and alpha components are floating point numbers in the + * range 0 to 1. If the values passed in are outside that range, they + * will be clamped. + * + * The default source pattern is opaque black, (that is, it is + * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)). + **/ +void +cairo_set_source_rgba (cairo_t *cr, + double red, double green, double blue, + double alpha) +{ + cairo_pattern_t *pattern; + + if (unlikely (cr->status)) + return; + + if (_current_source_matches_solid (cr, red, green, blue, alpha)) + return; + + /* push the current pattern to the freed lists */ + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + + pattern = cairo_pattern_create_rgba (red, green, blue, alpha); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +/** + * cairo_set_source_surface: + * @cr: a cairo context + * @surface: a surface to be used to set the source pattern + * @x: User-space X coordinate for surface origin + * @y: User-space Y coordinate for surface origin + * + * This is a convenience function for creating a pattern from @surface + * and setting it as the source in @cr with cairo_set_source(). + * + * The @x and @y parameters give the user-space coordinate at which + * the surface origin should appear. (The surface origin is its + * upper-left corner before any transformation has been applied.) The + * @x and @y parameters are negated and then set as translation values + * in the pattern matrix. + * + * Other than the initial translation pattern matrix, as described + * above, all other pattern attributes, (such as its extend mode), are + * set to the default values as in cairo_pattern_create_for_surface(). + * The resulting pattern can be queried with cairo_get_source() so + * that these attributes can be modified if desired, (eg. to create a + * repeating pattern with cairo_pattern_set_extend()). + **/ +void +cairo_set_source_surface (cairo_t *cr, + cairo_surface_t *surface, + double x, + double y) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + if (unlikely (cr->status)) + return; + + /* push the current pattern to the freed lists */ + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + + pattern = cairo_pattern_create_for_surface (surface); + + cairo_matrix_init_translate (&matrix, -x, -y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} +slim_hidden_def (cairo_set_source_surface); + +/** + * cairo_set_source + * @cr: a cairo context + * @source: a #cairo_pattern_t to be used as the source for + * subsequent drawing operations. + * + * Sets the source pattern within @cr to @source. This pattern + * will then be used for any subsequent drawing operation until a new + * source pattern is set. + * + * Note: The pattern's transformation matrix will be locked to the + * user space in effect at the time of cairo_set_source(). This means + * that further modifications of the current transformation matrix + * will not affect the source pattern. See cairo_pattern_set_matrix(). + * + * The default source pattern is a solid pattern that is opaque black, + * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, + * 0.0)). + **/ +void +cairo_set_source (cairo_t *cr, cairo_pattern_t *source) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (source == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (source->status) { + _cairo_set_error (cr, source->status); + return; + } + + status = _cairo_gstate_set_source (cr->gstate, source); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_source); + +/** + * cairo_get_source: + * @cr: a cairo context + * + * Gets the current source pattern for @cr. + * + * Return value: the current source pattern. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_pattern_reference(). + **/ +cairo_pattern_t * +cairo_get_source (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_pattern_create_in_error (cr->status); + + return _cairo_gstate_get_source (cr->gstate); +} + +/** + * cairo_set_tolerance: + * @cr: a #cairo_t + * @tolerance: the tolerance, in device units (typically pixels) + * + * Sets the tolerance used when converting paths into trapezoids. + * Curved segments of the path will be subdivided until the maximum + * deviation between the original path and the polygonal approximation + * is less than @tolerance. The default value is 0.1. A larger + * value will give better performance, a smaller value, better + * appearance. (Reducing the value from the default value of 0.1 + * is unlikely to improve appearance significantly.) The accuracy of paths + * within Cairo is limited by the precision of its internal arithmetic, and + * the prescribed @tolerance is restricted to the smallest + * representable internal value. + **/ +void +cairo_set_tolerance (cairo_t *cr, double tolerance) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (tolerance < CAIRO_TOLERANCE_MINIMUM) + tolerance = CAIRO_TOLERANCE_MINIMUM; + + status = _cairo_gstate_set_tolerance (cr->gstate, tolerance); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_tolerance); + +/** + * cairo_set_antialias: + * @cr: a #cairo_t + * @antialias: the new antialiasing mode + * + * Set the antialiasing mode of the rasterizer used for drawing shapes. + * This value is a hint, and a particular backend may or may not support + * a particular value. At the current time, no backend supports + * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes. + * + * Note that this option does not affect text rendering, instead see + * cairo_font_options_set_antialias(). + **/ +void +cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_antialias (cr->gstate, antialias); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_fill_rule: + * @cr: a #cairo_t + * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t + * + * Set the current fill rule within the cairo context. The fill rule + * is used to determine which regions are inside or outside a complex + * (potentially self-intersecting) path. The current fill rule affects + * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details + * on the semantics of each available fill rule. + * + * The default fill rule is %CAIRO_FILL_RULE_WINDING. + **/ +void +cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_line_width: + * @cr: a #cairo_t + * @width: a line width + * + * Sets the current line width within the cairo context. The line + * width value specifies the diameter of a pen that is circular in + * user space, (though device-space pen may be an ellipse in general + * due to scaling/shear/rotation of the CTM). + * + * Note: When the description above refers to user space and CTM it + * refers to the user space and CTM in effect at the time of the + * stroking operation, not the user space and CTM in effect at the + * time of the call to cairo_set_line_width(). The simplest usage + * makes both of these spaces identical. That is, if there is no + * change to the CTM between a call to cairo_set_line_width() and the + * stroking operation, then one can just pass user-space values to + * cairo_set_line_width() and ignore this note. + * + * As with the other stroke parameters, the current line width is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default line width value is 2.0. + **/ +void +cairo_set_line_width (cairo_t *cr, double width) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (width < 0.) + width = 0.; + + status = _cairo_gstate_set_line_width (cr->gstate, width); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_width); + +/** + * cairo_set_line_cap: + * @cr: a cairo context + * @line_cap: a line cap style + * + * Sets the current line cap style within the cairo context. See + * #cairo_line_cap_t for details about how the available line cap + * styles are drawn. + * + * As with the other stroke parameters, the current line cap style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default line cap style is %CAIRO_LINE_CAP_BUTT. + **/ +void +cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_line_cap (cr->gstate, line_cap); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_cap); + +/** + * cairo_set_line_join: + * @cr: a cairo context + * @line_join: a line join style + * + * Sets the current line join style within the cairo context. See + * #cairo_line_join_t for details about how the available line join + * styles are drawn. + * + * As with the other stroke parameters, the current line join style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default line join style is %CAIRO_LINE_JOIN_MITER. + **/ +void +cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_line_join (cr->gstate, line_join); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_join); + +/** + * cairo_set_dash: + * @cr: a cairo context + * @dashes: an array specifying alternate lengths of on and off stroke portions + * @num_dashes: the length of the dashes array + * @offset: an offset into the dash pattern at which the stroke should start + * + * Sets the dash pattern to be used by cairo_stroke(). A dash pattern + * is specified by @dashes, an array of positive values. Each value + * provides the length of alternate "on" and "off" portions of the + * stroke. The @offset specifies an offset into the pattern at which + * the stroke begins. + * + * Each "on" segment will have caps applied as if the segment were a + * separate sub-path. In particular, it is valid to use an "on" length + * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order + * to distributed dots or squares along a path. + * + * Note: The length values are in user-space units as evaluated at the + * time of stroking. This is not necessarily the same as the user + * space at the time of cairo_set_dash(). + * + * If @num_dashes is 0 dashing is disabled. + * + * If @num_dashes is 1 a symmetric pattern is assumed with alternating + * on and off portions of the size specified by the single value in + * @dashes. + * + * If any value in @dashes is negative, or if all values are 0, then + * @cr will be put into an error state with a status of + * %CAIRO_STATUS_INVALID_DASH. + **/ +void +cairo_set_dash (cairo_t *cr, + const double *dashes, + int num_dashes, + double offset) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_dash (cr->gstate, + dashes, num_dashes, offset); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_dash_count: + * @cr: a #cairo_t + * + * This function returns the length of the dash array in @cr (0 if dashing + * is not currently in effect). + * + * See also cairo_set_dash() and cairo_get_dash(). + * + * Return value: the length of the dash array, or 0 if no dash array set. + * + * Since: 1.4 + */ +int +cairo_get_dash_count (cairo_t *cr) +{ + int num_dashes; + + if (unlikely (cr->status)) + return 0; + + _cairo_gstate_get_dash (cr->gstate, NULL, &num_dashes, NULL); + + return num_dashes; +} + +/** + * cairo_get_dash: + * @cr: a #cairo_t + * @dashes: return value for the dash array, or %NULL + * @offset: return value for the current dash offset, or %NULL + * + * Gets the current dash array. If not %NULL, @dashes should be big + * enough to hold at least the number of values returned by + * cairo_get_dash_count(). + * + * Since: 1.4 + **/ +void +cairo_get_dash (cairo_t *cr, + double *dashes, + double *offset) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_get_dash (cr->gstate, dashes, NULL, offset); +} + +/** + * cairo_set_miter_limit: + * @cr: a cairo context + * @limit: miter limit to set + * + * Sets the current miter limit within the cairo context. + * + * If the current line join style is set to %CAIRO_LINE_JOIN_MITER + * (see cairo_set_line_join()), the miter limit is used to determine + * whether the lines should be joined with a bevel instead of a miter. + * Cairo divides the length of the miter by the line width. + * If the result is greater than the miter limit, the style is + * converted to a bevel. + * + * As with the other stroke parameters, the current line miter limit is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default miter limit value is 10.0, which will convert joins + * with interior angles less than 11 degrees to bevels instead of + * miters. For reference, a miter limit of 2.0 makes the miter cutoff + * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 + * degrees. + * + * A miter limit for a desired angle can be computed as: miter limit = + * 1/sin(angle/2) + **/ +void +cairo_set_miter_limit (cairo_t *cr, double limit) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_miter_limit (cr->gstate, limit); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_translate: + * @cr: a cairo context + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Modifies the current transformation matrix (CTM) by translating the + * user-space origin by (@tx, @ty). This offset is interpreted as a + * user-space coordinate according to the CTM in place before the new + * call to cairo_translate(). In other words, the translation of the + * user-space origin takes place after any existing transformation. + **/ +void +cairo_translate (cairo_t *cr, double tx, double ty) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_translate (cr->gstate, tx, ty); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_translate); + +/** + * cairo_scale: + * @cr: a cairo context + * @sx: scale factor for the X dimension + * @sy: scale factor for the Y dimension + * + * Modifies the current transformation matrix (CTM) by scaling the X + * and Y user-space axes by @sx and @sy respectively. The scaling of + * the axes takes place after any existing transformation of user + * space. + **/ +void +cairo_scale (cairo_t *cr, double sx, double sy) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_scale (cr->gstate, sx, sy); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_scale); + +/** + * cairo_rotate: + * @cr: a cairo context + * @angle: angle (in radians) by which the user-space axes will be + * rotated + * + * Modifies the current transformation matrix (CTM) by rotating the + * user-space axes by @angle radians. The rotation of the axes takes + * places after any existing transformation of user space. The + * rotation direction for positive angles is from the positive X axis + * toward the positive Y axis. + **/ +void +cairo_rotate (cairo_t *cr, double angle) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_rotate (cr->gstate, angle); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_transform: + * @cr: a cairo context + * @matrix: a transformation to be applied to the user-space axes + * + * Modifies the current transformation matrix (CTM) by applying + * @matrix as an additional transformation. The new transformation of + * user space takes place after any existing transformation. + **/ +void +cairo_transform (cairo_t *cr, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_transform (cr->gstate, matrix); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_transform); + +/** + * cairo_set_matrix: + * @cr: a cairo context + * @matrix: a transformation matrix from user space to device space + * + * Modifies the current transformation matrix (CTM) by setting it + * equal to @matrix. + **/ +void +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_matrix (cr->gstate, matrix); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_matrix); + +/** + * cairo_identity_matrix: + * @cr: a cairo context + * + * Resets the current transformation matrix (CTM) by setting it equal + * to the identity matrix. That is, the user-space and device-space + * axes will be aligned and one user-space unit will transform to one + * device-space unit. + **/ +void +cairo_identity_matrix (cairo_t *cr) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_identity_matrix (cr->gstate); +} + +/** + * cairo_user_to_device: + * @cr: a cairo context + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * Transform a coordinate from user space to device space by + * multiplying the given point by the current transformation matrix + * (CTM). + **/ +void +cairo_user_to_device (cairo_t *cr, double *x, double *y) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_device (cr->gstate, x, y); +} +slim_hidden_def (cairo_user_to_device); + +/** + * cairo_user_to_device_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from user space to device space. This + * function is similar to cairo_user_to_device() except that the + * translation components of the CTM will be ignored when transforming + * (@dx,@dy). + **/ +void +cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); +} +slim_hidden_def (cairo_user_to_device_distance); + +/** + * cairo_device_to_user: + * @cr: a cairo + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * Transform a coordinate from device space to user space by + * multiplying the given point by the inverse of the current + * transformation matrix (CTM). + **/ +void +cairo_device_to_user (cairo_t *cr, double *x, double *y) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_device_to_user (cr->gstate, x, y); +} + +/** + * cairo_device_to_user_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from device space to user space. This + * function is similar to cairo_device_to_user() except that the + * translation components of the inverse CTM will be ignored when + * transforming (@dx,@dy). + **/ +void +cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) +{ + if (unlikely (cr->status)) + return; + + _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); +} + +/** + * cairo_new_path: + * @cr: a cairo context + * + * Clears the current path. After this call there will be no path and + * no current point. + **/ +void +cairo_new_path (cairo_t *cr) +{ + if (unlikely (cr->status)) + return; + + _cairo_path_fixed_fini (cr->path); + _cairo_path_fixed_init (cr->path); +} +slim_hidden_def(cairo_new_path); + +/** + * cairo_move_to: + * @cr: a cairo context + * @x: the X coordinate of the new position + * @y: the Y coordinate of the new position + * + * Begin a new sub-path. After this call the current point will be (@x, + * @y). + **/ +void +cairo_move_to (cairo_t *cr, double x, double y) +{ + cairo_status_t status; + cairo_fixed_t x_fixed, y_fixed; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_move_to); + +/** + * cairo_new_sub_path: + * @cr: a cairo context + * + * Begin a new sub-path. Note that the existing path is not + * affected. After this call there will be no current point. + * + * In many cases, this call is not needed since new sub-paths are + * frequently started with cairo_move_to(). + * + * A call to cairo_new_sub_path() is particularly useful when + * beginning a new sub-path with one of the cairo_arc() calls. This + * makes things easier as it is no longer necessary to manually + * compute the arc's initial coordinates for a call to + * cairo_move_to(). + * + * Since: 1.2 + **/ +void +cairo_new_sub_path (cairo_t *cr) +{ + if (unlikely (cr->status)) + return; + + _cairo_path_fixed_new_sub_path (cr->path); +} + +/** + * cairo_line_to: + * @cr: a cairo context + * @x: the X coordinate of the end of the new line + * @y: the Y coordinate of the end of the new line + * + * Adds a line to the path from the current point to position (@x, @y) + * in user-space coordinates. After this call the current point + * will be (@x, @y). + * + * If there is no current point before the call to cairo_line_to() + * this function will behave as cairo_move_to(@cr, @x, @y). + **/ +void +cairo_line_to (cairo_t *cr, double x, double y) +{ + cairo_status_t status; + cairo_fixed_t x_fixed, y_fixed; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_line_to); + +/** + * cairo_curve_to: + * @cr: a cairo context + * @x1: the X coordinate of the first control point + * @y1: the Y coordinate of the first control point + * @x2: the X coordinate of the second control point + * @y2: the Y coordinate of the second control point + * @x3: the X coordinate of the end of the curve + * @y3: the Y coordinate of the end of the curve + * + * Adds a cubic Bézier spline to the path from the current point to + * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and + * (@x2, @y2) as the control points. After this call the current point + * will be (@x3, @y3). + * + * If there is no current point before the call to cairo_curve_to() + * this function will behave as if preceded by a call to + * cairo_move_to(@cr, @x1, @y1). + **/ +void +cairo_curve_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_status_t status; + cairo_fixed_t x1_fixed, y1_fixed; + cairo_fixed_t x2_fixed, y2_fixed; + cairo_fixed_t x3_fixed, y3_fixed; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); + _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); + _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); + + x1_fixed = _cairo_fixed_from_double (x1); + y1_fixed = _cairo_fixed_from_double (y1); + + x2_fixed = _cairo_fixed_from_double (x2); + y2_fixed = _cairo_fixed_from_double (y2); + + x3_fixed = _cairo_fixed_from_double (x3); + y3_fixed = _cairo_fixed_from_double (y3); + + status = _cairo_path_fixed_curve_to (cr->path, + x1_fixed, y1_fixed, + x2_fixed, y2_fixed, + x3_fixed, y3_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_curve_to); + +/** + * cairo_arc: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Adds a circular arc of the given @radius to the current path. The + * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in + * the direction of increasing angles to end at @angle2. If @angle2 is + * less than @angle1 it will be progressively increased by 2*M_PI + * until it is greater than @angle1. + * + * If there is a current point, an initial line segment will be added + * to the path to connect the current point to the beginning of the + * arc. If this initial line is undesired, it can be avoided by + * calling cairo_new_sub_path() before calling cairo_arc(). + * + * Angles are measured in radians. An angle of 0.0 is in the direction + * of the positive X axis (in user space). An angle of %M_PI/2.0 radians + * (90 degrees) is in the direction of the positive Y axis (in + * user space). Angles increase in the direction from the positive X + * axis toward the positive Y axis. So with the default transformation + * matrix, angles increase in a clockwise direction. + * + * (To convert from degrees to radians, use degrees * (M_PI / + * 180.).) + * + * This function gives the arc in the direction of increasing angles; + * see cairo_arc_negative() to get the arc in the direction of + * decreasing angles. + * + * The arc is circular in user space. To achieve an elliptical arc, + * you can scale the current transformation matrix by different + * amounts in the X and Y directions. For example, to draw an ellipse + * in the box given by @x, @y, @width, @height: + * + * + * cairo_save (cr); + * cairo_translate (cr, x + width / 2., y + height / 2.); + * cairo_scale (cr, width / 2., height / 2.); + * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); + * cairo_restore (cr); + * + **/ +void +cairo_arc (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2) +{ + if (unlikely (cr->status)) + return; + + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) { + cairo_line_to (cr, xc, yc); + return; + } + + while (angle2 < angle1) + angle2 += 2 * M_PI; + + cairo_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + _cairo_arc_path (cr, xc, yc, radius, + angle1, angle2); +} + +/** + * cairo_arc_negative: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Adds a circular arc of the given @radius to the current path. The + * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in + * the direction of decreasing angles to end at @angle2. If @angle2 is + * greater than @angle1 it will be progressively decreased by 2*M_PI + * until it is less than @angle1. + * + * See cairo_arc() for more details. This function differs only in the + * direction of the arc between the two angles. + **/ +void +cairo_arc_negative (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2) +{ + if (unlikely (cr->status)) + return; + + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) + return; + + while (angle2 > angle1) + angle2 -= 2 * M_PI; + + cairo_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + _cairo_arc_path_negative (cr, xc, yc, radius, + angle1, angle2); +} + +/* XXX: NYI +void +cairo_arc_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double radius) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_arc_to (cr->gstate, + x1, y1, + x2, y2, + radius); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +*/ + +/** + * cairo_rel_move_to: + * @cr: a cairo context + * @dx: the X offset + * @dy: the Y offset + * + * Begin a new sub-path. After this call the current point will offset + * by (@x, @y). + * + * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy) + * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @dy). + * + * It is an error to call this function with no current point. Doing + * so will cause @cr to shutdown with a status of + * %CAIRO_STATUS_NO_CURRENT_POINT. + **/ +void +cairo_rel_move_to (cairo_t *cr, double dx, double dy) +{ + cairo_fixed_t dx_fixed, dy_fixed; + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); + + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_rel_line_to: + * @cr: a cairo context + * @dx: the X offset to the end of the new line + * @dy: the Y offset to the end of the new line + * + * Relative-coordinate version of cairo_line_to(). Adds a line to the + * path from the current point to a point that is offset from the + * current point by (@dx, @dy) in user space. After this call the + * current point will be offset by (@dx, @dy). + * + * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy) + * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @dy). + * + * It is an error to call this function with no current point. Doing + * so will cause @cr to shutdown with a status of + * %CAIRO_STATUS_NO_CURRENT_POINT. + **/ +void +cairo_rel_line_to (cairo_t *cr, double dx, double dy) +{ + cairo_fixed_t dx_fixed, dy_fixed; + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); + + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_rel_line_to); + +/** + * cairo_rel_curve_to: + * @cr: a cairo context + * @dx1: the X offset to the first control point + * @dy1: the Y offset to the first control point + * @dx2: the X offset to the second control point + * @dy2: the Y offset to the second control point + * @dx3: the X offset to the end of the curve + * @dy3: the Y offset to the end of the curve + * + * Relative-coordinate version of cairo_curve_to(). All offsets are + * relative to the current point. Adds a cubic Bézier spline to the + * path from the current point to a point offset from the current + * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and + * (@dx2, @dy2) as the control points. After this call the current + * point will be offset by (@dx3, @dy3). + * + * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1, + * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to + * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3). + * + * It is an error to call this function with no current point. Doing + * so will cause @cr to shutdown with a status of + * %CAIRO_STATUS_NO_CURRENT_POINT. + **/ +void +cairo_rel_curve_to (cairo_t *cr, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3) +{ + cairo_fixed_t dx1_fixed, dy1_fixed; + cairo_fixed_t dx2_fixed, dy2_fixed; + cairo_fixed_t dx3_fixed, dy3_fixed; + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3); + + dx1_fixed = _cairo_fixed_from_double (dx1); + dy1_fixed = _cairo_fixed_from_double (dy1); + + dx2_fixed = _cairo_fixed_from_double (dx2); + dy2_fixed = _cairo_fixed_from_double (dy2); + + dx3_fixed = _cairo_fixed_from_double (dx3); + dy3_fixed = _cairo_fixed_from_double (dy3); + + status = _cairo_path_fixed_rel_curve_to (cr->path, + dx1_fixed, dy1_fixed, + dx2_fixed, dy2_fixed, + dx3_fixed, dy3_fixed); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_rectangle: + * @cr: a cairo context + * @x: the X coordinate of the top left corner of the rectangle + * @y: the Y coordinate to the top left corner of the rectangle + * @width: the width of the rectangle + * @height: the height of the rectangle + * + * Adds a closed sub-path rectangle of the given size to the current + * path at position (@x, @y) in user-space coordinates. + * + * This function is logically equivalent to: + * + * 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); + * + **/ +void +cairo_rectangle (cairo_t *cr, + double x, double y, + double width, double height) +{ + if (unlikely (cr->status)) + return; + + cairo_move_to (cr, x, y); + cairo_rel_line_to (cr, width, 0); + cairo_rel_line_to (cr, 0, height); + cairo_rel_line_to (cr, -width, 0); + cairo_close_path (cr); +} + +#if 0 +/* XXX: NYI */ +void +cairo_stroke_to_path (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */ + + status = _cairo_gstate_stroke_path (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +#endif + +/** + * cairo_close_path: + * @cr: a cairo context + * + * Adds a line segment to the path from the current point to the + * beginning of the current sub-path, (the most recent point passed to + * cairo_move_to()), and closes this sub-path. After this call the + * current point will be at the joined endpoint of the sub-path. + * + * The behavior of cairo_close_path() is distinct from simply calling + * cairo_line_to() with the equivalent coordinate in the case of + * stroking. When a closed sub-path is stroked, there are no caps on + * the ends of the sub-path. Instead, there is a line join connecting + * the final and initial segments of the sub-path. + * + * If there is no current point before the call to cairo_close_path(), + * this function will have no effect. + * + * Note: As of cairo version 1.2.4 any call to cairo_close_path() will + * place an explicit MOVE_TO element into the path immediately after + * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for + * example). This can simplify path processing in some cases as it may + * not be necessary to save the "last move_to point" during processing + * as the MOVE_TO immediately after the CLOSE_PATH will provide that + * point. + **/ +void +cairo_close_path (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_path_fixed_close_path (cr->path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_close_path); + +/** + * cairo_path_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user-space coordinates covering the + * points on the current path. If the current path is empty, returns + * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule, + * surface dimensions and clipping are not taken into account. + * + * Contrast with cairo_fill_extents() and cairo_stroke_extents() which + * return the extents of only the area that would be "inked" by + * the corresponding drawing operations. + * + * The result of cairo_path_extents() is defined as equivalent to the + * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the + * line width approaches 0.0, (but never reaching the empty-rectangle + * returned by cairo_stroke_extents() for a line width of 0.0). + * + * Specifically, this means that zero-area sub-paths such as + * cairo_move_to();cairo_line_to() segments, (even degenerate cases + * where the coordinates to both calls are identical), will be + * considered as contributing to the extents. However, a lone + * cairo_move_to() will not contribute to the results of + * cairo_path_extents(). + * + * Since: 1.6 + **/ +void +cairo_path_extents (cairo_t *cr, + double *x1, double *y1, double *x2, double *y2) +{ + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + _cairo_gstate_path_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); +} + +/** + * cairo_paint: + * @cr: a cairo context + * + * A drawing operator that paints the current source everywhere within + * the current clip region. + **/ +void +cairo_paint (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_paint (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_paint); + +/** + * cairo_paint_with_alpha: + * @cr: a cairo context + * @alpha: alpha value, between 0 (transparent) and 1 (opaque) + * + * A drawing operator that paints the current source everywhere within + * the current clip region using a mask of constant alpha value + * @alpha. The effect is similar to cairo_paint(), but the drawing + * is faded out using the alpha value. + **/ +void +cairo_paint_with_alpha (cairo_t *cr, + double alpha) +{ + cairo_status_t status; + cairo_color_t color; + cairo_solid_pattern_t pattern; + + if (unlikely (cr->status)) + return; + + if (CAIRO_ALPHA_IS_OPAQUE (alpha)) { + cairo_paint (cr); + return; + } + + if (CAIRO_ALPHA_IS_ZERO (alpha) && + _cairo_operator_bounded_by_mask (cr->gstate->op)) { + return; + } + + _cairo_color_init_rgba (&color, 0., 0., 0., alpha); + _cairo_pattern_init_solid (&pattern, &color); + + status = _cairo_gstate_mask (cr->gstate, &pattern.base); + if (unlikely (status)) + _cairo_set_error (cr, status); + + _cairo_pattern_fini (&pattern.base); +} + +/** + * cairo_mask: + * @cr: a cairo context + * @pattern: a #cairo_pattern_t + * + * A drawing operator that paints the current source + * using the alpha channel of @pattern as a mask. (Opaque + * areas of @pattern are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (pattern == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (pattern->status) { + _cairo_set_error (cr, pattern->status); + return; + } + + status = _cairo_gstate_mask (cr->gstate, pattern); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_mask); + +/** + * cairo_mask_surface: + * @cr: a cairo context + * @surface: a #cairo_surface_t + * @surface_x: X coordinate at which to place the origin of @surface + * @surface_y: Y coordinate at which to place the origin of @surface + * + * A drawing operator that paints the current source + * using the alpha channel of @surface as a mask. (Opaque + * areas of @surface are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + if (unlikely (cr->status)) + return; + + pattern = cairo_pattern_create_for_surface (surface); + + cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); +} + +/** + * cairo_stroke: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. After + * cairo_stroke(), the current path will be cleared from the cairo + * context. See cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + * + * Note: Degenerate segments and sub-paths are treated specially and + * provide a useful result. These can result in two different + * situations: + * + * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap + * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these + * segments will be drawn as circular dots or squares respectively. In + * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares + * is determined by the direction of the underlying path. + * + * 2. A sub-path created by cairo_move_to() followed by either a + * cairo_close_path() or one or more calls to cairo_line_to() to the + * same coordinate as the cairo_move_to(). If the cap style is + * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular + * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate + * sub-path will not be drawn at all, (since the correct orientation + * is indeterminate). + * + * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything + * to be drawn in the case of either degenerate segments or sub-paths. + **/ +void +cairo_stroke (cairo_t *cr) +{ + cairo_stroke_preserve (cr); + + cairo_new_path (cr); +} +slim_hidden_def(cairo_stroke); + +/** + * cairo_stroke_preserve: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. Unlike + * cairo_stroke(), cairo_stroke_preserve() preserves the path within the + * cairo context. + * + * See cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + **/ +void +cairo_stroke_preserve (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_stroke (cr->gstate, cr->path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_stroke_preserve); + +/** + * cairo_fill: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule, (each sub-path is implicitly closed before being + * filled). After cairo_fill(), the current path will be cleared from + * the cairo context. See cairo_set_fill_rule() and + * cairo_fill_preserve(). + **/ +void +cairo_fill (cairo_t *cr) +{ + cairo_fill_preserve (cr); + + cairo_new_path (cr); +} + +/** + * cairo_fill_preserve: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule, (each sub-path is implicitly closed before being + * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the + * path within the cairo context. + * + * See cairo_set_fill_rule() and cairo_fill(). + **/ +void +cairo_fill_preserve (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_fill (cr->gstate, cr->path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_fill_preserve); + +/** + * cairo_copy_page: + * @cr: a cairo context + * + * Emits the current page for backends that support multiple pages, but + * doesn't clear it, so, the contents of the current page will be retained + * for the next page too. Use cairo_show_page() if you want to get an + * empty page after the emission. + * + * This is a convenience function that simply calls + * cairo_surface_copy_page() on @cr's target. + **/ +void +cairo_copy_page (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_copy_page (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_page: + * @cr: a cairo context + * + * Emits and clears the current page for backends that support multiple + * pages. Use cairo_copy_page() if you don't want to clear the page. + * + * This is a convenience function that simply calls + * cairo_surface_show_page() on @cr's target. + **/ +void +cairo_show_page (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_show_page (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_in_stroke: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * affected by a cairo_stroke() operation given the current path and + * stroking parameters. Surface dimensions and clipping are not taken + * into account. + * + * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + * + * Return value: A non-zero value if the point is inside, or zero if + * outside. + **/ +cairo_bool_t +cairo_in_stroke (cairo_t *cr, double x, double y) +{ + cairo_status_t status; + cairo_bool_t inside = FALSE; + + if (unlikely (cr->status)) + return FALSE; + + status = _cairo_gstate_in_stroke (cr->gstate, + cr->path, + x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); + + return inside; +} + +/** + * cairo_in_fill: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * affected by a cairo_fill() operation given the current path and + * filling parameters. Surface dimensions and clipping are not taken + * into account. + * + * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). + * + * Return value: A non-zero value if the point is inside, or zero if + * outside. + **/ +cairo_bool_t +cairo_in_fill (cairo_t *cr, double x, double y) +{ + if (unlikely (cr->status)) + return FALSE; + + return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y); +} + +/** + * cairo_stroke_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area that + * would be affected, (the "inked" area), by a cairo_stroke() + * operation given the current path and stroke parameters. + * If the current path is empty, returns an empty rectangle ((0,0), (0,0)). + * Surface dimensions and clipping are not taken into account. + * + * Note that if the line width is set to exactly zero, then + * cairo_stroke_extents() will return an empty rectangle. Contrast with + * cairo_path_extents() which can be used to compute the non-empty + * bounds as the line width approaches zero. + * + * Note that cairo_stroke_extents() must necessarily do more work to + * compute the precise inked areas in light of the stroke parameters, + * so cairo_path_extents() may be more desirable for sake of + * performance if non-inked path extents are desired. + * + * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + **/ +void +cairo_stroke_extents (cairo_t *cr, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + status = _cairo_gstate_stroke_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_fill_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area that + * would be affected, (the "inked" area), by a cairo_fill() operation + * given the current path and fill parameters. If the current path is + * empty, returns an empty rectangle ((0,0), (0,0)). Surface + * dimensions and clipping are not taken into account. + * + * Contrast with cairo_path_extents(), which is similar, but returns + * non-zero extents for some paths with no inked area, (such as a + * simple line segment). + * + * Note that cairo_fill_extents() must necessarily do more work to + * compute the precise inked areas in light of the fill rule, so + * cairo_path_extents() may be more desirable for sake of performance + * if the non-inked path extents are desired. + * + * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). + **/ +void +cairo_fill_extents (cairo_t *cr, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + status = _cairo_gstate_fill_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_clip: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * After cairo_clip(), the current path will be cleared from the cairo + * context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * temporary restriction of the clip region can be achieved by + * calling cairo_clip() within a cairo_save()/cairo_restore() + * pair. The only other means of increasing the size of the clip + * region is cairo_reset_clip(). + **/ +void +cairo_clip (cairo_t *cr) +{ + cairo_clip_preserve (cr); + + cairo_new_path (cr); +} + +/** + * cairo_clip_preserve: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within + * the cairo context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip_preserve() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * temporary restriction of the clip region can be achieved by + * calling cairo_clip_preserve() within a cairo_save()/cairo_restore() + * pair. The only other means of increasing the size of the clip + * region is cairo_reset_clip(). + **/ +void +cairo_clip_preserve (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_clip (cr->gstate, cr->path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_clip_preserve); + +/** + * cairo_reset_clip: + * @cr: a cairo context + * + * Reset the current clip region to its original, unrestricted + * state. That is, set the clip region to an infinitely large shape + * containing the target surface. Equivalently, if infinity is too + * hard to grasp, one can imagine the clip region being reset to the + * exact bounds of the target surface. + * + * Note that code meant to be reusable should not call + * cairo_reset_clip() as it will cause results unexpected by + * higher-level code which calls cairo_clip(). Consider using + * cairo_save() and cairo_restore() around cairo_clip() as a more + * robust means of temporarily restricting the clip region. + **/ +void +cairo_reset_clip (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_reset_clip (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_clip_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area inside the + * current clip. + * + * Since: 1.4 + **/ +void +cairo_clip_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2) +{ + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { + *x1 = -INFINITY; + *y1 = -INFINITY; + *x2 = +INFINITY; + *y2 = +INFINITY; + } +} + +/** + * cairo_in_clip: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * visible through the current clip, i.e. the area that would be filled by + * a cairo_paint() operation. + * + * See cairo_clip(), and cairo_clip_preserve(). + * + * Return value: A non-zero value if the point is inside, or zero if + * outside. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_in_clip (cairo_t *cr, double x, double y) +{ + if (unlikely (cr->status)) + return FALSE; + + return _cairo_gstate_in_clip (cr->gstate, x, y); +} + +static cairo_rectangle_list_t * +_cairo_rectangle_list_create_in_error (cairo_status_t status) +{ + cairo_rectangle_list_t *list; + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + + list = malloc (sizeof (cairo_rectangle_list_t)); + if (unlikely (list == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + } + + list->status = status; + list->rectangles = NULL; + list->num_rectangles = 0; + return list; +} + +/** + * cairo_copy_clip_rectangle_list: + * @cr: a cairo context + * + * Gets the current clip region as a list of rectangles in user coordinates. + * Never returns %NULL. + * + * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to + * indicate that the clip region cannot be represented as a list of + * user-space rectangles. The status may have other values to indicate + * other errors. + * + * Returns: the current clip region as a list of rectangles in user coordinates, + * which should be destroyed using cairo_rectangle_list_destroy(). + * + * Since: 1.4 + **/ +cairo_rectangle_list_t * +cairo_copy_clip_rectangle_list (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_rectangle_list_create_in_error (cr->status); + + return _cairo_gstate_copy_clip_rectangle_list (cr->gstate); +} + +/** + * cairo_select_font_face: + * @cr: a #cairo_t + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Note: The cairo_select_font_face() function call is part of what + * the cairo designers call the "toy" text API. It is convenient for + * short demos and simple programs, but it is not expected to be + * adequate for serious text-using applications. + * + * Selects a family and style of font from a simplified description as + * a family name, slant and weight. Cairo provides no operation to + * list available family names on the system (this is a "toy", + * remember), but the standard CSS2 generic family names, ("serif", + * "sans-serif", "cursive", "fantasy", "monospace"), are likely to + * work as expected. + * + * If @family starts with the string "@cairo:", or if no native font + * backends are compiled in, cairo will use an internal font family. + * The internal font family recognizes many modifiers in the @family + * string, most notably, it recognizes the string "monospace". That is, + * the family name "@cairo:monospace" will use the monospace version of + * the internal font family. + * + * For "real" font selection, see the font-backend-specific + * font_face_create functions for the font backend you are using. (For + * example, if you are using the freetype-based cairo-ft font backend, + * see cairo_ft_font_face_create_for_ft_face() or + * cairo_ft_font_face_create_for_pattern().) The resulting font face + * could then be used with cairo_scaled_font_create() and + * cairo_set_scaled_font(). + * + * Similarly, when using the "real" font support, you can call + * directly into the underlying font system, (such as fontconfig or + * freetype), for operations such as listing available fonts, etc. + * + * It is expected that most applications will need to use a more + * comprehensive font handling and text layout library, (for example, + * pango), in conjunction with cairo. + * + * If text is drawn without a call to cairo_select_font_face(), (nor + * cairo_set_font_face() nor cairo_set_scaled_font()), the default + * family is platform-specific, but is essentially "sans-serif". + * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is + * %CAIRO_FONT_WEIGHT_NORMAL. + * + * This function is equivalent to a call to cairo_toy_font_face_create() + * followed by cairo_set_font_face(). + **/ +void +cairo_select_font_face (cairo_t *cr, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_font_extents: + * @cr: a #cairo_t + * @extents: a #cairo_font_extents_t object into which the results + * will be stored. + * + * Gets the font extents for the currently selected font. + **/ +void +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents) +{ + cairo_status_t status; + + extents->ascent = 0.0; + extents->descent = 0.0; + extents->height = 0.0; + extents->max_x_advance = 0.0; + extents->max_y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_get_font_extents (cr->gstate, extents); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_font_face: + * @cr: a #cairo_t + * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font + * + * Replaces the current #cairo_font_face_t object in the #cairo_t with + * @font_face. The replaced font face in the #cairo_t will be + * destroyed if there are no other references to it. + **/ +void +cairo_set_font_face (cairo_t *cr, + cairo_font_face_t *font_face) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_font_face (cr->gstate, font_face); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_font_face: + * @cr: a #cairo_t + * + * Gets the current font face for a #cairo_t. + * + * Return value: the current font face. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_font_face_reference(). + * + * This function never returns %NULL. If memory cannot be allocated, a + * special "nil" #cairo_font_face_t object will be returned on which + * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using + * this nil object will cause its error state to propagate to other + * objects it is passed to, (for example, calling + * cairo_set_font_face() with a nil font will trigger an error that + * will shutdown the #cairo_t object). + **/ +cairo_font_face_t * +cairo_get_font_face (cairo_t *cr) +{ + cairo_status_t status; + cairo_font_face_t *font_face; + + if (unlikely (cr->status)) + return (cairo_font_face_t*) &_cairo_font_face_nil; + + status = _cairo_gstate_get_font_face (cr->gstate, &font_face); + if (unlikely (status)) { + _cairo_set_error (cr, status); + return (cairo_font_face_t*) &_cairo_font_face_nil; + } + + return font_face; +} + +/** + * cairo_set_font_size: + * @cr: a #cairo_t + * @size: the new font size, in user space units + * + * Sets the current font matrix to a scale by a factor of @size, replacing + * any font matrix previously set with cairo_set_font_size() or + * cairo_set_font_matrix(). This results in a font size of @size user space + * units. (More precisely, this matrix will result in the font's + * em-square being a @size by @size square in user space.) + * + * If text is drawn without a call to cairo_set_font_size(), (nor + * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default + * font size is 10.0. + **/ +void +cairo_set_font_size (cairo_t *cr, double size) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_font_size (cr->gstate, size); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_font_size); + +/** + * cairo_set_font_matrix + * @cr: a #cairo_t + * @matrix: a #cairo_matrix_t describing a transform to be applied to + * the current font. + * + * Sets the current font matrix to @matrix. The font matrix gives a + * transformation from the design space of the font (in this space, + * the em-square is 1 unit by 1 unit) to user space. Normally, a + * simple scale is used (see cairo_set_font_size()), but a more + * complex font matrix can be used to shear the font + * or stretch it unequally along the two axes + **/ +void +cairo_set_font_matrix (cairo_t *cr, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = _cairo_gstate_set_font_matrix (cr->gstate, matrix); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_font_matrix + * @cr: a #cairo_t + * @matrix: return value for the matrix + * + * Stores the current font matrix into @matrix. See + * cairo_set_font_matrix(). + **/ +void +cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) +{ + if (unlikely (cr->status)) { + cairo_matrix_init_identity (matrix); + return; + } + + _cairo_gstate_get_font_matrix (cr->gstate, matrix); +} + +/** + * cairo_set_font_options: + * @cr: a #cairo_t + * @options: font options to use + * + * Sets a set of custom font rendering options for the #cairo_t. + * Rendering options are derived by merging these options with the + * options derived from underlying surface; if the value in @options + * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value + * from the surface is used. + **/ +void +cairo_set_font_options (cairo_t *cr, + const cairo_font_options_t *options) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) { + _cairo_set_error (cr, status); + return; + } + + _cairo_gstate_set_font_options (cr->gstate, options); +} +slim_hidden_def (cairo_set_font_options); + +/** + * cairo_get_font_options: + * @cr: a #cairo_t + * @options: a #cairo_font_options_t object into which to store + * the retrieved options. All existing values are overwritten + * + * Retrieves font rendering options set via #cairo_set_font_options. + * Note that the returned options do not include any options derived + * from the underlying surface; they are literally the options + * passed to cairo_set_font_options(). + **/ +void +cairo_get_font_options (cairo_t *cr, + cairo_font_options_t *options) +{ + /* check that we aren't trying to overwrite the nil object */ + if (cairo_font_options_status (options)) + return; + + if (unlikely (cr->status)) { + _cairo_font_options_init_default (options); + return; + } + + _cairo_gstate_get_font_options (cr->gstate, options); +} + +/** + * cairo_set_scaled_font: + * @cr: a #cairo_t + * @scaled_font: a #cairo_scaled_font_t + * + * Replaces the current font face, font matrix, and font options in + * the #cairo_t with those of the #cairo_scaled_font_t. Except for + * some translation, the current CTM of the #cairo_t should be the + * same as that of the #cairo_scaled_font_t, which can be accessed + * using cairo_scaled_font_get_ctm(). + * + * Since: 1.2 + **/ +void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_bool_t was_previous; + + if (unlikely (cr->status)) + return; + + if (scaled_font == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto BAIL; + } + + status = scaled_font->status; + if (unlikely (status)) + goto BAIL; + + if (scaled_font == cr->gstate->scaled_font) + return; + + was_previous = scaled_font == cr->gstate->previous_scaled_font; + + status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); + if (unlikely (status)) + goto BAIL; + + status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); + if (unlikely (status)) + goto BAIL; + + _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); + + /* XXX: Mozilla code assumes that the ctm of a scaled font doesn't need to + * match the context ctm. This assumption breaks the previous_scaled_font + * cache. So we avoid using the cache for now. + if (was_previous) + cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font); + */ + + return; + +BAIL: + _cairo_set_error (cr, status); +} + +/** + * cairo_get_scaled_font: + * @cr: a #cairo_t + * + * Gets the current scaled font for a #cairo_t. + * + * Return value: the current scaled font. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_scaled_font_reference(). + * + * This function never returns %NULL. If memory cannot be allocated, a + * special "nil" #cairo_scaled_font_t object will be returned on which + * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using + * this nil object will cause its error state to propagate to other + * objects it is passed to, (for example, calling + * cairo_set_scaled_font() with a nil font will trigger an error that + * will shutdown the #cairo_t object). + * + * Since: 1.4 + **/ +cairo_scaled_font_t * +cairo_get_scaled_font (cairo_t *cr) +{ + cairo_status_t status; + cairo_scaled_font_t *scaled_font; + + if (unlikely (cr->status)) + return _cairo_scaled_font_create_in_error (cr->status); + + status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); + if (unlikely (status)) { + _cairo_set_error (cr, status); + return _cairo_scaled_font_create_in_error (status); + } + + return scaled_font; +} + +/** + * cairo_text_extents: + * @cr: a #cairo_t + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * @extents: a #cairo_text_extents_t object into which the results + * will be stored + * + * Gets the extents for a string of text. The extents describe a + * user-space rectangle that encloses the "inked" portion of the text, + * (as it would be drawn by cairo_show_text()). Additionally, the + * x_advance and y_advance values indicate the amount by which the + * current point would be advanced by cairo_show_text(). + * + * Note that whitespace characters do not directly contribute to the + * size of the rectangle (extents.width and extents.height). They do + * contribute indirectly by changing the position of non-whitespace + * characters. In particular, trailing whitespace characters are + * likely to not affect the size of the rectangle, though they will + * affect the x_advance and y_advance values. + **/ +void +cairo_text_extents (cairo_t *cr, + const char *utf8, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_glyph_t *glyphs = NULL; + int num_glyphs; + double x, y; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + if (utf8 == NULL) + return; + + cairo_get_current_point (cr, &x, &y); + + status = _cairo_gstate_text_to_glyphs (cr->gstate, + x, y, + utf8, strlen (utf8), + &glyphs, &num_glyphs, + NULL, NULL, + NULL); + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_gstate_glyph_extents (cr->gstate, + glyphs, num_glyphs, + extents); + cairo_glyph_free (glyphs); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_glyph_extents: + * @cr: a #cairo_t + * @glyphs: an array of #cairo_glyph_t objects + * @num_glyphs: the number of elements in @glyphs + * @extents: a #cairo_text_extents_t object into which the results + * will be stored + * + * Gets the extents for an array of glyphs. The extents describe a + * user-space rectangle that encloses the "inked" portion of the + * glyphs, (as they would be drawn by cairo_show_glyphs()). + * Additionally, the x_advance and y_advance values indicate the + * amount by which the current point would be advanced by + * cairo_show_glyphs(). + * + * Note that whitespace glyphs do not contribute to the size of the + * rectangle (extents.width and extents.height). + **/ +void +cairo_glyph_extents (cairo_t *cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (num_glyphs < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (glyphs == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, + extents); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_text: + * @cr: a cairo context + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * + * A drawing operator that generates the shape from a string of UTF-8 + * characters, rendered according to the current font_face, font_size + * (font_matrix), and font_options. + * + * This function first computes a set of glyphs for the string of + * text. The first glyph is placed so that its origin is at the + * current point. The origin of each subsequent glyph is offset from + * that of the previous glyph by the advance values of the previous + * glyph. + * + * After this call the current point is moved to the origin of where + * the next glyph would be placed in this same progression. That is, + * the current point will be at the origin of the final glyph offset + * by its advance values. This allows for easy display of a single + * logical string with multiple calls to cairo_show_text(). + * + * Note: The cairo_show_text() function call is part of what the cairo + * designers call the "toy" text API. It is convenient for short demos + * and simple programs, but it is not expected to be adequate for + * serious text-using applications. See cairo_show_glyphs() for the + * "real" text display API in cairo. + **/ +void +cairo_show_text (cairo_t *cr, const char *utf8) +{ + cairo_text_extents_t extents; + cairo_status_t status; + cairo_glyph_t *glyphs, *last_glyph; + cairo_text_cluster_t *clusters; + int utf8_len, num_glyphs, num_clusters; + cairo_text_cluster_flags_t cluster_flags; + double x, y; + cairo_bool_t has_show_text_glyphs; + cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; + + if (unlikely (cr->status)) + return; + + if (utf8 == NULL) + return; + + cairo_get_current_point (cr, &x, &y); + + utf8_len = strlen (utf8); + + has_show_text_glyphs = + cairo_surface_has_show_text_glyphs (cairo_get_target (cr)); + + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + + if (has_show_text_glyphs) { + clusters = stack_clusters; + num_clusters = ARRAY_LENGTH (stack_clusters); + } else { + clusters = NULL; + num_clusters = 0; + } + + status = _cairo_gstate_text_to_glyphs (cr->gstate, + x, y, + utf8, utf8_len, + &glyphs, &num_glyphs, + has_show_text_glyphs ? &clusters : NULL, &num_clusters, + &cluster_flags); + if (unlikely (status)) + goto BAIL; + + if (num_glyphs == 0) + return; + + status = _cairo_gstate_show_text_glyphs (cr->gstate, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags); + if (unlikely (status)) + goto BAIL; + + last_glyph = &glyphs[num_glyphs - 1]; + status = _cairo_gstate_glyph_extents (cr->gstate, + last_glyph, 1, + &extents); + if (unlikely (status)) + goto BAIL; + + x = last_glyph->x + extents.x_advance; + y = last_glyph->y + extents.y_advance; + cairo_move_to (cr, x, y); + + BAIL: + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); + if (clusters != stack_clusters) + cairo_text_cluster_free (clusters); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_glyphs: + * @cr: a cairo context + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * + * A drawing operator that generates the shape from an array of glyphs, + * rendered according to the current font face, font size + * (font matrix), and font options. + **/ +void +cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (num_glyphs < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (glyphs == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = _cairo_gstate_show_text_glyphs (cr->gstate, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_text_glyphs: + * @cr: a cairo context + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * @clusters: array of cluster mapping information + * @num_clusters: number of clusters in the mapping + * @cluster_flags: cluster mapping flags + * + * This operation has rendering effects similar to cairo_show_glyphs() + * but, if the target surface supports it, uses the provided text and + * cluster mapping to embed the text for the glyphs shown in the output. + * If the target does not support the extended attributes, this function + * acts like the basic cairo_show_glyphs() as if it had been passed + * @glyphs and @num_glyphs. + * + * The mapping between @utf8 and @glyphs is provided by an array of + * clusters. Each cluster covers a number of + * text bytes and glyphs, and neighboring clusters cover neighboring + * areas of @utf8 and @glyphs. The clusters should collectively cover @utf8 + * and @glyphs in entirety. + * + * The first cluster always covers bytes from the beginning of @utf8. + * If @cluster_flags do not have the %CAIRO_TEXT_CLUSTER_FLAG_BACKWARD + * set, the first cluster also covers the beginning + * of @glyphs, otherwise it covers the end of the @glyphs array and + * following clusters move backward. + * + * See #cairo_text_cluster_t for constraints on valid clusters. + * + * Since: 1.8 + **/ +void +cairo_show_text_glyphs (cairo_t *cr, + const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + /* A slew of sanity checks */ + + /* Special case for NULL and -1 */ + if (utf8 == NULL && utf8_len == -1) + utf8_len = 0; + + /* No NULLs for non-zeros */ + if ((num_glyphs && glyphs == NULL) || + (utf8_len && utf8 == NULL) || + (num_clusters && clusters == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + /* A -1 for utf8_len means NUL-terminated */ + if (utf8_len == -1) + utf8_len = strlen (utf8); + + /* Apart from that, no negatives */ + if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + /* Make sure clusters cover the entire glyphs and utf8 arrays, + * and that cluster boundaries are UTF-8 boundaries. */ + status = _cairo_validate_text_clusters (utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + if (status == CAIRO_STATUS_INVALID_CLUSTERS) { + /* Either got invalid UTF-8 text, or cluster mapping is bad. + * Differentiate those. */ + + cairo_status_t status2; + + status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL); + if (status2) + status = status2; + + _cairo_set_error (cr, status); + return; + } + + if (num_glyphs == 0 && utf8_len == 0) + return; + + status = _cairo_gstate_show_text_glyphs (cr->gstate, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_text_path: + * @cr: a cairo context + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * + * Adds closed paths for text to the current path. The generated + * path if filled, achieves an effect similar to that of + * cairo_show_text(). + * + * Text conversion and positioning is done similar to cairo_show_text(). + * + * Like cairo_show_text(), After this call the current point is + * moved to the origin of where the next glyph would be placed in + * this same progression. That is, the current point will be at + * the origin of the final glyph offset by its advance values. + * This allows for chaining multiple calls to to cairo_text_path() + * without having to set current point in between. + * + * Note: The cairo_text_path() function call is part of what the cairo + * designers call the "toy" text API. It is convenient for short demos + * and simple programs, but it is not expected to be adequate for + * serious text-using applications. See cairo_glyph_path() for the + * "real" text path API in cairo. + **/ +void +cairo_text_path (cairo_t *cr, const char *utf8) +{ + cairo_status_t status; + cairo_text_extents_t extents; + cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_glyph_t *glyphs, *last_glyph; + int num_glyphs; + double x, y; + + if (unlikely (cr->status)) + return; + + if (utf8 == NULL) + return; + + cairo_get_current_point (cr, &x, &y); + + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + + status = _cairo_gstate_text_to_glyphs (cr->gstate, + x, y, + utf8, strlen (utf8), + &glyphs, &num_glyphs, + NULL, NULL, + NULL); + + if (unlikely (status)) + goto BAIL; + + if (num_glyphs == 0) + return; + + status = _cairo_gstate_glyph_path (cr->gstate, + glyphs, num_glyphs, + cr->path); + + if (unlikely (status)) + goto BAIL; + + last_glyph = &glyphs[num_glyphs - 1]; + status = _cairo_gstate_glyph_extents (cr->gstate, + last_glyph, 1, + &extents); + + if (unlikely (status)) + goto BAIL; + + x = last_glyph->x + extents.x_advance; + y = last_glyph->y + extents.y_advance; + cairo_move_to (cr, x, y); + + BAIL: + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_glyph_path: + * @cr: a cairo context + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * + * Adds closed paths for the glyphs to the current path. The generated + * path if filled, achieves an effect similar to that of + * cairo_show_glyphs(). + **/ +void +cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (num_glyphs < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (glyphs == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = _cairo_gstate_glyph_path (cr->gstate, + glyphs, num_glyphs, + cr->path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_operator: + * @cr: a cairo context + * + * Gets the current compositing operator for a cairo context. + * + * Return value: the current compositing operator. + **/ +cairo_operator_t +cairo_get_operator (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_OPERATOR_DEFAULT; + + return _cairo_gstate_get_operator (cr->gstate); +} + +/** + * cairo_get_tolerance: + * @cr: a cairo context + * + * Gets the current tolerance value, as set by cairo_set_tolerance(). + * + * Return value: the current tolerance value. + **/ +double +cairo_get_tolerance (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_TOLERANCE_DEFAULT; + + return _cairo_gstate_get_tolerance (cr->gstate); +} +slim_hidden_def (cairo_get_tolerance); + +/** + * cairo_get_antialias: + * @cr: a cairo context + * + * Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias(). + * + * Return value: the current shape antialiasing mode. + **/ +cairo_antialias_t +cairo_get_antialias (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_ANTIALIAS_DEFAULT; + + return _cairo_gstate_get_antialias (cr->gstate); +} + +/** + * cairo_has_current_point: + * @cr: a cairo context + * + * Returns whether a current point is defined on the current path. + * See cairo_get_current_point() for details on the current point. + * + * Return value: whether a current point is defined. + * + * Since: 1.6 + **/ +cairo_bool_t +cairo_has_current_point (cairo_t *cr) +{ + if (unlikely (cr->status)) + return FALSE; + + return cr->path->has_current_point; +} + +/** + * cairo_get_current_point: + * @cr: a cairo context + * @x: return value for X coordinate of the current point + * @y: return value for Y coordinate of the current point + * + * Gets the current point of the current path, which is + * conceptually the final point reached by the path so far. + * + * The current point is returned in the user-space coordinate + * system. If there is no defined current point or if @cr is in an + * error status, @x and @y will both be set to 0.0. It is possible to + * check this in advance with cairo_has_current_point(). + * + * Most path construction functions alter the current point. See the + * following for details on how they affect the current point: + * cairo_new_path(), cairo_new_sub_path(), + * cairo_append_path(), cairo_close_path(), + * cairo_move_to(), cairo_line_to(), cairo_curve_to(), + * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(), + * cairo_arc(), cairo_arc_negative(), cairo_rectangle(), + * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path(). + * + * Some functions use and alter the current point but do not + * otherwise change current path: + * cairo_show_text(). + * + * Some functions unset the current path and as a result, current point: + * cairo_fill(), cairo_stroke(). + **/ +void +cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret) +{ + cairo_fixed_t x_fixed, y_fixed; + double x, y; + + if (cr->status == CAIRO_STATUS_SUCCESS && + _cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) + { + x = _cairo_fixed_to_double (x_fixed); + y = _cairo_fixed_to_double (y_fixed); + _cairo_gstate_backend_to_user (cr->gstate, &x, &y); + } + else + { + x = 0.0; + y = 0.0; + } + + if (x_ret) + *x_ret = x; + if (y_ret) + *y_ret = y; +} +slim_hidden_def(cairo_get_current_point); + +/** + * cairo_get_fill_rule: + * @cr: a cairo context + * + * Gets the current fill rule, as set by cairo_set_fill_rule(). + * + * Return value: the current fill rule. + **/ +cairo_fill_rule_t +cairo_get_fill_rule (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_FILL_RULE_DEFAULT; + + return _cairo_gstate_get_fill_rule (cr->gstate); +} + +/** + * cairo_get_line_width: + * @cr: a cairo context + * + * This function returns the current line width value exactly as set by + * cairo_set_line_width(). Note that the value is unchanged even if + * the CTM has changed between the calls to cairo_set_line_width() and + * cairo_get_line_width(). + * + * Return value: the current line width. + **/ +double +cairo_get_line_width (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_WIDTH_DEFAULT; + + return _cairo_gstate_get_line_width (cr->gstate); +} +slim_hidden_def (cairo_get_line_width); + +/** + * cairo_get_line_cap: + * @cr: a cairo context + * + * Gets the current line cap style, as set by cairo_set_line_cap(). + * + * Return value: the current line cap style. + **/ +cairo_line_cap_t +cairo_get_line_cap (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_CAP_DEFAULT; + + return _cairo_gstate_get_line_cap (cr->gstate); +} + +/** + * cairo_get_line_join: + * @cr: a cairo context + * + * Gets the current line join style, as set by cairo_set_line_join(). + * + * Return value: the current line join style. + **/ +cairo_line_join_t +cairo_get_line_join (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_JOIN_DEFAULT; + + return _cairo_gstate_get_line_join (cr->gstate); +} + +/** + * cairo_get_miter_limit: + * @cr: a cairo context + * + * Gets the current miter limit, as set by cairo_set_miter_limit(). + * + * Return value: the current miter limit. + **/ +double +cairo_get_miter_limit (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_MITER_LIMIT_DEFAULT; + + return _cairo_gstate_get_miter_limit (cr->gstate); +} + +/** + * cairo_get_matrix: + * @cr: a cairo context + * @matrix: return value for the matrix + * + * Stores the current transformation matrix (CTM) into @matrix. + **/ +void +cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) +{ + if (unlikely (cr->status)) { + cairo_matrix_init_identity (matrix); + return; + } + + _cairo_gstate_get_matrix (cr->gstate, matrix); +} +slim_hidden_def (cairo_get_matrix); + +/** + * cairo_get_target: + * @cr: a cairo context + * + * Gets the target surface for the cairo context as passed to + * cairo_create(). + * + * This function will always return a valid pointer, but the result + * can be a "nil" surface if @cr is already in an error state, + * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). + * A nil surface is indicated by cairo_surface_status() + * != %CAIRO_STATUS_SUCCESS. + * + * Return value: the target surface. This object is owned by cairo. To + * keep a reference to it, you must call cairo_surface_reference(). + **/ +cairo_surface_t * +cairo_get_target (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_surface_create_in_error (cr->status); + + return _cairo_gstate_get_original_target (cr->gstate); +} +slim_hidden_def (cairo_get_target); + +/** + * cairo_get_group_target: + * @cr: a cairo context + * + * Gets the current destination surface for the context. This is either + * the original target surface as passed to cairo_create() or the target + * surface for the current group as started by the most recent call to + * cairo_push_group() or cairo_push_group_with_content(). + * + * This function will always return a valid pointer, but the result + * can be a "nil" surface if @cr is already in an error state, + * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). + * A nil surface is indicated by cairo_surface_status() + * != %CAIRO_STATUS_SUCCESS. + * + * Return value: the target surface. This object is owned by cairo. To + * keep a reference to it, you must call cairo_surface_reference(). + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_get_group_target (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_surface_create_in_error (cr->status); + + return _cairo_gstate_get_target (cr->gstate); +} + +/** + * cairo_copy_path: + * @cr: a cairo context + * + * Creates a copy of the current path and returns it to the user as a + * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate + * over the returned data structure. + * + * This function will always return a valid pointer, but the result + * will have no data (data==%NULL and + * num_data==0), if either of the following + * conditions hold: + * + * + * If there is insufficient memory to copy the path. In this + * case path->status will be set to + * %CAIRO_STATUS_NO_MEMORY. + * If @cr is already in an error state. In this case + * path->status will contain the same status that + * would be returned by cairo_status(). + * + * + * Return value: the copy of the current path. The caller owns the + * returned object and should call cairo_path_destroy() when finished + * with it. + **/ +cairo_path_t * +cairo_copy_path (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_path_create_in_error (cr->status); + + return _cairo_path_create (cr->path, cr->gstate); +} + +/** + * cairo_copy_path_flat: + * @cr: a cairo context + * + * Gets a flattened copy of the current path and returns it to the + * user as a #cairo_path_t. See #cairo_path_data_t for hints on + * how to iterate over the returned data structure. + * + * This function is like cairo_copy_path() except that any curves + * in the path will be approximated with piecewise-linear + * approximations, (accurate to within the current tolerance + * value). That is, the result is guaranteed to not have any elements + * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a + * series of %CAIRO_PATH_LINE_TO elements. + * + * This function will always return a valid pointer, but the result + * will have no data (data==%NULL and + * num_data==0), if either of the following + * conditions hold: + * + * + * If there is insufficient memory to copy the path. In this + * case path->status will be set to + * %CAIRO_STATUS_NO_MEMORY. + * If @cr is already in an error state. In this case + * path->status will contain the same status that + * would be returned by cairo_status(). + * + * + * Return value: the copy of the current path. The caller owns the + * returned object and should call cairo_path_destroy() when finished + * with it. + **/ +cairo_path_t * +cairo_copy_path_flat (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_path_create_in_error (cr->status); + + return _cairo_path_create_flat (cr->path, cr->gstate); +} + +/** + * cairo_append_path: + * @cr: a cairo context + * @path: path to be appended + * + * Append the @path onto the current path. The @path may be either the + * return value from one of cairo_copy_path() or + * cairo_copy_path_flat() or it may be constructed manually. See + * #cairo_path_t for details on how the path data structure should be + * initialized, and note that path->status must be + * initialized to %CAIRO_STATUS_SUCCESS. + **/ +void +cairo_append_path (cairo_t *cr, + const cairo_path_t *path) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (path == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (path->status) { + if (path->status > CAIRO_STATUS_SUCCESS && + path->status <= CAIRO_STATUS_LAST_STATUS) + _cairo_set_error (cr, path->status); + else + _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS); + return; + } + + if (path->num_data == 0) + return; + + if (path->data == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = _cairo_path_append_to_context (path, cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_status: + * @cr: a cairo context + * + * Checks whether an error has previously occurred for this context. + * + * Returns: the current status of this context, see #cairo_status_t + **/ +cairo_status_t +cairo_status (cairo_t *cr) +{ + return cr->status; +} +slim_hidden_def (cairo_status); diff --git a/libs/cairo/src/cairo.h b/libs/cairo/src/cairo.h new file mode 100644 index 000000000..3a34e80bf --- /dev/null +++ b/libs/cairo/src/cairo.h @@ -0,0 +1,2696 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CAIRO_H +#define CAIRO_H + +#include "cairo-version.h" +#include "cairo-features.h" +#include "cairo-deprecated.h" + +#ifdef __cplusplus +# define CAIRO_BEGIN_DECLS extern "C" { +# define CAIRO_END_DECLS } +#else +# define CAIRO_BEGIN_DECLS +# define CAIRO_END_DECLS +#endif + +#ifndef cairo_public +# if defined (_MSC_VER) && ! defined (CAIRO_WIN32_STATIC_BUILD) +# define cairo_public __declspec(dllimport) +# else +# define cairo_public +# endif +#endif + +CAIRO_BEGIN_DECLS + +#define CAIRO_VERSION_ENCODE(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define CAIRO_VERSION CAIRO_VERSION_ENCODE( \ + CAIRO_VERSION_MAJOR, \ + CAIRO_VERSION_MINOR, \ + CAIRO_VERSION_MICRO) + + +#define CAIRO_VERSION_STRINGIZE_(major, minor, micro) \ + #major"."#minor"."#micro +#define CAIRO_VERSION_STRINGIZE(major, minor, micro) \ + CAIRO_VERSION_STRINGIZE_(major, minor, micro) + +#define CAIRO_VERSION_STRING CAIRO_VERSION_STRINGIZE( \ + CAIRO_VERSION_MAJOR, \ + CAIRO_VERSION_MINOR, \ + CAIRO_VERSION_MICRO) + + +cairo_public int +cairo_version (void); + +cairo_public const char* +cairo_version_string (void); + +/** + * cairo_bool_t: + * + * #cairo_bool_t is used for boolean values. Returns of type + * #cairo_bool_t will always be either 0 or 1, but testing against + * these values explicitly is not encouraged; just use the + * value as a boolean condition. + * + * + * if (cairo_in_stroke (cr, x, y)) { + * /* do something */ + * } + * + **/ +typedef int cairo_bool_t; + +/** + * cairo_t: + * + * A #cairo_t contains the current state of the rendering device, + * including coordinates of yet to be drawn shapes. + * + * Cairo contexts, as #cairo_t objects are named, are central to + * cairo and all drawing with cairo is always done to a #cairo_t + * object. + * + * Memory management of #cairo_t is done with + * cairo_reference() and cairo_destroy(). + **/ +typedef struct _cairo cairo_t; + +/** + * cairo_surface_t: + * + * A #cairo_surface_t represents an image, either as the destination + * of a drawing operation or as source when drawing onto another + * surface. To draw to a #cairo_surface_t, create a cairo context + * with the surface as the target, using cairo_create(). + * + * There are different subtypes of #cairo_surface_t for + * different drawing backends; for example, cairo_image_surface_create() + * creates a bitmap image in memory. + * The type of a surface can be queried with cairo_surface_get_type(). + * + * The initial contents of a surface after creation depend upon the manner + * of its creation. If cairo creates the surface and backing storage for + * the user, it will be initially cleared; for example, + * cairo_image_surface_create() and cairo_surface_create_similar(). + * Alternatively, if the user passes in a reference to some backing storage + * and asks cairo to wrap that in a #cairo_surface_t, then the contents are + * not modified; for example, cairo_image_surface_create_for_data() and + * cairo_xlib_surface_create(). + * + * Memory management of #cairo_surface_t is done with + * cairo_surface_reference() and cairo_surface_destroy(). + **/ +typedef struct _cairo_surface cairo_surface_t; + +/** + * cairo_device_t: + * + * A #cairo_device_t represents the driver interface for drawing + * operations to a #cairo_surface_t. There are different subtypes of + * #cairo_device_t for different drawing backends; for example, + * cairo_xcb_device_create() creates a device that wraps the connection + * to an X Windows System using the XCB library. + * + * The type of a device can be queried with cairo_device_get_type(). + * + * Memory management of #cairo_device_t is done with + * cairo_device_reference() and cairo_device_destroy(). + * + * Since: 1.10 + **/ +typedef struct _cairo_device cairo_device_t; + +/** + * cairo_matrix_t: + * @xx: xx component of the affine transformation + * @yx: yx component of the affine transformation + * @xy: xy component of the affine transformation + * @yy: yy component of the affine transformation + * @x0: X translation component of the affine transformation + * @y0: Y translation component of the affine transformation + * + * A #cairo_matrix_t holds an affine transformation, such as a scale, + * rotation, shear, or a combination of those. The transformation of + * a point (x, y) is given by: + * + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; + * + **/ +typedef struct _cairo_matrix { + double xx; double yx; + double xy; double yy; + double x0; double y0; +} cairo_matrix_t; + +/** + * cairo_pattern_t: + * + * A #cairo_pattern_t represents a source when drawing onto a + * surface. There are different subtypes of #cairo_pattern_t, + * for different types of sources; for example, + * cairo_pattern_create_rgb() creates a pattern for a solid + * opaque color. + * + * Other than various cairo_pattern_create_type() + * functions, some of the pattern types can be implicitly created + * using various cairo_set_source_type() functions; + * for example cairo_set_source_rgb(). + * + * The type of a pattern can be queried with cairo_pattern_get_type(). + * + * Memory management of #cairo_pattern_t is done with + * cairo_pattern_reference() and cairo_pattern_destroy(). + **/ +typedef struct _cairo_pattern cairo_pattern_t; + +/** + * cairo_destroy_func_t: + * @data: The data element being destroyed. + * + * #cairo_destroy_func_t the type of function which is called when a + * data element is destroyed. It is passed the pointer to the data + * element and should free any memory and resources allocated for it. + **/ +typedef void (*cairo_destroy_func_t) (void *data); + +/** + * cairo_surface_func_t: + * @surface: The surface being referred to. + * + * #cairo_surface_func_t the type of function which is used for callback + * when a surface needs to be apssed as a parameter. + */ +typedef void (*cairo_surface_func_t) (cairo_surface_t *surface); + +/** + * cairo_user_data_key_t: + * @unused: not used; ignore. + * + * #cairo_user_data_key_t is used for attaching user data to cairo + * data structures. The actual contents of the struct is never used, + * and there is no need to initialize the object; only the unique + * address of a #cairo_data_key_t object is used. Typically, you + * would just use the address of a static #cairo_data_key_t object. + **/ +typedef struct _cairo_user_data_key { + int unused; +} cairo_user_data_key_t; + +/** + * cairo_status_t: + * @CAIRO_STATUS_SUCCESS: no error has occurred + * @CAIRO_STATUS_NO_MEMORY: out of memory + * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() + * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() + * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined + * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) + * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t + * @CAIRO_STATUS_NULL_POINTER: %NULL pointer + * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 + * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid + * @CAIRO_STATUS_READ_ERROR: error while reading from input stream + * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream + * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished + * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation + * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation + * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t + * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t + * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* + * @CAIRO_STATUS_FILE_NOT_FOUND: file not found + * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting + * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2) + * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4) + * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4) + * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6) + * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6) + * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8) + * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) + * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8) + * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8) + * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8) + * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8) + * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8) + * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10) + * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) + * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10) + * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10) + * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of + * status values defined in this enumeration. When using this value, note + * that the version of cairo at run-time may have additional status values + * defined than the value of this symbol at compile-time. (Since 1.10) + * + * #cairo_status_t is used to indicate errors that can occur when + * using Cairo. In some cases it is returned directly by functions. + * but when using #cairo_t, the last error, if any, is stored in + * the context and can be retrieved with cairo_status(). + * + * New entries may be added in future versions. Use cairo_status_to_string() + * to get a human-readable representation of an error message. + **/ +typedef enum _cairo_status { + CAIRO_STATUS_SUCCESS = 0, + + CAIRO_STATUS_NO_MEMORY, + CAIRO_STATUS_INVALID_RESTORE, + CAIRO_STATUS_INVALID_POP_GROUP, + CAIRO_STATUS_NO_CURRENT_POINT, + CAIRO_STATUS_INVALID_MATRIX, + CAIRO_STATUS_INVALID_STATUS, + CAIRO_STATUS_NULL_POINTER, + CAIRO_STATUS_INVALID_STRING, + CAIRO_STATUS_INVALID_PATH_DATA, + CAIRO_STATUS_READ_ERROR, + CAIRO_STATUS_WRITE_ERROR, + CAIRO_STATUS_SURFACE_FINISHED, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH, + CAIRO_STATUS_PATTERN_TYPE_MISMATCH, + CAIRO_STATUS_INVALID_CONTENT, + CAIRO_STATUS_INVALID_FORMAT, + CAIRO_STATUS_INVALID_VISUAL, + CAIRO_STATUS_FILE_NOT_FOUND, + CAIRO_STATUS_INVALID_DASH, + CAIRO_STATUS_INVALID_DSC_COMMENT, + CAIRO_STATUS_INVALID_INDEX, + CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, + CAIRO_STATUS_TEMP_FILE_ERROR, + CAIRO_STATUS_INVALID_STRIDE, + CAIRO_STATUS_FONT_TYPE_MISMATCH, + CAIRO_STATUS_USER_FONT_IMMUTABLE, + CAIRO_STATUS_USER_FONT_ERROR, + CAIRO_STATUS_NEGATIVE_COUNT, + CAIRO_STATUS_INVALID_CLUSTERS, + CAIRO_STATUS_INVALID_SLANT, + CAIRO_STATUS_INVALID_WEIGHT, + CAIRO_STATUS_INVALID_SIZE, + CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, + CAIRO_STATUS_DEVICE_TYPE_MISMATCH, + CAIRO_STATUS_DEVICE_ERROR, + CAIRO_STATUS_NO_DEVICE, + + CAIRO_STATUS_LAST_STATUS +} cairo_status_t; + +/** + * cairo_content_t: + * @CAIRO_CONTENT_COLOR: The surface will hold color content only. + * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. + * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. + * + * #cairo_content_t is used to describe the content that a surface will + * contain, whether color information, alpha information (translucence + * vs. opacity), or both. + * + * Note: The large values here are designed to keep #cairo_content_t + * values distinct from #cairo_format_t values so that the + * implementation can detect the error if users confuse the two types. + **/ +typedef enum _cairo_content { + CAIRO_CONTENT_COLOR = 0x1000, + CAIRO_CONTENT_ALPHA = 0x2000, + CAIRO_CONTENT_COLOR_ALPHA = 0x3000 +} cairo_content_t; + +/** + * cairo_write_func_t: + * @closure: the output closure + * @data: the buffer containing the data to write + * @length: the amount of data to write + * + * #cairo_write_func_t is the type of function which is called when a + * backend needs to write data to an output stream. It is passed the + * closure which was specified by the user at the time the write + * function was registered, the data to write and the length of the + * data in bytes. The write function should return + * %CAIRO_STATUS_SUCCESS if all the data was successfully written, + * %CAIRO_STATUS_WRITE_ERROR otherwise. + * + * Returns: the status code of the write operation + **/ +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); + +/** + * cairo_read_func_t: + * @closure: the input closure + * @data: the buffer into which to read the data + * @length: the amount of data to read + * + * #cairo_read_func_t is the type of function which is called when a + * backend needs to read data from an input stream. It is passed the + * closure which was specified by the user at the time the read + * function was registered, the buffer to read the data into and the + * length of the data in bytes. The read function should return + * %CAIRO_STATUS_SUCCESS if all the data was successfully read, + * %CAIRO_STATUS_READ_ERROR otherwise. + * + * Returns: the status code of the read operation + **/ +typedef cairo_status_t (*cairo_read_func_t) (void *closure, + unsigned char *data, + unsigned int length); + +/* Functions for manipulating state objects */ +cairo_public cairo_t * +cairo_create (cairo_surface_t *target); + +cairo_public cairo_t * +cairo_reference (cairo_t *cr); + +cairo_public void +cairo_destroy (cairo_t *cr); + +cairo_public unsigned int +cairo_get_reference_count (cairo_t *cr); + +cairo_public void * +cairo_get_user_data (cairo_t *cr, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_set_user_data (cairo_t *cr, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_public void +cairo_save (cairo_t *cr); + +cairo_public void +cairo_restore (cairo_t *cr); + +cairo_public void +cairo_push_group (cairo_t *cr); + +cairo_public void +cairo_push_group_with_content (cairo_t *cr, cairo_content_t content); + +cairo_public cairo_pattern_t * +cairo_pop_group (cairo_t *cr); + +cairo_public void +cairo_pop_group_to_source (cairo_t *cr); + +/* Modify state */ + +/** + * cairo_operator_t: + * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) + * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) + * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer + * (bounded) + * @CAIRO_OPERATOR_IN: draw source where there was destination content + * (unbounded) + * @CAIRO_OPERATOR_OUT: draw source where there was no destination + * content (unbounded) + * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and + * only there + * @CAIRO_OPERATOR_DEST: ignore the source + * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source + * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was + * source content (unbounded) + * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no + * source content + * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content + * and only there (unbounded) + * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only + * one of them + * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated + * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are + * disjoint geometries + * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied. + * This causes the result to be at least as dark as the darker inputs. + * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and + * multiplied. This causes the result to be at least as light as the lighter + * inputs. + * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the + * lightness of the destination color. + * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it + * is darker, otherwise keeps the source. + * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it + * is lighter, otherwise keeps the source. + * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect + * the source color. + * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect + * the source color. + * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source + * color. + * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source + * color. + * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and + * destination color. + * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but + * with lower contrast. + * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source + * and the saturation and luminosity of the target. + * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation + * of the source and the hue and luminosity of the target. Painting with + * this mode onto a gray area prduces no change. + * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation + * of the source and the luminosity of the target. This preserves the gray + * levels of the target and is useful for coloring monochrome images or + * tinting color images. + * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of + * the source and the hue and saturation of the target. This produces an + * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. + * + * #cairo_operator_t is used to set the compositing operator for all cairo + * drawing operations. + * + * The default operator is %CAIRO_OPERATOR_OVER. + * + * The operators marked as unbounded modify their + * destination even outside of the mask layer (that is, their effect is not + * bound by the mask layer). However, their effect can still be limited by + * way of clipping. + * + * To keep things simple, the operator descriptions here + * document the behavior for when both source and destination are either fully + * transparent or fully opaque. The actual implementation works for + * translucent layers too. + * For a more detailed explanation of the effects of each operator, including + * the mathematical definitions, see + * http://cairographics.org/operators/. + **/ +typedef enum _cairo_operator { + CAIRO_OPERATOR_CLEAR, + + CAIRO_OPERATOR_SOURCE, + CAIRO_OPERATOR_OVER, + CAIRO_OPERATOR_IN, + CAIRO_OPERATOR_OUT, + CAIRO_OPERATOR_ATOP, + + CAIRO_OPERATOR_DEST, + CAIRO_OPERATOR_DEST_OVER, + CAIRO_OPERATOR_DEST_IN, + CAIRO_OPERATOR_DEST_OUT, + CAIRO_OPERATOR_DEST_ATOP, + + CAIRO_OPERATOR_XOR, + CAIRO_OPERATOR_ADD, + CAIRO_OPERATOR_SATURATE, + + CAIRO_OPERATOR_MULTIPLY, + CAIRO_OPERATOR_SCREEN, + CAIRO_OPERATOR_OVERLAY, + CAIRO_OPERATOR_DARKEN, + CAIRO_OPERATOR_LIGHTEN, + CAIRO_OPERATOR_COLOR_DODGE, + CAIRO_OPERATOR_COLOR_BURN, + CAIRO_OPERATOR_HARD_LIGHT, + CAIRO_OPERATOR_SOFT_LIGHT, + CAIRO_OPERATOR_DIFFERENCE, + CAIRO_OPERATOR_EXCLUSION, + CAIRO_OPERATOR_HSL_HUE, + CAIRO_OPERATOR_HSL_SATURATION, + CAIRO_OPERATOR_HSL_COLOR, + CAIRO_OPERATOR_HSL_LUMINOSITY +} cairo_operator_t; + +cairo_public void +cairo_set_operator (cairo_t *cr, cairo_operator_t op); + +cairo_public void +cairo_set_source (cairo_t *cr, cairo_pattern_t *source); + +cairo_public void +cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); + +cairo_public void +cairo_set_source_rgba (cairo_t *cr, + double red, double green, double blue, + double alpha); + +cairo_public void +cairo_set_source_surface (cairo_t *cr, + cairo_surface_t *surface, + double x, + double y); + +cairo_public void +cairo_set_tolerance (cairo_t *cr, double tolerance); + +/** + * cairo_antialias_t: + * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for + * the subsystem and target device + * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask + * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using + * shades of gray for black text on a white background, for example). + * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking + * advantage of the order of subpixel elements on devices + * such as LCD panels + * + * Specifies the type of antialiasing to do when rendering text or shapes. + **/ +typedef enum _cairo_antialias { + CAIRO_ANTIALIAS_DEFAULT, + CAIRO_ANTIALIAS_NONE, + CAIRO_ANTIALIAS_GRAY, + CAIRO_ANTIALIAS_SUBPIXEL +} cairo_antialias_t; + +cairo_public void +cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); + +/** + * cairo_fill_rule_t: + * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from + * left-to-right, counts +1. If the path crosses the ray + * from right to left, counts -1. (Left and right are determined + * from the perspective of looking along the ray from the starting + * point.) If the total count is non-zero, the point will be filled. + * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of + * intersections, without regard to the orientation of the contour. If + * the total number of intersections is odd, the point will be + * filled. + * + * #cairo_fill_rule_t is used to select how paths are filled. For both + * fill rules, whether or not a point is included in the fill is + * determined by taking a ray from that point to infinity and looking + * at intersections with the path. The ray can be in any direction, + * as long as it doesn't pass through the end point of a segment + * or have a tricky intersection such as intersecting tangent to the path. + * (Note that filling is not actually implemented in this way. This + * is just a description of the rule that is applied.) + * + * The default fill rule is %CAIRO_FILL_RULE_WINDING. + * + * New entries may be added in future versions. + **/ +typedef enum _cairo_fill_rule { + CAIRO_FILL_RULE_WINDING, + CAIRO_FILL_RULE_EVEN_ODD +} cairo_fill_rule_t; + +cairo_public void +cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); + +cairo_public void +cairo_set_line_width (cairo_t *cr, double width); + +/** + * cairo_line_cap_t: + * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point + * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point + * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point + * + * Specifies how to render the endpoints of the path when stroking. + * + * The default line cap style is %CAIRO_LINE_CAP_BUTT. + **/ +typedef enum _cairo_line_cap { + CAIRO_LINE_CAP_BUTT, + CAIRO_LINE_CAP_ROUND, + CAIRO_LINE_CAP_SQUARE +} cairo_line_cap_t; + +cairo_public void +cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); + +/** + * cairo_line_join_t: + * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see + * cairo_set_miter_limit() + * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the + * joint point + * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half + * the line width from the joint point + * + * Specifies how to render the junction of two lines when stroking. + * + * The default line join style is %CAIRO_LINE_JOIN_MITER. + **/ +typedef enum _cairo_line_join { + CAIRO_LINE_JOIN_MITER, + CAIRO_LINE_JOIN_ROUND, + CAIRO_LINE_JOIN_BEVEL +} cairo_line_join_t; + +cairo_public void +cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join); + +cairo_public void +cairo_set_dash (cairo_t *cr, + const double *dashes, + int num_dashes, + double offset); + +cairo_public void +cairo_set_miter_limit (cairo_t *cr, double limit); + +cairo_public void +cairo_translate (cairo_t *cr, double tx, double ty); + +cairo_public void +cairo_scale (cairo_t *cr, double sx, double sy); + +cairo_public void +cairo_rotate (cairo_t *cr, double angle); + +cairo_public void +cairo_transform (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_identity_matrix (cairo_t *cr); + +cairo_public void +cairo_user_to_device (cairo_t *cr, double *x, double *y); + +cairo_public void +cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy); + +cairo_public void +cairo_device_to_user (cairo_t *cr, double *x, double *y); + +cairo_public void +cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy); + +/* Path creation functions */ +cairo_public void +cairo_new_path (cairo_t *cr); + +cairo_public void +cairo_move_to (cairo_t *cr, double x, double y); + +cairo_public void +cairo_new_sub_path (cairo_t *cr); + +cairo_public void +cairo_line_to (cairo_t *cr, double x, double y); + +cairo_public void +cairo_curve_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double x3, double y3); + +cairo_public void +cairo_arc (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2); + +cairo_public void +cairo_arc_negative (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2); + +/* XXX: NYI +cairo_public void +cairo_arc_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double radius); +*/ + +cairo_public void +cairo_rel_move_to (cairo_t *cr, double dx, double dy); + +cairo_public void +cairo_rel_line_to (cairo_t *cr, double dx, double dy); + +cairo_public void +cairo_rel_curve_to (cairo_t *cr, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3); + +cairo_public void +cairo_rectangle (cairo_t *cr, + double x, double y, + double width, double height); + +/* XXX: NYI +cairo_public void +cairo_stroke_to_path (cairo_t *cr); +*/ + +cairo_public void +cairo_close_path (cairo_t *cr); + +cairo_public void +cairo_path_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/* Painting functions */ +cairo_public void +cairo_paint (cairo_t *cr); + +cairo_public void +cairo_paint_with_alpha (cairo_t *cr, + double alpha); + +cairo_public void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern); + +cairo_public void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y); + +cairo_public void +cairo_stroke (cairo_t *cr); + +cairo_public void +cairo_stroke_preserve (cairo_t *cr); + +cairo_public void +cairo_fill (cairo_t *cr); + +cairo_public void +cairo_fill_preserve (cairo_t *cr); + +cairo_public void +cairo_copy_page (cairo_t *cr); + +cairo_public void +cairo_show_page (cairo_t *cr); + +/* Insideness testing */ +cairo_public cairo_bool_t +cairo_in_stroke (cairo_t *cr, double x, double y); + +cairo_public cairo_bool_t +cairo_in_fill (cairo_t *cr, double x, double y); + +cairo_public cairo_bool_t +cairo_in_clip (cairo_t *cr, double x, double y); + +/* Rectangular extents */ +cairo_public void +cairo_stroke_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +cairo_public void +cairo_fill_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/* Clipping */ +cairo_public void +cairo_reset_clip (cairo_t *cr); + +cairo_public void +cairo_clip (cairo_t *cr); + +cairo_public void +cairo_clip_preserve (cairo_t *cr); + +cairo_public void +cairo_clip_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/** + * cairo_rectangle_t: + * @x: X coordinate of the left side of the rectangle + * @y: Y coordinate of the the top side of the rectangle + * @width: width of the rectangle + * @height: height of the rectangle + * + * A data structure for holding a rectangle. + * + * Since: 1.4 + **/ +typedef struct _cairo_rectangle { + double x, y, width, height; +} cairo_rectangle_t; + +/** + * cairo_rectangle_list_t: + * @status: Error status of the rectangle list + * @rectangles: Array containing the rectangles + * @num_rectangles: Number of rectangles in this list + * + * A data structure for holding a dynamically allocated + * array of rectangles. + * + * Since: 1.4 + **/ +typedef struct _cairo_rectangle_list { + cairo_status_t status; + cairo_rectangle_t *rectangles; + int num_rectangles; +} cairo_rectangle_list_t; + +cairo_public cairo_rectangle_list_t * +cairo_copy_clip_rectangle_list (cairo_t *cr); + +cairo_public void +cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); + +/* Font/Text functions */ + +/** + * cairo_scaled_font_t: + * + * A #cairo_scaled_font_t is a font scaled to a particular size and device + * resolution. A #cairo_scaled_font_t is most useful for low-level font + * usage where a library or application wants to cache a reference + * to a scaled font to speed up the computation of metrics. + * + * There are various types of scaled fonts, depending on the + * font backend they use. The type of a + * scaled font can be queried using cairo_scaled_font_get_type(). + * + * Memory management of #cairo_scaled_font_t is done with + * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). + **/ +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 + * font backend they use. The type of a + * font face can be queried using cairo_font_face_get_type(). + * + * Memory management of #cairo_font_face_t is done with + * cairo_font_face_reference() and cairo_font_face_destroy(). + **/ +typedef struct _cairo_font_face cairo_font_face_t; + +/** + * cairo_glyph_t: + * @index: glyph index in the font. The exact interpretation of the + * glyph index depends on the font technology being used. + * @x: the offset in the X direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * @y: the offset in the Y direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * + * The #cairo_glyph_t structure holds information about a single glyph + * when drawing or measuring text. A font is (in simple terms) a + * collection of shapes used to draw text. A glyph is one of these + * shapes. There can be multiple glyphs for a single character + * (alternates to be used in different contexts, for example), or a + * glyph can be a ligature of multiple + * characters. Cairo doesn't expose any way of converting input text + * into glyphs, so in order to use the Cairo interfaces that take + * arrays of glyphs, you must directly access the appropriate + * underlying font system. + * + * Note that the offsets given by @x and @y are not cumulative. When + * drawing or measuring text, each glyph is individually positioned + * with respect to the overall origin + **/ +typedef struct { + unsigned long index; + double x; + double y; +} cairo_glyph_t; + +cairo_public cairo_glyph_t * +cairo_glyph_allocate (int num_glyphs); + +cairo_public void +cairo_glyph_free (cairo_glyph_t *glyphs); + +/** + * cairo_text_cluster_t: + * @num_bytes: the number of bytes of UTF-8 text covered by cluster + * @num_glyphs: the number of glyphs covered by cluster + * + * The #cairo_text_cluster_t structure holds information about a single + * text cluster. A text cluster is a minimal + * mapping of some glyphs corresponding to some UTF-8 text. + * + * For a cluster to be valid, both @num_bytes and @num_glyphs should + * be non-negative, and at least one should be non-zero. + * Note that clusters with zero glyphs are not as well supported as + * normal clusters. For example, PDF rendering applications typically + * ignore those clusters when PDF text is being selected. + * + * See cairo_show_text_glyphs() for how clusters are used in advanced + * text operations. + * + * Since: 1.8 + **/ +typedef struct { + int num_bytes; + int num_glyphs; +} cairo_text_cluster_t; + +cairo_public cairo_text_cluster_t * +cairo_text_cluster_allocate (int num_clusters); + +cairo_public void +cairo_text_cluster_free (cairo_text_cluster_t *clusters); + +/** + * cairo_text_cluster_flags_t: + * @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array + * map to glyphs in the glyph array from end to start. + * + * Specifies properties of a text cluster mapping. + * + * Since: 1.8 + **/ +typedef enum _cairo_text_cluster_flags { + CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001 +} cairo_text_cluster_flags_t; + +/** + * cairo_text_extents_t: + * @x_bearing: the horizontal distance from the origin to the + * leftmost part of the glyphs as drawn. Positive if the + * glyphs lie entirely to the right of the origin. + * @y_bearing: the vertical distance from the origin to the + * topmost part of the glyphs as drawn. Positive only if the + * glyphs lie completely below the origin; will usually be + * negative. + * @width: width of the glyphs as drawn + * @height: height of the glyphs as drawn + * @x_advance:distance to advance in the X direction + * after drawing these glyphs + * @y_advance: distance to advance in the Y direction + * after drawing these glyphs. Will typically be zero except + * for vertical text layout as found in East-Asian languages. + * + * The #cairo_text_extents_t structure stores the extents of a single + * glyph or a string of glyphs in user-space coordinates. Because text + * extents are in user-space coordinates, they are mostly, but not + * entirely, independent of the current transformation matrix. If you call + * cairo_scale(cr, 2.0, 2.0), text will + * be drawn twice as big, but the reported text extents will not be + * doubled. They will change slightly due to hinting (so you can't + * assume that metrics are independent of the transformation matrix), + * but otherwise will remain unchanged. + **/ +typedef struct { + double x_bearing; + double y_bearing; + double width; + double height; + double x_advance; + double y_advance; +} cairo_text_extents_t; + +/** + * cairo_font_extents_t: + * @ascent: the distance that the font extends above the baseline. + * Note that this is not always exactly equal to the maximum + * of the extents of all the glyphs in the font, but rather + * is picked to express the font designer's intent as to + * how the font should align with elements above it. + * @descent: the distance that the font extends below the baseline. + * This value is positive for typical fonts that include + * portions below the baseline. Note that this is not always + * exactly equal to the maximum of the extents of all the + * glyphs in the font, but rather is picked to express the + * font designer's intent as to how the the font should + * align with elements below it. + * @height: the recommended vertical distance between baselines when + * setting consecutive lines of text with the font. This + * is greater than @ascent+@descent by a + * quantity known as the line spacing + * or external leading. When space + * is at a premium, most fonts can be set with only + * a distance of @ascent+@descent between lines. + * @max_x_advance: the maximum distance in the X direction that + * the the origin is advanced for any glyph in the font. + * @max_y_advance: the maximum distance in the Y direction that + * the the origin is advanced for any glyph in the font. + * this will be zero for normal fonts used for horizontal + * writing. (The scripts of East Asia are sometimes written + * vertically.) + * + * The #cairo_font_extents_t structure stores metric information for + * a font. Values are given in the current user-space coordinate + * system. + * + * Because font metrics are in user-space coordinates, they are + * mostly, but not entirely, independent of the current transformation + * matrix. If you call cairo_scale(cr, 2.0, 2.0), + * text will be drawn twice as big, but the reported text extents will + * not be doubled. They will change slightly due to hinting (so you + * can't assume that metrics are independent of the transformation + * matrix), but otherwise will remain unchanged. + **/ +typedef struct { + double ascent; + double descent; + double height; + double max_x_advance; + double max_y_advance; +} cairo_font_extents_t; + +/** + * cairo_font_slant_t: + * @CAIRO_FONT_SLANT_NORMAL: Upright font style + * @CAIRO_FONT_SLANT_ITALIC: Italic font style + * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style + * + * Specifies variants of a font face based on their slant. + **/ +typedef enum _cairo_font_slant { + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_SLANT_ITALIC, + CAIRO_FONT_SLANT_OBLIQUE +} cairo_font_slant_t; + +/** + * cairo_font_weight_t: + * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight + * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight + * + * Specifies variants of a font face based on their weight. + **/ +typedef enum _cairo_font_weight { + CAIRO_FONT_WEIGHT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD +} cairo_font_weight_t; + +/** + * cairo_subpixel_order_t: + * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for + * for the target device + * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally + * with red at the left + * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally + * with blue at the left + * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically + * with red at the top + * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically + * with blue at the top + * + * The subpixel order specifies the order of color elements within + * each pixel on the display device when rendering with an + * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. + **/ +typedef enum _cairo_subpixel_order { + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_SUBPIXEL_ORDER_RGB, + CAIRO_SUBPIXEL_ORDER_BGR, + CAIRO_SUBPIXEL_ORDER_VRGB, + CAIRO_SUBPIXEL_ORDER_VBGR +} cairo_subpixel_order_t; + +/** + * cairo_hint_style_t: + * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for + * font backend and target device + * @CAIRO_HINT_STYLE_NONE: Do not hint outlines + * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve + * contrast while retaining good fidelity to the original + * shapes. + * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength + * giving a compromise between fidelity to the original shapes + * and contrast + * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast + * + * Specifies the type of hinting to do on font outlines. Hinting + * is the process of fitting outlines to the pixel grid in order + * to improve the appearance of the result. Since hinting outlines + * involves distorting them, it also reduces the faithfulness + * to the original outline shapes. Not all of the outline hinting + * styles are supported by all font backends. + * + * New entries may be added in future versions. + **/ +typedef enum _cairo_hint_style { + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_STYLE_NONE, + CAIRO_HINT_STYLE_SLIGHT, + CAIRO_HINT_STYLE_MEDIUM, + CAIRO_HINT_STYLE_FULL +} cairo_hint_style_t; + +/** + * cairo_hint_metrics_t: + * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default + * manner for the font backend and target device + * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics + * @CAIRO_HINT_METRICS_ON: Hint font metrics + * + * Specifies whether to hint font metrics; hinting font metrics + * means quantizing them so that they are integer values in + * device space. Doing this improves the consistency of + * letter and line spacing, however it also means that text + * will be laid out differently at different zoom factors. + **/ +typedef enum _cairo_hint_metrics { + CAIRO_HINT_METRICS_DEFAULT, + CAIRO_HINT_METRICS_OFF, + CAIRO_HINT_METRICS_ON +} cairo_hint_metrics_t; + +/** + * cairo_font_options_t: + * + * An opaque structure holding all options that are used when + * rendering fonts. + * + * Individual features of a #cairo_font_options_t can be set or + * accessed using functions named + * cairo_font_options_set_feature_name and + * cairo_font_options_get_feature_name, like + * cairo_font_options_set_antialias() and + * cairo_font_options_get_antialias(). + * + * New features may be added to a #cairo_font_options_t in the + * future. For this reason, cairo_font_options_copy(), + * cairo_font_options_equal(), cairo_font_options_merge(), and + * cairo_font_options_hash() should be used to copy, check + * for equality, merge, or compute a hash value of + * #cairo_font_options_t objects. + **/ +typedef struct _cairo_font_options cairo_font_options_t; + +cairo_public cairo_font_options_t * +cairo_font_options_create (void); + +cairo_public cairo_font_options_t * +cairo_font_options_copy (const cairo_font_options_t *original); + +cairo_public void +cairo_font_options_destroy (cairo_font_options_t *options); + +cairo_public cairo_status_t +cairo_font_options_status (cairo_font_options_t *options); + +cairo_public void +cairo_font_options_merge (cairo_font_options_t *options, + const cairo_font_options_t *other); +cairo_public cairo_bool_t +cairo_font_options_equal (const cairo_font_options_t *options, + const cairo_font_options_t *other); + +cairo_public unsigned long +cairo_font_options_hash (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_antialias (cairo_font_options_t *options, + cairo_antialias_t antialias); +cairo_public cairo_antialias_t +cairo_font_options_get_antialias (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_subpixel_order (cairo_font_options_t *options, + cairo_subpixel_order_t subpixel_order); +cairo_public cairo_subpixel_order_t +cairo_font_options_get_subpixel_order (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_hint_style (cairo_font_options_t *options, + cairo_hint_style_t hint_style); +cairo_public cairo_hint_style_t +cairo_font_options_get_hint_style (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_hint_metrics (cairo_font_options_t *options, + cairo_hint_metrics_t hint_metrics); +cairo_public cairo_hint_metrics_t +cairo_font_options_get_hint_metrics (const cairo_font_options_t *options); + +/* This interface is for dealing with text as text, not caring about the + font object inside the the cairo_t. */ + +cairo_public void +cairo_select_font_face (cairo_t *cr, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +cairo_public void +cairo_set_font_size (cairo_t *cr, double size); + +cairo_public void +cairo_set_font_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_get_font_matrix (cairo_t *cr, + cairo_matrix_t *matrix); + +cairo_public void +cairo_set_font_options (cairo_t *cr, + const cairo_font_options_t *options); + +cairo_public void +cairo_get_font_options (cairo_t *cr, + cairo_font_options_t *options); + +cairo_public void +cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); + +cairo_public cairo_font_face_t * +cairo_get_font_face (cairo_t *cr); + +cairo_public void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font); + +cairo_public cairo_scaled_font_t * +cairo_get_scaled_font (cairo_t *cr); + +cairo_public void +cairo_show_text (cairo_t *cr, const char *utf8); + +cairo_public void +cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); + +cairo_public void +cairo_show_text_glyphs (cairo_t *cr, + const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags); + +cairo_public void +cairo_text_path (cairo_t *cr, const char *utf8); + +cairo_public void +cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); + +cairo_public void +cairo_text_extents (cairo_t *cr, + const char *utf8, + cairo_text_extents_t *extents); + +cairo_public void +cairo_glyph_extents (cairo_t *cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_public void +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents); + +/* Generic identifier for a font style */ + +cairo_public cairo_font_face_t * +cairo_font_face_reference (cairo_font_face_t *font_face); + +cairo_public void +cairo_font_face_destroy (cairo_font_face_t *font_face); + +cairo_public unsigned int +cairo_font_face_get_reference_count (cairo_font_face_t *font_face); + +cairo_public cairo_status_t +cairo_font_face_status (cairo_font_face_t *font_face); + + +/** + * cairo_font_type_t: + * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api + * @CAIRO_FONT_TYPE_FT: The font is of type FreeType + * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 + * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6) + * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) + * + * #cairo_font_type_t is used to describe the type of a given font + * face or scaled font. The font types are also known as "font + * backends" within cairo. + * + * The type of a font face is determined by the function used to + * create it, which will generally be of the form + * cairo_type_font_face_create(). The font face type can be queried + * with cairo_font_face_get_type() + * + * The various #cairo_font_face_t functions can be used with a font face + * of any type. + * + * The type of a scaled font is determined by the type of the font + * face passed to cairo_scaled_font_create(). The scaled font type can + * be queried with cairo_scaled_font_get_type() + * + * The various #cairo_scaled_font_t functions can be used with scaled + * fonts of any type, but some font backends also provide + * type-specific functions that must only be called with a scaled font + * of the appropriate type. These functions have names that begin with + * cairo_type_scaled_font() such as cairo_ft_scaled_font_lock_face(). + * + * The behavior of calling a type-specific function with a scaled font + * of the wrong type is undefined. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_font_type { + CAIRO_FONT_TYPE_TOY, + CAIRO_FONT_TYPE_FT, + CAIRO_FONT_TYPE_WIN32, + CAIRO_FONT_TYPE_QUARTZ, + CAIRO_FONT_TYPE_USER, + CAIRO_FONT_TYPE_DWRITE +} cairo_font_type_t; + +cairo_public cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face); + +cairo_public void * +cairo_font_face_get_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_font_face_set_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +/* Portable interface to general font features. */ + +cairo_public cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); + +cairo_public cairo_scaled_font_t * +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); + +cairo_public unsigned int +cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font); + +cairo_public cairo_status_t +cairo_scaled_font_status (cairo_scaled_font_t *scaled_font); + +cairo_public cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font); + +cairo_public void * +cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_public void +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +cairo_public void +cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_text_extents_t *extents); + +cairo_public void +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_public cairo_status_t +cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + +cairo_public cairo_font_face_t * +cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *font_matrix); + +cairo_public void +cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *ctm); + +cairo_public void +cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *scale_matrix); + +cairo_public void +cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, + cairo_font_options_t *options); + + +/* Toy fonts */ + +cairo_public cairo_font_face_t * +cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +cairo_public const char * +cairo_toy_font_face_get_family (cairo_font_face_t *font_face); + +cairo_public cairo_font_slant_t +cairo_toy_font_face_get_slant (cairo_font_face_t *font_face); + +cairo_public cairo_font_weight_t +cairo_toy_font_face_get_weight (cairo_font_face_t *font_face); + + +/* User fonts */ + +cairo_public cairo_font_face_t * +cairo_user_font_face_create (void); + +/* User-font method signatures */ + +/** + * cairo_user_scaled_font_init_func_t: + * @scaled_font: the scaled-font being created + * @cr: a cairo context, in font space + * @extents: font extents to fill in, in font space + * + * #cairo_user_scaled_font_init_func_t is the type of function which is + * called when a scaled-font needs to be created for a user font-face. + * + * The cairo context @cr is not used by the caller, but is prepared in font + * space, similar to what the cairo contexts passed to the render_glyph + * method will look like. The callback can use this context for extents + * computation for example. After the callback is called, @cr is checked + * for any error status. + * + * The @extents argument is where the user font sets the font extents for + * @scaled_font. It is in font space, which means that for most cases its + * ascent and descent members should add to 1.0. @extents is preset to + * hold a value of 1.0 for ascent, height, and max_x_advance, and 0.0 for + * descent and max_y_advance members. + * + * The callback is optional. If not set, default font extents as described + * in the previous paragraph will be used. + * + * Note that @scaled_font is not fully initialized at this + * point and trying to use it for text operations in the callback will result + * in deadlock. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *extents); + +/** + * cairo_user_scaled_font_render_glyph_func_t: + * @scaled_font: user scaled-font + * @glyph: glyph code to render + * @cr: cairo context to draw to, in font space + * @extents: glyph extents to fill in, in font space + * + * #cairo_user_scaled_font_render_glyph_func_t is the type of function which + * is called when a user scaled-font needs to render a glyph. + * + * The callback is mandatory, and expected to draw the glyph with code @glyph to + * the cairo context @cr. @cr is prepared such that the glyph drawing is done in + * font space. That is, the matrix set on @cr is the scale matrix of @scaled_font, + * The @extents argument is where the user font sets the font extents for + * @scaled_font. However, if user prefers to draw in user space, they can + * achieve that by changing the matrix on @cr. All cairo rendering operations + * to @cr are permitted, however, the result is undefined if any source other + * than the default source on @cr is used. That means, glyph bitmaps should + * be rendered using cairo_mask() instead of cairo_paint(). + * + * Other non-default settings on @cr include a font size of 1.0 (given that + * it is set up to be in font space), and font options corresponding to + * @scaled_font. + * + * The @extents argument is preset to have x_bearing, + * width, and y_advance of zero, + * y_bearing set to -font_extents.ascent, + * height to font_extents.ascent+font_extents.descent, + * and x_advance to font_extents.max_x_advance. + * The only field user needs to set in majority of cases is + * x_advance. + * If the width field is zero upon the callback returning + * (which is its preset value), the glyph extents are automatically computed + * based on the drawings done to @cr. This is in most cases exactly what the + * desired behavior is. However, if for any reason the callback sets the + * extents, it must be ink extents, and include the extents of all drawing + * done to @cr in the callback. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, or + * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents); + +/** + * cairo_user_scaled_font_text_to_glyphs_func_t: + * @scaled_font: the scaled-font being created + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes + * @glyphs: pointer to array of glyphs to fill, in font space + * @num_glyphs: pointer to number of glyphs + * @clusters: pointer to array of cluster mapping information to fill, or %NULL + * @num_clusters: pointer to number of clusters + * @cluster_flags: pointer to location to store cluster flags corresponding to the + * output @clusters + * + * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which + * is called to convert input text to an array of glyphs. This is used by the + * cairo_show_text() operation. + * + * Using this callback the user-font has full control on glyphs and their + * positions. That means, it allows for features like ligatures and kerning, + * as well as complex shaping required for scripts like + * Arabic and Indic. + * + * The @num_glyphs argument is preset to the number of glyph entries available + * in the @glyphs buffer. If the @glyphs buffer is %NULL, the value of + * @num_glyphs will be zero. If the provided glyph array is too short for + * the conversion (or for convenience), a new glyph array may be allocated + * using cairo_glyph_allocate() and placed in @glyphs. Upon return, + * @num_glyphs should contain the number of generated glyphs. If the value + * @glyphs points at has changed after the call, the caller will free the + * allocated glyph array using cairo_glyph_free(). + * The callback should populate the glyph indices and positions (in font space) + * assuming that the text is to be shown at the origin. + * + * If @clusters is not %NULL, @num_clusters and @cluster_flags are also + * non-%NULL, and cluster mapping should be computed. The semantics of how + * cluster array allocation works is similar to the glyph array. That is, + * if @clusters initially points to a non-%NULL value, that array may be used + * as a cluster buffer, and @num_clusters points to the number of cluster + * entries available there. If the provided cluster array is too short for + * the conversion (or for convenience), a new cluster array may be allocated + * using cairo_text_cluster_allocate() and placed in @clusters. Upon return, + * @num_clusters should contain the number of generated clusters. + * If the value @clusters points at has changed after the call, the caller + * will free the allocated cluster array using cairo_text_cluster_free(). + * + * The callback is optional. If @num_glyphs is negative upon + * the callback returning or if the return value + * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback + * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t. + * + * Note: While cairo does not impose any limitation on glyph indices, + * some applications may assume that a glyph index fits in a 16-bit + * unsigned integer. As such, it is advised that user-fonts keep their + * glyphs in the 0 to 65535 range. Furthermore, some applications may + * assume that glyph 0 is a special glyph-not-found glyph. User-fonts + * are advised to use glyph 0 for such purposes and do not use that + * glyph value for other purposes. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + +/** + * cairo_user_scaled_font_unicode_to_glyph_func_t: + * @scaled_font: the scaled-font being created + * @unicode: input unicode character code-point + * @glyph_index: output glyph index + * + * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which + * is called to convert an input Unicode character to a single glyph. + * This is used by the cairo_show_text() operation. + * + * This callback is used to provide the same functionality as the + * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t) + * but has much less control on the output, + * in exchange for increased ease of use. The inherent assumption to using + * this callback is that each character maps to one glyph, and that the + * mapping is context independent. It also assumes that glyphs are positioned + * according to their advance width. These mean no ligatures, kerning, or + * complex scripts can be implemented using this callback. + * + * The callback is optional, and only used if text_to_glyphs callback is not + * set or fails to return glyphs. If this callback is not set or if it returns + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode + * code-points to glyph indices is assumed. + * + * Note: While cairo does not impose any limitation on glyph indices, + * some applications may assume that a glyph index fits in a 16-bit + * unsigned integer. As such, it is advised that user-fonts keep their + * glyphs in the 0 to 65535 range. Furthermore, some applications may + * assume that glyph 0 is a special glyph-not-found glyph. User-fonts + * are advised to use glyph 0 for such purposes and do not use that + * glyph value for other purposes. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph_index); + +/* User-font method setters */ + +cairo_public void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func); + +cairo_public void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func); + +cairo_public void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); + +cairo_public void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func); + +/* User-font method getters */ + +cairo_public cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); + + +/* Query functions */ + +cairo_public cairo_operator_t +cairo_get_operator (cairo_t *cr); + +cairo_public cairo_pattern_t * +cairo_get_source (cairo_t *cr); + +cairo_public double +cairo_get_tolerance (cairo_t *cr); + +cairo_public cairo_antialias_t +cairo_get_antialias (cairo_t *cr); + +cairo_public cairo_bool_t +cairo_has_current_point (cairo_t *cr); + +cairo_public void +cairo_get_current_point (cairo_t *cr, double *x, double *y); + +cairo_public cairo_fill_rule_t +cairo_get_fill_rule (cairo_t *cr); + +cairo_public double +cairo_get_line_width (cairo_t *cr); + +cairo_public cairo_line_cap_t +cairo_get_line_cap (cairo_t *cr); + +cairo_public cairo_line_join_t +cairo_get_line_join (cairo_t *cr); + +cairo_public double +cairo_get_miter_limit (cairo_t *cr); + +cairo_public int +cairo_get_dash_count (cairo_t *cr); + +cairo_public void +cairo_get_dash (cairo_t *cr, double *dashes, double *offset); + +cairo_public void +cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix); + +cairo_public cairo_surface_t * +cairo_get_target (cairo_t *cr); + +cairo_public cairo_surface_t * +cairo_get_group_target (cairo_t *cr); + +/** + * cairo_path_data_type_t: + * @CAIRO_PATH_MOVE_TO: A move-to operation + * @CAIRO_PATH_LINE_TO: A line-to operation + * @CAIRO_PATH_CURVE_TO: A curve-to operation + * @CAIRO_PATH_CLOSE_PATH: A close-path operation + * + * #cairo_path_data_t is used to describe the type of one portion + * of a path when represented as a #cairo_path_t. + * See #cairo_path_data_t for details. + **/ +typedef enum _cairo_path_data_type { + CAIRO_PATH_MOVE_TO, + CAIRO_PATH_LINE_TO, + CAIRO_PATH_CURVE_TO, + CAIRO_PATH_CLOSE_PATH +} cairo_path_data_type_t; + +/** + * cairo_path_data_t: + * + * #cairo_path_data_t is used to represent the path data inside a + * #cairo_path_t. + * + * The data structure is designed to try to balance the demands of + * efficiency and ease-of-use. A path is represented as an array of + * #cairo_path_data_t, which is a union of headers and points. + * + * Each portion of the path is represented by one or more elements in + * the array, (one header followed by 0 or more points). The length + * value of the header is the number of array elements for the current + * portion including the header, (ie. length == 1 + # of points), and + * where the number of points for each element type is as follows: + * + * + * %CAIRO_PATH_MOVE_TO: 1 point + * %CAIRO_PATH_LINE_TO: 1 point + * %CAIRO_PATH_CURVE_TO: 3 points + * %CAIRO_PATH_CLOSE_PATH: 0 points + * + * + * The semantics and ordering of the coordinate values are consistent + * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and + * cairo_close_path(). + * + * Here is sample code for iterating through a #cairo_path_t: + * + * + * int i; + * cairo_path_t *path; + * cairo_path_data_t *data; + *   + * path = cairo_copy_path (cr); + *   + * for (i=0; i < path->num_data; i += path->data[i].header.length) { + * data = &path->data[i]; + * switch (data->header.type) { + * case CAIRO_PATH_MOVE_TO: + * do_move_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_LINE_TO: + * do_line_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_CURVE_TO: + * do_curve_to_things (data[1].point.x, data[1].point.y, + * data[2].point.x, data[2].point.y, + * data[3].point.x, data[3].point.y); + * break; + * case CAIRO_PATH_CLOSE_PATH: + * do_close_path_things (); + * break; + * } + * } + * cairo_path_destroy (path); + * + * + * As of cairo 1.4, cairo does not mind if there are more elements in + * a portion of the path than needed. Such elements can be used by + * users of the cairo API to hold extra values in the path data + * structure. For this reason, it is recommended that applications + * always use data->header.length to + * iterate over the path data, instead of hardcoding the number of + * elements for each element type. + **/ +typedef union _cairo_path_data_t cairo_path_data_t; +union _cairo_path_data_t { + struct { + cairo_path_data_type_t type; + int length; + } header; + struct { + double x, y; + } point; +}; + +/** + * cairo_path_t: + * @status: the current error status + * @data: the elements in the path + * @num_data: the number of elements in the data array + * + * A data structure for holding a path. This data structure serves as + * the return value for cairo_copy_path() and + * cairo_copy_path_flat() as well the input value for + * cairo_append_path(). + * + * See #cairo_path_data_t for hints on how to iterate over the + * actual data within the path. + * + * The num_data member gives the number of elements in the data + * array. This number is larger than the number of independent path + * portions (defined in #cairo_path_data_type_t), since the data + * includes both headers and coordinates for each portion. + **/ +typedef struct cairo_path { + cairo_status_t status; + cairo_path_data_t *data; + int num_data; +} cairo_path_t; + +cairo_public cairo_path_t * +cairo_copy_path (cairo_t *cr); + +cairo_public cairo_path_t * +cairo_copy_path_flat (cairo_t *cr); + +cairo_public void +cairo_append_path (cairo_t *cr, + const cairo_path_t *path); + +cairo_public void +cairo_path_destroy (cairo_path_t *path); + +/* Error status queries */ + +cairo_public cairo_status_t +cairo_status (cairo_t *cr); + +cairo_public const char * +cairo_status_to_string (cairo_status_t status); + +/* Backend device manipulation */ + +cairo_public cairo_device_t * +cairo_device_reference (cairo_device_t *device); + +/** + * cairo_device_type_t: + * @CAIRO_DEVICE_TYPE_DRM: The surface is of type Direct Render Manager + * @CAIRO_DEVICE_TYPE_GL: The surface is of type OpenGL + * @CAIRO_DEVICE_TYPE_SCRIPT: The surface is of type script + * @CAIRO_DEVICE_TYPE_XCB: The surface is of type xcb + * @CAIRO_DEVICE_TYPE_XLIB: The surface is of type xlib + * @CAIRO_DEVICE_TYPE_XML: The surface is of type XML + * cairo_surface_create_for_rectangle() + * + * #cairo_device_type_t is used to describe the type of a given + * device. The devices types are also known as "backends" within cairo. + * + * The device type can be queried with cairo_device_get_type() + * + * The various #cairo_device_t functions can be used with surfaces of + * any type, but some backends also provide type-specific functions + * that must only be called with a device of the appropriate + * type. These functions have names that begin with + * cairo_type_device such as cairo_xcb_device_debug_set_render_version(). + * + * The behavior of calling a type-specific function with a surface of + * the wrong type is undefined. + * + * New entries may be added in future versions. + * + * Since: 1.10 + **/ +typedef enum _cairo_device_type { + CAIRO_DEVICE_TYPE_DRM, + CAIRO_DEVICE_TYPE_GL, + CAIRO_DEVICE_TYPE_SCRIPT, + CAIRO_DEVICE_TYPE_XCB, + CAIRO_DEVICE_TYPE_XLIB, + CAIRO_DEVICE_TYPE_XML +} cairo_device_type_t; + +cairo_public cairo_device_type_t +cairo_device_get_type (cairo_device_t *device); + +cairo_public cairo_status_t +cairo_device_status (cairo_device_t *device); + +cairo_public cairo_status_t +cairo_device_acquire (cairo_device_t *device); + +cairo_public void +cairo_device_release (cairo_device_t *device); + +cairo_public void +cairo_device_flush (cairo_device_t *device); + +cairo_public void +cairo_device_finish (cairo_device_t *device); + +cairo_public void +cairo_device_destroy (cairo_device_t *device); + +cairo_public unsigned int +cairo_device_get_reference_count (cairo_device_t *device); + +cairo_public void * +cairo_device_get_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_device_set_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + + +/* Surface manipulation */ + +cairo_public cairo_surface_t * +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_surface_create_for_rectangle (cairo_surface_t *target, + double x, + double y, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_surface_reference (cairo_surface_t *surface); + +cairo_public void +cairo_surface_finish (cairo_surface_t *surface); + +cairo_public void +cairo_surface_destroy (cairo_surface_t *surface); + +cairo_public cairo_device_t * +cairo_surface_get_device (cairo_surface_t *surface); + +cairo_public unsigned int +cairo_surface_get_reference_count (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_surface_status (cairo_surface_t *surface); + +/** + * cairo_surface_type_t: + * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image + * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf + * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps + * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib + * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb + * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz + * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz + * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32 + * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos + * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb + * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg + * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2 + * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface + * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image + * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 + * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10 + * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10 + * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 + * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 + * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 + * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10 + * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10 + * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10 + * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with + * cairo_surface_create_for_rectangle(), since 1.10 + * @CAIRO_SURFACE_TYPE_D2D: The surface is of type Direct2D + * + * #cairo_surface_type_t is used to describe the type of a given + * surface. The surface types are also known as "backends" or "surface + * backends" within cairo. + * + * The type of a surface is determined by the function used to create + * it, which will generally be of the form cairo_type_surface_create(), + * (though see cairo_surface_create_similar() as well). + * + * The surface type can be queried with cairo_surface_get_type() + * + * The various #cairo_surface_t functions can be used with surfaces of + * any type, but some backends also provide type-specific functions + * that must only be called with a surface of the appropriate + * type. These functions have names that begin with + * cairo_type_surface such as cairo_image_surface_get_width(). + * + * The behavior of calling a type-specific function with a surface of + * the wrong type is undefined. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_surface_type { + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_SURFACE_TYPE_PDF, + CAIRO_SURFACE_TYPE_PS, + CAIRO_SURFACE_TYPE_XLIB, + CAIRO_SURFACE_TYPE_XCB, + CAIRO_SURFACE_TYPE_GLITZ, + CAIRO_SURFACE_TYPE_QUARTZ, + CAIRO_SURFACE_TYPE_WIN32, + CAIRO_SURFACE_TYPE_BEOS, + CAIRO_SURFACE_TYPE_DIRECTFB, + CAIRO_SURFACE_TYPE_SVG, + CAIRO_SURFACE_TYPE_OS2, + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, + CAIRO_SURFACE_TYPE_SCRIPT, + CAIRO_SURFACE_TYPE_QT, + CAIRO_SURFACE_TYPE_RECORDING, + CAIRO_SURFACE_TYPE_VG, + CAIRO_SURFACE_TYPE_GL, + CAIRO_SURFACE_TYPE_DRM, + CAIRO_SURFACE_TYPE_TEE, + CAIRO_SURFACE_TYPE_XML, + CAIRO_SURFACE_TYPE_SKIA, + CAIRO_SURFACE_TYPE_SUBSURFACE, + CAIRO_SURFACE_TYPE_D2D +} cairo_surface_type_t; + +cairo_public cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface); + +cairo_public cairo_content_t +cairo_surface_get_content (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS + +cairo_public cairo_status_t +cairo_surface_write_to_png (cairo_surface_t *surface, + const char *filename); + +cairo_public cairo_status_t +cairo_surface_write_to_png_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure); + +#endif + +cairo_public void * +cairo_surface_get_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_public void +cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func); + +cairo_public void +cairo_surface_detach_snapshot (cairo_surface_t *snapshot); + +#define CAIRO_MIME_TYPE_JPEG "image/jpeg" +#define CAIRO_MIME_TYPE_PNG "image/png" +#define CAIRO_MIME_TYPE_JP2 "image/jp2" +#define CAIRO_MIME_TYPE_URI "text/x-uri" + +cairo_public void +cairo_surface_get_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char **data, + unsigned long *length); + +cairo_public cairo_status_t +cairo_surface_set_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char *data, + unsigned long length, + cairo_destroy_func_t destroy, + void *closure); + +cairo_public void +cairo_surface_get_font_options (cairo_surface_t *surface, + cairo_font_options_t *options); + +cairo_public void +cairo_surface_flush (cairo_surface_t *surface); + +cairo_public void +cairo_surface_mark_dirty (cairo_surface_t *surface); + +cairo_public void +cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, + int x, + int y, + int width, + int height); + +cairo_public void +cairo_surface_set_device_offset (cairo_surface_t *surface, + double x_offset, + double y_offset); + +cairo_public void +cairo_surface_get_device_offset (cairo_surface_t *surface, + double *x_offset, + double *y_offset); + +cairo_public void +cairo_surface_set_fallback_resolution (cairo_surface_t *surface, + double x_pixels_per_inch, + double y_pixels_per_inch); + +cairo_public void +cairo_surface_get_fallback_resolution (cairo_surface_t *surface, + double *x_pixels_per_inch, + double *y_pixels_per_inch); + +cairo_public void +cairo_surface_copy_page (cairo_surface_t *surface); + +cairo_public void +cairo_surface_show_page (cairo_surface_t *surface); + +cairo_public cairo_bool_t +cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); + +/** + * _cairo_subpixel_antialiasing_t: + * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled + * for this surface. + * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled + * for this surface. + */ +typedef enum _cairo_subpixel_antialiasing_t { + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED, + CAIRO_SUBPIXEL_ANTIALIASING_DISABLED +} cairo_subpixel_antialiasing_t; + +cairo_public void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled); + +cairo_public cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface); + +/* Image-surface functions */ + +/** + * cairo_format_t: + * @CAIRO_FORMAT_INVALID: no such format exists or is supported. + * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with + * alpha in the upper 8 bits, then red, then green, then blue. + * The 32-bit quantities are stored native-endian. Pre-multiplied + * alpha is used. (That is, 50% transparent red is 0x80800000, + * not 0x80ff0000.) + * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with + * the upper 8 bits unused. Red, Green, and Blue are stored + * in the remaining 24 bits in that order. + * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding + * an alpha value. + * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding + * an alpha value. Pixels are packed together into 32-bit + * quantities. The ordering of the bits matches the + * endianess of the platform. On a big-endian machine, the + * first pixel is in the uppermost bit, on a little-endian + * machine the first pixel is in the least-significant bit. + * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity + * with red in the upper 5 bits, then green in the middle + * 6 bits, and blue in the lower 5 bits. + * + * #cairo_format_t is used to identify the memory format of + * image data. + * + * New entries may be added in future versions. + **/ +typedef enum _cairo_format { + CAIRO_FORMAT_INVALID = -1, + CAIRO_FORMAT_ARGB32 = 0, + CAIRO_FORMAT_RGB24 = 1, + CAIRO_FORMAT_A8 = 2, + CAIRO_FORMAT_A1 = 3, + CAIRO_FORMAT_RGB16_565 = 4 +} cairo_format_t; + +cairo_public cairo_surface_t * +cairo_image_surface_create (cairo_format_t format, + int width, + int height); + +cairo_public int +cairo_format_stride_for_width (cairo_format_t format, + int width); + +cairo_public cairo_surface_t * +cairo_image_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride); + +cairo_public unsigned char * +cairo_image_surface_get_data (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_image_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_stride (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS + +cairo_public cairo_surface_t * +cairo_image_surface_create_from_png (const char *filename); + +cairo_public cairo_surface_t * +cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, + void *closure); + +#endif + +/* Recording-surface functions */ + +cairo_public cairo_surface_t * +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents); + +cairo_public void +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height); + +/* Null-surface functions */ + +cairo_public cairo_surface_t * +cairo_null_surface_create (cairo_content_t content); + +/* Pattern creation functions */ + +cairo_public cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue); + +cairo_public cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha); + +cairo_public cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface); + +cairo_public cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, + double x1, double y1); + +cairo_public cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1); + +cairo_public cairo_pattern_t * +cairo_pattern_reference (cairo_pattern_t *pattern); + +cairo_public void +cairo_pattern_destroy (cairo_pattern_t *pattern); + +cairo_public unsigned int +cairo_pattern_get_reference_count (cairo_pattern_t *pattern); + +cairo_public cairo_status_t +cairo_pattern_status (cairo_pattern_t *pattern); + +cairo_public void * +cairo_pattern_get_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_pattern_set_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +/** + * cairo_pattern_type_t: + * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) + * color. It may be opaque or translucent. + * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image). + * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient. + * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient. + * + * #cairo_pattern_type_t is used to describe the type of a given pattern. + * + * The type of a pattern is determined by the function used to create + * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() + * functions create SOLID patterns. The remaining + * cairo_pattern_create functions map to pattern types in obvious + * ways. + * + * The pattern type can be queried with cairo_pattern_get_type() + * + * Most #cairo_pattern_t functions can be called with a pattern of any + * type, (though trying to change the extend or filter for a solid + * pattern will have no effect). A notable exception is + * cairo_pattern_add_color_stop_rgb() and + * cairo_pattern_add_color_stop_rgba() which must only be called with + * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern + * will be shutdown and put into an error state. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_pattern_type { + CAIRO_PATTERN_TYPE_SOLID, + CAIRO_PATTERN_TYPE_SURFACE, + CAIRO_PATTERN_TYPE_LINEAR, + CAIRO_PATTERN_TYPE_RADIAL +} cairo_pattern_type_t; + +cairo_public cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern); + +cairo_public void +cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue); + +cairo_public void +cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue, + double alpha); + +cairo_public void +cairo_pattern_set_matrix (cairo_pattern_t *pattern, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_pattern_get_matrix (cairo_pattern_t *pattern, + cairo_matrix_t *matrix); + +/** + * cairo_extend_t: + * @CAIRO_EXTEND_NONE: pixels outside of the source pattern + * are fully transparent + * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating + * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting + * at the edges (Implemented for surface patterns since 1.6) + * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy + * the closest pixel from the source (Since 1.2; but only + * implemented for surface patterns since 1.6) + * + * #cairo_extend_t is used to describe how pattern color/alpha will be + * determined for areas "outside" the pattern's natural area, (for + * example, outside the surface bounds or outside the gradient + * geometry). + * + * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns + * and %CAIRO_EXTEND_PAD for gradient patterns. + * + * New entries may be added in future versions. + **/ +typedef enum _cairo_extend { + CAIRO_EXTEND_NONE, + CAIRO_EXTEND_REPEAT, + CAIRO_EXTEND_REFLECT, + CAIRO_EXTEND_PAD +} cairo_extend_t; + +cairo_public void +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); + +cairo_public cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern); + +/** + * cairo_filter_t: + * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar + * to %CAIRO_FILTER_NEAREST + * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality + * similar to %CAIRO_FILTER_BILINEAR + * @CAIRO_FILTER_BEST: The highest-quality available, performance may + * not be suitable for interactive use. + * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering + * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions + * @CAIRO_FILTER_GAUSSIAN: This filter value is currently + * unimplemented, and should not be used in current code. + * + * #cairo_filter_t is used to indicate what filtering should be + * applied when reading pixel values from patterns. See + * cairo_pattern_set_source() for indicating the desired filter to be + * used with a particular pattern. + */ +typedef enum _cairo_filter { + CAIRO_FILTER_FAST, + CAIRO_FILTER_GOOD, + CAIRO_FILTER_BEST, + CAIRO_FILTER_NEAREST, + CAIRO_FILTER_BILINEAR, + CAIRO_FILTER_GAUSSIAN +} cairo_filter_t; + +cairo_public void +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); + +cairo_public cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern); + +cairo_public cairo_status_t +cairo_pattern_get_rgba (cairo_pattern_t *pattern, + double *red, double *green, + double *blue, double *alpha); + +cairo_public cairo_status_t +cairo_pattern_get_surface (cairo_pattern_t *pattern, + cairo_surface_t **surface); + + +cairo_public cairo_status_t +cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, + int index, double *offset, + double *red, double *green, + double *blue, double *alpha); + +cairo_public cairo_status_t +cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, + int *count); + +cairo_public cairo_status_t +cairo_pattern_get_linear_points (cairo_pattern_t *pattern, + double *x0, double *y0, + double *x1, double *y1); + +cairo_public cairo_status_t +cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, + double *x0, double *y0, double *r0, + double *x1, double *y1, double *r1); + +/* Matrix functions */ + +cairo_public void +cairo_matrix_init (cairo_matrix_t *matrix, + double xx, double yx, + double xy, double yy, + double x0, double y0); + +cairo_public void +cairo_matrix_init_identity (cairo_matrix_t *matrix); + +cairo_public void +cairo_matrix_init_translate (cairo_matrix_t *matrix, + double tx, double ty); + +cairo_public void +cairo_matrix_init_scale (cairo_matrix_t *matrix, + double sx, double sy); + +cairo_public void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians); + +cairo_public void +cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); + +cairo_public void +cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); + +cairo_public void +cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); + +cairo_public cairo_status_t +cairo_matrix_invert (cairo_matrix_t *matrix); + +cairo_public void +cairo_matrix_multiply (cairo_matrix_t *result, + const cairo_matrix_t *a, + const cairo_matrix_t *b); + +cairo_public void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, + double *dx, double *dy); + +cairo_public void +cairo_matrix_transform_point (const cairo_matrix_t *matrix, + double *x, double *y); + +/* Region functions */ + +/** + * cairo_region_t: + * + * A #cairo_region_t represents a set of integer-aligned rectangles. + * + * It allows set-theoretical operations like cairo_region_union() and + * cairo_region_intersect() to be performed on them. + * + * Memory management of #cairo_region_t is done with + * cairo_region_reference() and cairo_region_destroy(). + * + * Since: 1.10 + **/ +typedef struct _cairo_region cairo_region_t; + +/** + * cairo_rectangle_int_t: + * @x: X coordinate of the left side of the rectangle + * @y: Y coordinate of the the top side of the rectangle + * @width: width of the rectangle + * @height: height of the rectangle + * + * A data structure for holding a rectangle with integer coordinates. + * + * Since: 1.10 + **/ + +typedef struct _cairo_rectangle_int { + int x, y; + int width, height; +} cairo_rectangle_int_t; + +typedef enum _cairo_region_overlap { + CAIRO_REGION_OVERLAP_IN, /* completely inside region */ + CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ + CAIRO_REGION_OVERLAP_PART /* partly inside region */ +} cairo_region_overlap_t; + +cairo_public cairo_region_t * +cairo_region_create (void); + +cairo_public cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_region_t * +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, + int count); + +cairo_public cairo_region_t * +cairo_region_copy (const cairo_region_t *original); + +cairo_public cairo_region_t * +cairo_region_reference (cairo_region_t *region); + +cairo_public void +cairo_region_destroy (cairo_region_t *region); + +cairo_public cairo_bool_t +cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b); + +cairo_public cairo_status_t +cairo_region_status (const cairo_region_t *region); + +cairo_public void +cairo_region_get_extents (const cairo_region_t *region, + cairo_rectangle_int_t *extents); + +cairo_public int +cairo_region_num_rectangles (const cairo_region_t *region); + +cairo_public void +cairo_region_get_rectangle (const cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_is_empty (const cairo_region_t *region); + +cairo_public cairo_region_overlap_t +cairo_region_contains_rectangle (const cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_contains_point (const cairo_region_t *region, int x, int y); + +cairo_public void +cairo_region_translate (cairo_region_t *region, int dx, int dy); + +cairo_public cairo_status_t +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_union (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_xor_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +/* Functions to be used while debugging (not intended for use in production code) */ +cairo_public void +cairo_debug_reset_static_data (void); + + +CAIRO_END_DECLS + +#endif /* CAIRO_H */ diff --git a/libs/cairo/src/cairoint.h b/libs/cairo/src/cairoint.h new file mode 100644 index 000000000..2f638f2d7 --- /dev/null +++ b/libs/cairo/src/cairoint.h @@ -0,0 +1,2554 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * These definitions are solely for use by the implementation of cairo + * and constitute no kind of standard. If you need any of these + * functions, please drop me a note. Either the library needs new + * functionality, or there's a way to do what you need using the + * existing published interfaces. cworth@cworth.org + */ + +#ifndef _CAIROINT_H_ +#define _CAIROINT_H_ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +#define cairo_public __declspec(dllexport) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif +#include +#include +#include + +#include "cairo.h" +#include + +#include "cairo-compiler-private.h" + +#if CAIRO_HAS_PS_SURFACE || \ + CAIRO_HAS_PDF_SURFACE || \ + CAIRO_HAS_SVG_SURFACE || \ + CAIRO_HAS_WIN32_SURFACE +#define CAIRO_HAS_FONT_SUBSET 1 +#endif + +#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET +#define CAIRO_HAS_PDF_OPERATORS 1 +#endif + +CAIRO_BEGIN_DECLS + +#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ +cairo_private FILE * +_cairo_win32_tmpfile (void); +#define tmpfile() _cairo_win32_tmpfile() +#endif + +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#undef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef NDEBUG +#undef assert +#define assert(expr) \ + do { if (!(expr)) fprintf(stderr, "Assertion failed at %s:%d: %s\n", \ + __FILE__, __LINE__, #expr); } while (0) +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.707106781186547524400844362104849039 +#endif + +#undef ARRAY_LENGTH +#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0]))) + +#undef STRINGIFY +#undef STRINGIFY_ARG +#define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) +#define STRINGIFY_ARG(contents) #contents + +#if defined (__GNUC__) +#define cairo_container_of(ptr, type, member) ({ \ + const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ + (type *) ((char *) mptr__ - offsetof (type, member)); \ +}) +#else +#define cairo_container_of(ptr, type, member) \ + ((type *)((char *) (ptr) - (char *) &((type *)0)->member)) +#endif + + +#define ASSERT_NOT_REACHED \ +do { \ + assert (!"reached"); \ +} while (0) +#define COMPILE_TIME_ASSERT1(condition, line) \ + typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1] +#define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line) +#define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__) + +#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff)) +#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff) + +#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff)) +#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00) +#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0) + +#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short) +#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short) + +/* Reverse the bits in a byte with 7 operations (no 64-bit): + * Devised by Sean Anderson, July 13, 2001. + * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits + */ +#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) + +/* Return the number of 1 bits in mask. + * + * GCC 3.4 supports a "population count" builtin, which on many targets is + * implemented with a single instruction. There is a fallback definition + * in libgcc in case a target does not have one, which should be just as + * good as the open-coded solution below, (which is "HACKMEM 169"). + */ +static inline int cairo_const +_cairo_popcount (uint32_t mask) +{ +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (mask); +#else + register int y; + + y = (mask >> 1) &033333333333; + y = mask - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); +#endif +} + +#ifdef WORDS_BIGENDIAN +#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c) +#else +#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c) +#endif + +#ifdef WORDS_BIGENDIAN + +#define cpu_to_be16(v) (v) +#define be16_to_cpu(v) (v) +#define cpu_to_be32(v) (v) +#define be32_to_cpu(v) (v) + +#else + +static inline uint16_t cairo_const +cpu_to_be16(uint16_t v) +{ + return (v << 8) | (v >> 8); +} + +static inline uint16_t cairo_const +be16_to_cpu(uint16_t v) +{ + return cpu_to_be16 (v); +} + +static inline uint32_t cairo_const +cpu_to_be32(uint32_t v) +{ + return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); +} + +static inline uint32_t cairo_const +be32_to_cpu(uint32_t v) +{ + return cpu_to_be32 (v); +} + +#endif + + +/* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales. + */ + +static inline int cairo_const +_cairo_isspace (int c) +{ + return (c == 0x20 || (c >= 0x09 && c <= 0x0d)); +} + +static inline int cairo_const +_cairo_isdigit (int c) +{ + return (c >= '0' && c <= '9'); +} + +#include "cairo-types-private.h" +#include "cairo-cache-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-spans-private.h" + +cairo_private void +_cairo_box_from_doubles (cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private void +_cairo_box_to_doubles (const cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private void +_cairo_box_from_rectangle (cairo_box_t *box, + const cairo_rectangle_int_t *rectangle); + +cairo_private cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle); + +cairo_private void +_cairo_box_round_to_rectangle (const cairo_box_t *box, + cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_boxes_get_extents (const cairo_box_t *boxes, + int num_boxes, + cairo_box_t *extents); + +static inline void +_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect) +{ + rect->x = CAIRO_RECT_INT_MIN; + rect->y = CAIRO_RECT_INT_MIN; + rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; + rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; +} + +cairo_private cairo_bool_t +_cairo_rectangle_intersect (cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src); + +cairo_private cairo_bool_t +_cairo_box_intersects_line_segment (cairo_box_t *box, + cairo_line_t *line) cairo_pure; + +cairo_private cairo_bool_t +_cairo_box_contains_point (cairo_box_t *box, + const cairo_point_t *point) cairo_pure; + +/* cairo-array.c structures and functions */ + +cairo_private void +_cairo_array_init (cairo_array_t *array, int element_size); + +cairo_private void +_cairo_array_init_snapshot (cairo_array_t *array, + const cairo_array_t *other); + +cairo_private void +_cairo_array_fini (cairo_array_t *array); + +cairo_private cairo_status_t +_cairo_array_grow_by (cairo_array_t *array, unsigned int additional); + +cairo_private void +_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements); + +cairo_private cairo_status_t +_cairo_array_append (cairo_array_t *array, const void *element); + +cairo_private cairo_status_t +_cairo_array_append_multiple (cairo_array_t *array, + const void *elements, + int num_elements); + +cairo_private cairo_status_t +_cairo_array_allocate (cairo_array_t *array, + unsigned int num_elements, + void **elements); + +cairo_private void * +_cairo_array_index (cairo_array_t *array, unsigned int index); + +cairo_private void +_cairo_array_copy_element (cairo_array_t *array, int index, void *dst); + +cairo_private int +_cairo_array_num_elements (cairo_array_t *array); + +cairo_private int +_cairo_array_size (cairo_array_t *array); + +typedef struct { + const cairo_user_data_key_t *key; + void *user_data; + cairo_destroy_func_t destroy; +} cairo_user_data_slot_t; + +cairo_private void +_cairo_user_data_array_init (cairo_user_data_array_t *array); + +cairo_private void +_cairo_user_data_array_fini (cairo_user_data_array_t *array); + +cairo_private void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key); + +cairo_private cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_private cairo_status_t +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + cairo_user_data_array_t *src); + +cairo_private void +_cairo_user_data_array_foreach (cairo_user_data_array_t *array, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure); + +#define _CAIRO_HASH_INIT_VALUE 5381 + +cairo_private unsigned long +_cairo_hash_string (const char *c); + +cairo_private unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *bytes, + unsigned int length); + +#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash) +#define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) + +#include "cairo-scaled-font-private.h" + +struct _cairo_font_face { + /* hash_entry must be first */ + cairo_hash_entry_t hash_entry; + cairo_status_t status; + cairo_reference_count_t ref_count; + cairo_user_data_array_t user_data; + const cairo_font_face_backend_t *backend; +}; + +cairo_private void +_cairo_reset_static_data (void); + +cairo_private void +_cairo_toy_font_face_reset_static_data (void); + +cairo_private void +_cairo_ft_font_reset_static_data (void); + +cairo_private void +_cairo_win32_font_reset_static_data (void); + +/* the font backend interface */ + +struct _cairo_unscaled_font_backend { + void (*destroy) (void *unscaled_font); +}; + +/* #cairo_toy_font_face_t - simple family/slant/weight font faces used for + * the built-in font API + */ + +typedef struct _cairo_toy_font_face { + cairo_font_face_t base; + const char *family; + cairo_bool_t owns_family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; + + cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */ +} cairo_toy_font_face_t; + +typedef enum _cairo_scaled_glyph_info { + CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), + CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), + CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2), + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3) +} cairo_scaled_glyph_info_t; + +typedef struct _cairo_scaled_font_subset { + cairo_scaled_font_t *scaled_font; + unsigned int font_id; + unsigned int subset_id; + + /* Index of glyphs array is subset_glyph_index. + * Value of glyphs array is scaled_font_glyph_index. + */ + unsigned long *glyphs; + unsigned long *to_unicode; + char **utf8; + char **glyph_names; + unsigned int num_glyphs; + cairo_bool_t is_composite; + cairo_bool_t is_scaled; +} cairo_scaled_font_subset_t; + +struct _cairo_scaled_font_backend { + cairo_font_type_t type; + + void + (*fini) (void *scaled_font); + + cairo_warn cairo_int_status_t + (*scaled_glyph_init) (void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + + /* A backend only needs to implement this or ucs4_to_index(), not + * both. This allows the backend to do something more sophisticated + * then just converting characters one by one. + */ + cairo_warn cairo_int_status_t + (*text_to_glyphs) (void *scaled_font, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + + unsigned long + (*ucs4_to_index) (void *scaled_font, + uint32_t ucs4); + cairo_warn cairo_int_status_t + (*show_glyphs) (void *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region, + int *remaining_glyphs); + + cairo_warn cairo_int_status_t + (*load_truetype_table)(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length); + + /* ucs4 is set to -1 if the unicode character could not be found + * for the glyph */ + cairo_warn cairo_int_status_t + (*index_to_ucs4)(void *scaled_font, + unsigned long index, + uint32_t *ucs4); +}; + +struct _cairo_font_face_backend { + cairo_font_type_t type; + + cairo_warn cairo_status_t + (*create_for_toy) (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); + + /* The destroy() function is allowed to resurrect the font face + * by re-referencing. This is needed for the FreeType backend. + */ + void + (*destroy) (void *font_face); + + cairo_warn cairo_status_t + (*scaled_font_create) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font); + + cairo_font_face_t * + (*get_implementation) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); +}; + +extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; + +/* concrete font backends */ +#if CAIRO_HAS_FT_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend; + +#endif + +#if CAIRO_HAS_WIN32_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend; + +#endif + +#if CAIRO_HAS_DWRITE_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend; + +#endif + +#if CAIRO_HAS_QUARTZ_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; + +#endif + +struct _cairo_surface_backend { + cairo_surface_type_t type; + + cairo_surface_t * + (*create_similar) (void *surface, + cairo_content_t content, + int width, + int height); + + cairo_warn cairo_status_t + (*finish) (void *surface); + + cairo_warn cairo_status_t + (*acquire_source_image) (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); + + void + (*release_source_image) (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra); + + cairo_warn cairo_status_t + (*acquire_dest_image) (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra); + + void + (*release_dest_image) (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra); + + /* Create a new surface (@clone_out) with the following + * characteristics: + * + * 1. It is as compatible as possible with @surface (in terms of + * efficiency) + * + * 2. It has the same contents as @src within the given rectangle. + * + * 3. The offset of the similar surface with respect to the original + * surface is returned in the clone_offset vector. + * - if you clone the entire surface, this vector is zero. + * - if you clone (src_x, src_y)x(w, h) the vector is (src_x, src_y); + */ + cairo_warn cairo_status_t + (*clone_similar) (void *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out); + + /* XXX remove to a separate cairo_surface_compositor_t */ + /* XXX: dst should be the first argument for consistency */ + cairo_warn cairo_int_status_t + (*composite) (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + void *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + + cairo_warn cairo_int_status_t + (*fill_rectangles) (void *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects); + + /* XXX: dst should be the first argument for consistency */ + cairo_warn cairo_int_status_t + (*composite_trapezoids) (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps, + cairo_region_t *region); + + cairo_warn cairo_span_renderer_t * + (*create_span_renderer) (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region); + + cairo_warn cairo_bool_t + (*check_span_renderer) (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *dst, + cairo_antialias_t antialias); + + cairo_warn cairo_int_status_t + (*copy_page) (void *surface); + + cairo_warn cairo_int_status_t + (*show_page) (void *surface); + + /* Get the extents of the current surface. For many surface types + * this will be as simple as { x=0, y=0, width=surface->width, + * height=surface->height}. + * + * If this function is not implemented, or if it returns + * FALSE the surface is considered to be + * boundless and infinite bounds are used for it. + */ + cairo_warn cairo_bool_t + (*get_extents) (void *surface, + cairo_rectangle_int_t *extents); + + /* + * This is an optional entry to let the surface manage its own glyph + * resources. If null, render against this surface, using image + * surfaces as glyphs. + */ + cairo_warn cairo_int_status_t + (*old_show_glyphs) (cairo_scaled_font_t *font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + void *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region); + + void + (*get_font_options) (void *surface, + cairo_font_options_t *options); + + cairo_warn cairo_status_t + (*flush) (void *surface); + + cairo_warn cairo_status_t + (*mark_dirty_rectangle) (void *surface, + int x, + int y, + int width, + int height); + + void + (*scaled_font_fini) (cairo_scaled_font_t *scaled_font); + + void + (*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + + /* OK, I'm starting over somewhat by defining the 5 top-level + * drawing operators for the surface backend here with consistent + * naming and argument-order conventions. */ + cairo_warn cairo_int_status_t + (*paint) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + + cairo_warn cairo_int_status_t + (*mask) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + + cairo_warn cairo_int_status_t + (*stroke) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + + cairo_warn cairo_int_status_t + (*fill) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + + cairo_warn cairo_int_status_t + (*show_glyphs) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *remaining_glyphs); + + cairo_surface_t * + (*snapshot) (void *surface); + + cairo_bool_t + (*is_similar) (void *surface_a, + void *surface_b); + + cairo_warn cairo_int_status_t + (*fill_stroke) (void *surface, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip); + + cairo_surface_t * + (*create_solid_pattern_surface) + (void *surface, + const cairo_solid_pattern_t *solid_pattern); + + cairo_bool_t + (*can_repaint_solid_pattern_surface) + (void *surface, + const cairo_solid_pattern_t *solid_pattern); + + cairo_bool_t + (*has_show_text_glyphs) (void *surface); + + cairo_warn cairo_int_status_t + (*show_text_glyphs) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); +}; + +#include "cairo-surface-private.h" + +struct _cairo_image_surface { + cairo_surface_t base; + + pixman_format_code_t pixman_format; + cairo_format_t format; + unsigned char *data; + + int width; + int height; + int stride; + int depth; + + pixman_image_t *pixman_image; + + unsigned owns_data : 1; + unsigned transparency : 2; +}; + +extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; + +#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE +#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD +#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD + +extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear; +extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; +extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white; + +typedef struct _cairo_surface_attributes { + cairo_matrix_t matrix; + cairo_extend_t extend; + cairo_filter_t filter; + cairo_bool_t has_component_alpha; + int x_offset; + int y_offset; + void *extra; +} cairo_surface_attributes_t; + +typedef struct _cairo_traps { + cairo_status_t status; + + const cairo_box_t *limits; + int num_limits; + + unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ + unsigned int has_intersections : 1; + unsigned int is_rectilinear : 1; + unsigned int is_rectangular : 1; + + int num_traps; + int traps_size; + cairo_trapezoid_t *traps; + cairo_trapezoid_t traps_embedded[16]; +} cairo_traps_t; + +#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL +#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL + +#define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" +#define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" +#define CAIRO_FT_FONT_FAMILY_DEFAULT "" +#define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" + +#if CAIRO_HAS_DWRITE_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend + +#elif CAIRO_HAS_WIN32_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend + +#elif CAIRO_HAS_QUARTZ_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend + +#elif CAIRO_HAS_FT_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend + +#else + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend + +#endif + +#define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER +#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 usr_vector; +} cairo_stroke_face_t; + +/* cairo.c */ + +static inline double cairo_const +_cairo_restrict_value (double value, double min, double max) +{ + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +/* C99 round() rounds to the nearest integral value with halfway cases rounded + * away from 0. _cairo_round rounds halfway cases toward negative infinity. + * This matches the rounding behaviour of _cairo_lround. */ +static inline double cairo_const +_cairo_round (double r) +{ + return floor (r + .5); +} + +#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L +cairo_private int +_cairo_lround (double d) cairo_const; +#else +#define _cairo_lround lround +#endif + +cairo_private uint16_t +_cairo_half_from_float (float f) cairo_const; + +cairo_private cairo_bool_t +_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const; + +cairo_private cairo_bool_t +_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const; + +enum { + CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1, + CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2, +}; + +cairo_private uint32_t +_cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const; +/* cairo-color.c */ +cairo_private const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock) cairo_pure; + +#define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) +#define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) +#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) + +cairo_private uint16_t +_cairo_color_double_to_short (double d) cairo_const; + +cairo_private void +_cairo_color_init (cairo_color_t *color); + +cairo_private void +_cairo_color_init_rgb (cairo_color_t *color, + double red, double green, double blue); + +cairo_private void +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha); + +cairo_private void +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha); + +cairo_private void +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); + +cairo_private void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); + +cairo_private cairo_bool_t +_cairo_color_equal (const cairo_color_t *color_a, + const cairo_color_t *color_b) cairo_pure; + +cairo_private cairo_bool_t +_cairo_color_stop_equal (const cairo_color_stop_t *color_a, + const cairo_color_stop_t *color_b) cairo_pure; + +cairo_private cairo_content_t +_cairo_color_get_content (const cairo_color_t *color) cairo_pure; + +/* cairo-font-face.c */ + +extern const cairo_private cairo_font_face_t _cairo_font_face_nil; + +cairo_private void +_cairo_font_face_init (cairo_font_face_t *font_face, + const cairo_font_face_backend_t *backend); + +cairo_private cairo_status_t +_cairo_font_face_set_error (cairo_font_face_t *font_face, + cairo_status_t status); + +cairo_private void +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const cairo_unscaled_font_backend_t *backend); + +cairo_private_no_warn cairo_unscaled_font_t * +_cairo_unscaled_font_reference (cairo_unscaled_font_t *font); + +cairo_private void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); + +/* cairo-font-face-twin.c */ + +cairo_private cairo_font_face_t * +_cairo_font_face_twin_create_fallback (void); + +cairo_private cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); + +/* cairo-font-face-twin-data.c */ + +extern const cairo_private int8_t _cairo_twin_outlines[]; +extern const cairo_private uint16_t _cairo_twin_charmap[128]; + +/* cairo-font-options.c */ + +cairo_private void +_cairo_font_options_init_default (cairo_font_options_t *options); + +cairo_private void +_cairo_font_options_init_copy (cairo_font_options_t *options, + const cairo_font_options_t *other); + +cairo_private void +_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, + cairo_lcd_filter_t lcd_filter); + +cairo_private cairo_lcd_filter_t +_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); + +cairo_private void +_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, + cairo_round_glyph_positions_t round); + +cairo_private cairo_round_glyph_positions_t +_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options); + +/* cairo-hull.c */ +cairo_private cairo_status_t +_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); + +/* cairo-lzw.c */ +cairo_private unsigned char * +_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out); + +/* cairo-misc.c */ +cairo_private cairo_status_t +_cairo_validate_text_clusters (const char *utf8, + int utf8_len, + const cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags); + +cairo_private cairo_status_t +_cairo_intern_string (const char **str_inout, int len); + +cairo_private void +_cairo_intern_string_reset_static_data (void); + +/* cairo-path-fixed.c */ +cairo_private cairo_path_fixed_t * +_cairo_path_fixed_create (void); + +cairo_private void +_cairo_path_fixed_init (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, + const cairo_path_fixed_t *other); + +cairo_private void +_cairo_path_fixed_fini (cairo_path_fixed_t *path); + +cairo_private void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); + +cairo_private void +_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); + +cairo_private cairo_status_t +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); + +cairo_private cairo_status_t +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2); + +cairo_private cairo_status_t +_cairo_path_fixed_close_path (cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y); + +typedef cairo_status_t +(cairo_path_fixed_move_to_func_t) (void *closure, + const cairo_point_t *point); + +typedef cairo_status_t +(cairo_path_fixed_line_to_func_t) (void *closure, + const cairo_point_t *point); + +typedef cairo_status_t +(cairo_path_fixed_curve_to_func_t) (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2); + +typedef cairo_status_t +(cairo_path_fixed_close_path_func_t) (void *closure); + +cairo_private cairo_status_t +_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_curve_to_func_t *curve_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure); + +cairo_private cairo_status_t +_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure, + double tolerance); + +cairo_private cairo_bool_t +_cairo_path_fixed_extents (const cairo_path_fixed_t *path, + cairo_box_t *box); + +cairo_private void +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_transform (cairo_path_fixed_t *path, + const cairo_matrix_t *matrix); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, + cairo_box_t *box); + +/* cairo-path-in-fill.c */ +cairo_private cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y); + +/* cairo-path-fill.c */ +cairo_private cairo_status_t +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon); + +cairo_private cairo_int_status_t +_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_traps_t *traps); + +cairo_private cairo_status_t +_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes); + +cairo_private cairo_region_t * +_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_traps_t *traps); + +/* cairo-path-stroke.c */ +cairo_private cairo_status_t +_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_polygon_t *polygon); + +cairo_private cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_traps_t *traps); + +cairo_private cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_boxes_t *boxes); + +cairo_private cairo_status_t +_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_traps_t *traps); + +cairo_private cairo_status_t +_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure); + +/* cairo-scaled-font.c */ + +cairo_private void +_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, + cairo_status_t status); + +cairo_private cairo_scaled_font_t * +_cairo_scaled_font_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_scaled_font_reset_static_data (void); + +cairo_private cairo_status_t +_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend); + +cairo_private cairo_status_t +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics); + +/* This should only be called on an error path by a scaled_font constructor */ +cairo_private void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap); + +cairo_private void +_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics); + +cairo_private void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface); + +cairo_private void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface); + +cairo_private cairo_int_status_t +_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret); + +cairo_private double +_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_map_destroy (void); + +/* cairo-stroke-style.c */ + +cairo_private void +_cairo_stroke_style_init (cairo_stroke_style_t *style); + +cairo_private cairo_status_t +_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, + const cairo_stroke_style_t *other); + +cairo_private void +_cairo_stroke_style_fini (cairo_stroke_style_t *style); + +cairo_private void +_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double *dx, double *dy); + +cairo_private double +_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style); + +cairo_private double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style); + +cairo_private cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance); + +cairo_private void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes); + + +/* cairo-surface.c */ + +cairo_private cairo_surface_t * +_cairo_surface_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_surface_copy_mime_data (cairo_surface_t *dst, + cairo_surface_t *src); + +cairo_private cairo_status_t +_cairo_surface_set_error (cairo_surface_t *surface, + cairo_status_t status); + +cairo_private void +_cairo_surface_set_resolution (cairo_surface_t *surface, + double x_res, + double y_res); + +cairo_private cairo_surface_t * +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, + int width, + int height); + +cairo_private cairo_surface_t * +_cairo_surface_create_similar_solid (cairo_surface_t *other, + cairo_content_t content, + int width, + int height, + const cairo_color_t *color, + cairo_bool_t allow_fallback); + +cairo_private cairo_surface_t * +_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, + const cairo_solid_pattern_t *solid_pattern); + +cairo_private cairo_int_status_t +_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, + cairo_surface_t *solid_surface, + const cairo_solid_pattern_t *solid_pattern); + +cairo_private void +_cairo_surface_init (cairo_surface_t *surface, + const cairo_surface_backend_t *backend, + cairo_device_t *device, + cairo_content_t content); + +cairo_private void +_cairo_surface_set_font_options (cairo_surface_t *surface, + cairo_font_options_t *options); + +cairo_private cairo_status_t +_cairo_surface_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_fill_rectangle (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + int x, + int y, + int width, + int height); + +cairo_private cairo_status_t +_cairo_surface_fill_region (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_region_t *region); + +cairo_private cairo_status_t +_cairo_surface_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects); + +cairo_private cairo_status_t +_cairo_surface_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fill_stroke (cairo_surface_t *surface, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_stroke (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fill (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_show_text_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_paint_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_mask_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_stroke_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_fill_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_glyphs_extents (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_composite_trapezoids (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int ntraps, + cairo_region_t *clip_region); + +cairo_private cairo_span_renderer_t * +_cairo_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects, + cairo_region_t *clip_region); + +cairo_private cairo_bool_t +_cairo_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias); + +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra); + +cairo_private cairo_status_t +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra); + +cairo_private void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra); + +cairo_private cairo_status_t +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out); + +cairo_private cairo_surface_t * +_cairo_surface_snapshot (cairo_surface_t *surface); + +cairo_private cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend); + +cairo_private cairo_bool_t +_cairo_surface_is_similar (cairo_surface_t *surface_a, + cairo_surface_t *surface_b); + +cairo_private cairo_bool_t +_cairo_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, + cairo_surface_attributes_t *src_attr, + int src_width, + int src_height, + cairo_surface_attributes_t *mask_attr, + int mask_width, + int mask_height, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + +cairo_private cairo_status_t +_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, + cairo_surface_attributes_t *src_attr, + int src_width, + int src_height, + int mask_width, + int mask_height, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region); + +cairo_private cairo_bool_t +_cairo_surface_is_opaque (const cairo_surface_t *surface); + +cairo_private int +_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface); + +cairo_private void +_cairo_surface_set_device_scale (cairo_surface_t *surface, + double sx, + double sy); + +cairo_private cairo_bool_t +_cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; + +cairo_private void +_cairo_surface_release_device_reference (cairo_surface_t *surface); + +/* cairo-image-surface.c */ + +/* XXX: In cairo 1.2.0 we added a new %CAIRO_FORMAT_RGB16_565 but + * neglected to adjust this macro. The net effect is that it's + * impossible to externally create an image surface with this + * format. This is perhaps a good thing since we also neglected to fix + * up things like cairo_surface_write_to_png() for the new format + * (-Wswitch-enum will tell you where). Is it obvious that format was + * added in haste? + * + * The reason for the new format was to allow the xlib backend to be + * used on X servers with a 565 visual. So the new format did its job + * for that, even without being considered "valid" for the sake of + * things like cairo_image_surface_create(). + * + * Since 1.2.0 we ran into the same situtation with X servers with BGR + * visuals. This time we invented #cairo_internal_format_t instead, + * (see it for more discussion). + * + * The punchline is that %CAIRO_FORMAT_VALID must not conside any + * internal format to be valid. Also we need to decide if the + * RGB16_565 should be moved to instead be an internal format. If so, + * this macro need not change for it. (We probably will need to leave + * an RGB16_565 value in the header files for the sake of code that + * might have that value in it.) + * + * If we do decide to start fully supporting RGB16_565 as an external + * format, then %CAIRO_FORMAT_VALID needs to be adjusted to include + * it. But that should not happen before all necessary code is fixed + * to support it (at least cairo_surface_write_to_png() and a few spots + * in cairo-xlib-surface.c--again see -Wswitch-enum). + */ +#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ + (format) <= CAIRO_FORMAT_RGB16_565) + +/* pixman-required stride alignment in bytes. */ +#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) +#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \ + ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT) + +#define CAIRO_CONTENT_VALID(content) ((content) && \ + (((content) & ~(CAIRO_CONTENT_COLOR | \ + CAIRO_CONTENT_ALPHA | \ + CAIRO_CONTENT_COLOR_ALPHA))\ + == 0)) + +static inline cairo_bool_t +_cairo_valid_stride_alignment(int stride) +{ + return !(stride & (CAIRO_STRIDE_ALIGNMENT-1)); +} + +cairo_private int +_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; + +cairo_private cairo_format_t +_cairo_format_from_content (cairo_content_t content) cairo_const; + +cairo_private cairo_format_t +_cairo_format_from_pixman_format (pixman_format_code_t pixman_format); + +cairo_private cairo_content_t +_cairo_content_from_format (cairo_format_t format) cairo_const; + +cairo_private cairo_content_t +_cairo_content_from_pixman_format (pixman_format_code_t pixman_format); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + pixman_format_code_t pixman_format); + +cairo_private pixman_format_code_t +_cairo_format_to_pixman_format_code (cairo_format_t format); + +cairo_private cairo_bool_t +_pixman_format_from_masks (cairo_format_masks_t *masks, + pixman_format_code_t *format_ret); + +cairo_private cairo_bool_t +_pixman_format_to_masks (pixman_format_code_t pixman_format, + cairo_format_masks_t *masks); + +cairo_private void +_cairo_image_reset_static_data (void); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_with_pixman_format (unsigned char *data, + pixman_format_code_t pixman_format, + int width, + int height, + int stride); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_with_content (cairo_content_t content, + int width, + int height); + +cairo_private void +_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_coerce (cairo_image_surface_t *surface); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, + cairo_format_t format); + +cairo_private void +_cairo_image_surface_span_render_row (int y, + const cairo_half_open_span_t *spans, + unsigned num_spans, + uint8_t *data, + uint32_t stride); + +cairo_private cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image); + +cairo_private cairo_bool_t +_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; + +cairo_private cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure; + +/* cairo-pen.c */ +cairo_private cairo_status_t +_cairo_pen_init (cairo_pen_t *pen, + double radius, + double tolerance, + const cairo_matrix_t *ctm); + +cairo_private void +_cairo_pen_init_empty (cairo_pen_t *pen); + +cairo_private cairo_status_t +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other); + +cairo_private void +_cairo_pen_fini (cairo_pen_t *pen); + +cairo_private cairo_status_t +_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points); + +cairo_private cairo_status_t +_cairo_pen_add_points_for_slopes (cairo_pen_t *pen, + cairo_point_t *a, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); + +cairo_private int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); + +cairo_private int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); + +/* cairo-polygon.c */ +cairo_private void +_cairo_polygon_init (cairo_polygon_t *polygon); + +cairo_private void +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *boxes, + int num_boxes); + +cairo_private void +_cairo_polygon_fini (cairo_polygon_t *polygon); + +cairo_private cairo_status_t +_cairo_polygon_add_line (cairo_polygon_t *polygon, + const cairo_line_t *line, + int top, int bottom, + int dir); + +cairo_private cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2); + +cairo_private cairo_status_t +_cairo_polygon_move_to (cairo_polygon_t *polygon, + const cairo_point_t *point); + +cairo_private cairo_status_t +_cairo_polygon_line_to (cairo_polygon_t *polygon, + const cairo_point_t *point); + +cairo_private cairo_status_t +_cairo_polygon_close (cairo_polygon_t *polygon); + +#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status + +/* cairo-spline.c */ +cairo_private cairo_bool_t +_cairo_spline_init (cairo_spline_t *spline, + cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *a, const cairo_point_t *b, + const cairo_point_t *c, const cairo_point_t *d); + +cairo_private cairo_status_t +_cairo_spline_decompose (cairo_spline_t *spline, double tolerance); + +cairo_private cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3); + +/* cairo-matrix.c */ +cairo_private void +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0); + +cairo_private void +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight); + +cairo_private void +_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, + cairo_box_t *bbox, + cairo_bool_t *is_tight); + +cairo_private cairo_bool_t +_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_bool_t +_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private double +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_status_t +_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, + double *sx, double *sy, int x_major); + +cairo_private cairo_bool_t +_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_bool_t +_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_bool_t +_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, + int *itx, int *ity); + +cairo_private cairo_bool_t +_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix); + +cairo_private cairo_bool_t +_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private void +_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, + double radius, + double *major, + double *minor); + +cairo_private double +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) cairo_pure; + +cairo_private void +_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, + pixman_transform_t *pixman_transform, + double xc, + double yc); + +/* cairo-traps.c */ +cairo_private void +_cairo_traps_init (cairo_traps_t *traps); + +cairo_private void +_cairo_traps_limit (cairo_traps_t *traps, + const cairo_box_t *boxes, + int num_boxes); + +cairo_private cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_boxes_t *boxes); + +cairo_private void +_cairo_traps_clear (cairo_traps_t *traps); + +cairo_private void +_cairo_traps_fini (cairo_traps_t *traps); + +#define _cairo_traps_status(T) (T)->status + +cairo_private void +_cairo_traps_translate (cairo_traps_t *traps, int x, int y); + +cairo_private cairo_status_t +_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, + const cairo_point_t *top_left, + const cairo_point_t *bottom_right); + +cairo_private void +_cairo_traps_add_trap (cairo_traps_t *traps, + cairo_fixed_t top, cairo_fixed_t bottom, + cairo_line_t *left, cairo_line_t *right); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *out); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes); + +cairo_private int +_cairo_traps_contain (const cairo_traps_t *traps, + double x, double y); + +cairo_private void +_cairo_traps_extents (const cairo_traps_t *traps, + cairo_box_t *extents); + +cairo_private cairo_int_status_t +_cairo_traps_extract_region (cairo_traps_t *traps, + cairo_region_t **region); + +cairo_private cairo_status_t +_cairo_traps_path (const cairo_traps_t *traps, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, + cairo_trapezoid_t *src_traps, + int num_traps, + double tx, double ty, + double sx, double sy); + +/* cairo-pattern.c */ + +cairo_private cairo_pattern_t * +_cairo_pattern_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_pattern_create_copy (cairo_pattern_t **pattern, + const cairo_pattern_t *other); + +cairo_private cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private cairo_status_t +_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + const cairo_color_t *color); + +cairo_private void +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface); + +cairo_private void +_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, + double x0, double y0, double x1, double y1); + +cairo_private void +_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, + double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1); + +cairo_private void +_cairo_pattern_fini (cairo_pattern_t *pattern); + +cairo_private cairo_pattern_t * +_cairo_pattern_create_solid (const cairo_color_t *color); + +cairo_private void +_cairo_pattern_transform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm_inverse); + +cairo_private cairo_bool_t +_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents, + cairo_color_t *color); + +cairo_private cairo_bool_t +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_bool_t +_cairo_pattern_is_clear (const cairo_pattern_t *pattern); + +cairo_private_no_warn cairo_filter_t +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, + double *pad_out); + +enum { + CAIRO_PATTERN_ACQUIRE_NONE = 0x0, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1 +}; +cairo_private cairo_int_status_t +_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned int flags, + cairo_surface_t **surface_out, + cairo_surface_attributes_t *attributes); + +cairo_private void +_cairo_pattern_release_surface (const cairo_pattern_t *pattern, + cairo_surface_t *surface, + cairo_surface_attributes_t *attributes); + +cairo_private cairo_int_status_t +_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + unsigned int flags, + cairo_surface_t **src_out, + cairo_surface_t **mask_out, + cairo_surface_attributes_t *src_attributes, + cairo_surface_attributes_t *mask_attributes); + +cairo_private void +_cairo_pattern_get_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents); + +cairo_private unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern); + +cairo_private unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_linear_pattern_t *linear); + +cairo_private unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial); + +cairo_private cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b); + +cairo_private unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b); + +cairo_private cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, + const cairo_pattern_t *b); + +cairo_private void +_cairo_pattern_reset_static_data (void); + +#if CAIRO_HAS_DRM_SURFACE + +cairo_private void +_cairo_drm_device_reset_static_data (void); + +#endif + +cairo_private void +_cairo_clip_reset_static_data (void); + +/* cairo-unicode.c */ + +cairo_private int +_cairo_utf8_get_char_validated (const char *p, + uint32_t *unicode); + +cairo_private cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written); + +cairo_private int +_cairo_ucs4_to_utf8 (uint32_t unicode, + char *utf8); + +#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT +# define CAIRO_HAS_UTF8_TO_UTF16 1 +#endif +#if CAIRO_HAS_UTF8_TO_UTF16 +cairo_private cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + int *items_written); +#endif + +/* cairo-observer.c */ + +cairo_private void +_cairo_observers_notify (cairo_list_t *observers, void *arg); + +/* Avoid unnecessary PLT entries. */ +slim_hidden_proto (cairo_clip_preserve); +slim_hidden_proto (cairo_close_path); +slim_hidden_proto (cairo_create); +slim_hidden_proto (cairo_curve_to); +slim_hidden_proto (cairo_destroy); +slim_hidden_proto (cairo_fill_preserve); +slim_hidden_proto (cairo_font_face_destroy); +slim_hidden_proto (cairo_font_face_get_user_data); +slim_hidden_proto_no_warn (cairo_font_face_reference); +slim_hidden_proto (cairo_font_face_set_user_data); +slim_hidden_proto (cairo_font_options_equal); +slim_hidden_proto (cairo_font_options_hash); +slim_hidden_proto (cairo_font_options_merge); +slim_hidden_proto (cairo_font_options_set_antialias); +slim_hidden_proto (cairo_font_options_set_hint_metrics); +slim_hidden_proto (cairo_font_options_set_hint_style); +slim_hidden_proto (cairo_font_options_set_subpixel_order); +slim_hidden_proto (cairo_font_options_status); +slim_hidden_proto (cairo_format_stride_for_width); +slim_hidden_proto (cairo_get_current_point); +slim_hidden_proto (cairo_get_line_width); +slim_hidden_proto (cairo_get_matrix); +slim_hidden_proto (cairo_get_target); +slim_hidden_proto (cairo_get_tolerance); +slim_hidden_proto (cairo_glyph_allocate); +slim_hidden_proto (cairo_glyph_free); +slim_hidden_proto (cairo_image_surface_create); +slim_hidden_proto (cairo_image_surface_create_for_data); +slim_hidden_proto (cairo_image_surface_get_data); +slim_hidden_proto (cairo_image_surface_get_format); +slim_hidden_proto (cairo_image_surface_get_height); +slim_hidden_proto (cairo_image_surface_get_stride); +slim_hidden_proto (cairo_image_surface_get_width); +slim_hidden_proto (cairo_line_to); +slim_hidden_proto (cairo_mask); +slim_hidden_proto (cairo_matrix_init); +slim_hidden_proto (cairo_matrix_init_identity); +slim_hidden_proto (cairo_matrix_init_rotate); +slim_hidden_proto (cairo_matrix_init_scale); +slim_hidden_proto (cairo_matrix_init_translate); +slim_hidden_proto (cairo_matrix_invert); +slim_hidden_proto (cairo_matrix_multiply); +slim_hidden_proto (cairo_matrix_scale); +slim_hidden_proto (cairo_matrix_transform_distance); +slim_hidden_proto (cairo_matrix_transform_point); +slim_hidden_proto (cairo_matrix_translate); +slim_hidden_proto (cairo_move_to); +slim_hidden_proto (cairo_new_path); +slim_hidden_proto (cairo_paint); +slim_hidden_proto (cairo_pattern_create_for_surface); +slim_hidden_proto (cairo_pattern_create_rgb); +slim_hidden_proto (cairo_pattern_create_rgba); +slim_hidden_proto (cairo_pattern_destroy); +slim_hidden_proto (cairo_pattern_get_extend); +slim_hidden_proto_no_warn (cairo_pattern_reference); +slim_hidden_proto (cairo_pattern_set_matrix); +slim_hidden_proto (cairo_pop_group); +slim_hidden_proto (cairo_push_group_with_content); +slim_hidden_proto (cairo_rel_line_to); +slim_hidden_proto (cairo_restore); +slim_hidden_proto (cairo_save); +slim_hidden_proto (cairo_scale); +slim_hidden_proto (cairo_scaled_font_create); +slim_hidden_proto (cairo_scaled_font_destroy); +slim_hidden_proto (cairo_scaled_font_extents); +slim_hidden_proto (cairo_scaled_font_get_ctm); +slim_hidden_proto (cairo_scaled_font_get_font_face); +slim_hidden_proto (cairo_scaled_font_get_font_matrix); +slim_hidden_proto (cairo_scaled_font_get_font_options); +slim_hidden_proto (cairo_scaled_font_glyph_extents); +slim_hidden_proto_no_warn (cairo_scaled_font_reference); +slim_hidden_proto (cairo_scaled_font_status); +slim_hidden_proto (cairo_scaled_font_get_user_data); +slim_hidden_proto (cairo_scaled_font_set_user_data); +slim_hidden_proto (cairo_scaled_font_text_to_glyphs); +slim_hidden_proto (cairo_set_font_options); +slim_hidden_proto (cairo_set_font_size); +slim_hidden_proto (cairo_set_line_cap); +slim_hidden_proto (cairo_set_line_join); +slim_hidden_proto (cairo_set_line_width); +slim_hidden_proto (cairo_set_matrix); +slim_hidden_proto (cairo_set_operator); +slim_hidden_proto (cairo_set_source); +slim_hidden_proto (cairo_set_source_rgb); +slim_hidden_proto (cairo_set_source_surface); +slim_hidden_proto (cairo_set_tolerance); +slim_hidden_proto (cairo_status); +slim_hidden_proto (cairo_stroke); +slim_hidden_proto (cairo_stroke_preserve); +slim_hidden_proto (cairo_surface_copy_page); +slim_hidden_proto (cairo_surface_destroy); +slim_hidden_proto (cairo_surface_finish); +slim_hidden_proto (cairo_surface_flush); +slim_hidden_proto (cairo_surface_get_content); +slim_hidden_proto (cairo_surface_get_device_offset); +slim_hidden_proto (cairo_surface_get_font_options); +slim_hidden_proto (cairo_surface_get_mime_data); +slim_hidden_proto (cairo_surface_get_type); +slim_hidden_proto (cairo_surface_has_show_text_glyphs); +slim_hidden_proto (cairo_surface_set_subpixel_antialiasing); +slim_hidden_proto (cairo_surface_get_subpixel_antialiasing); +slim_hidden_proto (cairo_surface_mark_dirty); +slim_hidden_proto (cairo_surface_mark_dirty_rectangle); +slim_hidden_proto_no_warn (cairo_surface_reference); +slim_hidden_proto (cairo_surface_set_device_offset); +slim_hidden_proto (cairo_surface_set_fallback_resolution); +slim_hidden_proto (cairo_surface_set_mime_data); +slim_hidden_proto (cairo_surface_show_page); +slim_hidden_proto (cairo_surface_status); +slim_hidden_proto (cairo_text_cluster_allocate); +slim_hidden_proto (cairo_text_cluster_free); +slim_hidden_proto (cairo_toy_font_face_create); +slim_hidden_proto (cairo_toy_font_face_get_slant); +slim_hidden_proto (cairo_toy_font_face_get_weight); +slim_hidden_proto (cairo_translate); +slim_hidden_proto (cairo_transform); +slim_hidden_proto (cairo_user_font_face_create); +slim_hidden_proto (cairo_user_font_face_set_init_func); +slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); +slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); +slim_hidden_proto (cairo_user_to_device); +slim_hidden_proto (cairo_user_to_device_distance); +slim_hidden_proto (cairo_version_string); +slim_hidden_proto (cairo_region_create); +slim_hidden_proto (cairo_region_create_rectangle); +slim_hidden_proto (cairo_region_create_rectangles); +slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_reference); +slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_equal); +slim_hidden_proto (cairo_region_status); +slim_hidden_proto (cairo_region_get_extents); +slim_hidden_proto (cairo_region_num_rectangles); +slim_hidden_proto (cairo_region_get_rectangle); +slim_hidden_proto (cairo_region_is_empty); +slim_hidden_proto (cairo_region_contains_rectangle); +slim_hidden_proto (cairo_region_contains_point); +slim_hidden_proto (cairo_region_translate); +slim_hidden_proto (cairo_region_subtract); +slim_hidden_proto (cairo_region_subtract_rectangle); +slim_hidden_proto (cairo_region_intersect); +slim_hidden_proto (cairo_region_intersect_rectangle); +slim_hidden_proto (cairo_region_union); +slim_hidden_proto (cairo_region_union_rectangle); +slim_hidden_proto (cairo_region_xor); +slim_hidden_proto (cairo_region_xor_rectangle); + +#if CAIRO_HAS_PNG_FUNCTIONS + +slim_hidden_proto (cairo_surface_write_to_png_stream); + +#endif + +cairo_private_no_warn cairo_filter_t +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, + double *pad_out); + +CAIRO_END_DECLS + +#include "cairo-mutex-private.h" +#include "cairo-fixed-private.h" +#include "cairo-wideint-private.h" +#include "cairo-malloc-private.h" +#include "cairo-hash-private.h" + +#if HAVE_VALGRIND +#include + +#define VG(x) x + +cairo_private void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); + +#else + +#define VG(x) +#define _cairo_debug_check_image_surface_is_defined(X) + +#endif + +cairo_private void +_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); + +cairo_private void +_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip); + +#endif diff --git a/libs/cairo/src/check-has-hidden-symbols.c b/libs/cairo/src/check-has-hidden-symbols.c new file mode 100644 index 000000000..120412776 --- /dev/null +++ b/libs/cairo/src/check-has-hidden-symbols.c @@ -0,0 +1,3 @@ +#include "cairoint.h" + +CAIRO_HAS_HIDDEN_SYMBOLS diff --git a/libs/cairo/src/check-link.c b/libs/cairo/src/check-link.c new file mode 100644 index 000000000..66ca1b241 --- /dev/null +++ b/libs/cairo/src/check-link.c @@ -0,0 +1,24 @@ +#define CAIRO_VERSION_H 1 + +#include + +/* get the "real" version info instead of dummy cairo-version.h */ +#undef CAIRO_VERSION_H +#include "../cairo-version.h" + +#include + +int +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/libs/cairo/src/filterpublic.awk b/libs/cairo/src/filterpublic.awk new file mode 100644 index 000000000..98846102b --- /dev/null +++ b/libs/cairo/src/filterpublic.awk @@ -0,0 +1,22 @@ +#!/bin/awk -f +# Reads cairo header files on stdin, and outputs a file with defines for +# renaming all public functions to Mozilla-specific names. +# Usage: +# cat *.h | awk -f ./filterpublic.awk | sort > cairo-rename.h +# +# pixman: +# grep '(' ../../libpixman/src/pixman.h | grep '^[a-z]' | sed 's, *(.*$,,' | sed 's,^.* ,,' + +BEGIN { state = "public"; } + +/^cairo_public/ { state = "function"; next; } +/[a-zA-Z_]+/ { + if (state == "function") { + print "#define " $1 " _moz_" $1; + state = "public"; + } + } + +# catch some one-off things +END { +} diff --git a/libs/cairo/src/pixman-rename.h b/libs/cairo/src/pixman-rename.h new file mode 100644 index 000000000..431cd92e9 --- /dev/null +++ b/libs/cairo/src/pixman-rename.h @@ -0,0 +1,145 @@ +#ifdef MOZ_TREE_PIXMAN +#define pixman_composite_glyphs _moz_pixman_composite_glyphs +#define pixman_composite_glyphs_no_mask _moz_pixman_composite_glyphs_no_mask +#define pixman_glyph_cache_create _moz_pixman_glyph_cache_create +#define pixman_glyph_cache_destroy _moz_pixman_glyph_cache_destroy +#define pixman_glyph_cache_freeze _moz_pixman_glyph_cache_freeze +#define pixman_glyph_cache_insert _moz_pixman_glyph_cache_insert +#define pixman_glyph_cache_lookup _moz_pixman_glyph_cache_lookup +#define pixman_glyph_cache_remove _moz_pixman_glyph_cache_remove +#define pixman_glyph_cache_thaw _moz_pixman_glyph_cache_thaw +#define pixman_glyph_get_extents _moz_pixman_glyph_get_extents +#define pixman_glyph_get_mask_format _moz_pixman_glyph_get_mask_format +#define pixman_region_set_static_pointers _moz_pixman_region_set_static_pointers +#define pixman_region_init _moz_pixman_region_init +#define pixman_region_init_rect _moz_pixman_region_init_rect +#define pixman_region_init_rects _moz_pixman_region_init_rects +#define pixman_region_init_with_extents _moz_pixman_region_init_with_extents +#define pixman_region_fini _moz_pixman_region_fini +#define pixman_region_translate _moz_pixman_region_translate +#define pixman_region_copy _moz_pixman_region_copy +#define pixman_region_intersect _moz_pixman_region_intersect +#define pixman_region_union _moz_pixman_region_union +#define pixman_region_union_rect _moz_pixman_region_union_rect +#define pixman_region_subtract _moz_pixman_region_subtract +#define pixman_region_inverse _moz_pixman_region_inverse +#define pixman_region_contains_point _moz_pixman_region_contains_point +#define pixman_region_contains_rectangle _moz_pixman_region_contains_rectangle +#define pixman_region_not_empty _moz_pixman_region_not_empty +#define pixman_region_extents _moz_pixman_region_extents +#define pixman_region_n_rects _moz_pixman_region_n_rects +#define pixman_region_rectangles _moz_pixman_region_rectangles +#define pixman_region_equal _moz_pixman_region_equal +#define pixman_region_selfcheck _moz_pixman_region_selfcheck +#define pixman_region_reset _moz_pixman_region_reset +#define pixman_region_clear _moz_pixman_region_clear +#define pixman_region_print _moz_pixman_region_print +#define pixman_region32_init _moz_pixman_region32_init +#define pixman_region32_init_rect _moz_pixman_region32_init_rect +#define pixman_region32_init_rects _moz_pixman_region32_init_rects +#define pixman_region32_init_with_extents _moz_pixman_region32_init_with_extents +#define pixman_region32_init_from_image _moz_pixman_region32_init_from_image +#define pixman_region32_fini _moz_pixman_region32_fini +#define pixman_region32_translate _moz_pixman_region32_translate +#define pixman_region32_copy _moz_pixman_region32_copy +#define pixman_region32_intersect _moz_pixman_region32_intersect +#define pixman_region32_intersect_rect _moz_pixman_region32_intersect_rect +#define pixman_region32_union _moz_pixman_region32_union +#define pixman_region32_union_rect _moz_pixman_region32_union_rect +#define pixman_region32_subtract _moz_pixman_region32_subtract +#define pixman_region32_inverse _moz_pixman_region32_inverse +#define pixman_region32_contains_point _moz_pixman_region32_contains_point +#define pixman_region32_contains_rectangle _moz_pixman_region32_contains_rectangle +#define pixman_region32_not_empty _moz_pixman_region32_not_empty +#define pixman_region32_extents _moz_pixman_region32_extents +#define pixman_region32_n_rects _moz_pixman_region32_n_rects +#define pixman_region32_rectangles _moz_pixman_region32_rectangles +#define pixman_region32_equal _moz_pixman_region32_equal +#define pixman_region32_selfcheck _moz_pixman_region32_selfcheck +#define pixman_region32_reset _moz_pixman_region32_reset +#define pixman_region32_clear _moz_pixman_region32_clear +#define pixman_region32_print _moz_pixman_region32_print +#define pixman_blt _moz_pixman_blt +#define pixman_fill _moz_pixman_fill +#define pixman_transform_point_3d _moz_pixman_transform_point_3d +#define pixman_version _moz_pixman_version +#define pixman_version_string _moz_pixman_version_string +#define pixman_format_supported_destination _moz_pixman_format_supported_destination +#define pixman_format_supported_source _moz_pixman_format_supported_source +#define pixman_image_create_solid_fill _moz_pixman_image_create_solid_fill +#define pixman_image_create_linear_gradient _moz_pixman_image_create_linear_gradient +#define pixman_image_create_radial_gradient _moz_pixman_image_create_radial_gradient +#define pixman_image_create_conical_gradient _moz_pixman_image_create_conical_gradient +#define pixman_image_create_bits _moz_pixman_image_create_bits +#define pixman_image_ref _moz_pixman_image_ref +#define pixman_image_unref _moz_pixman_image_unref +#define pixman_image_set_clip_region _moz_pixman_image_set_clip_region +#define pixman_image_set_clip_region32 _moz_pixman_image_set_clip_region32 +#define pixman_image_set_has_client_clip _moz_pixman_image_set_has_client_clip +#define pixman_image_set_transform _moz_pixman_image_set_transform +#define pixman_image_set_repeat _moz_pixman_image_set_repeat +#define pixman_image_set_filter _moz_pixman_image_set_filter +#define pixman_image_set_source_clipping _moz_pixman_image_set_source_clipping +#define pixman_image_set_alpha_map _moz_pixman_image_set_alpha_map +#define pixman_image_set_component_alpha _moz_pixman_image_set_component_alpha +#define pixman_image_set_accessors _moz_pixman_image_set_accessors +#define pixman_image_set_indexed _moz_pixman_image_set_indexed +#define pixman_image_get_data _moz_pixman_image_get_data +#define pixman_image_get_width _moz_pixman_image_get_width +#define pixman_image_get_height _moz_pixman_image_get_height +#define pixman_image_get_stride _moz_pixman_image_get_stride +#define pixman_image_get_depth _moz_pixman_image_get_depth +#define pixman_image_fill_rectangles _moz_pixman_image_fill_rectangles +#define pixman_compute_composite_region _moz_pixman_compute_composite_region +#define pixman_image_composite _moz_pixman_image_composite +#define pixman_sample_ceil_y _moz_pixman_sample_ceil_y +#define pixman_sample_floor_y _moz_pixman_sample_floor_y +#define pixman_edge_step _moz_pixman_edge_step +#define pixman_edge_init _moz_pixman_edge_init +#define pixman_line_fixed_edge_init _moz_pixman_line_fixed_edge_init +#define pixman_rasterize_edges _moz_pixman_rasterize_edges +#define pixman_add_traps _moz_pixman_add_traps +#define pixman_add_trapezoids _moz_pixman_add_trapezoids +#define pixman_add_triangles _moz_pixman_add_triangles +#define pixman_composite_trapezoids _moz_pixman_composite_trapezoids +#define pixman_composite_triangles _moz_pixman_composite_triangles +#define pixman_rasterize_trapezoid _moz_pixman_rasterize_trapezoid +#define pixman_disable_out_of_bounds_workaround _moz_pixman_disable_out_of_bounds_workaround +#define pixman_f_transform_bounds _moz_pixman_f_transform_bounds +#define pixman_f_transform_from_pixman_transform _moz_pixman_f_transform_from_pixman_transform +#define pixman_f_transform_init_identity _moz_pixman_f_transform_init_identity +#define pixman_f_transform_init_rotate _moz_pixman_f_transform_init_rotate +#define pixman_f_transform_init_scale _moz_pixman_f_transform_init_scale +#define pixman_f_transform_init_translate _moz_pixman_f_transform_init_translate +#define pixman_f_transform_invert _moz_pixman_f_transform_invert +#define pixman_f_transform_multiply _moz_pixman_f_transform_multiply +#define pixman_f_transform_point _moz_pixman_f_transform_point +#define pixman_f_transform_point_3d _moz_pixman_f_transform_point_3d +#define pixman_f_transform_rotate _moz_pixman_f_transform_rotate +#define pixman_f_transform_scale _moz_pixman_f_transform_scale +#define pixman_f_transform_translate _moz_pixman_f_transform_translate +#define pixman_image_composite32 _moz_pixman_image_composite32 +#define pixman_image_fill_boxes _moz_pixman_image_fill_boxes +#define pixman_image_get_component_alpha _moz_pixman_image_get_component_alpha +#define pixman_image_get_destroy_data _moz_pixman_image_get_destroy_data +#define pixman_image_get_format _moz_pixman_image_get_format +#define pixman_image_set_destroy_function _moz_pixman_image_set_destroy_function +#define pixman_region_init_from_image _moz_pixman_region_init_from_image +#define pixman_region_intersect_rect _moz_pixman_region_intersect_rect +#define pixman_transform_bounds _moz_pixman_transform_bounds +#define pixman_transform_from_pixman_f_transform _moz_pixman_transform_from_pixman_f_transform +#define pixman_transform_init_identity _moz_pixman_transform_init_identity +#define pixman_transform_init_rotate _moz_pixman_transform_init_rotate +#define pixman_transform_init_scale _moz_pixman_transform_init_scale +#define pixman_transform_init_translate _moz_pixman_transform_init_translate +#define pixman_transform_invert _moz_pixman_transform_invert +#define pixman_transform_is_identity _moz_pixman_transform_is_identity +#define pixman_transform_is_int_translate _moz_pixman_transform_is_int_translate +#define pixman_transform_is_inverse _moz_pixman_transform_is_inverse +#define pixman_transform_is_scale _moz_pixman_transform_is_scale +#define pixman_transform_multiply _moz_pixman_transform_multiply +#define pixman_transform_point _moz_pixman_transform_point +#define pixman_transform_rotate _moz_pixman_transform_rotate +#define pixman_transform_scale _moz_pixman_transform_scale +#define pixman_transform_translate _moz_pixman_transform_translate +#endif diff --git a/libs/cairo/src/test-fallback-surface.c b/libs/cairo/src/test-fallback-surface.c new file mode 100644 index 000000000..ff07bc81e --- /dev/null +++ b/libs/cairo/src/test-fallback-surface.c @@ -0,0 +1,206 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This isn't a "real" surface, but just something to be used by the + * test suite to test a mythical backend that uses nothing but + * fallbacks. + * + * The defining feature of this backend is that it has as many %NULL + * backend function entries as possible. The ones that aren't %NULL are + * simply those that must be implemented to have working fallbacks. + * (Except for create_similar---fallbacks would work fine without + * that---I implemented it here in order to create as many surfaces as + * possible of type test_fallback_surface_t during the test suite + * run). + * + * It's possible that this code might serve as a good starting point + * for someone working on bringing up a new backend, starting with the + * minimal all-fallbacks approach and working up gradually from + * there. + */ + +#include "cairoint.h" + +#include "test-fallback-surface.h" +#include "cairo-error-private.h" + +typedef struct _test_fallback_surface { + cairo_surface_t base; + + /* This is a cairo_image_surface to hold the actual contents. */ + cairo_surface_t *backing; +} test_fallback_surface_t; + +static const cairo_surface_backend_t test_fallback_surface_backend; + +slim_hidden_proto (_cairo_test_fallback_surface_create); + +cairo_surface_t * +_cairo_test_fallback_surface_create (cairo_content_t content, + int width, + int height) +{ + test_fallback_surface_t *surface; + cairo_surface_t *backing; + + backing = _cairo_image_surface_create_with_content (content, width, height); + if (cairo_surface_status (backing)) + return backing; + + surface = malloc (sizeof (test_fallback_surface_t)); + if (unlikely (surface == NULL)) { + cairo_surface_destroy (backing); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &test_fallback_surface_backend, + NULL, /* device */ + content); + + surface->backing = backing; + + return &surface->base; +} +slim_hidden_def (_cairo_test_fallback_surface_create); + +static cairo_surface_t * +_test_fallback_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + assert (CAIRO_CONTENT_VALID (content)); + + return _cairo_test_fallback_surface_create (content, + width, height); +} + +static cairo_status_t +_test_fallback_surface_finish (void *abstract_surface) +{ + test_fallback_surface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->backing); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_test_fallback_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + test_fallback_surface_t *surface = abstract_surface; + + return _cairo_surface_acquire_source_image (surface->backing, + image_out, image_extra); +} + +static void +_test_fallback_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + test_fallback_surface_t *surface = abstract_surface; + + _cairo_surface_release_source_image (surface->backing, + image, image_extra); +} + +static cairo_status_t +_test_fallback_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + test_fallback_surface_t *surface = abstract_surface; + + return _cairo_surface_acquire_dest_image (surface->backing, + interest_rect, + image_out, + image_rect_out, + image_extra); +} + +static void +_test_fallback_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + test_fallback_surface_t *surface = abstract_surface; + + _cairo_surface_release_dest_image (surface->backing, + interest_rect, + image, + image_rect, + image_extra); +} + +static cairo_status_t +_test_fallback_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + test_fallback_surface_t *surface = abstract_surface; + + if (src->backend == surface->base.backend) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_bool_t +_test_fallback_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + test_fallback_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->backing, rectangle); +} + +static const cairo_surface_backend_t test_fallback_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + _test_fallback_surface_create_similar, + _test_fallback_surface_finish, + _test_fallback_surface_acquire_source_image, + _test_fallback_surface_release_source_image, + _test_fallback_surface_acquire_dest_image, + _test_fallback_surface_release_dest_image, + _test_fallback_surface_clone_similar, + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _test_fallback_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* show_glyphs */ + NULL /* snapshot */ +}; diff --git a/libs/cairo/src/test-fallback-surface.h b/libs/cairo/src/test-fallback-surface.h new file mode 100644 index 000000000..c2534c184 --- /dev/null +++ b/libs/cairo/src/test-fallback-surface.h @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TEST_FALLBACK_SURFACE_H +#define TEST_FALLBACK_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_fallback_surface_create (cairo_content_t content, + int width, + int height); + +CAIRO_END_DECLS + +#endif /* TEST_FALLBACK_SURFACE_H */ diff --git a/libs/cairo/src/test-meta-surface.c b/libs/cairo/src/test-meta-surface.c new file mode 100644 index 000000000..305174eb5 --- /dev/null +++ b/libs/cairo/src/test-meta-surface.c @@ -0,0 +1,311 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This isn't a "real" surface, but just something to be used by the + * test suite to help exercise the meta-surface paths in cairo. + * + * The defining feature of this backend is that it uses a meta surface + * to record all operations, and then replays everything to an image + * surface. + * + * It's possible that this code might serve as a good starting point + * for someone working on bringing up a new meta-surface-based + * backend. + */ + +#include "cairoint.h" + +#include "test-meta-surface.h" + +#include "cairo-meta-surface-private.h" + +typedef struct _test_meta_surface { + cairo_surface_t base; + + /* This is a cairo_meta_surface to record all operations. */ + cairo_surface_t *meta; + + /* And this is a cairo_image_surface to hold the final result. */ + cairo_surface_t *image; + + cairo_bool_t image_reflects_meta; +} test_meta_surface_t; + +static const cairo_surface_backend_t test_meta_surface_backend; + +static cairo_int_status_t +_test_meta_surface_show_page (void *abstract_surface); + +cairo_surface_t * +_cairo_test_meta_surface_create (cairo_content_t content, + int width, + int height) +{ + test_meta_surface_t *surface; + cairo_status_t status; + + surface = malloc (sizeof (test_meta_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_surface_init (&surface->base, &test_meta_surface_backend, + content); + + surface->meta = _cairo_meta_surface_create (content, width, height); + status = cairo_surface_status (surface->meta); + if (status) + goto FAIL_CLEANUP_SURFACE; + + surface->image = _cairo_image_surface_create_with_content (content, + width, height); + status = cairo_surface_status (surface->image); + if (status) + goto FAIL_CLEANUP_META; + + surface->image_reflects_meta = FALSE; + + return &surface->base; + + FAIL_CLEANUP_META: + cairo_surface_destroy (surface->meta); + FAIL_CLEANUP_SURFACE: + free (surface); + FAIL: + return _cairo_surface_create_in_error (status); +} + +static cairo_status_t +_test_meta_surface_finish (void *abstract_surface) +{ + test_meta_surface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->meta); + cairo_surface_destroy (surface->image); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_test_meta_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + test_meta_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (! surface->image_reflects_meta) { + status = _test_meta_surface_show_page (abstract_surface); + if (status) + return status; + } + + return _cairo_surface_acquire_source_image (surface->image, + image_out, image_extra); +} + +static void +_test_meta_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + test_meta_surface_t *surface = abstract_surface; + + _cairo_surface_release_source_image (surface->image, + image, image_extra); +} + +static cairo_int_status_t +_test_meta_surface_show_page (void *abstract_surface) +{ + test_meta_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->image_reflects_meta) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_meta_surface_replay (surface->meta, surface->image); + if (status) + return status; + + surface->image_reflects_meta = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_test_meta_surface_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + test_meta_surface_t *surface = abstract_surface; + + return _cairo_surface_intersect_clip_path (surface->meta, + path, fill_rule, + tolerance, antialias); +} + +static cairo_int_status_t +_test_meta_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_get_extents (surface->image, rectangle); +} + +static cairo_int_status_t +_test_meta_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_paint (surface->meta, op, source, extents); +} + +static cairo_int_status_t +_test_meta_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_mask (surface->meta, op, source, mask, extents); +} + +static cairo_int_status_t +_test_meta_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_stroke (surface->meta, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, extents); +} + +static cairo_int_status_t +_test_meta_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_fill (surface->meta, op, source, + path, fill_rule, + tolerance, antialias, extents); +} + +static cairo_bool_t +_test_meta_surface_has_show_text_glyphs (void *abstract_surface) +{ + test_meta_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->meta); +} + +static cairo_int_status_t +_test_meta_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) +{ + test_meta_surface_t *surface = abstract_surface; + + surface->image_reflects_meta = FALSE; + + return _cairo_surface_show_text_glyphs (surface->meta, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags, + scaled_font, extents); +} + + +static cairo_surface_t * +_test_meta_surface_snapshot (void *abstract_other) +{ + test_meta_surface_t *other = abstract_other; + + return _cairo_surface_snapshot (other->meta); +} + +static const cairo_surface_backend_t test_meta_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, + NULL, /* create_similar */ + _test_meta_surface_finish, + _test_meta_surface_acquire_source_image, + _test_meta_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + _test_meta_surface_show_page, + NULL, /* set_clip_region */ + _test_meta_surface_intersect_clip_path, + _test_meta_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _test_meta_surface_paint, + _test_meta_surface_mask, + _test_meta_surface_stroke, + _test_meta_surface_fill, + NULL, /* show_glyphs */ + _test_meta_surface_snapshot, + NULL, /* is_similar */ + NULL, /* reset */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + _test_meta_surface_has_show_text_glyphs, + _test_meta_surface_show_text_glyphs +}; diff --git a/libs/cairo/src/test-meta-surface.h b/libs/cairo/src/test-meta-surface.h new file mode 100644 index 000000000..78b7a5752 --- /dev/null +++ b/libs/cairo/src/test-meta-surface.h @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TEST_META_SURFACE_H +#define TEST_META_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_meta_surface_create (cairo_content_t content, + int width, + int height); + +CAIRO_END_DECLS + +#endif /* TEST_META_SURFACE_H */ diff --git a/libs/cairo/src/test-paginated-surface.c b/libs/cairo/src/test-paginated-surface.c new file mode 100644 index 000000000..5d09308f4 --- /dev/null +++ b/libs/cairo/src/test-paginated-surface.c @@ -0,0 +1,260 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This isn't a "real" surface, but just something to be used by the + * test suite to help exercise the paginated-surface paths in cairo. + * + * The defining feature of this backend is that it uses a paginated + * surface to record all operations, and then replays everything to an + * image surface. + * + * It's possible that this code might serve as a good starting point + * for someone working on bringing up a new paginated-surface-based + * backend. + */ + +#include "cairoint.h" + +#include "test-paginated-surface.h" + +#include "cairo-error-private.h" +#include "cairo-paginated-private.h" + +typedef struct _test_paginated_surface { + cairo_surface_t base; + cairo_surface_t *target; + cairo_paginated_mode_t paginated_mode; +} test_paginated_surface_t; + +static const cairo_surface_backend_t test_paginated_surface_backend; +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend; + +cairo_surface_t * +_cairo_test_paginated_surface_create (cairo_surface_t *target) +{ + cairo_status_t status; + cairo_surface_t *paginated; + test_paginated_surface_t *surface; + + status = cairo_surface_status (target); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = malloc (sizeof (test_paginated_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &test_paginated_surface_backend, + NULL, /* device */ + target->content); + + surface->target = cairo_surface_reference (target); + + paginated = _cairo_paginated_surface_create (&surface->base, + target->content, + &test_paginated_surface_paginated_backend); + status = paginated->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return paginated; + } + + cairo_surface_destroy (target); + free (surface); + return _cairo_surface_create_in_error (status); +} + +static cairo_status_t +_test_paginated_surface_finish (void *abstract_surface) +{ + test_paginated_surface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->target); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_test_paginated_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + test_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static cairo_int_status_t +_test_paginated_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_paint (surface->target, op, source, clip); +} + +static cairo_int_status_t +_test_paginated_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_mask (surface->target, + op, source, mask, clip); +} + +static cairo_int_status_t +_test_paginated_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_stroke (surface->target, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_test_paginated_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_fill (surface->target, op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_bool_t +_test_paginated_surface_has_show_text_glyphs (void *abstract_surface) +{ + test_paginated_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_test_paginated_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_show_text_glyphs (surface->target, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); +} + + +static void +_test_paginated_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t mode) +{ + test_paginated_surface_t *surface = abstract_surface; + + surface->paginated_mode = mode; +} + +static const cairo_surface_backend_t test_paginated_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + + /* Since we are a paginated user, we get to regard most of the + * surface backend interface as historical cruft and ignore it. */ + + NULL, /* create_similar */ + _test_paginated_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _test_paginated_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here is the more "modern" section of the surface backend + * interface which is mostly just drawing functions */ + + _test_paginated_surface_paint, + _test_paginated_surface_mask, + _test_paginated_surface_stroke, + _test_paginated_surface_fill, + NULL, /* replaced by show_text_glyphs */ + + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + _test_paginated_surface_has_show_text_glyphs, + _test_paginated_surface_show_text_glyphs +}; + +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = { + NULL, /* start_page */ + _test_paginated_surface_set_paginated_mode +}; diff --git a/libs/cairo/src/test-paginated-surface.h b/libs/cairo/src/test-paginated-surface.h new file mode 100644 index 000000000..5f2d1d593 --- /dev/null +++ b/libs/cairo/src/test-paginated-surface.h @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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/libs/libpixman/moz.build b/libs/libpixman/moz.build index f8f60a5e1..2cdffd8cd 100644 --- a/libs/libpixman/moz.build +++ b/libs/libpixman/moz.build @@ -60,9 +60,7 @@ SOURCES += [ ALLOW_COMPILER_WARNINGS = True FINAL_LIBRARY = 'gkmedias' -LOCAL_INCLUDES += [ - '../cairo/cairo/src', -] +LOCAL_INCLUDES += ['../cairo/src'] if CONFIG['MOZ_USE_PTHREADS']: DEFINES['HAVE_PTHREADS'] = True -- cgit v1.2.3