[Windows][DirectX] Update canvas code to pass more tests
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 03:30:36 +0000 (03:30 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 03:30:36 +0000 (03:30 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191337
<rdar://problem/45878801>

Reviewed by Dean Jackson.

Update the Direct2D code paths to comply with our canvas tests, improving the
the test results scores to 579/770.

PathDirect2D was updated with an implementation of 'addArcTo' based on work by
Dirk Schulze <vbs85@gmx.de> (see https://hg.mozilla.org/mozilla-central/rev/b116b49459f8).

Tests: canvas/philip/tests

* platform/graphics/ImageBuffer.cpp:
(WebCore::ImageBuffer::createCompatibleBuffer): Direct2D needs access to the graphics
context to create the buffer.
* platform/graphics/ImageBuffer.h:
* platform/graphics/Path.h:
* platform/graphics/win/GraphicsContextDirect2D.cpp:
(WebCore::GraphicsContext::drawPattern): Flush needed.
(WebCore::GraphicsContext::drawRect): Ditto.
(WebCore::GraphicsContextPlatformPrivate::setMiterLimit): Correct for Direct2D definition of miter limit.
(WebCore::GraphicsContextPlatformPrivate::strokeStyleProperties const): Added helper function.
(WebCore::GraphicsContextPlatformPrivate::recomputeStrokeStyle): Update for new helper.
(WebCore::GraphicsContext::drawLine): Ditto.
(WebCore::drawWithShadowHelper): Ditto.
(WebCore::GraphicsContext::fillRect): Add flush.
(WebCore::GraphicsContext::platformFillRoundedRect): Update for helper.
(WebCore::GraphicsContext::clipPath): Add flush.
(WebCore::GraphicsContext::strokeRect): Ditto.
(WebCore::GraphicsContext::drawLineForText): Update for upstream changes.
(WebCore::GraphicsContext::drawLinesForText): Ditto.
* platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
* platform/graphics/win/ImageBufferDirect2D.cpp:
(WebCore::createCroppedImageIfNecessary): Add missing implementations.
(WebCore::createBitmapImageAfterScalingIfNeeded): Ditto.
(WebCore::ImageBuffer::copyImage const): Ditto.
(WebCore::ImageBuffer::sinkIntoImage): Ditto.
(WebCore::ImageBuffer::fastCopyImageMode): Ditto.
(WebCore::ImageBuffer::sinkIntoNativeImage): Ditto.
(WebCore::ImageBuffer::copyNativeImage const): Ditto.
* platform/graphics/win/PathDirect2D.cpp:
(WebCore::Path::operator=):
(WebCore::Path::drawDidComplete): This should never have been const.
It manipulates the path!
(WebCore::Path::transform): Properly transform existing geometries.
(WebCore::Path::openFigureAtCurrentPointIfNecessary): Added.
(WebCore::Path::moveTo):
(WebCore::Path::addLineTo): Make sure figure starts at a valid point.
(WebCore::Path::addQuadCurveTo): Ditto.
(WebCore::Path::addBezierCurveTo): Ditto.
(WebCore::Path::addArcTo): Add implementation.
(WebCore::Path::closeSubpath):
(WebCore::drawArcSection):
(WebCore::Path::addArc): Update to build large arcs out of small arc segments. If the
arc is effectively a complete circle, use the ellipse drawing routines.
(WebCore::Path::addRect): Make sure we start at a valid starting point.
(WebCore::Path::addEllipse): Correct for definition of D2D ellipse.
(WebCore::Path::drawDidComplete const): Deleted.
* platform/graphics/win/SimpleFontDataDirect2D.cpp:
(WebCore::Font::platformWidthForGlyph const):
* rendering/svg/RenderSVGResourceClipper.cpp:
(WebCore::RenderSVGResourceClipper::applyClippingToContext):
* rendering/svg/RenderSVGResourceFilter.cpp:
(WebCore::RenderSVGResourceFilter::applyResource):
* rendering/svg/RenderSVGResourceMasker.cpp:
(WebCore::RenderSVGResourceMasker::applyResource):
* rendering/svg/SVGRenderingContext.cpp:
(WebCore::SVGRenderingContext::createImageBuffer):
* rendering/svg/SVGRenderingContext.h:

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

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ImageBuffer.cpp
Source/WebCore/platform/graphics/ImageBuffer.h
Source/WebCore/platform/graphics/Path.h
Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp
Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h
Source/WebCore/platform/graphics/win/ImageBufferDirect2D.cpp
Source/WebCore/platform/graphics/win/PathDirect2D.cpp
Source/WebCore/platform/graphics/win/SimpleFontDataDirect2D.cpp
Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp
Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp
Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp
Source/WebCore/rendering/svg/SVGRenderingContext.cpp
Source/WebCore/rendering/svg/SVGRenderingContext.h

index 9182b39..4ac537b 100644 (file)
@@ -1,3 +1,77 @@
+2018-11-07  Brent Fulgham  <bfulgham@apple.com>
+
+        [Windows][DirectX] Update canvas code to pass more tests
+        https://bugs.webkit.org/show_bug.cgi?id=191337
+        <rdar://problem/45878801>
+
+        Reviewed by Dean Jackson.
+
+        Update the Direct2D code paths to comply with our canvas tests, improving the
+        the test results scores to 579/770.
+
+        PathDirect2D was updated with an implementation of 'addArcTo' based on work by
+        Dirk Schulze <vbs85@gmx.de> (see https://hg.mozilla.org/mozilla-central/rev/b116b49459f8).
+
+        Tests: canvas/philip/tests
+
+        * platform/graphics/ImageBuffer.cpp:
+        (WebCore::ImageBuffer::createCompatibleBuffer): Direct2D needs access to the graphics
+        context to create the buffer.
+        * platform/graphics/ImageBuffer.h:
+        * platform/graphics/Path.h:
+        * platform/graphics/win/GraphicsContextDirect2D.cpp:
+        (WebCore::GraphicsContext::drawPattern): Flush needed.
+        (WebCore::GraphicsContext::drawRect): Ditto.
+        (WebCore::GraphicsContextPlatformPrivate::setMiterLimit): Correct for Direct2D definition of miter limit.
+        (WebCore::GraphicsContextPlatformPrivate::strokeStyleProperties const): Added helper function.
+        (WebCore::GraphicsContextPlatformPrivate::recomputeStrokeStyle): Update for new helper.
+        (WebCore::GraphicsContext::drawLine): Ditto.
+        (WebCore::drawWithShadowHelper): Ditto.
+        (WebCore::GraphicsContext::fillRect): Add flush.
+        (WebCore::GraphicsContext::platformFillRoundedRect): Update for helper.
+        (WebCore::GraphicsContext::clipPath): Add flush.
+        (WebCore::GraphicsContext::strokeRect): Ditto.
+        (WebCore::GraphicsContext::drawLineForText): Update for upstream changes.
+        (WebCore::GraphicsContext::drawLinesForText): Ditto.
+        * platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
+        * platform/graphics/win/ImageBufferDirect2D.cpp:
+        (WebCore::createCroppedImageIfNecessary): Add missing implementations.
+        (WebCore::createBitmapImageAfterScalingIfNeeded): Ditto.
+        (WebCore::ImageBuffer::copyImage const): Ditto.
+        (WebCore::ImageBuffer::sinkIntoImage): Ditto.
+        (WebCore::ImageBuffer::fastCopyImageMode): Ditto.
+        (WebCore::ImageBuffer::sinkIntoNativeImage): Ditto.
+        (WebCore::ImageBuffer::copyNativeImage const): Ditto.
+        * platform/graphics/win/PathDirect2D.cpp:
+        (WebCore::Path::operator=):
+        (WebCore::Path::drawDidComplete): This should never have been const.
+        It manipulates the path!
+        (WebCore::Path::transform): Properly transform existing geometries.
+        (WebCore::Path::openFigureAtCurrentPointIfNecessary): Added.
+        (WebCore::Path::moveTo):
+        (WebCore::Path::addLineTo): Make sure figure starts at a valid point.
+        (WebCore::Path::addQuadCurveTo): Ditto.
+        (WebCore::Path::addBezierCurveTo): Ditto.
+        (WebCore::Path::addArcTo): Add implementation.
+        (WebCore::Path::closeSubpath):
+        (WebCore::drawArcSection):
+        (WebCore::Path::addArc): Update to build large arcs out of small arc segments. If the
+        arc is effectively a complete circle, use the ellipse drawing routines.
+        (WebCore::Path::addRect): Make sure we start at a valid starting point.
+        (WebCore::Path::addEllipse): Correct for definition of D2D ellipse.
+        (WebCore::Path::drawDidComplete const): Deleted.
+        * platform/graphics/win/SimpleFontDataDirect2D.cpp:
+        (WebCore::Font::platformWidthForGlyph const):
+        * rendering/svg/RenderSVGResourceClipper.cpp:
+        (WebCore::RenderSVGResourceClipper::applyClippingToContext):
+        * rendering/svg/RenderSVGResourceFilter.cpp:
+        (WebCore::RenderSVGResourceFilter::applyResource):
+        * rendering/svg/RenderSVGResourceMasker.cpp:
+        (WebCore::RenderSVGResourceMasker::applyResource):
+        * rendering/svg/SVGRenderingContext.cpp:
+        (WebCore::SVGRenderingContext::createImageBuffer):
+        * rendering/svg/SVGRenderingContext.h:
+
 2018-11-07  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Add an editing command for creating and inserting child lists
index 5c07eef..8c302f5 100644 (file)
@@ -212,7 +212,11 @@ std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize
 
 std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext& context)
 {
+#if USE(DIRECT2D)
+    return create(size, context.renderingMode(), &context, resolutionScale, colorSpace);
+#else
     return create(size, context.renderingMode(), resolutionScale, colorSpace);
+#endif
 }
 
 IntSize ImageBuffer::compatibleBufferSize(const FloatSize& size, const GraphicsContext& context)
index c7dc20a..380b992 100644 (file)
@@ -141,6 +141,8 @@ private:
     static RetainPtr<CGImageRef> sinkIntoNativeImage(std::unique_ptr<ImageBuffer>);
     void flushContext() const;
 #elif USE(DIRECT2D)
+    COMPtr<ID2D1Bitmap> copyNativeImage(BackingStoreCopy = CopyBackingStore) const;
+    static COMPtr<ID2D1Bitmap> sinkIntoNativeImage(std::unique_ptr<ImageBuffer>);
     void flushContext() const;
 #endif
     
index fba8501..cc12b86 100644 (file)
@@ -197,9 +197,10 @@ namespace WebCore {
         ID2D1GeometrySink* activePath() const { return m_activePath.get(); }
         void appendGeometry(ID2D1Geometry*);
         void createGeometryWithFillMode(WindRule, COMPtr<ID2D1GeometryGroup>&) const;
-        void drawDidComplete() const;
+        void drawDidComplete();
 
         HRESULT initializePathState();
+        void openFigureAtCurrentPointIfNecessary();
 #endif
 
 #ifndef NDEBUG
@@ -211,6 +212,7 @@ namespace WebCore {
         COMPtr<ID2D1GeometryGroup> m_path;
         COMPtr<ID2D1PathGeometry> m_activePathGeometry;
         COMPtr<ID2D1GeometrySink> m_activePath;
+        bool m_doesHaveOpenFigure { false };
 #else
         PlatformPathPtr m_path { nullptr };
 #endif
index 97a5820..113819d 100644 (file)
@@ -596,6 +596,9 @@ void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const
 
     COMPtr<ID2D1BitmapBrush> patternBrush;
     HRESULT hr = context->CreateBitmapBrush(tileImage.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
+    ASSERT(SUCCEEDED(hr));
+    if (!SUCCEEDED(hr))
+        return;
 
     drawWithoutShadow(destRect, [this, destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
         const D2D1_RECT_F d2dRect = destRect;
@@ -692,8 +695,10 @@ void GraphicsContextPlatformPrivate::setStrokeStyle(StrokeStyle strokeStyle)
     m_strokeSyleIsDirty = true;
 }
 
-void GraphicsContextPlatformPrivate::setMiterLimit(float miterLimit)
+void GraphicsContextPlatformPrivate::setMiterLimit(float canvasMiterLimit)
 {
+    // Direct2D miter limit is in terms of HALF the line thickness.
+    float miterLimit = 0.5f * canvasMiterLimit;
     if (WTF::areEssentiallyEqual(miterLimit, m_miterLimit))
         return;
 
@@ -746,6 +751,11 @@ void GraphicsContextPlatformPrivate::setDashes(const DashArray& dashes)
     m_strokeSyleIsDirty = true;
 }
 
+D2D1_STROKE_STYLE_PROPERTIES GraphicsContextPlatformPrivate::strokeStyleProperties() const
+{
+    return D2D1::StrokeStyleProperties(m_lineCap, m_lineCap, m_lineCap, m_lineJoin, m_miterLimit, D2D1_DASH_STYLE_SOLID, 0.0f);
+}
+
 void GraphicsContextPlatformPrivate::recomputeStrokeStyle()
 {
     if (!m_strokeSyleIsDirty)
@@ -753,19 +763,23 @@ void GraphicsContextPlatformPrivate::recomputeStrokeStyle()
 
     m_d2dStrokeStyle = nullptr;
 
-    if ((m_strokeStyle != SolidStroke) && (m_strokeStyle != NoStroke)) {
-        float patternOffset = m_patternOffset / m_strokeThickness;
+    DashArray dashes;
+    float patternOffset = 0;
+    auto dashStyle = D2D1_DASH_STYLE_SOLID;
 
-        DashArray dashes = m_dashes;
+    if ((m_strokeStyle != SolidStroke) && (m_strokeStyle != NoStroke)) {
+        dashStyle = D2D1_DASH_STYLE_CUSTOM;
+        patternOffset = m_patternOffset / m_strokeThickness;
+        dashes = m_dashes;
 
         // In Direct2D, dashes and dots are defined in terms of the ratio of the dash length to the line thickness.
         for (auto& dash : dashes)
             dash /= m_strokeThickness;
-
-        auto strokeStyleProperties = D2D1::StrokeStyleProperties(m_lineCap, m_lineCap, m_lineCap, m_lineJoin, m_strokeThickness, D2D1_DASH_STYLE_CUSTOM, patternOffset);
-        GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes.data(), dashes.size(), &m_d2dStrokeStyle);
     }
 
+    auto strokeStyleProperties = D2D1::StrokeStyleProperties(m_lineCap, m_lineCap, m_lineCap, m_lineJoin, m_miterLimit, dashStyle, patternOffset);
+    GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes.data(), dashes.size(), &m_d2dStrokeStyle);
+
     m_strokeSyleIsDirty = false;
 }
 
@@ -819,7 +833,7 @@ void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point
 
         float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth);
         const float dashes[2] = { patternWidth, patternWidth };
-        auto strokeStyleProperties = D2D1::StrokeStyleProperties();
+        auto strokeStyleProperties = m_data->strokeStyleProperties();
         GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes, ARRAYSIZE(dashes), &d2dStrokeStyle);
 
         m_data->setPatternWidth(patternWidth);
@@ -926,8 +940,6 @@ void GraphicsContext::drawPath(const Path& path)
         auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
         renderTarget->DrawGeometry(path.platformPath(), brush, strokeThickness(), m_data->strokeStyle());
     });
-
-    flush();
 }
 
 void GraphicsContext::drawWithoutShadow(const FloatRect& /*boundingRect*/, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
@@ -1047,8 +1059,6 @@ void GraphicsContext::fillPath(const Path& path)
         auto brush = m_state.fillPattern ? patternFillBrush() : solidFillBrush();
         renderTarget->FillGeometry(pathToFill.get(), brush);
     });
