[Cairo][SVG] marker-mid isn't shown on a joint of rectilinearly connected line-to...
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Mar 2020 12:41:52 +0000 (12:41 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Mar 2020 12:41:52 +0000 (12:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=113849

Reviewed by Adrian Perez de Castro.

Source/WebCore:

Marker-mid of svg is not displayed because path elements that added to
cairo backend are optimized. If the new line_to has same slope with
the previous path element, then the path element is joined to previous
path element.

Example:

added path elements : moveto(-5,-2), lineto(0,-2), lineto(5,-2)
cairo_path_data : moveto(-5,-2), lineto(5, -2)

This patch stores all of path informations separately in order to avoid
this problem. When generating positions of markers, we use stored path
informations instead of cairo_path_data.

When a new operation can't be directly stored in an ElementPath, we fallback to use cairo_path_data() in
Path::apply().

* platform/graphics/Path.h: Add new constructor that receives a cairo context, make ensureCairoPath() private
and add m_elements member.
* platform/graphics/cairo/FontCairo.cpp:
(WebCore::Font::platformPathForGlyph const): Create a cairo context for the path and use the new constructor
that receives a RefPtr<cairo_t>&&.
* platform/graphics/cairo/PathCairo.cpp:
(WebCore::Path::Path): Initialize m_elements to an empty vector when created without a cairo context.
(WebCore::Path::operator=): Also copy m_elements.
(WebCore::Path::clear): Initialize m_elements to an empty vector.
(WebCore::Path::translate): Apply the translate to elements in m_elements.
(WebCore::Path::appendElement): Helper to add an operation to m_elements.
(WebCore::Path::moveToSlowCase): Call appendElement() if m_elements is not nullopt.
(WebCore::Path::addLineToSlowCase): Ditto.
(WebCore::Path::addRect): Ditto.
(WebCore::Path::addQuadCurveToSlowCase): Ditto.
(WebCore::Path::addBezierCurveToSlowCase): Ditto.
(WebCore::Path::addArcSlowCase): Set m_elements to nullopt.
(WebCore::Path::addArcTo): Ditto.
(WebCore::Path::addEllipse): Ditto.
(WebCore::Path::addPath): Ditto.
(WebCore::Path::closeSubpath): Call appendElement() if m_elements is not nullopt.
(WebCore::Path::applySlowCase const): Use elements from m_elements if it's not nullopt, otherwise fallback to
use cairo_path_data.
(WebCore::Path::transform): Apply the transform to elements in m_elements.

LayoutTests:

Remove svg/custom/local-url-reference-marker.html from expectations and rebaseline other tests.

* platform/gtk/TestExpectations:
* platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-01-b-manual-expected.txt:
* platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-02-b-manual-expected.txt:
* platform/gtk/imported/w3c/web-platform-tests/svg/painting/marker-008-expected.txt:
* platform/gtk/imported/w3c/web-platform-tests/svg/text/reftests/textpath-shape-001-expected.txt:
* platform/gtk/svg/text/text-path-01-b-expected.png:
* platform/gtk/svg/text/text-path-01-b-expected.txt:
* platform/wpe/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@258492 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-01-b-manual-expected.txt
LayoutTests/platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-02-b-manual-expected.txt
LayoutTests/platform/gtk/imported/w3c/web-platform-tests/svg/painting/marker-008-expected.txt
LayoutTests/platform/gtk/imported/w3c/web-platform-tests/svg/text/reftests/textpath-shape-001-expected.txt
LayoutTests/platform/gtk/svg/text/text-path-01-b-expected.png
LayoutTests/platform/gtk/svg/text/text-path-01-b-expected.txt
LayoutTests/platform/wpe/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/Path.h
Source/WebCore/platform/graphics/cairo/FontCairo.cpp
Source/WebCore/platform/graphics/cairo/PathCairo.cpp

index bf0f2ba..1b326e1 100644 (file)
@@ -1,3 +1,21 @@
+2020-03-16  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [Cairo][SVG] marker-mid isn't shown on a joint of rectilinearly connected line-to path segments
+        https://bugs.webkit.org/show_bug.cgi?id=113849
+
+        Reviewed by Adrian Perez de Castro.
+
+        Remove svg/custom/local-url-reference-marker.html from expectations and rebaseline other tests.
+
+        * platform/gtk/TestExpectations:
+        * platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-01-b-manual-expected.txt:
+        * platform/gtk/imported/w3c/web-platform-tests/svg/import/text-path-02-b-manual-expected.txt:
+        * platform/gtk/imported/w3c/web-platform-tests/svg/painting/marker-008-expected.txt:
+        * platform/gtk/imported/w3c/web-platform-tests/svg/text/reftests/textpath-shape-001-expected.txt:
+        * platform/gtk/svg/text/text-path-01-b-expected.png:
+        * platform/gtk/svg/text/text-path-01-b-expected.txt:
+        * platform/wpe/TestExpectations:
+
 2020-03-16  Nikolas Zimmermann  <nzimmermann@igalia.com>
 
         [Gtk] Update pixel test baseline in svg/ subdirectory
index 32e7e14..710ee93 100644 (file)
@@ -3604,8 +3604,6 @@ webkit.org/b/107825 media/audio-delete-while-step-button-clicked.html [ Failure
 webkit.org/b/107825 media/nodesFromRect-shadowContent.html [ Failure Crash Timeout ]
 webkit.org/b/107825 media/video-controls-fullscreen-volume.html [ Failure Crash ]
 
-webkit.org/b/113849 svg/custom/local-url-reference-marker.html [ ImageOnlyFailure ]
-
 # Test marked in TestExpectations as failing on non-retina displays, passing on GTK port
 webkit.org/b/129113 fast/multicol/newmulticol/clipping.html [ Pass ]
 
index 6a77ca4..77b7b59 100644 (file)
@@ -18,7 +18,7 @@ layer at (0,0) size 800x600
               chunk 1 text run 2 at (211.37,63.08) startOffset 1 endOffset 2 width 13.69: "e"
               chunk 1 text run 3 at (220.27,49.99) startOffset 2 endOffset 3 width 18.00: "x"
               chunk 1 text run 4 at (229.72,39.54) startOffset 3 endOffset 4 width 10.20: "t"
-              chunk 1 text run 5 at (237.20,33.54) startOffset 4 endOffset 5 width 9.00: " "
+              chunk 1 text run 5 at (237.20,33.53) startOffset 4 endOffset 5 width 9.00: " "
               chunk 1 text run 6 at (249.38,27.89) startOffset 5 endOffset 6 width 18.00: "o"
               chunk 1 text run 7 at (267.06,29.00) startOffset 6 endOffset 7 width 18.00: "n"
               chunk 1 text run 8 at (278.59,35.91) startOffset 7 endOffset 8 width 9.00: " "
@@ -45,7 +45,7 @@ layer at (0,0) size 800x600
             RenderSVGInlineText {#text} at (45,0) size 156x112
               chunk 1 text run 1 at (64.17,96.02) startOffset 0 endOffset 1 width 10.20: "t"
               chunk 1 text run 2 at (71.65,90.02) startOffset 1 endOffset 2 width 9.00: " "
-              chunk 1 text run 3 at (83.84,84.39) startOffset 2 endOffset 3 width 18.00: "o"
+              chunk 1 text run 3 at (83.85,84.39) startOffset 2 endOffset 3 width 18.00: "o"
               chunk 1 text run 4 at (101.52,85.48) startOffset 3 endOffset 4 width 18.00: "n"
               chunk 1 text run 5 at (113.05,92.40) startOffset 4 endOffset 5 width 9.00: " "
               chunk 1 text run 6 at (122.09,101.16) startOffset 5 endOffset 6 width 16.20: "a"
index 4d5a075..d797d04 100644 (file)
@@ -15,7 +15,7 @@ layer at (0,0) size 800x600
             RenderSVGTSpan {tspan} at (0,0) size 208x124
               RenderSVGInlineText {#text} at (0,0) size 208x124
                 chunk 1 text run 1 at (199.12,88.96) startOffset 16 endOffset 17 width 25.80: "N"
-                chunk 1 text run 2 at (208.45,68.19) startOffset 17 endOffset 18 width 19.80: "e"
+                chunk 1 text run 2 at (208.44,68.18) startOffset 17 endOffset 18 width 19.80: "e"
                 chunk 1 text run 3 at (218.99,51.66) startOffset 18 endOffset 19 width 19.44: "g"
                 chunk 1 text run 4 at (232.27,37.27) startOffset 19 endOffset 20 width 19.80: "a"
                 chunk 1 text run 5 at (244.77,29.42) startOffset 20 endOffset 21 width 9.84: "t"
@@ -66,13 +66,13 @@ layer at (0,0) size 800x600
                   chunk 1 text run 4 at (286.01,42.80) startOffset 3 endOffset 4 width 7.80: "i"
                   chunk 1 text run 5 at (291.95,49.56) startOffset 4 endOffset 5 width 10.20: "t"
                   chunk 1 text run 6 at (297.50,56.64) startOffset 5 endOffset 6 width 7.80: "i"
-                  chunk 1 text run 7 at (305.18,67.01) startOffset 6 endOffset 7 width 18.00: "v"
+                  chunk 1 text run 7 at (305.18,67.00) startOffset 6 endOffset 7 width 18.00: "v"
                   chunk 1 text run 8 at (317.13,81.15) startOffset 7 endOffset 8 width 19.08: "e"
                   chunk 1 text run 9 at (328.39,90.49) startOffset 8 endOffset 9 width 10.20: " "
                   chunk 1 text run 10 at (341.33,98.03) startOffset 9 endOffset 10 width 19.80: "o"
                   chunk 1 text run 11 at (355.23,103.11) startOffset 10 endOffset 11 width 9.84: "f"
                   chunk 1 text run 12 at (363.89,104.73) startOffset 11 endOffset 12 width 7.80: "f"
-                  chunk 1 text run 13 at (376.37,104.23) startOffset 12 endOffset 13 width 17.28: "s"
+                  chunk 1 text run 13 at (376.37,104.22) startOffset 12 endOffset 13 width 17.28: "s"
                   chunk 1 text run 14 at (392.89,96.17) startOffset 13 endOffset 14 width 19.80: "e"
                   chunk 1 text run 15 at (404.03,86.40) startOffset 14 endOffset 15 width 9.84: "t"
                   chunk 1 text run 16 at (411.43,79.65) startOffset 15 endOffset 16 width 10.20: " "
@@ -97,7 +97,7 @@ layer at (0,0) size 800x600
                 chunk 1 text run 12 at (198.36,161.23) startOffset 11 endOffset 12 width 7.80: "f"
                 chunk 1 text run 13 at (210.84,160.73) startOffset 12 endOffset 13 width 17.28: "s"
                 chunk 1 text run 14 at (227.36,152.66) startOffset 13 endOffset 14 width 19.80: "e"
-                chunk 1 text run 15 at (238.49,142.90) startOffset 14 endOffset 15 width 9.84: "t"
+                chunk 1 text run 15 at (238.49,142.89) startOffset 14 endOffset 15 width 9.84: "t"
                 chunk 1 text run 16 at (245.90,136.15) startOffset 15 endOffset 16 width 10.20: " "
             RenderSVGInlineText {#text} at (0,0) size 0x0
     RenderSVGContainer {g} at (16,518) size 380x60
index 6f48398..3e850c0 100644 (file)
@@ -14,6 +14,6 @@ layer at (0,0) size 500x300
     RenderSVGPath {polygon} at (200,149) size 100x56 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [end marker=markerTest] [points="250 150 200 150"]
     RenderSVGPath {polyline} at (150,199) size 150x56 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [end marker=markerTest] [points="150 200 200 200 250 200"]
     RenderSVGPath {line} at (300,49) size 100x2 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [x1=300.00] [y1=50.00] [x2=400.00] [y2=50.00]
-    RenderSVGPath {path} at (300,99) size 100x2 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [data="M 300 100 L 350 100 L 400 100"]
+    RenderSVGPath {path} at (300,99) size 100x56 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [data="M 300 100 L 350 100 L 400 100"]
     RenderSVGPath {polygon} at (300,149) size 100x56 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [points="300 150 350 150"]
-    RenderSVGPath {polyline} at (300,199) size 100x2 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [points="300 200 350 200 400 200"]
+    RenderSVGPath {polyline} at (300,199) size 100x56 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#000000]}] [middle marker=markerTest] [points="300 200 350 200 400 200"]
index 8fe6b3d..a89ee9a 100644 (file)
@@ -32,7 +32,7 @@ layer at (0,0) size 800x600
             chunk 1 text run 18 at (190.00,226.78) startOffset 17 endOffset 18 width 4.80: " "
             chunk 1 text run 19 at (189.67,233.66) startOffset 18 endOffset 19 width 9.00: "s"
             chunk 1 text run 20 at (187.42,239.83) startOffset 19 endOffset 20 width 4.20: "i"
-            chunk 1 text run 21 at (184.79,243.47) startOffset 20 endOffset 21 width 4.80: "t"
+            chunk 1 text run 21 at (184.78,243.47) startOffset 20 endOffset 21 width 4.80: "t"
             chunk 1 text run 22 at (181.16,246.60) startOffset 21 endOffset 22 width 4.80: " "
             chunk 1 text run 23 at (174.60,249.47) startOffset 22 endOffset 23 width 9.60: "a"
             chunk 1 text run 24 at (162.64,250.00) startOffset 23 endOffset 24 width 14.40: "m"
@@ -42,7 +42,7 @@ layer at (0,0) size 800x600
             chunk 1 text run 28 at (133.84,250.00) startOffset 27 endOffset 28 width 4.80: " "
             chunk 1 text run 29 at (126.94,250.00) startOffset 28 endOffset 29 width 9.00: "c"
             chunk 1 text run 30 at (117.64,250.00) startOffset 29 endOffset 30 width 9.60: "o"
-            chunk 1 text run 31 at (108.05,249.91) startOffset 30 endOffset 31 width 9.60: "n"
+            chunk 1 text run 31 at (108.04,249.91) startOffset 30 endOffset 31 width 9.60: "n"
             chunk 1 text run 32 at (99.33,246.92) startOffset 31 endOffset 32 width 9.00: "s"
             chunk 1 text run 33 at (92.87,240.34) startOffset 32 endOffset 33 width 9.60: "e"
             chunk 1 text run 34 at (90.06,231.56) startOffset 33 endOffset 34 width 9.00: "c"
@@ -57,7 +57,7 @@ layer at (0,0) size 800x600
             chunk 1 text run 43 at (90.49,165.60) startOffset 42 endOffset 43 width 4.20: "i"
             chunk 1 text run 44 at (93.29,159.01) startOffset 43 endOffset 44 width 10.20: "p"
             chunk 1 text run 45 at (98.23,153.83) startOffset 44 endOffset 45 width 4.20: "i"
-            chunk 1 text run 46 at (104.10,150.88) startOffset 45 endOffset 46 width 9.00: "s"
+            chunk 1 text run 46 at (104.11,150.88) startOffset 45 endOffset 46 width 9.00: "s"
         RenderSVGInlineText {#text} at (0,0) size 0x0
       RenderSVGContainer {use} at (482,249) size 169x169
         RenderSVGEllipse {circle} at (482,249) size 169x169 [stroke={[type=SOLID] [color=#ADD8E6]}] [cx=340.00] [cy=200.00] [r=50.00]
index f31082c..4602a2c 100644 (file)
Binary files a/LayoutTests/platform/gtk/svg/text/text-path-01-b-expected.png and b/LayoutTests/platform/gtk/svg/text/text-path-01-b-expected.png differ
index 8f83beb..4c4ac46 100644 (file)
@@ -17,7 +17,7 @@ layer at (0,0) size 800x600
               chunk 1 text run 2 at (211.37,63.08) startOffset 1 endOffset 2 width 13.69: "e"
               chunk 1 text run 3 at (220.27,49.99) startOffset 2 endOffset 3 width 18.00: "x"
               chunk 1 text run 4 at (229.72,39.54) startOffset 3 endOffset 4 width 10.20: "t"
-              chunk 1 text run 5 at (237.20,33.54) startOffset 4 endOffset 5 width 9.00: " "
+              chunk 1 text run 5 at (237.20,33.53) startOffset 4 endOffset 5 width 9.00: " "
               chunk 1 text run 6 at (249.38,27.89) startOffset 5 endOffset 6 width 18.00: "o"
               chunk 1 text run 7 at (267.06,29.00) startOffset 6 endOffset 7 width 18.00: "n"
               chunk 1 text run 8 at (278.59,35.91) startOffset 7 endOffset 8 width 9.00: " "
@@ -43,7 +43,7 @@ layer at (0,0) size 800x600
             RenderSVGInlineText {#text} at (45,0) size 156x112
               chunk 1 text run 1 at (64.17,96.02) startOffset 0 endOffset 1 width 10.20: "t"
               chunk 1 text run 2 at (71.65,90.02) startOffset 1 endOffset 2 width 9.00: " "
-              chunk 1 text run 3 at (83.84,84.39) startOffset 2 endOffset 3 width 18.00: "o"
+              chunk 1 text run 3 at (83.85,84.39) startOffset 2 endOffset 3 width 18.00: "o"
               chunk 1 text run 4 at (101.52,85.48) startOffset 3 endOffset 4 width 18.00: "n"
               chunk 1 text run 5 at (113.05,92.40) startOffset 4 endOffset 5 width 9.00: " "
               chunk 1 text run 6 at (122.09,101.16) startOffset 5 endOffset 6 width 16.20: "a"
index e10cae8..8029b20 100644 (file)
@@ -1948,7 +1948,6 @@ webkit.org/b/135529 svg/css/parse-length.html [ Failure ]
 webkit.org/b/163831 svg/custom/acid3-test-77.html [ Failure ]
 webkit.org/b/134758 svg/custom/composited-svg-with-opacity.html [ ImageOnlyFailure Pass ]
 webkit.org/b/131347 svg/custom/hidpi-masking-clipping.svg [ Failure ImageOnlyFailure Timeout ]
-webkit.org/b/113849 svg/custom/local-url-reference-marker.html [ ImageOnlyFailure ]
 webkit.org/b/160137 svg/custom/non-scaling-stroke.svg [ Failure Pass ]
 webkit.org/b/160137 svg/custom/non-scaling-stroke-update.svg [ ImageOnlyFailure Pass ]
 webkit.org/b/112228 svg/custom/resources-css-scaled.html [ ImageOnlyFailure ]
index e314554..4a3eee2 100644 (file)
@@ -1,3 +1,52 @@
+2020-03-16  Hurnjoo Lee  <hurnjoo.lee@samsung.com>, Fujii Hironori  <Hironori.Fujii@sony.com>, Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [Cairo][SVG] marker-mid isn't shown on a joint of rectilinearly connected line-to path segments
+        https://bugs.webkit.org/show_bug.cgi?id=113849
+
+        Reviewed by Adrian Perez de Castro.
+
+        Marker-mid of svg is not displayed because path elements that added to
+        cairo backend are optimized. If the new line_to has same slope with
+        the previous path element, then the path element is joined to previous
+        path element.
+
+        Example:
+
+        added path elements : moveto(-5,-2), lineto(0,-2), lineto(5,-2)
+        cairo_path_data : moveto(-5,-2), lineto(5, -2)
+
+        This patch stores all of path informations separately in order to avoid
+        this problem. When generating positions of markers, we use stored path
+        informations instead of cairo_path_data.
+
+        When a new operation can't be directly stored in an ElementPath, we fallback to use cairo_path_data() in
+        Path::apply().
+
+        * platform/graphics/Path.h: Add new constructor that receives a cairo context, make ensureCairoPath() private
+        and add m_elements member.
+        * platform/graphics/cairo/FontCairo.cpp:
+        (WebCore::Font::platformPathForGlyph const): Create a cairo context for the path and use the new constructor
+        that receives a RefPtr<cairo_t>&&.
+        * platform/graphics/cairo/PathCairo.cpp:
+        (WebCore::Path::Path): Initialize m_elements to an empty vector when created without a cairo context.
+        (WebCore::Path::operator=): Also copy m_elements.
+        (WebCore::Path::clear): Initialize m_elements to an empty vector.
+        (WebCore::Path::translate): Apply the translate to elements in m_elements.
+        (WebCore::Path::appendElement): Helper to add an operation to m_elements.
+        (WebCore::Path::moveToSlowCase): Call appendElement() if m_elements is not nullopt.
+        (WebCore::Path::addLineToSlowCase): Ditto.
+        (WebCore::Path::addRect): Ditto.
+        (WebCore::Path::addQuadCurveToSlowCase): Ditto.
+        (WebCore::Path::addBezierCurveToSlowCase): Ditto.
+        (WebCore::Path::addArcSlowCase): Set m_elements to nullopt.
+        (WebCore::Path::addArcTo): Ditto.
+        (WebCore::Path::addEllipse): Ditto.
+        (WebCore::Path::addPath): Ditto.
+        (WebCore::Path::closeSubpath): Call appendElement() if m_elements is not nullopt.
+        (WebCore::Path::applySlowCase const): Use elements from m_elements if it's not nullopt, otherwise fallback to
+        use cairo_path_data.
+        (WebCore::Path::transform): Apply the transform to elements in m_elements.
+
 2020-03-16  Rob Buis  <rbuis@igalia.com>
 
         Remove addHTTPOriginIfNeeded calls
index 3a85319..84320e7 100644 (file)
@@ -122,6 +122,9 @@ public:
 #if USE(CG)
     Path(RetainPtr<CGMutablePathRef>&&);
 #endif
+#if USE(CAIRO)
+    explicit Path(RefPtr<cairo_t>&&);
+#endif
     WEBCORE_EXPORT ~Path();
 
     WEBCORE_EXPORT Path(const Path&);
@@ -192,10 +195,8 @@ public:
     PlatformPathPtr platformPath() const { return m_path; }
 #endif
 
+#if !USE(CAIRO)
     // ensurePlatformPath() will allocate a PlatformPath if it has not yet been and will never return null.
-#if USE(CAIRO)
-    cairo_t* ensureCairoPath();
-#else
     WEBCORE_EXPORT PlatformPathPtr ensurePlatformPath();
 #endif
 
@@ -257,6 +258,11 @@ private:
 #endif
 
 #if USE(CAIRO)
+    cairo_t* ensureCairoPath();
+    void appendElement(PathElement::Type, Vector<FloatPoint, 3>&&);
+#endif
+
+#if USE(CAIRO)
     RefPtr<cairo_t> m_path;
 #else
     mutable PlatformPathStorageType m_path;
@@ -274,6 +280,9 @@ private:
 #if USE(CG)
     mutable bool m_copyPathBeforeMutation { false };
 #endif
+#if USE(CAIRO)
+    Optional<Vector<PathElement>> m_elements;
+#endif
 };
 
 WTF::TextStream& operator<<(WTF::TextStream&, const Path&);
index 4b8eaeb..8e16074 100644 (file)
@@ -43,6 +43,7 @@
 #include "ImageBuffer.h"
 #include "Pattern.h"
 #include "PlatformContextCairo.h"
+#include "RefPtrCairo.h"
 #include "ShadowBlur.h"
 
 namespace WebCore {
@@ -81,19 +82,18 @@ void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const G
 
 Path Font::platformPathForGlyph(Glyph glyph) const
 {
-    Path path;
-    cairo_t* cr = path.ensureCairoPath();
+    RefPtr<cairo_t> cr = adoptRef(cairo_create(adoptRef(cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1)).get()));
 
     cairo_glyph_t cairoGlyph = { glyph, 0, 0 };
-    cairo_set_scaled_font(cr, platformData().scaledFont());
-    cairo_glyph_path(cr, &cairoGlyph, 1);
+    cairo_set_scaled_font(cr.get(), platformData().scaledFont());
+    cairo_glyph_path(cr.get(), &cairoGlyph, 1);
 
     float syntheticBoldOffset = this->syntheticBoldOffset();
     if (syntheticBoldOffset) {
-        cairo_translate(cr, syntheticBoldOffset, 0);
-        cairo_glyph_path(cr, &cairoGlyph, 1);
+        cairo_translate(cr.get(), syntheticBoldOffset, 0);
+        cairo_glyph_path(cr.get(), &cairoGlyph, 1);
     }
-    return path;
+    return Path(WTFMove(cr));
 }
 
 } // namespace WebCore
index 48275fb..973d393 100644 (file)
 
 namespace WebCore {
 
-Path::Path() = default;
+Path::Path()
+    : m_elements(Vector<PathElement>())
+{
+}
+
+Path::Path(RefPtr<cairo_t>&& path)
+    : m_path(WTFMove(path))
+{
+}
 
 Path::~Path() = default;
 
@@ -54,6 +62,7 @@ Path::Path(const Path& other)
 
     CairoUniquePtr<cairo_path_t> pathCopy(cairo_copy_path(other.m_path.get()));
     cairo_append_path(ensureCairoPath(), pathCopy.get());
+    m_elements = other.m_elements;
 }
 
 cairo_t* Path::ensureCairoPath()
@@ -70,12 +79,14 @@ Path& Path::operator=(const Path& other)
 
     if (other.isNull()) {
         m_path = nullptr;
+        m_elements = Vector<PathElement>();
         return *this;
     }
 
     clear();
     CairoUniquePtr<cairo_path_t> pathCopy(cairo_copy_path(other.m_path.get()));
     cairo_append_path(ensureCairoPath(), pathCopy.get());
+    m_elements = other.m_elements;
 
     return *this;
 }
@@ -87,6 +98,7 @@ void Path::clear()
 
     cairo_identity_matrix(m_path.get());
     cairo_new_path(m_path.get());
+    m_elements = Vector<PathElement>();
 }
 
 bool Path::isEmptySlowCase() const
@@ -106,21 +118,87 @@ FloatPoint Path::currentPointSlowCase() const
 void Path::translate(const FloatSize& p)
 {
     cairo_translate(ensureCairoPath(), -p.width(), -p.height());
+
+    if (!m_elements)
+        return;
+
+    for (auto& element : m_elements.value()) {
+        switch (element.type) {
+        case PathElement::Type::MoveToPoint:
+        case PathElement::Type::AddLineToPoint:
+            element.points[0].move(p);
+            break;
+        case PathElement::Type::AddQuadCurveToPoint:
+            element.points[0].move(p);
+            element.points[1].move(p);
+            break;
+        case PathElement::Type::AddCurveToPoint:
+            element.points[0].move(p);
+            element.points[1].move(p);
+            element.points[2].move(p);
+            break;
+        case PathElement::Type::CloseSubpath:
+            break;
+        }
+    }
+}
+
+void Path::appendElement(PathElement::Type type, Vector<FloatPoint, 3>&& points)
+{
+    PathElement element;
+    element.type = type;
+    switch (type) {
+    case PathElement::Type::MoveToPoint:
+    case PathElement::Type::AddLineToPoint:
+        element.points[0] = points[0];
+        break;
+    case PathElement::Type::AddQuadCurveToPoint:
+        element.points[0] = points[0];
+        element.points[1] = points[1];
+        break;
+    case PathElement::Type::AddCurveToPoint:
+        element.points[0] = points[0];
+        element.points[1] = points[1];
+        element.points[2] = points[2];
+        break;
+    case PathElement::Type::CloseSubpath:
+        break;
+    }
+    m_elements->append(WTFMove(element));
 }
 
 void Path::moveToSlowCase(const FloatPoint& p)
 {
     cairo_move_to(ensureCairoPath(), p.x(), p.y());
+    if (m_elements)
+        appendElement(PathElement::Type::MoveToPoint, { p });
 }
 
 void Path::addLineToSlowCase(const FloatPoint& p)
 {
     cairo_line_to(ensureCairoPath(), p.x(), p.y());
+    if (m_elements)
+        appendElement(PathElement::Type::AddLineToPoint, { p });
 }
 
 void Path::addRect(const FloatRect& rect)
 {
     cairo_rectangle(ensureCairoPath(), rect.x(), rect.y(), rect.width(), rect.height());
+
+    if (!m_elements)
+        return;
+
+    FloatPoint point(rect.location());
+    appendElement(PathElement::Type::MoveToPoint, { point });
+    point.move(rect.width(), 0);
+    appendElement(PathElement::Type::AddLineToPoint, { point });
+    point.move(0, rect.height());
+    appendElement(PathElement::Type::AddLineToPoint, { point });
+    point.move(-rect.width(), 0);
+    appendElement(PathElement::Type::AddLineToPoint, { point });
+    appendElement(PathElement::Type::CloseSubpath, { });
+    if (cairo_has_current_point(m_path.get()))
+        appendElement(PathElement::Type::MoveToPoint, { currentPointSlowCase() });
 }
 
 void Path::addQuadCurveToSlowCase(const FloatPoint& controlPoint, const FloatPoint& point)
@@ -136,16 +214,21 @@ void Path::addQuadCurveToSlowCase(const FloatPoint& controlPoint, const FloatPoi
         x  + 2.0 / 3.0 * (x1 - x),  y  + 2.0 / 3.0 * (y1 - y),
         x2 + 2.0 / 3.0 * (x1 - x2), y2 + 2.0 / 3.0 * (y1 - y2),
         x2, y2);
+    if (m_elements)
+        appendElement(PathElement::Type::AddQuadCurveToPoint, { controlPoint, point  });
 }
 
 void Path::addBezierCurveToSlowCase(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& controlPoint3)
 {
     cairo_curve_to(ensureCairoPath(), controlPoint1.x(), controlPoint1.y(),
         controlPoint2.x(), controlPoint2.y(), controlPoint3.x(), controlPoint3.y());
+    if (m_elements)
+        appendElement(PathElement::Type::AddCurveToPoint, { controlPoint1, controlPoint2, controlPoint3 });
 }
 
 void Path::addArcSlowCase(const FloatPoint& p, float r, float startAngle, float endAngle, bool anticlockwise)
 {
+    m_elements = WTF::nullopt;
     cairo_t* cr = ensureCairoPath();
     float sweep = endAngle - startAngle;
     const float twoPI = 2 * piFloat;
@@ -185,6 +268,8 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
     if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || !radius
         || !areaOfTriangleFormedByPoints(p0, p1, p2)) {
         cairo_line_to(m_path.get(), p1.x(), p1.y());
+        if (m_elements)
+            appendElement(PathElement::Type::AddLineToPoint, { p1 });
         return;
     }
 
@@ -196,6 +281,8 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
     // all points on a line logic
     if (cos_phi == -1) {
         cairo_line_to(m_path.get(), p1.x(), p1.y());
+        if (m_elements)
+            appendElement(PathElement::Type::AddLineToPoint, { p1 });
         return;
     }
     if (cos_phi == 1) {
@@ -204,9 +291,12 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
         double factor_max = max_length / p1p0_length;
         FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y()));
         cairo_line_to(m_path.get(), ep.x(), ep.y());
+        if (m_elements)
+            appendElement(PathElement::Type::AddLineToPoint, { ep });
         return;
     }
 
+    m_elements = WTF::nullopt;
     float tangent = radius / tan(acos(cos_phi) / 2);
     float factor_p1p0 = tangent / p1p0_length;
     FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
@@ -250,6 +340,7 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
 
 void Path::addEllipse(FloatPoint point, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise)
 {
+    m_elements = WTF::nullopt;
     cairo_t* cr = ensureCairoPath();
     cairo_save(cr);
     cairo_translate(cr, point.x(), point.y());
@@ -266,6 +357,7 @@ void Path::addEllipse(FloatPoint point, float radiusX, float radiusY, float rota
 
 void Path::addEllipse(const FloatRect& rect)
 {
+    m_elements = WTF::nullopt;
     cairo_t* cr = ensureCairoPath();
     cairo_save(cr);
     float yRadius = .5 * rect.height();
@@ -285,6 +377,8 @@ void Path::addPath(const Path& path, const AffineTransform& transform)
     if (cairo_matrix_invert(&matrix) != CAIRO_STATUS_SUCCESS)
         return;
 
+    m_elements = WTF::nullopt;
+
     cairo_t* cr = path.cairoPath();
     cairo_save(cr);
     cairo_transform(cr, &matrix);
@@ -296,6 +390,11 @@ void Path::addPath(const Path& path, const AffineTransform& transform)
 void Path::closeSubpath()
 {
     cairo_close_path(ensureCairoPath());
+    if (m_elements) {
+        appendElement(PathElement::Type::CloseSubpath, { });
+        if (cairo_has_current_point(m_path.get()))
+            appendElement(PathElement::Type::MoveToPoint, { currentPointSlowCase() });
+    }
 }
 
 FloatRect Path::boundingRectSlowCase() const
@@ -348,6 +447,12 @@ bool Path::strokeContains(StrokeStyleApplier& applier, const FloatPoint& point)
 
 void Path::applySlowCase(const PathApplierFunction& function) const
 {
+    if (m_elements) {
+        for (const auto& element : m_elements.value())
+            function(element);
+        return;
+    }
+
     CairoUniquePtr<cairo_path_t> pathCopy(cairo_copy_path(m_path.get()));
     cairo_path_data_t* data;
     PathElement pathElement;
@@ -390,6 +495,29 @@ void Path::transform(const AffineTransform& transform)
     cairo_matrix_t matrix = toCairoMatrix(transform);
     cairo_matrix_invert(&matrix);
     cairo_transform(ensureCairoPath(), &matrix);
+
+    if (!m_elements)
+        return;
+
+    for (auto& element : m_elements.value()) {
+        switch (element.type) {
+        case PathElement::Type::MoveToPoint:
+        case PathElement::Type::AddLineToPoint:
+            element.points[0] = transform.mapPoint(element.points[0]);
+            break;
+        case PathElement::Type::AddQuadCurveToPoint:
+            element.points[0] = transform.mapPoint(element.points[0]);
+            element.points[1] = transform.mapPoint(element.points[1]);
+            break;
+        case PathElement::Type::AddCurveToPoint:
+            element.points[0] = transform.mapPoint(element.points[0]);
+            element.points[1] = transform.mapPoint(element.points[1]);
+            element.points[2] = transform.mapPoint(element.points[2]);
+            break;
+        case PathElement::Type::CloseSubpath:
+            break;
+        }
+    }
 }
 
 bool Path::isNull() const