-
-    flush();
 }
 
 void GraphicsContext::strokePath(const Path& path)
@@ -1092,8 +1102,6 @@ void GraphicsContext::strokePath(const Path& path)
         auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
         renderTarget->DrawGeometry(path.platformPath(), brush, strokeThickness(), m_data->strokeStyle());
     });
-
-    flush();
 }
 
 void GraphicsContext::fillRect(const FloatRect& rect)
@@ -1329,6 +1337,8 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
         return;
     }
 
+    ASSERT(!path.activePath());
+
     COMPtr<ID2D1GeometryGroup> pathToClip;
     path.createGeometryWithFillMode(clipRule, pathToClip);
 
@@ -1687,24 +1697,23 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMo
     return rect;
 }
 
-void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing, bool doubleLines, StrokeStyle strokeStyle)
+void GraphicsContext::drawLineForText(const FloatRect& rect, bool printing, bool doubleLines, StrokeStyle strokeStyle)
 {
     DashArray widths;
     widths.append(0);
-    widths.append(width);
-    drawLinesForText(point, widths, printing, doubleLines, strokeStyle);
+    widths.append(rect.width());
+    drawLinesForText(rect.location(), rect.height(), widths, printing, doubleLines, strokeStyle);
 }
 
-void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
+void GraphicsContext::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
 {
     if (paintingDisabled())
-        return;
 
     if (!widths.size())
         return;
 
     if (m_impl) {
-        m_impl->drawLinesForText(point, widths, printing, doubleLines, strokeThickness());
+        m_impl->drawLinesForText(point, thickness, widths, printing, doubleLines);
         return;
     }
 
index 8d789a8..8c6d93d 100644 (file)
@@ -92,6 +92,8 @@ public:
 
     float currentGlobalAlpha() const;
 
+    D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties() const;
+
 private:
     void recomputeStrokeStyle();
 
@@ -124,7 +126,7 @@ private:
     StrokeStyle m_strokeStyle { SolidStroke };
     DashArray m_dashes;
 
-    float m_miterLimit { 1.0f };
+    float m_miterLimit { 10.0f };
     float m_dashOffset { 0 };
     float m_patternWidth { 1.0f };
     float m_patternOffset { 0 };
index 1064b6c..ea6a17f 100644 (file)
@@ -137,21 +137,56 @@ void ImageBuffer::flushContext() const
     context().flush();
 }
 
-RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, PreserveResolution) const
+static COMPtr<ID2D1Bitmap> createCroppedImageIfNecessary(ID2D1Bitmap* image, const IntSize& bounds)
 {
-    notImplemented();
-    return nullptr;
+    FloatSize imageSize = image ? image->GetSize() : FloatSize();
+
+    if (image && (static_cast<size_t>(imageSize.width()) != static_cast<size_t>(bounds.width()) || static_cast<size_t>(imageSize.height()) != static_cast<size_t>(bounds.height()))) {
+        D2D_POINT_2U origin = { };
+        D2D1_RECT_U croppedDimenstions = IntRect(IntPoint(), bounds);
+        COMPtr<ID2D1Bitmap> croppedImage;
+        HRESULT hr = croppedImage->CopyFromBitmap(&origin, image, &croppedDimenstions);
+        if (SUCCEEDED(hr))
+            return croppedImage;
+    }
+
+    return image;
+}
+
+static RefPtr<Image> createBitmapImageAfterScalingIfNeeded(COMPtr<ID2D1Bitmap>&& image, IntSize internalSize, IntSize logicalSize, IntSize backingStoreSize, float resolutionScale, PreserveResolution preserveResolution)
+{
+    if (resolutionScale == 1 || preserveResolution == PreserveResolution::Yes)
+        image = createCroppedImageIfNecessary(image.get(), internalSize);
+    else {
+        // FIXME: Need to implement scaled version
+        notImplemented();
+    }
+
+    if (!image)
+        return nullptr;
+
+    return BitmapImage::create(WTFMove(image));
+}
+
+RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, PreserveResolution preserveResolution) const
+{
+    COMPtr<ID2D1Bitmap> image;
+    if (m_resolutionScale == 1 || preserveResolution == PreserveResolution::Yes)
+        image = copyNativeImage(copyBehavior);
+    else
+        image = copyNativeImage(DontCopyBackingStore);
+
+    return createBitmapImageAfterScalingIfNeeded(WTFMove(image), internalSize(), logicalSize(), m_data.backingStoreSize, m_resolutionScale, preserveResolution);
 }
 
-RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, PreserveResolution)
+RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, PreserveResolution preserveResolution)
 {
     IntSize internalSize = imageBuffer->internalSize();
     IntSize logicalSize = imageBuffer->logicalSize();
     IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
     float resolutionScale = imageBuffer->m_resolutionScale;
 
-    notImplemented();
-    return nullptr;
+    return createBitmapImageAfterScalingIfNeeded(sinkIntoNativeImage(WTFMove(imageBuffer)), internalSize, logicalSize, backingStoreSize, resolutionScale, preserveResolution);
 }
 
 BackingStoreCopy ImageBuffer::fastCopyImageMode()
@@ -159,6 +194,41 @@ BackingStoreCopy ImageBuffer::fastCopyImageMode()
     return DontCopyBackingStore;
 }
 
+COMPtr<ID2D1Bitmap> ImageBuffer::sinkIntoNativeImage(std::unique_ptr<ImageBuffer> imageBuffer)
+{
+    // FIXME: See if we can reuse the on-hardware image.
+    return imageBuffer->copyNativeImage(DontCopyBackingStore);
+}
+
+COMPtr<ID2D1Bitmap> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
+{
+    auto bitmapTarget = reinterpret_cast<ID2D1BitmapRenderTarget*>(context().platformContext());
+
+    COMPtr<ID2D1Bitmap> image;
+    HRESULT hr = bitmapTarget->GetBitmap(&image);
+    ASSERT(SUCCEEDED(hr));
+
+    if (!context().isAcceleratedContext()) {
+        switch (copyBehavior) {
+        case DontCopyBackingStore:
+            break;
+        case CopyBackingStore:
+            D2D1_RECT_U backingStoreDimenstions = IntRect(IntPoint(), m_data.backingStoreSize);
+            image->CopyFromMemory(&backingStoreDimenstions, m_data.data, 32);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+        }
+    }
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    else
+        image = m_data.surface->createImage();
+#endif
+
+    return image;
+}
+
 void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
 {
     imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
index d980b04..eb85631 100644 (file)
@@ -171,6 +171,8 @@ Path::Path(Path&& other)
 
 Path& Path::operator=(const Path& other)
 {
+    if (this == &other)
+        return *this;
     m_path = other.m_path;
     m_activePath = other.m_activePath;
     m_activePathGeometry = other.m_activePathGeometry;
@@ -208,7 +210,7 @@ HRESULT Path::initializePathState()
     return m_activePathGeometry->Open(&m_activePath);
 }
 
-void Path::drawDidComplete() const
+void Path::drawDidComplete()
 {
     FloatPoint currentPoint = this->currentPoint();
 
@@ -222,6 +224,7 @@ void Path::drawDidComplete() const
     m_activePath->SetFillMode(D2D1_FILL_MODE_WINDING);
 
     m_activePath->BeginFigure(currentPoint, D2D1_FIGURE_BEGIN_FILLED);
+    m_doesHaveOpenFigure = true;
 }
 
 bool Path::contains(const FloatPoint& point, WindRule rule) const
@@ -267,17 +270,22 @@ void Path::transform(const AffineTransform& transform)
     if (transform.isIdentity() || isEmpty())
         return;
 
+    std::optional<FloatPoint> currentPoint;
+    if (hasCurrentPoint())
+        currentPoint = this->currentPoint();
+
     bool pathIsActive = false;
     if (m_activePath) {
+        m_activePath->EndFigure(D2D1_FIGURE_END_OPEN);
+        m_doesHaveOpenFigure = false;
         m_activePath->Close();
         m_activePath = nullptr;
         m_activePathGeometry = nullptr;
         pathIsActive = true;
     }
 
-    const D2D1_MATRIX_3X2_F& d2dTransform = static_cast<const D2D1_MATRIX_3X2_F>(transform);
     COMPtr<ID2D1TransformedGeometry> transformedPath;
-    if (!SUCCEEDED(GraphicsContext::systemFactory()->CreateTransformedGeometry(m_path.get(), d2dTransform, &transformedPath)))
+    if (!SUCCEEDED(GraphicsContext::systemFactory()->CreateTransformedGeometry(m_path.get(), transform, &transformedPath)))
         return;
 
     Vector<ID2D1Geometry*> geometries;
@@ -295,6 +303,15 @@ void Path::transform(const AffineTransform& transform)
 
     HRESULT hr = GraphicsContext::systemFactory()->CreateGeometryGroup(fillMode, geometries.data(), geometries.size(), &m_path);
     RELEASE_ASSERT(SUCCEEDED(hr));
+
+    if (!currentPoint)
+        return;
+
+    m_activePath->SetFillMode(fillMode);
+
+    auto transformedPoint = transform.mapPoint(currentPoint.value());
+    m_activePath->BeginFigure(transformedPoint, D2D1_FIGURE_BEGIN_FILLED);
+    m_doesHaveOpenFigure = true;
 }
 
 FloatRect Path::boundingRect() const
@@ -336,26 +353,42 @@ FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
     return boundingRect();
 }
 
+void Path::openFigureAtCurrentPointIfNecessary()
+{
+    if (m_doesHaveOpenFigure)
+        return;
+
+    m_activePath->SetFillMode(D2D1_FILL_MODE_WINDING);
+    m_activePath->BeginFigure(currentPoint(), D2D1_FIGURE_BEGIN_FILLED);
+    m_doesHaveOpenFigure = true;
+}
+
 void Path::moveTo(const FloatPoint& point)
 {
-    if (!m_activePath) {
-        GraphicsContext::systemFactory()->CreatePathGeometry(&m_activePathGeometry);
+    if (m_activePath) {
+        m_activePath->Close();
+        m_activePath = nullptr;
+        m_activePathGeometry = nullptr;
+        m_doesHaveOpenFigure = false;
+    }
 
-        appendGeometry(m_activePathGeometry.get());
+    GraphicsContext::systemFactory()->CreatePathGeometry(&m_activePathGeometry);
 
-        if (!SUCCEEDED(m_activePathGeometry->Open(&m_activePath)))
-            return;
+    appendGeometry(m_activePathGeometry.get());
 
-        m_activePath->SetFillMode(D2D1_FILL_MODE_WINDING);
-    }
+    if (!SUCCEEDED(m_activePathGeometry->Open(&m_activePath)))
+        return;
 
+    m_activePath->SetFillMode(D2D1_FILL_MODE_WINDING);
     m_activePath->BeginFigure(point, D2D1_FIGURE_BEGIN_FILLED);
+    m_doesHaveOpenFigure = true;
 }
 
 void Path::addLineTo(const FloatPoint& point)
 {
     ASSERT(m_activePath.get());
 
+    openFigureAtCurrentPointIfNecessary();
     m_activePath->AddLine(point);
 }
 
@@ -363,6 +396,7 @@ void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
 {
     ASSERT(m_activePath.get());
 
+    openFigureAtCurrentPointIfNecessary();
     m_activePath->AddQuadraticBezier(D2D1::QuadraticBezierSegment(cp, p));
 }
 
@@ -370,17 +404,48 @@ void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const
 {
     ASSERT(m_activePath.get());
 
-    FloatPoint beforePoint = currentPoint();
-
+    openFigureAtCurrentPointIfNecessary();
     m_activePath->AddBezier(D2D1::BezierSegment(cp1, cp2, p));
 }
 
 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
 {
-    UNUSED_PARAM(p1);
-    UNUSED_PARAM(p2);
-    UNUSED_PARAM(radius);
-    notImplemented();
+    ASSERT(m_activePath.get());
+
+    FloatPoint p0 = currentPoint();
+
+    if (p1 == p0 || p1 == p2 || WTF::areEssentiallyEqual(radius, 0.0f))
+        return addLineTo(p1);
+
+    float direction = (p2.x() - p1.x()) * (p0.y() - p1.y()) + (p2.y() - p1.y()) * (p1.x() - p0.x());
+    if (WTF::areEssentiallyEqual(direction, 0.0f))
+        return addLineTo(p1);
+
+    auto a2 = toFloatPoint(p0 - p1).lengthSquared();
+    auto b2 = toFloatPoint(p1 - p2).lengthSquared();
+    auto c2 = toFloatPoint(p0 - p2).lengthSquared();
+
+    double cosx = (a2 + b2 - c2) / (2.0 * std::sqrt(a2 * b2));
+    double sinx = std::sqrt(1.0 - cosx * cosx);
+    double d = radius / ((1 - cosx) / sinx);
+
+    auto an = toFloatPoint(p1 - p0).scaled(1.0 / std::sqrt(a2));
+    auto bn = toFloatPoint(p1 - p2).scaled(1.0 / std::sqrt(b2));
+
+    auto startPoint = toFloatPoint(p1 - an.scaled(d));
+    auto p4 = toFloatPoint(p1 - bn.scaled(d));
+
+    bool anticlockwise = (direction < 0);
+    an.scale(radius * (anticlockwise ? 1 : -1));
+
+    FloatPoint center(startPoint.x() + an.y(), startPoint.y() - an.x());
+
+    double angle0 = atan2(startPoint.y() - center.y(), startPoint.x() - center.x());
+    double angle1 = atan2(p4.y() - center.y(), p4.x() - center.x());
+
+    openFigureAtCurrentPointIfNecessary();
+    addLineTo(startPoint);
+    addArc(center, radius, angle0, angle1, anticlockwise);
 }
 
 static bool equalRadiusWidths(const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
@@ -423,8 +488,14 @@ void Path::closeSubpath()
     if (isNull())
         return;
 
-    ASSERT(m_activePath.get());
-    m_activePath->EndFigure(D2D1_FIGURE_END_OPEN);
+    if (m_activePath) {
+        m_activePath->EndFigure(D2D1_FIGURE_END_CLOSED);
+        m_activePath->Close();
+        m_activePath = nullptr;
+        m_activePathGeometry = nullptr;
+    }
+
+    m_doesHaveOpenFigure = false;
 }
 
 static FloatPoint arcStart(const FloatPoint& center, float radius, float startAngle)
@@ -436,21 +507,22 @@ static FloatPoint arcStart(const FloatPoint& center, float radius, float startAn
     return startingPoint;
 }
 
-static void drawArcSection(ID2D1GeometrySink* sink, const FloatPoint& center, float radius, float startAngle, float endAngle, bool clockwise)
+const float twoPi = 2.0f * piFloat;
+
+static void drawArcSection(ID2D1GeometrySink* sink, const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise)
 {
     // Direct2D wants us to specify the end point of the arc, not the center. It will be drawn from
     // whatever the current point in the 'm_activePath' is.
-    FloatPoint p = center;
+    FloatPoint endPoint = center;
     float endX = radius * std::cos(endAngle);
     float endY = radius * std::sin(endAngle);
-    p.move(endX, endY);
+    endPoint.move(endX, endY);
 
-    float arcDeg = rad2deg(endAngle - startAngle);
-    D2D1_SWEEP_DIRECTION direction = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
-    sink->AddArc(D2D1::ArcSegment(p, D2D1::SizeF(radius, radius), arcDeg, direction, D2D1_ARC_SIZE_SMALL));
+    D2D1_SWEEP_DIRECTION direction = anticlockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE : D2D1_SWEEP_DIRECTION_CLOCKWISE;
+    sink->AddArc(D2D1::ArcSegment(endPoint, D2D1::SizeF(radius, radius), 0, direction, D2D1_ARC_SIZE_SMALL));
 }
 
-void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool clockwise)
+void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise)
 {
     auto arcStartPoint = arcStart(center, radius, startAngle);
     if (!m_activePath)
@@ -461,23 +533,41 @@ void Path::addArc(const FloatPoint& center, float radius, float startAngle, floa
         addLineTo(arcStartPoint);
     }
 
-    // Direct2D has problems drawing large arcs. It gets confused if drawing a complete (or
-    // nearly complete) circle in the counter-clockwise direction. So, draw any arcs larger
-    // than 180 degrees in two pieces.
-    float fullSweep = endAngle - startAngle;
-    float negate = fullSweep < 0 ? -1.0f : 1.0f;
-    float maxSweep = negate * std::min(std::abs(fullSweep), piFloat);
-    float firstArcEnd = startAngle + maxSweep;
-    drawArcSection(m_activePath.get(), center, radius, startAngle, firstArcEnd, clockwise);
+    if (WTF::areEssentiallyEqual(std::abs(endAngle - startAngle), twoPi))
+        return addEllipse(FloatRect(center.x() - radius, center.y() - radius, 2.0 * radius, 2.0 * radius));
+
+    if (anticlockwise) {
+        if (endAngle > startAngle) {
+            endAngle -= twoPi * std::ceil((endAngle - startAngle) / twoPi);
+            ASSERT(endAngle <= startAngle);
+        }
+    } else {
+        if (startAngle > endAngle) {
+            startAngle -= twoPi * std::ceil((startAngle - endAngle) / twoPi);
+            ASSERT(startAngle <= endAngle);
+        }
+    }
 
-    if (WTF::areEssentiallyEqual(firstArcEnd, endAngle))
-        return;
+    const float delta = anticlockwise ? -piOverTwoFloat : piOverTwoFloat;
+    float remainingArcAngle = endAngle - startAngle;
 
-    drawArcSection(m_activePath.get(), center, radius, firstArcEnd, endAngle, clockwise);
+    while ((remainingArcAngle > 0 && remainingArcAngle > delta) || (remainingArcAngle < 0 && remainingArcAngle < delta)) {
+        const double currentEndAngle = startAngle + delta;
+        drawArcSection(m_activePath.get(), center, radius, startAngle, currentEndAngle, anticlockwise);
+        startAngle = currentEndAngle;
+        remainingArcAngle -= delta;
+    }
+
+    // Handle any remaining part of the arc:
+    if (std::abs(remainingArcAngle) > 1e-6)
+        drawArcSection(m_activePath.get(), center, radius, startAngle, startAngle + remainingArcAngle, anticlockwise);
 }
 
 void Path::addRect(const FloatRect& r)
 {
+    if (!m_activePath)
+        moveTo(r.location());
+
     COMPtr<ID2D1RectangleGeometry> rectangle;
     HRESULT hr = GraphicsContext::systemFactory()->CreateRectangleGeometry(r, &rectangle);
     RELEASE_ASSERT(SUCCEEDED(hr));
@@ -494,8 +584,12 @@ void Path::addEllipse(FloatPoint p, float radiusX, float radiusY, float rotation
 
 void Path::addEllipse(const FloatRect& r)
 {
+    if (!m_activePath)
+        moveTo(r.location());
+
     COMPtr<ID2D1EllipseGeometry> ellipse;
-    HRESULT hr = GraphicsContext::systemFactory()->CreateEllipseGeometry(D2D1::Ellipse(r.center(), r.width(), r.height()), &ellipse);
+    // Note: The radii of the ellipse contained within a rectange are half the width and height of the rect.
+    HRESULT hr = GraphicsContext::systemFactory()->CreateEllipseGeometry(D2D1::Ellipse(r.center(), 0.5 * r.width(), 0.5 * r.height()), &ellipse);
     RELEASE_ASSERT(SUCCEEDED(hr));
     appendGeometry(ellipse.get());
 }
index a5c8dae..9ab5e43 100644 (file)
@@ -207,8 +207,6 @@ float Font::platformWidthForGlyph(Glyph glyph) const
     if (m_platformData.useGDI())
         return widthForGDIGlyph(glyph);
 
-    ASSERT(glyph);
-
     auto font = m_platformData.dwFont();
     RELEASE_ASSERT(font);
 
index 9cc4295..7398ef3 100644 (file)
@@ -142,7 +142,7 @@ bool RenderSVGResourceClipper::applyClippingToContext(RenderElement& renderer, c
 
     if (shouldCreateClipperMaskImage && !repaintRect.isEmpty()) {
         // FIXME (149469): This image buffer should not be unconditionally unaccelerated. Making it match the context breaks nested clipping, though.
-        clipperMaskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, ColorSpaceSRGB, Unaccelerated);
+        clipperMaskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, ColorSpaceSRGB, Unaccelerated, &context);
         if (!clipperMaskImage)
             return false;
 
index dea282c..3640692 100644 (file)
@@ -191,7 +191,7 @@ bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const Rende
     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
 
     RenderingMode renderingMode = renderer.settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
-    auto sourceGraphic = SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, ColorSpaceLinearRGB, renderingMode);
+    auto sourceGraphic = SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, ColorSpaceLinearRGB, renderingMode, context);
     if (!sourceGraphic) {
         ASSERT(!m_rendererFilterDataMap.contains(&renderer));
         filterData->savedContext = context;
index 570e648..aa5765e 100644 (file)
@@ -71,7 +71,7 @@ bool RenderSVGResourceMasker::applyResource(RenderElement& renderer, const Rende
         const SVGRenderStyle& svgStyle = style().svgStyle();
         ColorSpace colorSpace = svgStyle.colorInterpolation() == ColorInterpolation::LinearRGB ? ColorSpaceLinearRGB : ColorSpaceSRGB;
         // FIXME (149470): This image buffer should not be unconditionally unaccelerated. Making it match the context breaks alpha masking, though.
-        maskerData->maskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, colorSpace, Unaccelerated);
+        maskerData->maskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, colorSpace, Unaccelerated, context);
         if (!maskerData->maskImage)
             return false;
 
index a3b5315..495b134 100644 (file)
@@ -235,7 +235,7 @@ AffineTransform SVGRenderingContext::calculateTransformationToOutermostCoordinat
     return absoluteTransform;
 }
 
-std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, ColorSpace colorSpace, RenderingMode renderingMode)
+std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, ColorSpace colorSpace, RenderingMode renderingMode, const GraphicsContext* context)
 {
     IntRect paintRect = calculateImageBufferRect(targetRect, absoluteTransform);
     // Don't create empty ImageBuffers.
@@ -245,7 +245,12 @@ std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatR
     FloatSize scale;
     FloatSize clampedSize = ImageBuffer::clampedSize(paintRect.size(), scale);
 
+#if USE(DIRECT2D)
+    auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, context, 1, colorSpace);
+#else
+    UNUSED_PARAM(context);
     auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, 1, colorSpace);
+#endif
     if (!imageBuffer)
         return nullptr;
 
@@ -258,7 +263,7 @@ std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatR
     return imageBuffer;
 }
 
-std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const FloatRect& clampedRect, ColorSpace colorSpace, RenderingMode renderingMode)
+std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const FloatRect& clampedRect, ColorSpace colorSpace, RenderingMode renderingMode, const GraphicsContext* context)
 {
     IntSize clampedSize = roundedIntSize(clampedRect.size());
     FloatSize unclampedSize = roundedIntSize(targetRect.size());
@@ -267,7 +272,12 @@ std::unique_ptr<ImageBuffer> SVGRenderingContext::createImageBuffer(const FloatR
     if (clampedSize.isEmpty())
         return nullptr;
 
+#if USE(DIRECT2D)
+    auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, context, 1, colorSpace);
+#else
+    UNUSED_PARAM(context);
     auto imageBuffer = ImageBuffer::create(clampedSize, renderingMode, 1, colorSpace);
+#endif
     if (!imageBuffer)
         return nullptr;
 
index 8b9b74a..08a7878 100644 (file)
@@ -60,8 +60,8 @@ public:
     void prepareToRenderSVGContent(RenderElement&, PaintInfo&, NeedsGraphicsContextSave = DontSaveGraphicsContext);
     bool isRenderingPrepared() const { return m_renderingFlags & RenderingPrepared; }
 
-    static std::unique_ptr<ImageBuffer> createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, ColorSpace, RenderingMode);
-    static std::unique_ptr<ImageBuffer> createImageBuffer(const FloatRect& targetRect, const FloatRect& clampedRect, ColorSpace, RenderingMode);
+    static std::unique_ptr<ImageBuffer> createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, ColorSpace, RenderingMode, const GraphicsContext* = nullptr);
+    static std::unique_ptr<ImageBuffer> createImageBuffer(const FloatRect& targetRect, const FloatRect& clampedRect, ColorSpace, RenderingMode, const GraphicsContext* = nullptr);
 
     static void renderSubtreeToImageBuffer(ImageBuffer*, RenderElement&, const AffineTransform&);
     static void clipToImageBuffer(GraphicsContext&, const AffineTransform& absoluteTransform, const FloatRect& targetRect, std::unique_ptr<ImageBuffer>&, bool safeToClear);