[FTW] Refactor Direct2D code to follow Cairo's model to support modern WebKit
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jul 2019 22:36:14 +0000 (22:36 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jul 2019 22:36:14 +0000 (22:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200270

Reviewed by Dean Jackson.

Refactor the Direct2D code in WebCore so that the core routines can be shared
between GraphicsContext and GraphicsContextImpl. Implement PlatformContext,
BackingStoreBackend, and GraphicsContextImpl for the Direct2D engine.

This patch effectively just moves code around.

* PlatformFTW.cmake:
* platform/graphics/GraphicsContext.h:
* platform/graphics/GraphicsContextImpl.h:
* platform/graphics/ImageSource.cpp:
* platform/graphics/Pattern.h:
* platform/graphics/displaylists/DisplayListRecorder.cpp:
* platform/graphics/displaylists/DisplayListRecorder.h:
* platform/graphics/win/BackingStoreBackendDirect2D.h: Added.
* platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp: Added.
* platform/graphics/win/BackingStoreBackendDirect2DImpl.h: Added.
* platform/graphics/win/Direct2DOperations.cpp: Added.
* platform/graphics/win/Direct2DOperations.h: Added.
* platform/graphics/win/Direct2DUtilities.cpp: Added.
* platform/graphics/win/Direct2DUtilities.h: Added.
* platform/graphics/win/FontCascadeDirect2D.cpp:
* platform/graphics/win/GradientDirect2D.cpp:
* platform/graphics/win/GraphicsContextDirect2D.cpp:
* platform/graphics/win/GraphicsContextImplDirect2D.cpp: Added.
* platform/graphics/win/GraphicsContextImplDirect2D.h: Added.
* platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
* platform/graphics/win/ImageBufferDataDirect2D.h:
* platform/graphics/win/ImageBufferDirect2D.cpp:
* platform/graphics/win/NativeImageDirect2D.cpp:
* platform/graphics/win/PathDirect2D.cpp:
* platform/graphics/win/PatternDirect2D.cpp:
* platform/graphics/win/PlatformContextDirect2D.cpp: Added.
* platform/graphics/win/PlatformContextDirect2D.h: Added.
* platform/win/DragImageWin.cpp:
* svg/graphics/SVGImage.cpp:

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

33 files changed:
Source/WebCore/ChangeLog
Source/WebCore/PlatformFTW.cmake
Source/WebCore/platform/graphics/GLContext.h
Source/WebCore/platform/graphics/GraphicsContext.h
Source/WebCore/platform/graphics/GraphicsContextImpl.h
Source/WebCore/platform/graphics/ImageSource.cpp
Source/WebCore/platform/graphics/Pattern.h
Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp
Source/WebCore/platform/graphics/displaylists/DisplayListItems.h
Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp
Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h
Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2D.h [new file with mode: 0644]
Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.h [new file with mode: 0644]
Source/WebCore/platform/graphics/win/Direct2DOperations.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/win/Direct2DOperations.h [new file with mode: 0644]
Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/win/Direct2DUtilities.h [new file with mode: 0644]
Source/WebCore/platform/graphics/win/FontCascadeDirect2D.cpp
Source/WebCore/platform/graphics/win/GradientDirect2D.cpp
Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp
Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.h [new file with mode: 0644]
Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h
Source/WebCore/platform/graphics/win/ImageBufferDataDirect2D.h
Source/WebCore/platform/graphics/win/ImageBufferDirect2D.cpp
Source/WebCore/platform/graphics/win/NativeImageDirect2D.cpp
Source/WebCore/platform/graphics/win/PathDirect2D.cpp
Source/WebCore/platform/graphics/win/PatternDirect2D.cpp
Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h [new file with mode: 0644]
Source/WebCore/platform/win/DragImageDirect2D.cpp
Source/WebCore/svg/graphics/SVGImage.cpp

index cdd4a33..5ca8f43 100644 (file)
@@ -1,3 +1,46 @@
+2019-07-30  Brent Fulgham  <bfulgham@apple.com>
+
+        [FTW] Refactor Direct2D code to follow Cairo's model to support modern WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=200270
+
+        Reviewed by Dean Jackson.
+
+        Refactor the Direct2D code in WebCore so that the core routines can be shared
+        between GraphicsContext and GraphicsContextImpl. Implement PlatformContext,
+        BackingStoreBackend, and GraphicsContextImpl for the Direct2D engine.
+       
+        This patch effectively just moves code around.
+
+        * PlatformFTW.cmake:
+        * platform/graphics/GraphicsContext.h:
+        * platform/graphics/GraphicsContextImpl.h:
+        * platform/graphics/ImageSource.cpp:
+        * platform/graphics/Pattern.h:
+        * platform/graphics/displaylists/DisplayListRecorder.cpp:
+        * platform/graphics/displaylists/DisplayListRecorder.h:
+        * platform/graphics/win/BackingStoreBackendDirect2D.h: Added.
+        * platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp: Added.
+        * platform/graphics/win/BackingStoreBackendDirect2DImpl.h: Added.
+        * platform/graphics/win/Direct2DOperations.cpp: Added.
+        * platform/graphics/win/Direct2DOperations.h: Added.
+        * platform/graphics/win/Direct2DUtilities.cpp: Added.
+        * platform/graphics/win/Direct2DUtilities.h: Added.
+        * platform/graphics/win/FontCascadeDirect2D.cpp:
+        * platform/graphics/win/GradientDirect2D.cpp:
+        * platform/graphics/win/GraphicsContextDirect2D.cpp:
+        * platform/graphics/win/GraphicsContextImplDirect2D.cpp: Added.
+        * platform/graphics/win/GraphicsContextImplDirect2D.h: Added.
+        * platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
+        * platform/graphics/win/ImageBufferDataDirect2D.h:
+        * platform/graphics/win/ImageBufferDirect2D.cpp:
+        * platform/graphics/win/NativeImageDirect2D.cpp:
+        * platform/graphics/win/PathDirect2D.cpp:
+        * platform/graphics/win/PatternDirect2D.cpp:
+        * platform/graphics/win/PlatformContextDirect2D.cpp: Added.
+        * platform/graphics/win/PlatformContextDirect2D.h: Added.
+        * platform/win/DragImageWin.cpp:
+        * svg/graphics/SVGImage.cpp:
+
 2019-07-30  Myles C. Maxfield  <mmaxfield@apple.com>
 
         REGRESSION(r241288): Text on Yahoo Japan mobile looks too bold
index 7632416..fb051a7 100644 (file)
@@ -45,6 +45,7 @@ list(APPEND WebCore_SOURCES
 
     platform/graphics/GLContext.cpp
     platform/graphics/GraphicsContext3DPrivate.cpp
+    platform/graphics/GraphicsContextImpl.cpp
     platform/graphics/PlatformDisplay.cpp
 
     platform/graphics/egl/GLContextEGL.cpp
@@ -57,9 +58,12 @@ list(APPEND WebCore_SOURCES
 
     platform/graphics/opentype/OpenTypeUtilities.cpp
 
+    platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp
     platform/graphics/win/ColorDirect2D.cpp
     platform/graphics/win/ComplexTextControllerDirectWrite.cpp
     platform/graphics/win/DIBPixelData.cpp
+    platform/graphics/win/Direct2DOperations.cpp
+    platform/graphics/win/Direct2DUtilities.cpp
     platform/graphics/win/FloatPointDirect2D.cpp
     platform/graphics/win/FloatRectDirect2D.cpp
     platform/graphics/win/FloatSizeDirect2D.cpp
@@ -73,6 +77,7 @@ list(APPEND WebCore_SOURCES
     platform/graphics/win/GradientDirect2D.cpp
     platform/graphics/win/GraphicsContext3DDirect2D.cpp
     platform/graphics/win/GraphicsContextDirect2D.cpp
+    platform/graphics/win/GraphicsContextImplDirect2D.cpp
     platform/graphics/win/GraphicsContextWin.cpp
     platform/graphics/win/IconWin.cpp
     platform/graphics/win/ImageBufferDataDirect2D.cpp
@@ -87,6 +92,7 @@ list(APPEND WebCore_SOURCES
     platform/graphics/win/NativeImageDirect2D.cpp
     platform/graphics/win/PathDirect2D.cpp
     platform/graphics/win/PatternDirect2D.cpp
+    platform/graphics/win/PlatformContextDirect2D.cpp
     platform/graphics/win/SimpleFontDataWin.cpp
     platform/graphics/win/SimpleFontDataDirect2D.cpp
     platform/graphics/win/TextAnalyzerHelper.cpp
@@ -149,12 +155,19 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS
 
     page/win/FrameWin.h
 
+    platform/graphics/win/BackingStoreBackendDirect2D.h
+    platform/graphics/win/BackingStoreBackendDirect2DImpl.h
     platform/graphics/win/DIBPixelData.h
+    platform/graphics/win/Direct2DOperations.h
+    platform/graphics/win/Direct2DUtilities.h
     platform/graphics/win/FullScreenController.h
     platform/graphics/win/FullScreenControllerClient.h
+    platform/graphics/win/GraphicsContextImplDirect2D.h
     platform/graphics/win/ImageBufferDataDirect2D.h
+    platform/graphics/win/ImageDecoderDirect2D.h
     platform/graphics/win/LocalWindowsContext.h
     platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h
+    platform/graphics/win/PlatformContextDirect2D.h
     platform/graphics/win/SharedGDIObject.h
 
     platform/win/BString.h
index 7d164f7..0555da7 100644 (file)
@@ -43,6 +43,8 @@ typedef struct _cairo_device cairo_device_t;
 
 namespace WebCore {
 
+class IntSize;
+
 class GLContext {
     WTF_MAKE_NONCOPYABLE(GLContext); WTF_MAKE_FAST_ALLOCATED;
 public:
index dd23ad1..ddc7cac 100644 (file)
@@ -44,7 +44,10 @@ interface ID2D1DCRenderTarget;
 interface ID2D1RenderTarget;
 interface ID2D1Factory;
 interface ID2D1SolidColorBrush;
-typedef ID2D1RenderTarget PlatformGraphicsContext;
+namespace WebCore {
+class PlatformContextDirect2D;
+}
+typedef WebCore::PlatformContextDirect2D PlatformGraphicsContext;
 #elif USE(CAIRO)
 namespace WebCore {
 class PlatformContextCairo;
@@ -347,10 +350,6 @@ public:
     WEBCORE_EXPORT void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendMode::Normal, ImageOrientation = ImageOrientation());
 #endif
 
-#if USE(DIRECT2D)
-    void drawDeviceBitmap(const COMPtr<ID2D1Bitmap>&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendMode::Normal, ImageOrientation = ImageOrientation());
-#endif
-
 #if USE(CG) || USE(DIRECT2D)
     void applyStrokePattern();
     void applyFillPattern();
@@ -619,9 +618,7 @@ private:
 
 #if USE(DIRECT2D)
     void platformInit(HDC, ID2D1RenderTarget**, RECT, bool hasAlpha = false);
-    void platformInit(PlatformGraphicsContext*, BitmapRenderingContextType);
-    void drawWithoutShadow(const FloatRect& boundingRect, const WTF::Function<void(ID2D1RenderTarget*)>&);
-    void drawWithShadow(const FloatRect& boundingRect, const WTF::Function<void(ID2D1RenderTarget*)>&);
+    void platformInit(PlatformContextDirect2D*, BitmapRenderingContextType);
 #endif
 
     void savePlatformState();
index 5036892..0bad4e7 100644 (file)
@@ -71,7 +71,7 @@ public:
     virtual ImageDrawResult drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&) = 0;
     virtual ImageDrawResult drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&) = 0;
     virtual ImageDrawResult drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&) = 0;
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
     virtual void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation) = 0;
 #endif
     virtual void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendMode::Normal) = 0;
index a872676..213066d 100644 (file)
@@ -38,6 +38,7 @@
 
 #if USE(DIRECT2D)
 #include "GraphicsContext.h"
+#include "PlatformContextDirect2D.h"
 #endif
 
 namespace WebCore {
@@ -661,7 +662,7 @@ ImageOrientation ImageSource::frameOrientationAtIndex(size_t index)
 void ImageSource::setTargetContext(const GraphicsContext* targetContext)
 {
     if (isDecoderAvailable() && targetContext)
-        m_decoder->setTargetContext(targetContext->platformContext());
+        m_decoder->setTargetContext(targetContext->platformContext()->renderTarget());
 }
 #endif
 
index a267ea8..93b89d5 100644 (file)
@@ -38,6 +38,10 @@ typedef CGPatternRef PlatformPatternPtr;
 #elif USE(DIRECT2D)
 interface ID2D1BitmapBrush;
 typedef ID2D1BitmapBrush* PlatformPatternPtr;
+namespace WebCore {
+class PlatformContextDirect2D;
+}
+typedef WebCore::PlatformContextDirect2D PlatformGraphicsContext;
 #elif USE(CAIRO)
 typedef struct _cairo_pattern cairo_pattern_t;
 typedef cairo_pattern_t* PlatformPatternPtr;
@@ -62,6 +66,7 @@ public:
 #if !USE(DIRECT2D)
     PlatformPatternPtr createPlatformPattern(const AffineTransform& userSpaceTransformation) const;
 #else
+    PlatformPatternPtr createPlatformPattern(PlatformGraphicsContext&, float alpha, const AffineTransform& userSpaceTransformation) const;
     PlatformPatternPtr createPlatformPattern(const GraphicsContext&, float alpha, const AffineTransform& userSpaceTransformation) const;
 #endif
     void setPatternSpaceTransform(const AffineTransform& patternSpaceTransformation);
index 45f348c..b713e85 100644 (file)
@@ -486,7 +486,7 @@ static TextStream& operator<<(TextStream& ts, const DrawTiledScaledImage& item)
     return ts;
 }
 
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
 DrawNativeImage::DrawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
     : DrawingItem(ItemType::DrawNativeImage)
 #if USE(CG)
index 3ed8245..8bcce19 100644 (file)
@@ -70,7 +70,7 @@ enum class ItemType {
     DrawImage,
     DrawTiledImage,
     DrawTiledScaledImage,
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
     DrawNativeImage,
 #endif
     DrawPattern,
@@ -638,7 +638,7 @@ private:
     ImagePaintingOptions m_imagePaintingOptions;
 };
 
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
 class DrawNativeImage : public DrawingItem {
 public:
     static Ref<DrawNativeImage> create(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
index e6b1faf..e797a7f 100644 (file)
@@ -126,7 +126,7 @@ ImageDrawResult Recorder::drawTiledImage(Image& image, const FloatRect& destinat
     return ImageDrawResult::DidRecord;
 }
 
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
 void Recorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
 {
     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawNativeImage::create(image, imageSize, destRect, srcRect, op, blendMode, orientation)));
index 49e9181..31798c1 100644 (file)
@@ -91,7 +91,7 @@ private:
     ImageDrawResult drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&) override;
     ImageDrawResult drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&) override;
     ImageDrawResult drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&) override;
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
     void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation) override;
 #endif
     void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendMode::Normal) override;
diff --git a/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2D.h b/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2D.h
new file mode 100644 (file)
index 0000000..49098e1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "COMPtr.h"
+#include "IntRect.h"
+#include <wincodec.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+
+
+namespace WebCore {
+
+class BackingStoreBackendDirect2D {
+    WTF_MAKE_NONCOPYABLE(BackingStoreBackendDirect2D);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    virtual ~BackingStoreBackendDirect2D() = default;
+
+    IWICBitmap* surface() const { return m_surface.get(); }
+    const IntSize& size() const { return m_size; }
+
+    virtual void scroll(const IntRect& scrollRect, const IntSize& scrollOffset) = 0;
+
+protected:
+    BackingStoreBackendDirect2D(const IntSize& size)
+        : m_size(size)
+    {
+    }
+
+    COMPtr<IWICBitmap> m_surface;
+    void* m_surfaceBackingData { nullptr };
+    IntSize m_size;
+};
+
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp b/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.cpp
new file mode 100644 (file)
index 0000000..32ed181
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BackingStoreBackendDirect2DImpl.h"
+
+#if USE(DIRECT2D)
+
+#include "Direct2DUtilities.h"
+#include "FloatPoint.h"
+#include "FloatSize.h"
+#include "ImageDecoderDirect2D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include <wincodec.h>
+
+namespace WebCore {
+
+static const Seconds scrollHysteresisDuration { 300_ms };
+
+static COMPtr<IWICBitmap> createDirect2DImageSurfaceWithFastMalloc(const IntSize& size, double deviceScaleFactor, void*& backingStoreData)
+{
+    auto bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(size.width());
+    if (bytesPerRow.hasOverflowed())
+        return nullptr;
+
+    Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(size.height()) * bytesPerRow;
+    if (numBytes.hasOverflowed())
+        return nullptr;
+
+    backingStoreData = fastZeroedMalloc(numBytes.unsafeGet());
+
+    return Direct2D::createDirect2DImageSurfaceWithData(backingStoreData, size, bytesPerRow.unsafeGet());
+}
+
+BackingStoreBackendDirect2DImpl::BackingStoreBackendDirect2DImpl(const IntSize& size, float deviceScaleFactor)
+    : BackingStoreBackendDirect2D(size)
+    , m_scrolledHysteresis([this](PAL::HysteresisState state) {
+        if (state == PAL::HysteresisState::Stopped)
+            m_scrollSurface = nullptr;
+        }, scrollHysteresisDuration)
+{
+    IntSize scaledSize = m_size;
+    scaledSize.scale(deviceScaleFactor);
+    m_surface = createDirect2DImageSurfaceWithFastMalloc(scaledSize, deviceScaleFactor, m_surfaceBackingData);
+}
+
+BackingStoreBackendDirect2DImpl::~BackingStoreBackendDirect2DImpl()
+{
+    fastFree(m_surfaceBackingData);
+    fastFree(m_scrollSurfaceBackingData);
+}
+
+void BackingStoreBackendDirect2DImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
+{
+    IntRect targetRect = scrollRect;
+    targetRect.move(scrollOffset);
+    targetRect.shiftMaxXEdgeTo(targetRect.maxX() - scrollOffset.width());
+    targetRect.shiftMaxYEdgeTo(targetRect.maxY() - scrollOffset.height());
+    if (targetRect.isEmpty())
+        return;
+
+    if (!m_scrollSurface) {
+        auto size = Direct2D::bitmapSize(m_surface.get());
+        auto scale = Direct2D::bitmapResolution(m_surface.get());
+        ASSERT(scale.x() == scale.y());
+        m_scrollSurface = createDirect2DImageSurfaceWithFastMalloc(size, scale.x(), m_scrollSurfaceBackingData);
+    }
+
+    Direct2D::copyRectFromOneSurfaceToAnother(m_surface.get(), m_scrollSurface.get(), scrollOffset, targetRect);
+    Direct2D::copyRectFromOneSurfaceToAnother(m_scrollSurface.get(), m_surface.get(), IntSize(), targetRect);
+
+    m_scrolledHysteresis.impulse();
+}
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.h b/Source/WebCore/platform/graphics/win/BackingStoreBackendDirect2DImpl.h
new file mode 100644 (file)
index 0000000..86b7095
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "BackingStoreBackendDirect2D.h"
+#include "COMPtr.h"
+#include <pal/HysteresisActivity.h>
+
+interface IWICBitmap;
+
+namespace WebCore {
+
+class BackingStoreBackendDirect2DImpl final : public BackingStoreBackendDirect2D {
+public:
+    WEBCORE_EXPORT BackingStoreBackendDirect2DImpl(const IntSize&, float deviceScaleFactor);
+    virtual ~BackingStoreBackendDirect2DImpl();
+
+private:
+    void scroll(const IntRect&, const IntSize&) override;
+
+    COMPtr<IWICBitmap> m_scrollSurface;
+    void* m_scrollSurfaceBackingData { nullptr };
+
+    PAL::HysteresisActivity m_scrolledHysteresis;
+};
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/Direct2DOperations.cpp b/Source/WebCore/platform/graphics/win/Direct2DOperations.cpp
new file mode 100644 (file)
index 0000000..3a84ad4
--- /dev/null
@@ -0,0 +1,1203 @@
+/*
+ * Copyright (C) 2006-2019 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
+ * Copyright (C) 2010, 2011 Igalia S.L.
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Direct2DOperations.h"
+
+#if USE(DIRECT2D)
+
+#include "Direct2DUtilities.h"
+#include "FloatConversion.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "GraphicsContextPlatformPrivateDirect2D.h"
+#include "Image.h"
+#include "ImageBuffer.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "PlatformContextDirect2D.h"
+#include "ShadowBlur.h"
+#include <algorithm>
+#include <d2d1.h>
+
+namespace WebCore {
+namespace Direct2D {
+
+enum PatternAdjustment { NoAdjustment, AdjustPatternForGlobalAlpha };
+enum AlphaPreservation { DoNotPreserveAlpha, PreserveAlpha };
+
+
+class PlatformContextStateSaver {
+public:
+    PlatformContextStateSaver(PlatformContextDirect2D& context, bool saveAndRestore = true)
+        : m_context(context)
+        , m_saveAndRestore(saveAndRestore)
+    {
+        if (m_saveAndRestore)
+            m_context.save();
+    }
+
+    ~PlatformContextStateSaver()
+    {
+        if (m_saveAndRestore)
+            m_context.restore();
+    }
+
+    void save()
+    {
+        ASSERT(!m_saveAndRestore);
+        m_context.save();
+        m_saveAndRestore = true;
+    }
+
+    void restore()
+    {
+        ASSERT(m_saveAndRestore);
+        m_context.restore();
+        m_saveAndRestore = false;
+    }
+
+    bool didSave() const
+    {
+        return m_saveAndRestore;
+    }
+
+private:
+    PlatformContextDirect2D& m_context;
+    bool m_saveAndRestore { false };
+};
+
+// FIXME: Replace once GraphicsContext::dashedLineCornerWidthForStrokeWidth()
+// is refactored as a static public function.
+static float dashedLineCornerWidthForStrokeWidth(float strokeWidth, StrokeStyle strokeStyle, float strokeThickness)
+{
+    return strokeStyle == DottedStroke ? strokeThickness : std::min(2.0f * strokeThickness, std::max(strokeThickness, strokeWidth / 3.0f));
+}
+
+// FIXME: Replace once GraphicsContext::dashedLinePatternWidthForStrokeWidth()
+// is refactored as a static public function.
+static float dashedLinePatternWidthForStrokeWidth(float strokeWidth, StrokeStyle strokeStyle, float strokeThickness)
+{
+    return strokeStyle == DottedStroke ? strokeThickness : std::min(3.0f * strokeThickness, std::max(strokeThickness, strokeWidth / 3.0f));
+}
+
+// FIXME: Replace once GraphicsContext::dashedLinePatternOffsetForPatternAndStrokeWidth()
+// is refactored as a static public function.
+static float dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth)
+{
+    // Pattern starts with full fill and ends with the empty fill.
+    // 1. Let's start with the empty phase after the corner.
+    // 2. Check if we've got odd or even number of patterns and whether they fully cover the line.
+    // 3. In case of even number of patterns and/or remainder, move the pattern start position
+    // so that the pattern is balanced between the corners.
+    float patternOffset = patternWidth;
+    int numberOfSegments = std::floor(strokeWidth / patternWidth);
+    bool oddNumberOfSegments = numberOfSegments % 2;
+    float remainder = strokeWidth - (numberOfSegments * patternWidth);
+    if (oddNumberOfSegments && remainder)
+        patternOffset -= remainder / 2.0f;
+    else if (!oddNumberOfSegments) {
+        if (remainder)
+            patternOffset += patternOffset - (patternWidth + remainder) / 2.0f;
+        else
+            patternOffset += patternWidth / 2.0f;
+    }
+
+    return patternOffset;
+}
+
+// FIXME: Replace once GraphicsContext::centerLineAndCutOffCorners()
+// is refactored as a static public function.
+static Vector<FloatPoint> centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2)
+{
+    // Center line and cut off corners for pattern painting.
+    if (isVerticalLine) {
+        float centerOffset = (point2.x() - point1.x()) / 2.0f;
+        point1.move(centerOffset, cornerWidth);
+        point2.move(-centerOffset, -cornerWidth);
+    } else {
+        float centerOffset = (point2.y() - point1.y()) / 2.0f;
+        point1.move(cornerWidth, centerOffset);
+        point2.move(-cornerWidth, -centerOffset);
+    }
+
+    return { point1, point2 };
+}
+
+
+namespace State {
+
+void setStrokeThickness(PlatformContextDirect2D& platformContext, float strokeThickness)
+{
+    platformContext.setStrokeThickness(strokeThickness);
+}
+
+void setStrokeStyle(PlatformContextDirect2D& platformContext, StrokeStyle strokeStyle)
+{
+    platformContext.setStrokeStyle(strokeStyle);
+}
+
+void setCompositeOperation(PlatformContextDirect2D& platformContext, CompositeOperator compositeOperation, BlendMode blendMode)
+{
+    D2D1_BLEND_MODE targetBlendMode = D2D1_BLEND_MODE_SCREEN;
+    D2D1_COMPOSITE_MODE targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+
+    if (blendMode != BlendMode::Normal) {
+        switch (blendMode) {
+        case BlendMode::Multiply:
+            targetBlendMode = D2D1_BLEND_MODE_MULTIPLY;
+            break;
+        case BlendMode::Screen:
+            targetBlendMode = D2D1_BLEND_MODE_SCREEN;
+            break;
+        case BlendMode::Overlay:
+            targetBlendMode = D2D1_BLEND_MODE_OVERLAY;
+            break;
+        case BlendMode::Darken:
+            targetBlendMode = D2D1_BLEND_MODE_DARKEN;
+            break;
+        case BlendMode::Lighten:
+            targetBlendMode = D2D1_BLEND_MODE_LIGHTEN;
+            break;
+        case BlendMode::ColorDodge:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR_DODGE;
+            break;
+        case BlendMode::ColorBurn:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR_BURN;
+            break;
+        case BlendMode::HardLight:
+            targetBlendMode = D2D1_BLEND_MODE_HARD_LIGHT;
+            break;
+        case BlendMode::SoftLight:
+            targetBlendMode = D2D1_BLEND_MODE_SOFT_LIGHT;
+            break;
+        case BlendMode::Difference:
+            targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
+            break;
+        case BlendMode::Exclusion:
+            targetBlendMode = D2D1_BLEND_MODE_EXCLUSION;
+            break;
+        case BlendMode::Hue:
+            targetBlendMode = D2D1_BLEND_MODE_HUE;
+            break;
+        case BlendMode::Saturation:
+            targetBlendMode = D2D1_BLEND_MODE_SATURATION;
+            break;
+        case BlendMode::Color:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR;
+            break;
+        case BlendMode::Luminosity:
+            targetBlendMode = D2D1_BLEND_MODE_LUMINOSITY;
+            break;
+        case BlendMode::PlusDarker:
+            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
+            break;
+        case BlendMode::PlusLighter:
+            targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
+            break;
+        default:
+            break;
+        }
+    } else {
+        switch (compositeOperation) {
+        case CompositeClear:
+            // FIXME: targetBlendMode = D2D1_BLEND_MODE_CLEAR;
+            break;
+        case CompositeCopy:
+            // FIXME: targetBlendMode = D2D1_BLEND_MODE_COPY;
+            break;
+        case CompositeSourceOver:
+            // FIXME: kCGBlendModeNormal
+            break;
+        case CompositeSourceIn:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_IN;
+            break;
+        case CompositeSourceOut:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_OUT;
+            break;
+        case CompositeSourceAtop:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP;
+            break;
+        case CompositeDestinationOver:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OVER;
+            break;
+        case CompositeDestinationIn:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_IN;
+            break;
+        case CompositeDestinationOut:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OUT;
+            break;
+        case CompositeDestinationAtop:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
+            break;
+        case CompositeXOR:
+            targetCompositeMode = D2D1_COMPOSITE_MODE_XOR;
+            break;
+        case CompositePlusDarker:
+            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
+            break;
+        case CompositePlusLighter:
+            targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
+            break;
+        case CompositeDifference:
+            targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
+            break;
+        }
+    }
+
+    platformContext.setBlendMode(targetBlendMode);
+    platformContext.setCompositeMode(targetCompositeMode);
+}
+
+void setShouldAntialias(PlatformContextDirect2D& platformContext, bool enable)
+{
+    auto antialiasMode = enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED;
+    platformContext.renderTarget()->SetAntialiasMode(antialiasMode);
+}
+
+void setCTM(PlatformContextDirect2D& platformContext, const AffineTransform& transform)
+{
+    ASSERT(platformContext.renderTarget());
+    platformContext.renderTarget()->SetTransform(transform);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->setCTM(transform);
+}
+
+AffineTransform getCTM(PlatformContextDirect2D& platformContext)
+{
+    ASSERT(platformContext.renderTarget());
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext.renderTarget()->GetTransform(&currentTransform);
+    return currentTransform;
+}
+
+IntRect getClipBounds(PlatformContextDirect2D& platformContext)
+{
+    D2D1_SIZE_F clipSize;
+    if (auto clipLayer = platformContext.clipLayer())
+        clipSize = clipLayer->GetSize();
+    else
+        clipSize = platformContext.renderTarget()->GetSize();
+
+    FloatRect clipBounds(IntPoint(), clipSize);
+
+    return enclosingIntRect(clipBounds);
+}
+
+FloatRect roundToDevicePixels(PlatformContextDirect2D& platformContext, const FloatRect& rect)
+{
+    notImplemented();
+
+    return rect;
+}
+
+bool isAcceleratedContext(PlatformContextDirect2D& platformContext)
+{
+    auto renderProperties = D2D1::RenderTargetProperties();
+    renderProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE;
+    return platformContext.renderTarget()->IsSupported(&renderProperties);
+}
+
+} // namespace State
+
+FillSource::FillSource(const GraphicsContextState& state, PlatformContextDirect2D& platformContext)
+    : globalAlpha(state.alpha)
+    , color(state.fillColor)
+    , fillRule(state.fillRule)
+{
+    if (state.fillPattern) {
+        AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
+        brush = state.fillPattern->createPlatformPattern(platformContext, state.alpha, userToBaseCTM);
+    } else if (state.fillGradient)
+        brush = state.fillGradient->createPlatformGradientIfNecessary(platformContext.renderTarget());
+    else
+        brush = platformContext.brushWithColor(color);
+}
+
+StrokeSource::StrokeSource(const GraphicsContextState& state, PlatformContextDirect2D& platformContext)
+    : globalAlpha(state.alpha)
+    , thickness(state.strokeThickness)
+    , color(state.strokeColor)
+{
+    if (state.strokePattern) {
+        AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
+        brush = state.strokePattern->createPlatformPattern(platformContext, state.alpha, userToBaseCTM);
+    } else if (state.strokeGradient)
+        brush = state.strokeGradient->createPlatformGradientIfNecessary(platformContext.renderTarget());
+    else
+        brush = platformContext.brushWithColor(color);
+}
+
+ShadowState::ShadowState(const GraphicsContextState& state)
+    : offset(state.shadowOffset)
+    , blur(state.shadowBlur)
+    , color(state.shadowColor)
+    , ignoreTransforms(state.shadowsIgnoreTransforms)
+    , globalAlpha(state.alpha)
+    , globalCompositeOperator(state.compositeOperator)
+{
+}
+
+bool ShadowState::isVisible() const
+{
+    return color.isVisible() && (offset.width() || offset.height() || blur);
+}
+
+bool ShadowState::isRequired(PlatformContextDirect2D& platformContext) const
+{
+    // We can't avoid ShadowBlur if the shadow has blur.
+    if (color.isVisible() && blur)
+        return true;
+
+    // We can avoid ShadowBlur and optimize, since we're not drawing on a
+    // canvas and box shadows are affected by the transformation matrix.
+    if (!ignoreTransforms)
+        return false;
+
+    // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
+    if (State::getCTM(platformContext).isIdentity())
+        return false;
+
+    // Otherwise, no chance avoiding ShadowBlur.
+    return true;
+}
+
+void setLineCap(PlatformContextDirect2D& platformContext, LineCap lineCap)
+{
+    D2D1_CAP_STYLE capStyle = D2D1_CAP_STYLE_FLAT;
+    switch (lineCap) {
+    case RoundCap:
+        capStyle = D2D1_CAP_STYLE_ROUND;
+        break;
+    case SquareCap:
+        capStyle = D2D1_CAP_STYLE_SQUARE;
+        break;
+    case ButtCap:
+    default:
+        capStyle = D2D1_CAP_STYLE_FLAT;
+        break;
+    }
+
+    platformContext.setLineCap(capStyle);
+}
+
+void setLineDash(PlatformContextDirect2D& platformContext, const DashArray& dashes, float dashOffset)
+{
+    platformContext.setDashes(dashes);
+    platformContext.setDashOffset(dashOffset);
+}
+
+void setLineJoin(PlatformContextDirect2D& platformContext, LineJoin lineJoin)
+{
+    D2D1_LINE_JOIN joinStyle = D2D1_LINE_JOIN_MITER;
+    switch (lineJoin) {
+    case RoundJoin:
+        joinStyle = D2D1_LINE_JOIN_ROUND;
+        break;
+    case BevelJoin:
+        joinStyle = D2D1_LINE_JOIN_BEVEL;
+        break;
+    case MiterJoin:
+    default:
+        joinStyle = D2D1_LINE_JOIN_MITER;
+        break;
+    }
+
+    platformContext.setLineJoin(joinStyle);
+}
+
+void setMiterLimit(PlatformContextDirect2D& platformContext, float miterLimit)
+{
+    platformContext.setMiterLimit(miterLimit);
+}
+
+void fillRect(PlatformContextDirect2D& platformContext, const FloatRect& rect, const FillSource& fillSource, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+    PlatformContextStateSaver stateSaver(platformContext);
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, rect, &fillSource](ID2D1RenderTarget* renderTarget) {
+        const D2D1_RECT_F d2dRect = rect;
+        renderTarget->FillRectangle(&d2dRect, fillSource.brush.get());
+    };
+
+    if (shadowState.isVisible())
+        drawWithShadow(platformContext, rect, shadowState, drawFunction);
+    else
+        drawWithoutShadow(platformContext, rect, drawFunction);
+}
+
+void fillRect(PlatformContextDirect2D& platformContext, const FloatRect& rect, const Color& color, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+    PlatformContextStateSaver stateSaver(platformContext);
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, color, rect](ID2D1RenderTarget* renderTarget) {
+        const D2D1_RECT_F d2dRect = rect;
+        renderTarget->FillRectangle(&d2dRect, platformContext.brushWithColor(color).get());
+    };
+
+    if (shadowState.isVisible())
+        drawWithShadow(platformContext, rect, shadowState, drawFunction);
+    else
+        drawWithoutShadow(platformContext, rect, drawFunction);
+}
+
+void fillRoundedRect(PlatformContextDirect2D& platformContext, const FloatRoundedRect& rect, const Color& color, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+    context->SetTags(1, __LINE__);
+
+    const FloatRect& r = rect.rect();
+    const FloatRoundedRect::Radii& radii = rect.radii();
+    bool equalWidths = (radii.topLeft().width() == radii.topRight().width() && radii.topRight().width() == radii.bottomLeft().width() && radii.bottomLeft().width() == radii.bottomRight().width());
+    bool equalHeights = (radii.topLeft().height() == radii.bottomLeft().height() && radii.bottomLeft().height() == radii.topRight().height() && radii.topRight().height() == radii.bottomRight().height());
+    bool hasCustomFill = false; // FIXME: Why isn't a FillSource passed to this function?
+    if (!hasCustomFill && equalWidths && equalHeights && radii.topLeft().width() * 2 == r.width() && radii.topLeft().height() * 2 == r.height()) {
+        PlatformContextStateSaver stateSaver(platformContext);
+        Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, color, rect, radii, r](ID2D1RenderTarget* renderTarget) {
+            auto roundedRect = D2D1::RoundedRect(r, radii.topLeft().width(), radii.topLeft().height());
+            renderTarget->FillRoundedRectangle(&roundedRect, platformContext.brushWithColor(color).get());
+        };
+
+        if (shadowState.isVisible())
+            drawWithShadow(platformContext, r, shadowState, drawFunction);
+        else
+            drawWithoutShadow(platformContext, r, drawFunction);
+    } else {
+        PlatformContextStateSaver stateSaver(platformContext);
+        Path path;
+        path.addRoundedRect(rect);
+        fillPath(platformContext, path, color, shadowState);
+    }
+}
+
+void fillRectWithRoundedHole(PlatformContextDirect2D& platformContext, const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const FillSource& fillSource, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    Path path;
+    path.addRect(rect);
+
+    if (!roundedHoleRect.radii().isZero())
+        path.addRoundedRect(roundedHoleRect);
+    else
+        path.addRect(roundedHoleRect.rect());
+
+    FillSource fillWithHoleSource = fillSource;
+    fillWithHoleSource.fillRule = WindRule::EvenOdd;
+
+    fillPath(platformContext, path, fillSource, shadowState);
+}
+
+void fillRectWithGradient(PlatformContextDirect2D& platformContext, const FloatRect& rect, ID2D1Brush* gradient)
+{
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+    PlatformContextStateSaver stateSaver(platformContext);
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, rect, &gradient](ID2D1RenderTarget* renderTarget) {
+        const D2D1_RECT_F d2dRect = rect;
+        renderTarget->FillRectangle(&d2dRect, gradient);
+    };
+}
+
+void fillPath(PlatformContextDirect2D& platformContext, const Path& path, const FillSource& fillSource, const ShadowState& shadowState)
+{
+    if (path.activePath()) {
+        // Make sure it's closed. This might fail if the path was already closed, so
+        // ignore the return value.
+        path.activePath()->Close();
+    }
+
+    PlatformContextStateSaver stateSaver(platformContext);
+
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    COMPtr<ID2D1GeometryGroup> pathToFill;
+    path.createGeometryWithFillMode(fillSource.fillRule, pathToFill);
+
+    context->SetTags(1, __LINE__);
+
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, &pathToFill, &fillSource](ID2D1RenderTarget* renderTarget) {
+        renderTarget->FillGeometry(pathToFill.get(), fillSource.brush.get());
+    };
+
+    if (shadowState.isVisible()) {
+        FloatRect boundingRect = path.fastBoundingRect();
+        drawWithShadow(platformContext, boundingRect, shadowState, drawFunction);
+    } else {
+        FloatRect contextRect(FloatPoint(), context->GetSize());
+        drawWithoutShadow(platformContext, contextRect, drawFunction);
+    }
+
+    flush(platformContext);
+}
+
+void fillPath(PlatformContextDirect2D& platformContext, const Path& path, const Color& color, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    COMPtr<ID2D1GeometryGroup> pathToFill;
+    path.createGeometryWithFillMode(WindRule::EvenOdd, pathToFill);
+
+    context->SetTags(1, __LINE__);
+
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, &pathToFill, color](ID2D1RenderTarget* renderTarget) {
+        renderTarget->FillGeometry(pathToFill.get(), platformContext.brushWithColor(color).get());
+    };
+
+    if (shadowState.isVisible()) {
+        FloatRect boundingRect = path.fastBoundingRect();
+        drawWithShadow(platformContext, boundingRect, shadowState, drawFunction);
+    } else {
+        FloatRect contextRect(FloatPoint(), context->GetSize());
+        drawWithoutShadow(platformContext, contextRect, drawFunction);
+    }
+
+    flush(platformContext);
+}
+
+void strokeRect(PlatformContextDirect2D& platformContext, const FloatRect& rect, float lineWidth, const StrokeSource& strokeSource, const ShadowState& shadowState)
+{
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, &strokeSource, rect, lineWidth](ID2D1RenderTarget* renderTarget) {
+        renderTarget->SetTags(1, __LINE__);
+        const D2D1_RECT_F d2dRect = rect;
+        renderTarget->DrawRectangle(&d2dRect, strokeSource.brush.get(), lineWidth, platformContext.strokeStyle());
+    };
+
+    if (shadowState.isVisible())
+        drawWithShadow(platformContext, rect, shadowState, drawFunction);
+    else
+        drawWithoutShadow(platformContext, rect, drawFunction);
+}
+
+void strokePath(PlatformContextDirect2D& platformContext, const Path& path, const StrokeSource& strokeSource, const ShadowState& shadowState)
+{
+    auto context = platformContext.renderTarget();
+    
+    context->SetTags(1, __LINE__);
+
+    PlatformContextStateSaver stateSaver(platformContext);
+    auto boundingRect = path.fastBoundingRect();
+    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, &path, &strokeSource](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawGeometry(path.platformPath(), strokeSource.brush.get(), strokeSource.thickness, platformContext.strokeStyle());
+    };
+
+    if (shadowState.isVisible())
+        drawWithShadow(platformContext, boundingRect, shadowState, drawFunction);
+    else
+        drawWithoutShadow(platformContext, boundingRect, drawFunction);
+
+    flush(platformContext);
+}
+
+void drawPath(PlatformContextDirect2D& platformContext, const Path& path, const StrokeSource& strokeSource, const ShadowState&)
+{
+    auto context = platformContext.renderTarget();
+
+    if (path.activePath())
+        path.activePath()->Close();
+
+    context->SetTags(1, __LINE__);
+
+    auto rect = path.fastBoundingRect();
+    drawWithoutShadow(platformContext, rect, [&platformContext, &strokeSource, &path](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawGeometry(path.platformPath(), strokeSource.brush.get(), strokeSource.thickness, platformContext.strokeStyle());
+    });
+
+    flush(platformContext);
+}
+
+void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1Bitmap* bitmap, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur)
+{
+    COMPtr<ID2D1DeviceContext> deviceContext;
+    HRESULT hr = context->QueryInterface(&deviceContext);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    // Create the shadow effect
+    COMPtr<ID2D1Effect> shadowEffect;
+    hr = deviceContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    shadowEffect->SetInput(0, bitmap);
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, static_cast<D2D1_VECTOR_4F>(shadowColor));
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, shadowBlur);
+
+    COMPtr<ID2D1Effect> transformEffect;
+    hr = deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &transformEffect);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    transformEffect->SetInputEffect(0, shadowEffect.get());
+
+    auto translation = D2D1::Matrix3x2F::Translation(shadowOffset.width(), shadowOffset.height());
+    transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, translation);
+
+    COMPtr<ID2D1Effect> compositor;
+    hr = deviceContext->CreateEffect(CLSID_D2D1Composite, &compositor);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    compositor->SetInputEffect(0, transformEffect.get());
+    compositor->SetInput(1, bitmap);
+
+    // Flip the context
+    D2D1_MATRIX_3X2_F ctm;
+    deviceContext->GetTransform(&ctm);
+    auto translate = D2D1::Matrix3x2F::Translation(0.0f, deviceContext->GetSize().height);
+    auto flip = D2D1::Matrix3x2F::Scale(D2D1::SizeF(1.0f, -1.0f));
+    deviceContext->SetTransform(ctm * flip * translate);
+
+    deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
+}
+
+void drawWithShadow(PlatformContextDirect2D& platformContext, const FloatRect& boundingRect, const ShadowState& shadowState, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
+{
+    auto context = platformContext.renderTarget();
+
+    // Render the current geometry to a bitmap context
+    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget;
+    HRESULT hr = context->CreateCompatibleRenderTarget(&bitmapTarget);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    bitmapTarget->BeginDraw();
+    drawCommands(bitmapTarget.get());
+    hr = bitmapTarget->EndDraw();
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    COMPtr<ID2D1Bitmap> bitmap;
+    hr = bitmapTarget->GetBitmap(&bitmap);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    drawWithShadowHelper(context, bitmap.get(), shadowState.color, shadowState.offset, shadowState.blur);
+}
+
+void drawWithoutShadow(PlatformContextDirect2D& platformContext, const FloatRect& /*boundingRect*/, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
+{
+    drawCommands(platformContext.renderTarget());
+}
+
+void clearRect(PlatformContextDirect2D& platformContext, const FloatRect& rect)
+{
+    drawWithoutShadow(platformContext, rect, [&platformContext, rect](ID2D1RenderTarget* renderTarget) {
+        FloatRect renderTargetRect(FloatPoint(), renderTarget->GetSize());
+        FloatRect rectToClear(rect);
+
+        if (rectToClear.contains(renderTargetRect)) {
+            renderTarget->SetTags(1, __LINE__);
+            renderTarget->Clear(D2D1::ColorF(0, 0, 0, 0));
+            return;
+        }
+
+        if (!rectToClear.intersects(renderTargetRect))
+            return;
+
+        renderTarget->SetTags(1, __LINE__);
+        rectToClear.intersect(renderTargetRect);
+        renderTarget->FillRectangle(rectToClear, platformContext.brushWithColor(Color(D2D1::ColorF(0, 0, 0, 0))).get());
+    });
+}
+
+/*
+void drawGlyphs(PlatformContextDirect2D& platformContext, const FillSource& fillSource, const StrokeSource& strokeSource, const ShadowState& shadowState, const FloatPoint& point, DirectWriteScaledFont* scaledFont, double syntheticBoldOffset, const Vector<DirectWriteGlyphType>& glyphs, float xOffset, TextDrawingModeFlags textDrawingMode, float strokeThickness, const FloatSize& shadowOffset, const Color& shadowColor)
+{
+    notImplemented();
+}
+*/
+
+void drawNativeImage(PlatformContextDirect2D& platformContext, IWICBitmap* image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation, InterpolationQuality imageInterpolationQuality, float globalAlpha, const ShadowState& shadowState)
+{
+    COMPtr<ID2D1Bitmap> deviceBitmap;
+    HRESULT hr = platformContext.renderTarget()->CreateBitmapFromWicBitmap(image, &deviceBitmap);
+    if (!SUCCEEDED(hr))
+        return;
+
+    drawNativeImage(platformContext, deviceBitmap.get(), imageSize, destRect, srcRect, compositeOperator, blendMode, orientation, imageInterpolationQuality, globalAlpha, shadowState);
+}
+
+void drawNativeImage(PlatformContextDirect2D& platformContext, ID2D1Bitmap* image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation, InterpolationQuality imageInterpolationQuality, float globalAlpha, const ShadowState& shadowState)
+{
+    auto bitmapSize = image->GetSize();
+
+    float currHeight = orientation.usesWidthAsHeight() ? bitmapSize.width : bitmapSize.height;
+    if (currHeight <= srcRect.y())
+        return;
+
+    auto context = platformContext.renderTarget();
+
+    D2D1_MATRIX_3X2_F ctm;
+    context->GetTransform(&ctm);
+
+    AffineTransform transform(ctm);
+
+    PlatformContextStateSaver stateSaver(platformContext);
+
+    bool shouldUseSubimage = false;
+
+    // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
+    // and then set a clip to the portion that we want to display.
+    FloatRect adjustedDestRect = destRect;
+
+    if (srcRect.size() != imageSize) {
+        // FIXME: Implement image scaling
+        notImplemented();
+    }
+
+    // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
+    if (!shouldUseSubimage && currHeight < imageSize.height())
+        adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / imageSize.height());
+
+    State::setCompositeOperation(platformContext, compositeOperator, blendMode);
+
+    // ImageOrientation expects the origin to be at (0, 0).
+    transform.translate(adjustedDestRect.x(), adjustedDestRect.y());
+    context->SetTransform(transform);
+    adjustedDestRect.setLocation(FloatPoint());
+
+    if (orientation != DefaultImageOrientation) {
+        concatCTM(platformContext, orientation.transformFromDefault(adjustedDestRect.size()));
+        if (orientation.usesWidthAsHeight()) {
+            // The destination rect will have it's width and height already reversed for the orientation of
+            // the image, as it was needed for page layout, so we need to reverse it back here.
+            adjustedDestRect = FloatRect(adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.height(), adjustedDestRect.width());
+        }
+    }
+
+    context->SetTags(1, __LINE__);
+
+    Function<void(ID2D1RenderTarget*)> drawFunction = [image, adjustedDestRect, srcRect](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawBitmap(image, adjustedDestRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, static_cast<D2D1_RECT_F>(srcRect));
+    };
+
+    if (shadowState.isVisible())
+        drawWithShadow(platformContext, adjustedDestRect, shadowState, drawFunction);
+    else
+        drawWithoutShadow(platformContext, adjustedDestRect, drawFunction);
+
+    flush(platformContext);
+
+    if (!stateSaver.didSave())
+        context->SetTransform(ctm);
+}
+
+void drawPattern(PlatformContextDirect2D& platformContext, IWICBitmap* tileImage, const IntSize& size, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator compositeOperator, BlendMode blendMode)
+{
+    auto context = platformContext.renderTarget();
+    PlatformContextStateSaver stateSaver(platformContext);
+
+    clip(platformContext, destRect);
+
+    State::setCompositeOperation(platformContext, compositeOperator, blendMode);
+
+    auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
+    bitmapBrushProperties.extendModeX = D2D1_EXTEND_MODE_WRAP;
+    bitmapBrushProperties.extendModeY = D2D1_EXTEND_MODE_WRAP;
+
+    // Create a brush transformation so we paint using the section of the image we care about.
+    AffineTransform transformation = patternTransform;
+    transformation.translate(destRect.location());
+
+    auto brushProperties = D2D1::BrushProperties();
+    brushProperties.transform = transformation;
+    brushProperties.opacity = 1.0f;
+
+    // If we only want a subset of the bitmap, we need to create a cropped bitmap image. According to the documentation,
+    // this does not allocate new bitmap memory.
+    if (size.width() > destRect.width() || size.height() > destRect.height()) {
+        ASSERT(0);
+        /*
+        float dpiX = 0;
+        float dpiY = 0;
+        tileImage->GetDpi(&dpiX, &dpiY);
+        auto bitmapProperties = D2D1::BitmapProperties(tileImage->GetPixelFormat(), dpiX, dpiY);
+        COMPtr<ID2D1Bitmap> subImage;
+        HRESULT hr = context->CreateBitmap(IntSize(tileRect.size()), bitmapProperties, &subImage);
+        if (SUCCEEDED(hr)) {
+            D2D1_RECT_U finishRect = IntRect(tileRect);
+            hr = subImage->CopyFromBitmap(nullptr, tileImage.get(), &finishRect);
+            if (SUCCEEDED(hr))
+                tileImage = subImage;
+        }
+        */
+    }
+
+    COMPtr<ID2D1Bitmap> bitmap;
+    HRESULT hr = context->CreateBitmapFromWicBitmap(tileImage, nullptr, &bitmap);
+    if (!SUCCEEDED(hr))
+        return;
+
+    COMPtr<ID2D1BitmapBrush> patternBrush;
+    hr = context->CreateBitmapBrush(bitmap.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
+    ASSERT(SUCCEEDED(hr));
+    if (!SUCCEEDED(hr))
+        return;
+
+    drawWithoutShadow(platformContext, destRect, [destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
+        const D2D1_RECT_F d2dRect = destRect;
+        renderTarget->FillRectangle(&d2dRect, patternBrush.get());
+    });
+}
+
+void drawRect(PlatformContextDirect2D& platformContext, const FloatRect& rect, float borderThickness, const Color& fillColor, StrokeStyle strokeStyle, const Color& strokeColor)
+{
+    // FIXME: this function does not handle patterns and gradients like drawPath does, it probably should.
+    ASSERT(!rect.isEmpty());
+
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    drawWithoutShadow(platformContext, rect, [&platformContext, rect, fillColor, strokeColor, strokeStyle](ID2D1RenderTarget* renderTarget) {
+        const D2D1_RECT_F d2dRect = rect;
+        auto fillBrush = platformContext.brushWithColor(fillColor);
+        renderTarget->FillRectangle(&d2dRect, fillBrush.get());
+        if (strokeStyle != NoStroke) {
+            auto strokeBrush = platformContext.brushWithColor(strokeColor);
+            renderTarget->DrawRectangle(&d2dRect, strokeBrush.get(), platformContext.strokeThickness(), platformContext.strokeStyle());
+        }
+    });
+}
+
+void drawLine(PlatformContextDirect2D& platformContext, const FloatPoint& point1, const FloatPoint& point2, StrokeStyle strokeStyle, const Color& strokeColor, float strokeThickness, bool shouldAntialias)
+{
+    bool isVerticalLine = (point1.x() + strokeThickness == point2.x());
+    float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
+    if (!strokeThickness || !strokeWidth)
+        return;
+
+    auto context = platformContext.renderTarget();
+
+    float cornerWidth = 0;
+    bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke;
+
+    COMPtr<ID2D1StrokeStyle> d2dStrokeStyle;
+    PlatformContextStateSaver stateSaver(platformContext, drawsDashedLine);
+    if (drawsDashedLine) {
+        // Figure out end points to ensure we always paint corners.
+        cornerWidth = dashedLineCornerWidthForStrokeWidth(strokeWidth, strokeStyle, strokeThickness);
+        strokeWidth -= 2 * cornerWidth;
+        float patternWidth = dashedLinePatternWidthForStrokeWidth(strokeWidth, strokeStyle, strokeThickness);
+        // Check if corner drawing sufficiently covers the line.
+        if (strokeWidth <= patternWidth + 1)
+            return;
+
+        float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth);
+        const float dashes[2] = { patternWidth, patternWidth };
+        auto strokeStyleProperties = platformContext.strokeStyleProperties();
+        GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes, ARRAYSIZE(dashes), &d2dStrokeStyle);
+
+        platformContext.setPatternWidth(patternWidth);
+        platformContext.setPatternOffset(patternOffset);
+        platformContext.setDashes(DashArray(2, patternWidth));
+
+        d2dStrokeStyle = platformContext.strokeStyle();
+    }
+
+    auto centeredPoints = centerLineAndCutOffCorners(isVerticalLine, cornerWidth, point1, point2);
+    auto p1 = centeredPoints[0];
+    auto p2 = centeredPoints[1];
+
+    context->SetTags(1, __LINE__);
+
+    FloatRect boundingRect(p1, p2);
+
+    drawWithoutShadow(platformContext, boundingRect, [&platformContext, p1, p2, d2dStrokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawLine(p1, p2, platformContext.brushWithColor(strokeColor).get(), strokeThickness, d2dStrokeStyle.get());
+    });
+}
+
+void drawLinesForText(PlatformContextDirect2D&, const FloatPoint&, float, const DashArray&, bool, bool, const Color&)
+{
+    notImplemented();
+}
+
+void drawDotsForDocumentMarker(PlatformContextDirect2D&, const FloatRect&, DocumentMarkerLineStyle)
+{
+    notImplemented();
+}
+
+void fillEllipse(PlatformContextDirect2D& platformContext, const FloatRect& rect, const Color& fillColor, StrokeStyle strokeStyle, const Color& strokeColor, float strokeThickness)
+{
+    auto ellipse = D2D1::Ellipse(rect.center(), 0.5 * rect.width(), 0.5 * rect.height());
+
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    drawWithoutShadow(platformContext, rect, [&platformContext, ellipse, fillColor, strokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+        auto fillBrush = platformContext.brushWithColor(fillColor);
+        renderTarget->FillEllipse(&ellipse, fillBrush.get());
+        if (strokeStyle != StrokeStyle::NoStroke) {
+            auto strokeBrush = platformContext.brushWithColor(strokeColor);
+            renderTarget->DrawEllipse(&ellipse, strokeBrush.get(), strokeThickness, platformContext.strokeStyle());
+        }
+    });
+}
+
+void drawEllipse(PlatformContextDirect2D& platformContext, const FloatRect& rect, StrokeStyle strokeStyle, const Color& strokeColor, float strokeThickness)
+{
+    if (strokeStyle == StrokeStyle::NoStroke)
+        return;
+
+    auto ellipse = D2D1::Ellipse(rect.center(), 0.5 * rect.width(), 0.5 * rect.height());
+
+    auto context = platformContext.renderTarget();
+
+    context->SetTags(1, __LINE__);
+
+    drawWithoutShadow(platformContext, rect, [&platformContext, ellipse, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+        auto strokeBrush = platformContext.brushWithColor(strokeColor);
+        renderTarget->DrawEllipse(&ellipse, strokeBrush.get(), strokeThickness, platformContext.strokeStyle());
+    });
+}
+
+void drawFocusRing(PlatformContextDirect2D&, const Path&, float, const Color&)
+{
+    notImplemented();
+}
+
+void drawFocusRing(PlatformContextDirect2D&, const Vector<FloatRect>&, float, const Color&)
+{
+    notImplemented();
+}
+
+void flush(PlatformContextDirect2D& platformContext)
+{
+    ASSERT(platformContext.renderTarget());
+    D2D1_TAG first, second;
+    HRESULT hr = platformContext.renderTarget()->Flush(&first, &second);
+
+    RELEASE_ASSERT(SUCCEEDED(hr));
+}
+
+void save(PlatformContextDirect2D& platformContext)
+{
+    platformContext.save();
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->save();
+}
+
+void restore(PlatformContextDirect2D& platformContext)
+{
+    platformContext.restore();
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->restore();
+}
+
+void translate(PlatformContextDirect2D& platformContext, float x, float y)
+{
+    ASSERT(platformContext.renderTarget());
+
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext.renderTarget()->GetTransform(&currentTransform);
+
+    auto translation = D2D1::Matrix3x2F::Translation(x, y);
+    platformContext.renderTarget()->SetTransform(translation * currentTransform);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->translate(x, y);
+}
+
+void rotate(PlatformContextDirect2D& platformContext, float angleInRadians)
+{
+    ASSERT(platformContext.renderTarget());
+
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext.renderTarget()->GetTransform(&currentTransform);
+
+    auto rotation = D2D1::Matrix3x2F::Rotation(rad2deg(angleInRadians));
+    platformContext.renderTarget()->SetTransform(rotation * currentTransform);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->rotate(angleInRadians);
+}
+
+void scale(PlatformContextDirect2D& platformContext, const FloatSize& size)
+{
+    ASSERT(platformContext.renderTarget());
+
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext.renderTarget()->GetTransform(&currentTransform);
+
+    auto scale = D2D1::Matrix3x2F::Scale(size);
+    platformContext.renderTarget()->SetTransform(scale * currentTransform);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->scale(size);
+}
+
+void concatCTM(PlatformContextDirect2D& platformContext, const AffineTransform& affineTransform)
+{
+    ASSERT(platformContext.renderTarget());
+
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext.renderTarget()->GetTransform(&currentTransform);
+
+    D2D1_MATRIX_3X2_F transformToConcat = affineTransform;
+    platformContext.renderTarget()->SetTransform(transformToConcat * currentTransform);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->concatCTM(affineTransform);
+}
+
+void beginTransparencyLayer(PlatformContextDirect2D& platformContext, float opacity)
+{
+    PlatformContextDirect2D::TransparencyLayerState transparencyLayer;
+    transparencyLayer.opacity = opacity;
+
+    HRESULT hr = platformContext.renderTarget()->CreateCompatibleRenderTarget(&transparencyLayer.renderTarget);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+    platformContext.m_transparencyLayerStack.append(WTFMove(transparencyLayer));
+
+    platformContext.m_transparencyLayerStack.last().renderTarget->BeginDraw();
+    platformContext.m_transparencyLayerStack.last().renderTarget->Clear(D2D1::ColorF(0, 0, 0, 0));
+}
+
+void endTransparencyLayer(PlatformContextDirect2D& platformContext)
+{
+    auto currentLayer = platformContext.m_transparencyLayerStack.takeLast();
+    auto renderTarget = currentLayer.renderTarget;
+    if (!renderTarget)
+        return;
+
+    HRESULT hr = renderTarget->EndDraw();
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    COMPtr<ID2D1Bitmap> bitmap;
+    hr = renderTarget->GetBitmap(&bitmap);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    auto context = platformContext.renderTarget();
+
+    if (currentLayer.hasShadow)
+        drawWithShadowHelper(context, bitmap.get(), currentLayer.shadowColor, currentLayer.shadowOffset, currentLayer.shadowBlur);
+    else {
+        COMPtr<ID2D1BitmapBrush> bitmapBrush;
+        auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
+        auto brushProperties = D2D1::BrushProperties();
+        HRESULT hr = context->CreateBitmapBrush(bitmap.get(), bitmapBrushProperties, brushProperties, &bitmapBrush);
+        RELEASE_ASSERT(SUCCEEDED(hr));
+
+        auto size = bitmap->GetSize();
+        auto rectInDIP = D2D1::RectF(0, 0, size.width, size.height);
+        context->FillRectangle(rectInDIP, bitmapBrush.get());
+    }
+}
+
+void clip(PlatformContextDirect2D& platformContext, const FloatRect& rect)
+{
+    if (platformContext.m_renderStates.isEmpty())
+        save(platformContext);
+
+    platformContext.renderTarget()->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+    platformContext.m_renderStates.last().m_clips.append(PlatformContextDirect2D::AxisAlignedClip);
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->clip(rect);
+}
+
+void clipOut(PlatformContextDirect2D& platformContext, const FloatRect& rect)
+{
+    Path path;
+    path.addRect(rect);
+
+    clipOut(platformContext, path);
+}
+
+void clipOut(PlatformContextDirect2D& platformContext, const Path& path)
+{
+    // To clip Out we need the intersection of the infinite
+    // clipping rect and the path we just created.
+    D2D1_SIZE_F rendererSize = platformContext.renderTarget()->GetSize();
+    FloatRect clipBounds(0, 0, rendererSize.width, rendererSize.height);
+
+    Path boundingRect;
+    boundingRect.addRect(clipBounds);
+    boundingRect.appendGeometry(path.platformPath());
+
+    COMPtr<ID2D1GeometryGroup> pathToClip;
+    boundingRect.createGeometryWithFillMode(WindRule::EvenOdd, pathToClip);
+    clipPath(platformContext, pathToClip.get());
+}
+
+void clipPath(PlatformContextDirect2D& platformContext, const Path& path, WindRule clipRule)
+{
+    if (path.isEmpty()) {
+        Direct2D::clip(platformContext, FloatRect());
+        return;
+    }
+
+    COMPtr<ID2D1GeometryGroup> pathToClip;
+    path.createGeometryWithFillMode(clipRule, pathToClip);
+
+    clipPath(platformContext, pathToClip.get());
+
+    if (auto* graphicsContextPrivate = platformContext.graphicsContextPrivate())
+        graphicsContextPrivate->clip(path);
+}
+
+void clipPath(PlatformContextDirect2D& platformContext, ID2D1Geometry* path)
+{
+    ASSERT(platformContext.hasSavedState());
+    if (!platformContext.hasSavedState())
+        return;
+
+    COMPtr<ID2D1Layer> clipLayer;
+    HRESULT hr = platformContext.renderTarget()->CreateLayer(&clipLayer);
+    ASSERT(SUCCEEDED(hr));
+    if (!SUCCEEDED(hr))
+        return;
+
+    platformContext.renderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), path), clipLayer.get());
+    platformContext.pushRenderClip(PlatformContextDirect2D::LayerClip);
+    platformContext.setActiveLayer(WTFMove(clipLayer));
+}
+
+void clipToImageBuffer(PlatformContextDirect2D&, ID2D1RenderTarget*, const FloatRect&)
+{
+    notImplemented();
+}
+
+} // namespace Direct2D
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/Direct2DOperations.h b/Source/WebCore/platform/graphics/win/Direct2DOperations.h
new file mode 100644 (file)
index 0000000..eafe88c
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2006-2019 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
+ * Copyright (C) 2010, 2011 Igalia S.L.
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "DashArray.h"
+#include "GraphicsContext.h"
+#include "GraphicsTypes.h"
+#include <d2d1.h>
+
+namespace WebCore {
+
+class AffineTransform;
+class Color;
+class FloatRect;
+class FloatRoundedRect;
+class FloatSize;
+class Path;
+class PlatformContextCairo;
+
+struct GraphicsContextState;
+
+namespace Direct2D {
+
+namespace State {
+
+void setStrokeThickness(PlatformContextDirect2D&, float);
+void setStrokeStyle(PlatformContextDirect2D&, StrokeStyle);
+
+void setCompositeOperation(PlatformContextDirect2D&, CompositeOperator, BlendMode);
+void setShouldAntialias(PlatformContextDirect2D&, bool);
+
+void setCTM(PlatformContextDirect2D&, const AffineTransform&);
+AffineTransform getCTM(PlatformContextDirect2D&);
+
+IntRect getClipBounds(PlatformContextDirect2D&);
+FloatRect roundToDevicePixels(PlatformContextDirect2D&, const FloatRect&);
+
+bool isAcceleratedContext(PlatformContextDirect2D&);
+
+} // namespace State
+
+struct FillSource {
+    FillSource() = default;
+    explicit FillSource(const GraphicsContextState&, PlatformContextDirect2D&);
+
+    float globalAlpha { 0 };
+    COMPtr<ID2D1Brush> brush;
+    Color color;
+
+    WindRule fillRule { WindRule::NonZero };
+};
+
+struct StrokeSource {
+    StrokeSource() = default;
+    explicit StrokeSource(const GraphicsContextState&, PlatformContextDirect2D&);
+
+    float globalAlpha { 0 };
+    float thickness { 0 };
+    COMPtr<ID2D1Brush> brush;
+    Color color;
+};
+
+struct ShadowState {
+    ShadowState() = default;
+    WEBCORE_EXPORT explicit ShadowState(const GraphicsContextState&);
+
+    bool isVisible() const;
+    bool isRequired(PlatformContextDirect2D&) const;
+
+    FloatSize offset;
+    float blur { 0 };
+    Color color;
+    bool ignoreTransforms { false };
+
+    float globalAlpha { 1.0 };
+    CompositeOperator globalCompositeOperator { CompositeSourceOver };
+};
+
+void setLineCap(PlatformContextDirect2D&, LineCap);
+void setLineDash(PlatformContextDirect2D&, const DashArray&, float);
+void setLineJoin(PlatformContextDirect2D&, LineJoin);
+void setMiterLimit(PlatformContextDirect2D&, float);
+
+void fillRect(PlatformContextDirect2D&, const FloatRect&, const FillSource&, const ShadowState&);
+void fillRect(PlatformContextDirect2D&, const FloatRect&, const Color&, const ShadowState&);
+void fillRectWithGradient(PlatformContextDirect2D&, const FloatRect&, ID2D1Brush*);
+void fillRoundedRect(PlatformContextDirect2D&, const FloatRoundedRect&, const Color&, const ShadowState&);
+void fillRectWithRoundedHole(PlatformContextDirect2D&, const FloatRect&, const FloatRoundedRect&, const FillSource&, const ShadowState&);
+void fillPath(PlatformContextDirect2D&, const Path&, const FillSource&, const ShadowState&);
+void fillPath(PlatformContextDirect2D&, const Path&, const Color&, const ShadowState&);
+
+void strokeRect(PlatformContextDirect2D&, const FloatRect&, float, const StrokeSource&, const ShadowState&);
+void strokePath(PlatformContextDirect2D&, const Path&, const StrokeSource&, const ShadowState&);
+void clearRect(PlatformContextDirect2D&, const FloatRect&);
+
+// FIXME(IMPLEMENT): void drawGlyphs(PlatformContextDirect2D&, const FillSource&, const StrokeSource&, const ShadowState&, const FloatPoint&, DirectWriteScaledFont*, double, const Vector<DirectWriteGlyphType>&, float, TextDrawingModeFlags, float, const FloatSize&, const Color&);
+
+void drawNativeImage(PlatformContextDirect2D&, IWICBitmap*, const FloatSize& imageSize, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode, ImageOrientation, InterpolationQuality, float, const ShadowState&);
+void drawNativeImage(PlatformContextDirect2D&, ID2D1Bitmap*, const FloatSize& imageSize, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode, ImageOrientation, InterpolationQuality, float, const ShadowState&);
+void drawPath(PlatformContextDirect2D&, const Path&, const StrokeSource&, const ShadowState&);
+void drawPattern(PlatformContextDirect2D&, IWICBitmap*, const IntSize&, const FloatRect&, const FloatRect&, const AffineTransform&, const FloatPoint&, CompositeOperator, BlendMode);
+
+void drawWithoutShadow(PlatformContextDirect2D&, const FloatRect& boundingRect, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands);
+void drawWithShadow(PlatformContextDirect2D&, const FloatRect& boundingRect, const ShadowState&, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands);
+void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1Bitmap*, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur);
+
+void drawRect(PlatformContextDirect2D&, const FloatRect&, float, const Color&, StrokeStyle, const Color&);
+void drawLine(PlatformContextDirect2D&, const FloatPoint&, const FloatPoint&, StrokeStyle, const Color&, float, bool);
+void drawLinesForText(PlatformContextDirect2D&, const FloatPoint&, float thickness, const DashArray&, bool, bool, const Color&);
+void drawDotsForDocumentMarker(PlatformContextDirect2D&, const FloatRect&, DocumentMarkerLineStyle);
+void drawEllipse(PlatformContextDirect2D&, const FloatRect&, StrokeStyle, const Color&, float);
+void fillEllipse(PlatformContextDirect2D&, const FloatRect&, const Color&, StrokeStyle, const Color&, float);
+
+void drawFocusRing(PlatformContextDirect2D&, const Path&, float, const Color&);
+void drawFocusRing(PlatformContextDirect2D&, const Vector<FloatRect>&, float, const Color&);
+
+void flush(PlatformContextDirect2D&);
+void save(PlatformContextDirect2D&);
+void restore(PlatformContextDirect2D&);
+
+void translate(PlatformContextDirect2D&, float, float);
+void rotate(PlatformContextDirect2D&, float);
+void scale(PlatformContextDirect2D&, const FloatSize&);
+void concatCTM(PlatformContextDirect2D&, const AffineTransform&);
+
+void beginTransparencyLayer(PlatformContextDirect2D&, float);
+void endTransparencyLayer(PlatformContextDirect2D&);
+
+void clip(PlatformContextDirect2D&, const FloatRect&);
+
+void clipOut(PlatformContextDirect2D&, const FloatRect&);
+void clipOut(PlatformContextDirect2D&, const Path&);
+void clipPath(PlatformContextDirect2D&, const Path&, WindRule);
+void clipPath(PlatformContextDirect2D&, ID2D1Geometry* path);
+
+void clipToImageBuffer(PlatformContextDirect2D&, ID2D1RenderTarget*, const FloatRect&);
+
+} // namespace Direct2D
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp b/Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp
new file mode 100644 (file)
index 0000000..b5aa760
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ * Copyright (C) 2011 ProFUSION embedded systems
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Direct2DUtilities.h"
+
+#if USE(DIRECT2D)
+
+#include "COMPtr.h"
+#include "FloatPoint.h"
+#include "GraphicsContext.h"
+#include "ImageDecoderDirect2D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include <d2d1.h>
+#include <wincodec.h>
+
+
+namespace WebCore {
+
+namespace Direct2D {
+
+IntSize bitmapSize(IWICBitmapSource* bitmapSource)
+{
+    UINT width, height;
+    HRESULT hr = bitmapSource->GetSize(&width, &height);
+    if (!SUCCEEDED(hr))
+        return { };
+
+    return IntSize(width, height);
+}
+
+FloatPoint bitmapResolution(IWICBitmapSource* bitmapSource)
+{
+    constexpr double dpiBase = 96.0;
+
+    double dpiX, dpiY;
+    HRESULT hr = bitmapSource->GetResolution(&dpiX, &dpiY);
+    if (!SUCCEEDED(hr))
+        return { };
+
+    FloatPoint result(dpiX, dpiY);
+    result.scale(1 / dpiBase);
+    return result;
+}
+
+unsigned bitsPerPixel(GUID bitmapFormat)
+{
+    COMPtr<IWICComponentInfo> componentInfo;
+    HRESULT hr = ImageDecoderDirect2D::systemImagingFactory()->CreateComponentInfo(bitmapFormat, &componentInfo);
+    if (!SUCCEEDED(hr))
+        return 4;
+
+    COMPtr<IWICPixelFormatInfo> pixelFormat;
+    pixelFormat.query(componentInfo);
+    if (!pixelFormat)
+        return 4;
+
+    UINT bpp = 0;
+    hr = pixelFormat->GetBitsPerPixel(&bpp);
+    if (!SUCCEEDED(hr))
+        return 4;
+
+    return bpp;
+}
+
+COMPtr<IWICBitmap> createDirect2DImageSurfaceWithData(void* data, const IntSize& size, unsigned stride)
+{
+    Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(size.height()) * stride;
+    if (numBytes.hasOverflowed())
+        return nullptr;
+
+    COMPtr<IWICBitmap> surface;
+    HRESULT hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapFromMemory(size.width(), size.height(), GUID_WICPixelFormat32bppPBGRA, stride, static_cast<UINT>(numBytes.unsafeGet()), reinterpret_cast<BYTE*>(data), &surface);
+    if (!SUCCEEDED(hr))
+        return nullptr;
+
+    return surface;
+}
+
+COMPtr<ID2D1RenderTarget> createRenderTargetFromWICBitmap(IWICBitmap* bitmapSource)
+{
+    auto targetProperties = 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);
+
+    COMPtr<ID2D1RenderTarget> bitmapContext;
+    HRESULT hr = GraphicsContext::systemFactory()->CreateWicBitmapRenderTarget(bitmapSource, &targetProperties, &bitmapContext);
+    if (!bitmapContext || !SUCCEEDED(hr))
+        return nullptr;
+
+    return bitmapContext;
+}
+
+void copyRectFromOneSurfaceToAnother(IWICBitmap* from, IWICBitmap* to, const IntSize& sourceOffset, const IntRect& rect, const IntSize& destOffset)
+{
+    /*
+    RefPtr<cairo_t> context = adoptRef(cairo_create(to));
+    cairo_translate(context.get(), destOffset.width(), destOffset.height());
+    cairo_set_operator(context.get(), cairoOperator);
+    copyRectFromCairoSurfaceToContext(from, context.get(), sourceOffset, rect);
+    */
+}
+
+} // namespace Direct2D
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/Direct2DUtilities.h b/Source/WebCore/platform/graphics/win/Direct2DUtilities.h
new file mode 100644 (file)
index 0000000..08df25b
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ * Copyright (C) 2011 ProFUSION embedded systems
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "COMPtr.h"
+#include "GraphicsTypes.h"
+#include "IntSize.h"
+
+interface ID2D1RenderTarget;
+interface IWICBitmapSource;
+interface IWICBitmap;
+
+namespace WebCore {
+
+class FloatPoint;
+class IntRect;
+class IntSize;
+
+namespace Direct2D {
+
+IntSize bitmapSize(IWICBitmapSource*);
+FloatPoint bitmapResolution(IWICBitmapSource*);
+unsigned bitsPerPixel(GUID);
+COMPtr<IWICBitmap> createDirect2DImageSurfaceWithData(void* data, const IntSize&, unsigned stride);
+COMPtr<ID2D1RenderTarget> createRenderTargetFromWICBitmap(IWICBitmap*);
+
+void copyRectFromOneSurfaceToAnother(IWICBitmap* from, IWICBitmap* to, const IntSize& sourceOffset, const IntRect&, const IntSize& destOffset = IntSize());
+
+} // namespace Direct2D
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
index b5d2c41..877d6e3 100644 (file)
@@ -36,6 +36,7 @@
 #include "GraphicsContext.h"
 #include "GraphicsContextPlatformPrivateDirect2D.h"
 #include "IntRect.h"
+#include "PlatformContextDirect2D.h"
 #include "UniscribeController.h"
 #include "WebCoreTextRenderer.h"
 #include <d2d1.h>
@@ -47,7 +48,7 @@ namespace WebCore {
 void FontCascade::drawGlyphs(GraphicsContext& graphicsContext, const Font& font, const GlyphBuffer& glyphBuffer,
     unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode smoothingMode)
 {
-    auto context = graphicsContext.platformContext();
+    auto context = graphicsContext.platformContext()->renderTarget();
     bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing();
 
     switch (smoothingMode) {
index 16d2e79..db1c680 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "FloatPoint.h"
 #include "GraphicsContext.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <wtf/RetainPtr.h>
 
@@ -110,7 +111,7 @@ void Gradient::generateGradient(ID2D1RenderTarget* renderTarget)
 
 void Gradient::fill(GraphicsContext& context, const FloatRect& rect)
 {
-    auto d2dContext = context.platformContext();
+    auto d2dContext = context.platformContext()->renderTarget();
 
     WTF::switchOn(m_data,
         [&] (const LinearData& data) {
index 6f565ba..90569f7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,6 +27,7 @@
 #include "GraphicsContext.h"
 
 #include "COMPtr.h"
+#include "Direct2DOperations.h"
 #include "DisplayListRecorder.h"
 #include "FloatRoundedRect.h"
 #include "GraphicsContextPlatformPrivateDirect2D.h"
@@ -34,6 +35,7 @@
 #include "ImageDecoderDirect2D.h"
 #include "Logging.h"
 #include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <d2d1effects.h>
 #include <dwrite.h>
@@ -63,10 +65,11 @@ GraphicsContext::GraphicsContext(HDC hdc, ID2D1DCRenderTarget** renderTarget, RE
 
     (*renderTarget)->BindDC(hdc, &rect);
 
-    m_data = new GraphicsContextPlatformPrivate(*renderTarget, BitmapRenderingContextType::GPUMemory);
+    auto ownedPlatformContext = std::make_unique<PlatformContextDirect2D>(*renderTarget);
+    m_data = new GraphicsContextPlatformPrivate(WTFMove(ownedPlatformContext), BitmapRenderingContextType::GPUMemory);
 }
 
-GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, BitmapRenderingContextType rendererType)
+GraphicsContext::GraphicsContext(PlatformContextDirect2D* platformGraphicsContext, BitmapRenderingContextType rendererType)
 {
     platformInit(platformGraphicsContext, rendererType);
 }
@@ -124,8 +127,10 @@ void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
     if (!SUCCEEDED(hr))
         return;
 
-    m_data = new GraphicsContextPlatformPrivate(renderTarget.get(), BitmapRenderingContextType::GPUMemory);
+    auto ownedPlatformContext = std::make_unique<PlatformContextDirect2D>(renderTarget.get());
+    m_data = new GraphicsContextPlatformPrivate(WTFMove(ownedPlatformContext), BitmapRenderingContextType::GPUMemory);
     m_data->m_hdc = hdc;
+
     // Make sure the context starts in sync with our state.
     setPlatformFillColor(fillColor());
     setPlatformStrokeColor(strokeColor());
@@ -133,17 +138,17 @@ void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
     // FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
 }
 
-void GraphicsContext::platformInit(ID2D1RenderTarget* renderTarget)
+void GraphicsContext::platformInit(PlatformContextDirect2D* platformContext)
 {
-    platformInit(renderTarget, BitmapRenderingContextType::GPUMemory);
+    platformInit(platformContext, BitmapRenderingContextType::GPUMemory);
 }
 
-void GraphicsContext::platformInit(ID2D1RenderTarget* renderTarget, BitmapRenderingContextType renderingType)
+void GraphicsContext::platformInit(PlatformContextDirect2D* platformContext, BitmapRenderingContextType renderingType)
 {
-    if (!renderTarget)
+    if (!platformContext)
         return;
 
-    m_data = new GraphicsContextPlatformPrivate(renderTarget, renderingType);
+    m_data = new GraphicsContextPlatformPrivate(*platformContext, renderingType);
 
     // Make sure the context starts in sync with our state.
     setPlatformFillColor(fillColor());
@@ -157,30 +162,35 @@ void GraphicsContext::platformDestroy()
     delete m_data;
 }
 
-ID2D1RenderTarget* GraphicsContext::platformContext() const
+PlatformContextDirect2D* GraphicsContext::platformContext() const
 {
     ASSERT(!paintingDisabled());
-    return m_data->renderTarget();
+    return m_data->platformContext();
+}
+
+void GraphicsContextPlatformPrivate::syncContext(PlatformContextDirect2D&)
+{
+    notImplemented();
 }
 
 ID2D1RenderTarget* GraphicsContextPlatformPrivate::renderTarget()
 {
-    if (!m_transparencyLayerStack.isEmpty())
-        return m_transparencyLayerStack.last().renderTarget.get();
+    if (!m_platformContext.m_transparencyLayerStack.isEmpty())
+        return m_platformContext.m_transparencyLayerStack.last().renderTarget.get();
 
-    return m_renderTarget.get();
+    return m_platformContext.renderTarget();
 }
 
 void GraphicsContextPlatformPrivate::setAlpha(float alpha)
 {
-    ASSERT(m_transparencyLayerStack.isEmpty());
+    ASSERT(m_platformContext.m_transparencyLayerStack.isEmpty());
     m_alpha = alpha;
 }
 
 float GraphicsContextPlatformPrivate::currentGlobalAlpha() const
 {
-    if (!m_transparencyLayerStack.isEmpty())
-        return m_transparencyLayerStack.last().opacity;
+    if (!m_platformContext.m_transparencyLayerStack.isEmpty())
+        return m_platformContext.m_transparencyLayerStack.last().opacity;
 
     return m_alpha;
 }
@@ -190,9 +200,8 @@ void GraphicsContext::savePlatformState()
     ASSERT(!paintingDisabled());
     ASSERT(!m_impl);
 
-    // Note: Do not use this function within this class implementation, since we want to avoid the extra
-    // save of the secondary context (in GraphicsContextPlatformPrivateDirect2D.h).
-    m_data->save();
+    ASSERT(hasPlatformContext());
+    Direct2D::save(*platformContext());
 }
 
 void GraphicsContext::restorePlatformState()
@@ -200,97 +209,31 @@ void GraphicsContext::restorePlatformState()
     ASSERT(!paintingDisabled());
     ASSERT(!m_impl);
 
-    // Note: Do not use this function within this class implementation, since we want to avoid the extra
-    // restore of the secondary context (in GraphicsContextPlatformPrivateDirect2D.h).
-    m_data->restore();
-    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
-}
-
-void GraphicsContext::drawNativeImage(const COMPtr<IWICBitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
-{
-    COMPtr<ID2D1Bitmap> deviceBitmap;
-    HRESULT hr = platformContext()->CreateBitmapFromWicBitmap(image.get(), &deviceBitmap);
-    if (!SUCCEEDED(hr))
-        return;
-
-    drawDeviceBitmap(deviceBitmap, imageSize, destRect, srcRect, op, blendMode, orientation);
+    ASSERT(hasPlatformContext());
+    Direct2D::restore(*platformContext());
 }
 
-void GraphicsContext::drawDeviceBitmap(const COMPtr<ID2D1Bitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+void GraphicsContext::drawNativeImage(const COMPtr<IWICBitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation)
 {
     if (paintingDisabled())
         return;
 
     if (m_impl) {
-        // FIXME: Implement DisplayListRecorder support for drawNativeImage.
-        // m_displayListRecorder->drawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation);
-        notImplemented();
-        return;
-    }
-
-    auto bitmapSize = image->GetSize();
-
-    float currHeight = orientation.usesWidthAsHeight() ? bitmapSize.width : bitmapSize.height;
-    if (currHeight <= srcRect.y())
+        m_impl->drawNativeImage(image, imageSize, destRect, srcRect, compositeOperator, blendMode, orientation);
         return;
-
-    auto context = platformContext();
-
-    D2D1_MATRIX_3X2_F ctm;
-    context->GetTransform(&ctm);
-
-    AffineTransform transform(ctm);
-
-    D2DContextStateSaver stateSaver(*m_data);
-
-    bool shouldUseSubimage = false;
-
-    // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
-    // and then set a clip to the portion that we want to display.
-    FloatRect adjustedDestRect = destRect;
-
-    if (srcRect.size() != imageSize) {
-        // FIXME: Implement image scaling
-        notImplemented();
-    }
-
-    // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
-    if (!shouldUseSubimage && currHeight < imageSize.height())
-        adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / imageSize.height());
-
-    setPlatformCompositeOperation(op, blendMode);
-
-    // ImageOrientation expects the origin to be at (0, 0).
-    transform.translate(adjustedDestRect.x(), adjustedDestRect.y());
-    context->SetTransform(transform);
-    adjustedDestRect.setLocation(FloatPoint());
-
-    if (orientation != DefaultImageOrientation) {
-        this->concatCTM(orientation.transformFromDefault(adjustedDestRect.size()));
-        if (orientation.usesWidthAsHeight()) {
-            // The destination rect will have it's width and height already reversed for the orientation of
-            // the image, as it was needed for page layout, so we need to reverse it back here.
-            adjustedDestRect = FloatRect(adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.height(), adjustedDestRect.width());
-        }
     }
 
-    context->SetTags(1, __LINE__);
-
-    drawWithoutShadow(adjustedDestRect, [this, image, adjustedDestRect, srcRect](ID2D1RenderTarget* renderTarget) {
-        renderTarget->DrawBitmap(image.get(), adjustedDestRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, static_cast<D2D1_RECT_F>(srcRect));
-    });
-
-    flush();
-
-    if (!stateSaver.didSave())
-        context->SetTransform(ctm);
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    Direct2D::drawNativeImage(*platformContext(), image.get(), imageSize, destRect, srcRect, compositeOperator, blendMode, orientation, state.imageInterpolationQuality, state.alpha, Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend)
 {
     bool createdBitmap = m_impl || !m_data->m_hdc || isInTransparencyLayer();
     if (!createdBitmap) {
-        m_data->restore();
+        ASSERT(hasPlatformContext());
+        Direct2D::restore(*platformContext());
         return;
     }
 
@@ -305,13 +248,13 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo
     auto bitmapProperties = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED));
 
     COMPtr<ID2D1Bitmap> bitmap;
-    HRESULT hr = platformContext()->CreateBitmap(pixelData.size(), pixelData.buffer(), pixelData.bytesPerRow(), &bitmapProperties, &bitmap);
+    HRESULT hr = platformContext()->renderTarget()->CreateBitmap(pixelData.size(), pixelData.buffer(), pixelData.bytesPerRow(), &bitmapProperties, &bitmap);
     ASSERT(SUCCEEDED(hr));
 
     D2DContextStateSaver stateSaver(*m_data);
 
     // Note: The content in the HDC is inverted compared to Direct2D, so it needs to be flipped.
-    auto context = platformContext();
+    auto context = platformContext()->renderTarget();
 
     D2D1_MATRIX_3X2_F currentTransform;
     context->GetTransform(&currentTransform);
@@ -343,119 +286,76 @@ void GraphicsContext::drawDotsForDocumentMarker(const FloatRect& rect, DocumentM
 {
 }
 
-GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(ID2D1RenderTarget* renderTarget, GraphicsContext::BitmapRenderingContextType renderingType)
-    : m_renderTarget(renderTarget)
+GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(PlatformContextDirect2D& platformContext, GraphicsContext::BitmapRenderingContextType renderingType)
+    : m_platformContext(platformContext)
     , m_rendererType(renderingType)
 {
-    if (!m_renderTarget)
+    if (!m_platformContext.renderTarget())
         return;
 
     if (m_rendererType == GraphicsContext::BitmapRenderingContextType::GPUMemory)
         beginDraw();
 }
 
-GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
+GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(std::unique_ptr<PlatformContextDirect2D>&& ownedPlatformContext, GraphicsContext::BitmapRenderingContextType renderingType)
+    : m_ownedPlatformContext(WTFMove(ownedPlatformContext))
+    , m_platformContext(*ownedPlatformContext)
+    , m_rendererType(renderingType)
 {
-    if (!m_renderTarget)
+    if (!m_platformContext.renderTarget())
         return;
 
-    if (beginDrawCount)
-        endDraw();
+    if (m_rendererType == GraphicsContext::BitmapRenderingContextType::GPUMemory)
+        beginDraw();
 }
 
-COMPtr<ID2D1SolidColorBrush> GraphicsContextPlatformPrivate::brushWithColor(const D2D1_COLOR_F& color)
+GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
 {
-    RGBA32 colorKey = makeRGBA32FromFloats(color.r, color.g, color.b, color.a);
-
-    if (!colorKey) {
-        if (!m_zeroBrush)
-            m_renderTarget->CreateSolidColorBrush(color, &m_zeroBrush);
-        return m_zeroBrush;
-    }
-
-    if (colorKey == 0xFFFFFFFF) {
-        if (!m_whiteBrush)
-            m_renderTarget->CreateSolidColorBrush(color, &m_whiteBrush);
-        return m_whiteBrush;
-    }
-
-    auto existingBrush = m_solidColoredBrushCache.ensure(colorKey, [this, color] {
-        COMPtr<ID2D1SolidColorBrush> colorBrush;
-        m_renderTarget->CreateSolidColorBrush(color, &colorBrush);
-        return colorBrush;
-    });
+    if (!m_platformContext.renderTarget())
+        return;
 
-    return existingBrush.iterator->value;
+    if (beginDrawCount)
+        endDraw();
 }
 
 ID2D1SolidColorBrush* GraphicsContext::brushWithColor(const Color& color)
 {
-    return m_data->brushWithColor(colorWithGlobalAlpha(color)).get();
+    ASSERT(hasPlatformContext());
+    return platformContext()->brushWithColor(colorWithGlobalAlpha(color)).get();
 }
 
 void GraphicsContextPlatformPrivate::clip(const FloatRect& rect)
 {
-    if (m_renderStates.isEmpty())
-        save();
-
-    m_renderTarget->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
-    m_renderStates.last().m_clips.append(GraphicsContextPlatformPrivate::AxisAlignedClip);
 }
 
 void GraphicsContextPlatformPrivate::clip(const Path& path)
 {
-    clip(path.platformPath());
 }
 
 void GraphicsContextPlatformPrivate::clip(ID2D1Geometry* path)
 {
-    ASSERT(m_renderStates.size());
-    if (!m_renderStates.size())
-        return;
-
-    COMPtr<ID2D1Layer> clipLayer;
-    HRESULT hr = m_renderTarget->CreateLayer(&clipLayer);
-    ASSERT(SUCCEEDED(hr));
-    if (!SUCCEEDED(hr))
-        return;
-
-    m_renderTarget->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), path), clipLayer.get());
-    m_renderStates.last().m_clips.append(GraphicsContextPlatformPrivate::LayerClip);
-    m_renderStates.last().m_activeLayer = clipLayer;
 }
 
 void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& affineTransform)
 {
-    ASSERT(m_renderTarget.get());
-
-    D2D1_MATRIX_3X2_F currentTransform;
-    m_renderTarget->GetTransform(&currentTransform);
-
-    D2D1_MATRIX_3X2_F transformToConcat = affineTransform;
-    m_renderTarget->SetTransform(transformToConcat * currentTransform);
 }
 
 void GraphicsContextPlatformPrivate::flush()
 {
-    ASSERT(m_renderTarget.get());
-    D2D1_TAG first, second;
-    HRESULT hr = m_renderTarget->Flush(&first, &second);
-
-    RELEASE_ASSERT(SUCCEEDED(hr));
 }
 
 void GraphicsContextPlatformPrivate::beginDraw()
 {
-    ASSERT(m_renderTarget.get());
-    m_renderTarget->BeginDraw();
+    ASSERT(m_platformContext.renderTarget());
+    m_platformContext.renderTarget()->BeginDraw();
     ++beginDrawCount;
 }
 
 void GraphicsContextPlatformPrivate::endDraw()
 {
-    ASSERT(m_renderTarget.get());
+    ASSERT(m_platformContext.renderTarget());
     D2D1_TAG first, second;
-    HRESULT hr = m_renderTarget->EndDraw(&first, &second);
+    HRESULT hr = m_platformContext.renderTarget()->EndDraw(&first, &second);
 
     if (!SUCCEEDED(hr))
         WTFLogAlways("Failed in GraphicsContextPlatformPrivate::endDraw: hr=%xd, first=%ld, second=%ld", hr, first, second);
@@ -465,68 +365,28 @@ void GraphicsContextPlatformPrivate::endDraw()
 
 void GraphicsContextPlatformPrivate::restore()
 {
-    ASSERT(m_renderTarget.get());
-
-    auto restoreState = m_renderStates.takeLast();
-    m_renderTarget->RestoreDrawingState(restoreState.m_drawingStateBlock.get());
-
-    for (auto clipType = restoreState.m_clips.rbegin(); clipType != restoreState.m_clips.rend(); ++clipType) {
-        if (*clipType == GraphicsContextPlatformPrivate::AxisAlignedClip)
-            m_renderTarget->PopAxisAlignedClip();
-        else
-            m_renderTarget->PopLayer();
-    }
 }
 
 void GraphicsContextPlatformPrivate::save()
 {
-    ASSERT(m_renderTarget.get());
-
-    RenderState currentState;
-    GraphicsContext::systemFactory()->CreateDrawingStateBlock(&currentState.m_drawingStateBlock);
-
-    m_renderTarget->SaveDrawingState(currentState.m_drawingStateBlock.get());
-
-    m_renderStates.append(currentState);
 }
 
 void GraphicsContextPlatformPrivate::scale(const FloatSize& size)
 {
-    ASSERT(m_renderTarget.get());
-
-    D2D1_MATRIX_3X2_F currentTransform;
-    m_renderTarget->GetTransform(&currentTransform);
-
-    auto scale = D2D1::Matrix3x2F::Scale(size);
-    m_renderTarget->SetTransform(scale * currentTransform);
 }
 
 void GraphicsContextPlatformPrivate::setCTM(const AffineTransform& transform)
 {
-    ASSERT(m_renderTarget.get());
-    m_renderTarget->SetTransform(transform);
+    ASSERT(m_platformContext.renderTarget());
+    m_platformContext.renderTarget()->SetTransform(transform);
 }
 
 void GraphicsContextPlatformPrivate::translate(float x, float y)
 {
-    ASSERT(m_renderTarget.get());
-
-    D2D1_MATRIX_3X2_F currentTransform;
-    m_renderTarget->GetTransform(&currentTransform);
-
-    auto translation = D2D1::Matrix3x2F::Translation(x, y);
-    m_renderTarget->SetTransform(translation * currentTransform);
 }
 
 void GraphicsContextPlatformPrivate::rotate(float angle)
 {
-    ASSERT(m_renderTarget.get());
-
-    D2D1_MATRIX_3X2_F currentTransform;
-    m_renderTarget->GetTransform(&currentTransform);
-
-    auto rotation = D2D1::Matrix3x2F::Rotation(rad2deg(angle));
-    m_renderTarget->SetTransform(rotation * currentTransform);
 }
 
 D2D1_COLOR_F GraphicsContext::colorWithGlobalAlpha(const Color& color) const
@@ -539,22 +399,22 @@ D2D1_COLOR_F GraphicsContext::colorWithGlobalAlpha(const Color& color) const
 
 ID2D1Brush* GraphicsContext::solidStrokeBrush() const
 {
-    return m_data->m_solidStrokeBrush.get();
+    return platformContext()->m_solidStrokeBrush.get();
 }
 
 ID2D1Brush* GraphicsContext::solidFillBrush() const
 {
-    return m_data->m_solidFillBrush.get();
+    return platformContext()->m_solidFillBrush.get();
 }
 
 ID2D1Brush* GraphicsContext::patternStrokeBrush() const
 {
-    return m_data->m_patternStrokeBrush.get();
+    return platformContext()->m_patternStrokeBrush.get();
 }
 
 ID2D1Brush* GraphicsContext::patternFillBrush() const
 {
-    return m_data->m_patternFillBrush.get();
+    return platformContext()->m_patternFillBrush.get();
 }
 
 void GraphicsContext::beginDraw()
@@ -569,75 +429,23 @@ void GraphicsContext::endDraw()
 
 void GraphicsContext::flush()
 {
-    m_data->flush();
+    ASSERT(hasPlatformContext());
+    Direct2D::flush(*platformContext());
 }
 
-void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
+void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOperator, BlendMode blendMode)
 {
     if (paintingDisabled() || !patternTransform.isInvertible())
         return;
 
     if (m_impl) {
-        m_impl->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode);
+        m_impl->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, compositeOperator, blendMode);
         return;
     }
 
-    auto context = platformContext();
-    D2DContextStateSaver stateSaver(*m_data);
-
-    m_data->clip(destRect);
-
-    setPlatformCompositeOperation(op, blendMode);
-
-    auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
-    bitmapBrushProperties.extendModeX = D2D1_EXTEND_MODE_WRAP;
-    bitmapBrushProperties.extendModeY = D2D1_EXTEND_MODE_WRAP;
-
-    // Create a brush transformation so we paint using the section of the image we care about.
-    AffineTransform transformation = patternTransform;
-    transformation.translate(destRect.location());
-
-    auto brushProperties = D2D1::BrushProperties();
-    brushProperties.transform = transformation;
-    brushProperties.opacity = 1.0f;
-
-    auto tileImage = image.nativeImageForCurrentFrame();
-
-    // If we only want a subset of the bitmap, we need to create a cropped bitmap image. According to the documentation,
-    // this does not allocate new bitmap memory.
-    if (image.width() > destRect.width() || image.height() > destRect.height()) {
-        ASSERT(0);
-        /*
-        float dpiX = 0;
-        float dpiY = 0;
-        tileImage->GetDpi(&dpiX, &dpiY);
-        auto bitmapProperties = D2D1::BitmapProperties(tileImage->GetPixelFormat(), dpiX, dpiY);
-        COMPtr<ID2D1Bitmap> subImage;
-        HRESULT hr = context->CreateBitmap(IntSize(tileRect.size()), bitmapProperties, &subImage);
-        if (SUCCEEDED(hr)) {
-            D2D1_RECT_U finishRect = IntRect(tileRect);
-            hr = subImage->CopyFromBitmap(nullptr, tileImage.get(), &finishRect);
-            if (SUCCEEDED(hr))
-                tileImage = subImage;
-        }
-        */
-    }
-
-    COMPtr<ID2D1Bitmap> bitmap;
-    HRESULT hr = context->CreateBitmapFromWicBitmap(tileImage.get(), nullptr, &bitmap);
-    if (!SUCCEEDED(hr))
-        return;
-
-    COMPtr<ID2D1BitmapBrush> patternBrush;
-    hr = context->CreateBitmapBrush(bitmap.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
-    ASSERT(SUCCEEDED(hr));
-    if (!SUCCEEDED(hr))
-        return;
-
-    drawWithoutShadow(destRect, [this, destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
-        const D2D1_RECT_F d2dRect = destRect;
-        renderTarget->FillRectangle(&d2dRect, patternBrush.get());
-    });
+    ASSERT(hasPlatformContext());
+    if (auto tileImage = image.nativeImageForCurrentFrame())
+        Direct2D::drawPattern(*platformContext(), tileImage.get(), IntSize(image.size()), destRect, tileRect, patternTransform, phase, compositeOperator, blendMode);
 }
 
 void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
@@ -660,172 +468,63 @@ void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
         return;
     }
 
-    // FIXME: this function does not handle patterns and gradients like drawPath does, it probably should.
     ASSERT(!rect.isEmpty());
-
-    auto context = platformContext();
-
-    context->SetTags(1, __LINE__);
-
-    drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
-        const D2D1_RECT_F d2dRect = rect;
-        renderTarget->FillRectangle(&d2dRect, solidFillBrush());
-        renderTarget->DrawRectangle(&d2dRect, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    Direct2D::drawRect(*platformContext(), rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor);
 }
 
-void GraphicsContextPlatformPrivate::setLineCap(LineCap cap)
+void GraphicsContextPlatformPrivate::setLineCap(LineCap)
 {
-    if (m_lineCap == cap)
-        return;
-
-    D2D1_CAP_STYLE capStyle = D2D1_CAP_STYLE_FLAT;
-    switch (cap) {
-    case RoundCap:
-        capStyle = D2D1_CAP_STYLE_ROUND;
-        break;
-    case SquareCap:
-        capStyle = D2D1_CAP_STYLE_SQUARE;
-        break;
-    case ButtCap:
-    default:
-        capStyle = D2D1_CAP_STYLE_FLAT;
-        break;
-    }
-
-    m_lineCap = capStyle;
-    m_strokeSyleIsDirty = true;
 }
 
-void GraphicsContextPlatformPrivate::setLineJoin(LineJoin join)
+void GraphicsContextPlatformPrivate::setLineJoin(LineJoin)
 {
-    if (m_lineJoin == join)
-        return;
-
-    D2D1_LINE_JOIN joinStyle = D2D1_LINE_JOIN_MITER;
-    switch (join) {
-    case RoundJoin:
-        joinStyle = D2D1_LINE_JOIN_ROUND;
-        break;
-    case BevelJoin:
-        joinStyle = D2D1_LINE_JOIN_BEVEL;
-        break;
-    case MiterJoin:
-    default:
-        joinStyle = D2D1_LINE_JOIN_MITER;
-        break;
-    }
-
-    m_lineJoin = joinStyle;
-    m_strokeSyleIsDirty = true;
 }
 
-void GraphicsContextPlatformPrivate::setStrokeStyle(StrokeStyle strokeStyle)
+void GraphicsContextPlatformPrivate::setStrokeStyle(StrokeStyle)
 {
-    if (m_strokeStyle == strokeStyle)
-        return;
-
-    m_strokeStyle = strokeStyle;
-    m_strokeSyleIsDirty = true;
 }
 
 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;
-
-    m_miterLimit = miterLimit;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setDashOffset(canvasMiterLimit);
 }
 
 void GraphicsContextPlatformPrivate::setDashOffset(float dashOffset)
 {
-    if (WTF::areEssentiallyEqual(dashOffset, m_dashOffset))
-        return;
-
-    m_dashOffset = dashOffset;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setDashOffset(dashOffset);
 }
 
 void GraphicsContextPlatformPrivate::setPatternWidth(float patternWidth)
 {
-    if (WTF::areEssentiallyEqual(patternWidth, m_patternWidth))
-        return;
-
-    m_patternWidth = patternWidth;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setPatternWidth(patternWidth);
 }
 
 void GraphicsContextPlatformPrivate::setPatternOffset(float patternOffset)
 {
-    if (WTF::areEssentiallyEqual(patternOffset, m_patternOffset))
-        return;
-
-    m_patternOffset = patternOffset;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setPatternOffset(patternOffset);
 }
 
 void GraphicsContextPlatformPrivate::setStrokeThickness(float thickness)
 {
-    if (WTF::areEssentiallyEqual(thickness, m_strokeThickness))
-        return;
-
-    m_strokeThickness = thickness;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setStrokeThickness(thickness);
 }
 
 void GraphicsContextPlatformPrivate::setDashes(const DashArray& dashes)
 {
-    if (m_dashes == dashes)
-        return;
-
-    m_dashes = dashes;
-    m_strokeSyleIsDirty = true;
+    m_platformContext.setDashes(dashes);
 }
 
 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)
-        return;
-
-    m_d2dStrokeStyle = nullptr;
-
-    DashArray dashes;
-    float patternOffset = 0;
-    auto dashStyle = D2D1_DASH_STYLE_SOLID;
-
-    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_miterLimit, dashStyle, patternOffset);
-    GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes.data(), dashes.size(), &m_d2dStrokeStyle);
-
-    m_strokeSyleIsDirty = false;
-}
-
-ID2D1StrokeStyle* GraphicsContextPlatformPrivate::strokeStyle()
-{
-    recomputeStrokeStyle();
-    return m_d2dStrokeStyle.get();
+    return D2D1::StrokeStyleProperties(m_platformContext.m_lineCap, m_platformContext.m_lineCap, m_platformContext.m_lineCap, m_platformContext.m_lineJoin, m_platformContext.m_miterLimit, D2D1_DASH_STYLE_SOLID, 0.0f);
 }
 
 ID2D1StrokeStyle* GraphicsContext::platformStrokeStyle() const
 {
-    return m_data->strokeStyle();
+    ASSERT(hasPlatformContext());
+    return platformContext()->strokeStyle();
 }
 
 // This is only used to draw borders.
@@ -842,52 +541,9 @@ void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point
         return;
     }
 
-    float thickness = strokeThickness();
-    bool isVerticalLine = (point1.x() + thickness == point2.x());
-    float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
-    if (!thickness || !strokeWidth)
-        return;
-
-    auto context = platformContext();
-
-    StrokeStyle strokeStyle = this->strokeStyle();
-    float cornerWidth = 0;
-    bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke;
-
-    COMPtr<ID2D1StrokeStyle> d2dStrokeStyle;
-    D2DContextStateSaver stateSaver(*m_data, drawsDashedLine);
-    if (drawsDashedLine) {
-        // Figure out end points to ensure we always paint corners.
-        cornerWidth = dashedLineCornerWidthForStrokeWidth(strokeWidth);
-        strokeWidth -= 2 * cornerWidth;
-        float patternWidth = dashedLinePatternWidthForStrokeWidth(strokeWidth);
-        // Check if corner drawing sufficiently covers the line.
-        if (strokeWidth <= patternWidth + 1)
-            return;
-
-        float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth);
-        const float dashes[2] = { patternWidth, patternWidth };
-        auto strokeStyleProperties = m_data->strokeStyleProperties();
-        GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes, ARRAYSIZE(dashes), &d2dStrokeStyle);
-
-        m_data->setPatternWidth(patternWidth);
-        m_data->setPatternOffset(patternOffset);
-        m_data->setDashes(DashArray(2, patternWidth));
-
-        d2dStrokeStyle = m_data->strokeStyle();
-    }
-
-    auto centeredPoints = centerLineAndCutOffCorners(isVerticalLine, cornerWidth, point1, point2);
-    auto p1 = centeredPoints[0];
-    auto p2 = centeredPoints[1];
-
-    context->SetTags(1, __LINE__);
-
-    FloatRect boundingRect(p1, p2);
-
-    drawWithoutShadow(boundingRect, [this, p1, p2, d2dStrokeStyle](ID2D1RenderTarget* renderTarget) {
-        renderTarget->DrawLine(p1, p2, solidStrokeBrush(), strokeThickness(), d2dStrokeStyle.get());
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    Direct2D::drawLine(*platformContext(), point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias);
 }
 
 void GraphicsContext::drawEllipse(const FloatRect& rect)
@@ -900,17 +556,10 @@ void GraphicsContext::drawEllipse(const FloatRect& rect)
         return;
     }
 
-    auto ellipse = D2D1::Ellipse(rect.center(), 0.5 * rect.width(), 0.5 * rect.height());
 
-    auto context = platformContext();
-
-    context->SetTags(1, __LINE__);
-
-    drawWithoutShadow(rect, [this, ellipse](ID2D1RenderTarget* renderTarget) {
-        renderTarget->FillEllipse(&ellipse, solidFillBrush());
-
-        renderTarget->DrawEllipse(&ellipse, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    Direct2D::fillEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness);
 }
 
 void GraphicsContext::applyStrokePattern()
@@ -922,7 +571,7 @@ void GraphicsContext::applyStrokePattern()
     AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
 
     const float patternAlpha = 1;
-    m_data->m_patternStrokeBrush = adoptCOM(m_state.strokePattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
+    platformContext()->m_patternStrokeBrush = adoptCOM(m_state.strokePattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
 }
 
 void GraphicsContext::applyFillPattern()
@@ -934,7 +583,7 @@ void GraphicsContext::applyFillPattern()
     AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
 
     const float patternAlpha = 1;
-    m_data->m_patternFillBrush = adoptCOM(m_state.fillPattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
+    platformContext()->m_patternFillBrush = adoptCOM(m_state.fillPattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
 }
 
 void GraphicsContext::drawPath(const Path& path)
@@ -947,102 +596,10 @@ void GraphicsContext::drawPath(const Path& path)
         return;
     }
 
-    auto context = platformContext();
-    const GraphicsContextState& state = m_state;
-
-    if (state.fillGradient || state.strokeGradient) {
-        // We don't have any optimized way to fill & stroke a path using gradients
-        // FIXME: Be smarter about this.
-        fillPath(path);
-        strokePath(path);
-        return;
-    }
-
-    if (state.fillPattern)
-        applyFillPattern();
-
-    if (state.strokePattern)
-        applyStrokePattern();
-
-    if (path.activePath())
-        path.activePath()->Close();
-
-    context->SetTags(1, __LINE__);
-
-    auto rect = path.fastBoundingRect();
-    drawWithoutShadow(rect, [this, &path](ID2D1RenderTarget* renderTarget) {
-        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)
-{
-    drawCommands(platformContext());
-}
-
-static void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1Bitmap* bitmap, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur)
-{
-    COMPtr<ID2D1DeviceContext> deviceContext;
-    HRESULT hr = context->QueryInterface(&deviceContext);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    // Create the shadow effect
-    COMPtr<ID2D1Effect> shadowEffect;
-    hr = deviceContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    shadowEffect->SetInput(0, bitmap);
-    shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, static_cast<D2D1_VECTOR_4F>(shadowColor));
-    shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, shadowBlur);
-
-    COMPtr<ID2D1Effect> transformEffect;
-    hr = deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &transformEffect);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    transformEffect->SetInputEffect(0, shadowEffect.get());
-
-    auto translation = D2D1::Matrix3x2F::Translation(shadowOffset.width(), shadowOffset.height());
-    transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, translation);
-
-    COMPtr<ID2D1Effect> compositor;
-    hr = deviceContext->CreateEffect(CLSID_D2D1Composite, &compositor);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    compositor->SetInputEffect(0, transformEffect.get());
-    compositor->SetInput(1, bitmap);
-
-    // Flip the context
-    D2D1_MATRIX_3X2_F ctm;
-    deviceContext->GetTransform(&ctm);
-    auto translate = D2D1::Matrix3x2F::Translation(0.0f, deviceContext->GetSize().height);
-    auto flip = D2D1::Matrix3x2F::Scale(D2D1::SizeF(1.0f, -1.0f));
-    deviceContext->SetTransform(ctm * flip * translate);
-
-    deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
-}
-
-void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
-{
-    auto context = platformContext();
-
-    // Render the current geometry to a bitmap context
-    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget;
-    HRESULT hr = context->CreateCompatibleRenderTarget(&bitmapTarget);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    bitmapTarget->BeginDraw();
-    drawCommands(bitmapTarget.get());
-    hr = bitmapTarget->EndDraw();
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    COMPtr<ID2D1Bitmap> bitmap;
-    hr = bitmapTarget->GetBitmap(&bitmap);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    drawWithShadowHelper(context, bitmap.get(), m_state.shadowColor, m_state.shadowOffset, m_state.shadowBlur);
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::drawPath(context, path, Direct2D::StrokeSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::fillPath(const Path& path)
@@ -1055,50 +612,10 @@ void GraphicsContext::fillPath(const Path& path)
         return;
     }
 
-    if (path.activePath()) {
-        // Make sure it's closed. This might fail if the path was already closed, so
-        // ignore the return value.
-        path.activePath()->Close();
-    }
-
-    D2DContextStateSaver stateSaver(*m_data);
-
-    auto context = platformContext();
-
-    context->SetTags(1, __LINE__);
-
-    if (m_state.fillGradient) {
-        context->SetTags(1, __LINE__);
-
-        FloatRect boundingRect = path.fastBoundingRect();
-        WTF::Function<void(ID2D1RenderTarget*)> drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
-            renderTarget->FillGeometry(path.platformPath(), m_state.fillGradient->createPlatformGradientIfNecessary(renderTarget));
-        };
-
-        if (hasShadow())
-            drawWithShadow(boundingRect, drawFunction);
-        else
-            drawWithoutShadow(boundingRect, drawFunction);
-
-        flush();
-        return;
-    }
-
-    if (m_state.fillPattern)
-        applyFillPattern();
-
-    COMPtr<ID2D1GeometryGroup> pathToFill;
-    path.createGeometryWithFillMode(fillRule(), pathToFill);
-
-    context->SetTags(1, __LINE__);
-
-    FloatRect contextRect(FloatPoint(), context->GetSize());
-    drawWithoutShadow(contextRect, [this, &pathToFill](ID2D1RenderTarget* renderTarget) {
-        auto brush = m_state.fillPattern ? patternFillBrush() : solidFillBrush();
-        renderTarget->FillGeometry(pathToFill.get(), brush);
-    });
-
-    flush();
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::fillPath(context, path, Direct2D::FillSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::strokePath(const Path& path)
@@ -1111,40 +628,10 @@ void GraphicsContext::strokePath(const Path& path)
         return;
     }
 
-    auto context = platformContext();
-    
-    context->SetTags(1, __LINE__);
-
-    if (m_state.strokeGradient) {
-        context->SetTags(1, __LINE__);
-
-        D2DContextStateSaver stateSaver(*m_data);
-        auto boundingRect = path.fastBoundingRect();
-        WTF::Function<void(ID2D1RenderTarget*)> drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
-            renderTarget->DrawGeometry(path.platformPath(), m_state.strokeGradient->createPlatformGradientIfNecessary(renderTarget));
-        };
-
-        if (hasShadow())
-            drawWithShadow(boundingRect, drawFunction);
-        else
-            drawWithoutShadow(boundingRect, drawFunction);
-
-        flush();
-        return;
-    }
-
-    if (m_state.strokePattern)
-        applyStrokePattern();
-
-    context->SetTags(1, __LINE__);
-
-    FloatRect contextRect(FloatPoint(), context->GetSize());
-    drawWithoutShadow(contextRect, [this, &path](ID2D1RenderTarget* renderTarget) {
-        auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
-        renderTarget->DrawGeometry(path.platformPath(), brush, strokeThickness(), m_data->strokeStyle());
-    });
-
-    flush();
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::strokePath(context, path, Direct2D::StrokeSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::fillRect(const FloatRect& rect)
@@ -1157,41 +644,10 @@ void GraphicsContext::fillRect(const FloatRect& rect)
         return;
     }
 
-    auto context = platformContext();
-
-    if (m_state.fillGradient) {
-        context->SetTags(1, __LINE__);
-        D2DContextStateSaver stateSaver(*m_data);
-        WTF::Function<void(ID2D1RenderTarget*)> drawFunction = [this, rect](ID2D1RenderTarget* renderTarget) {
-            const D2D1_RECT_F d2dRect = rect;
-            renderTarget->FillRectangle(&d2dRect, m_state.fillGradient->createPlatformGradientIfNecessary(renderTarget));
-        };
-
-        if (hasShadow())
-            drawWithShadow(rect, drawFunction);
-        else
-            drawWithoutShadow(rect, drawFunction);
-        return;
-    }
-
-    if (m_state.fillPattern)
-        applyFillPattern();
-
-    context->SetTags(1, __LINE__);
-
-    bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
-    if (drawOwnShadow) {
-        // FIXME: Get ShadowBlur working on Direct2D
-        // ShadowBlur contextShadow(m_state);
-        // contextShadow.drawRectShadow(*this, FloatRoundedRect(rect));
-        notImplemented();
-    }
-
-    drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
-        const D2D1_RECT_F d2dRect = rect;
-        auto brush = m_state.fillPattern ? patternFillBrush() : solidFillBrush();
-        renderTarget->FillRectangle(&d2dRect, brush);
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::fillRect(context, rect, Direct2D::FillSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
@@ -1204,22 +660,10 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
         return;
     }
 
-    auto context = platformContext();
-
-    bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
-    if (drawOwnShadow) {
-        // FIXME: Get ShadowBlur working on Direct2D
-        // ShadowBlur contextShadow(m_state);
-        // contextShadow.drawRectShadow(*this, FloatRoundedRect(rect));
-        notImplemented();
-    }
-
-    context->SetTags(1, __LINE__);
-
-    drawWithoutShadow(rect, [this, rect, color](ID2D1RenderTarget* renderTarget) {
-        const D2D1_RECT_F d2dRect = rect;
-        renderTarget->FillRectangle(&d2dRect, brushWithColor(color));
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::fillRect(context, rect, color, Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
@@ -1229,38 +673,9 @@ void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, cons
 
     ASSERT(!m_impl);
 
-    auto context = platformContext();
-
-    bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
-    D2DContextStateSaver stateSaver(*m_data, drawOwnShadow);
-    if (drawOwnShadow) {
-        // FIXME: Get ShadowBlur working on Direct2D
-        // ShadowBlur contextShadow(m_state);
-        // contextShadow.drawRectShadow(*this, rect);
-        notImplemented();
-    }
-
-    context->SetTags(1, __LINE__);
-
-    const FloatRect& r = rect.rect();
-    const FloatRoundedRect::Radii& radii = rect.radii();
-    bool equalWidths = (radii.topLeft().width() == radii.topRight().width() && radii.topRight().width() == radii.bottomLeft().width() && radii.bottomLeft().width() == radii.bottomRight().width());
-    bool equalHeights = (radii.topLeft().height() == radii.bottomLeft().height() && radii.bottomLeft().height() == radii.topRight().height() && radii.topRight().height() == radii.bottomRight().height());
-    bool hasCustomFill = m_state.fillGradient || m_state.fillPattern;
-    if (!hasCustomFill && equalWidths && equalHeights && radii.topLeft().width() * 2 == r.width() && radii.topLeft().height() * 2 == r.height()) {
-        auto roundedRect = D2D1::RoundedRect(r, radii.topLeft().width(), radii.topLeft().height());
-        context->FillRoundedRectangle(roundedRect, brushWithColor(color));
-    } else {
-        D2DContextStateSaver stateSaver(*m_data);
-        setFillColor(color);
-
-        Path path;
-        path.addRoundedRect(rect);
-        fillPath(path);
-    }
-
-    if (drawOwnShadow)
-        stateSaver.restore();
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    Direct2D::fillRoundedRect(*platformContext(), rect, color, Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
@@ -1273,41 +688,10 @@ void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const Float
         return;
     }
 
-    auto context = platformContext();
-
-    context->SetTags(1, __LINE__);
-
-    Path path;
-    path.addRect(rect);
-
-    if (!roundedHoleRect.radii().isZero())
-        path.addRoundedRect(roundedHoleRect);
-    else
-        path.addRect(roundedHoleRect.rect());
-
-    WindRule oldFillRule = fillRule();
-    Color oldFillColor = fillColor();
-
-    setFillRule(WindRule::EvenOdd);
-    setFillColor(color);
-
-    // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole.
-    bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms;
-    D2DContextStateSaver stateSaver(*m_data, drawOwnShadow);
-    if (drawOwnShadow) {
-        // FIXME: Get ShadowBlur working on Direct2D
-        // ShadowBlur contextShadow(m_state);
-        // contextShadow.drawRectShadow(*this, rect);
-        notImplemented();
-    }
-
-    fillPath(path);
-
-    if (drawOwnShadow)
-        stateSaver.restore();
-
-    setFillRule(oldFillRule);
-    setFillColor(oldFillColor);
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::fillRectWithRoundedHole(context, rect, roundedHoleRect, Direct2D::FillSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::clip(const FloatRect& rect)
@@ -1320,7 +704,8 @@ void GraphicsContext::clip(const FloatRect& rect)
         return;
     }
 
-    m_data->clip(rect);
+    ASSERT(hasPlatformContext());
+    Direct2D::clip(*platformContext(), rect);
 }
 
 void GraphicsContext::clipOut(const FloatRect& rect)
@@ -1333,10 +718,8 @@ void GraphicsContext::clipOut(const FloatRect& rect)
         return;
     }
 
-    Path path;
-    path.addRect(rect);
-
-    clipOut(path);
+    ASSERT(hasPlatformContext());
+    Direct2D::clipOut(*platformContext(), rect);
 }
 
 void GraphicsContext::clipOut(const Path& path)
@@ -1349,19 +732,8 @@ void GraphicsContext::clipOut(const Path& path)
         return;
     }
 
-    // To clip Out we need the intersection of the infinite
-    // clipping rect and the path we just created.
-    D2D1_SIZE_F rendererSize = platformContext()->GetSize();
-    FloatRect clipBounds(0, 0, rendererSize.width, rendererSize.height);
-
-    Path boundingRect;
-    boundingRect.addRect(clipBounds);
-    boundingRect.appendGeometry(path.platformPath());
-
-    COMPtr<ID2D1GeometryGroup> pathToClip;
-    boundingRect.createGeometryWithFillMode(WindRule::EvenOdd, pathToClip);
-
-    m_data->clip(pathToClip.get());
+    ASSERT(hasPlatformContext());
+    Direct2D::clipOut(*platformContext(), path);
 }
 
 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
@@ -1374,18 +746,8 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
         return;
     }
 
-    auto context = platformContext();
-    if (path.isEmpty()) {
-        m_data->clip(FloatRect());
-        return;
-    }
-
-    ASSERT(!path.activePath());
-
-    COMPtr<ID2D1GeometryGroup> pathToClip;
-    path.createGeometryWithFillMode(clipRule, pathToClip);
-
-    m_data->clip(pathToClip.get());
+    ASSERT(hasPlatformContext());
+    Direct2D::clipPath(*platformContext(), path, clipRule);
 }
 
 IntRect GraphicsContext::clipBounds() const
@@ -1393,33 +755,15 @@ IntRect GraphicsContext::clipBounds() const
     if (paintingDisabled())
         return IntRect();
 
-    if (m_impl) {
-        WTFLogAlways("Getting the clip bounds not yet supported with display lists");
-        return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
-    }
-
-    D2D1_SIZE_F clipSize;
-    if (auto clipLayer = m_data->clipLayer())
-        clipSize = clipLayer->GetSize();
-    else
-        clipSize = platformContext()->GetSize();
-
-    FloatRect clipBounds(IntPoint(), clipSize);
+    if (m_impl)
+        return m_impl->clipBounds();
 
-    return enclosingIntRect(clipBounds);
+    ASSERT(hasPlatformContext());
+    return Direct2D::State::getClipBounds(*platformContext());
 }
 
 void GraphicsContextPlatformPrivate::beginTransparencyLayer(float opacity)
 {
-    TransparencyLayerState transparencyLayer;
-    transparencyLayer.opacity = opacity;
-
-    HRESULT hr = m_renderTarget->CreateCompatibleRenderTarget(&transparencyLayer.renderTarget);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-    m_transparencyLayerStack.append(WTFMove(transparencyLayer));
-
-    m_transparencyLayerStack.last().renderTarget->BeginDraw();
-    m_transparencyLayerStack.last().renderTarget->Clear(D2D1::ColorF(0, 0, 0, 0));
 }
 
 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
@@ -1433,38 +777,12 @@ void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
 
     m_state.alpha = opacity;
 
-    m_data->beginTransparencyLayer(opacity);
+    ASSERT(hasPlatformContext());
+    Direct2D::beginTransparencyLayer(*platformContext(), opacity);
 }
 
 void GraphicsContextPlatformPrivate::endTransparencyLayer()
 {
-    auto currentLayer = m_transparencyLayerStack.takeLast();
-    auto renderTarget = currentLayer.renderTarget;
-    if (!renderTarget)
-        return;
-
-    HRESULT hr = renderTarget->EndDraw();
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    COMPtr<ID2D1Bitmap> bitmap;
-    hr = renderTarget->GetBitmap(&bitmap);
-    RELEASE_ASSERT(SUCCEEDED(hr));
-
-    auto context = this->renderTarget();
-
-    if (currentLayer.hasShadow)
-        drawWithShadowHelper(context, bitmap.get(), currentLayer.shadowColor, currentLayer.shadowOffset, currentLayer.shadowBlur);
-    else {
-        COMPtr<ID2D1BitmapBrush> bitmapBrush;
-        auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
-        auto brushProperties = D2D1::BrushProperties();
-        HRESULT hr = context->CreateBitmapBrush(bitmap.get(), bitmapBrushProperties, brushProperties, &bitmapBrush);
-        RELEASE_ASSERT(SUCCEEDED(hr));
-
-        auto size = bitmap->GetSize();
-        auto rectInDIP = D2D1::RectF(0, 0, size.width, size.height);
-        context->FillRectangle(rectInDIP, bitmapBrush.get());
-    }
 }
 
 void GraphicsContext::endPlatformTransparencyLayer()
@@ -1472,7 +790,8 @@ void GraphicsContext::endPlatformTransparencyLayer()
     if (paintingDisabled())
         return;
 
-    m_data->endTransparencyLayer();
+    ASSERT(hasPlatformContext());
+    Direct2D::endTransparencyLayer(*platformContext());
 
     ASSERT(!m_impl);
 
@@ -1506,7 +825,8 @@ void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
     if (paintingDisabled())
         return;
 
-    m_data->setStrokeStyle(style);
+    ASSERT(hasPlatformContext());
+    Direct2D::State::setStrokeStyle(*platformContext(), style);
 }
 
 void GraphicsContext::setMiterLimit(float limit)
@@ -1520,7 +840,8 @@ void GraphicsContext::setMiterLimit(float limit)
         return;
     }
 
-    m_data->setMiterLimit(limit);
+    ASSERT(hasPlatformContext());
+    Direct2D::setMiterLimit(*platformContext(), limit);
 }
 
 void GraphicsContext::clearRect(const FloatRect& rect)
@@ -1533,23 +854,8 @@ void GraphicsContext::clearRect(const FloatRect& rect)
         return;
     }
 
-    drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
-        FloatRect renderTargetRect(FloatPoint(), renderTarget->GetSize());
-        FloatRect rectToClear(rect);
-
-        if (rectToClear.contains(renderTargetRect)) {
-            renderTarget->SetTags(1, __LINE__);
-            renderTarget->Clear(D2D1::ColorF(0, 0, 0, 0));
-            return;
-        }
-
-        if (!rectToClear.intersects(renderTargetRect))
-            return;
-
-        renderTarget->SetTags(1, __LINE__);
-        rectToClear.intersect(renderTargetRect);
-        renderTarget->FillRectangle(rectToClear, brushWithColor(Color(D2D1::ColorF(0, 0, 0, 0))));
-    });
+    ASSERT(hasPlatformContext());
+    Direct2D::clearRect(*platformContext(), rect);
 }
 
 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
@@ -1562,29 +868,10 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
         return;
     }
 
-    if (m_state.strokeGradient) {
-        WTF::Function<void(ID2D1RenderTarget*)> drawFunction = [this, rect, lineWidth](ID2D1RenderTarget* renderTarget) {
-            renderTarget->SetTags(1, __LINE__);
-            const D2D1_RECT_F d2dRect = rect;
-            renderTarget->DrawRectangle(&d2dRect, m_state.strokeGradient->createPlatformGradientIfNecessary(renderTarget), lineWidth, m_data->strokeStyle());
-        };
-
-        if (hasShadow())
-            drawWithShadow(rect, drawFunction);
-        else
-            drawWithoutShadow(rect, drawFunction);
-        return;
-    }
-
-    if (m_state.strokePattern)
-        applyStrokePattern();
-
-    drawWithoutShadow(rect, [this, rect, lineWidth](ID2D1RenderTarget* renderTarget) {
-        renderTarget->SetTags(1, __LINE__);
-        const D2D1_RECT_F d2dRect = rect;
-        auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
-        renderTarget->DrawRectangle(&d2dRect, brush, lineWidth, m_data->strokeStyle());
-    });
+    ASSERT(hasPlatformContext());
+    auto& state = this->state();
+    auto& context = *platformContext();
+    Direct2D::strokeRect(context, rect, lineWidth, Direct2D::StrokeSource(state, context), Direct2D::ShadowState(state));
 }
 
 void GraphicsContext::setLineCap(LineCap cap)
@@ -1597,7 +884,8 @@ void GraphicsContext::setLineCap(LineCap cap)
         return;
     }
 
-    m_data->setLineCap(cap);
+    ASSERT(hasPlatformContext());
+    Direct2D::setLineCap(*platformContext(), cap);
 }
 
 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
@@ -1618,8 +906,8 @@ void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
             dashOffset = fmod(dashOffset, length) + length;
     }
 
-    m_data->setDashes(dashes);
-    m_data->setDashOffset(dashOffset);
+    ASSERT(hasPlatformContext());
+    Direct2D::setLineDash(*platformContext(), dashes, dashOffset);
 }
 
 void GraphicsContext::setLineJoin(LineJoin join)
@@ -1632,7 +920,8 @@ void GraphicsContext::setLineJoin(LineJoin join)
         return;
     }
 
-    m_data->setLineJoin(join);
+    ASSERT(hasPlatformContext());
+    Direct2D::setLineJoin(*platformContext(), join);
 }
 
 void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
@@ -1650,7 +939,8 @@ void GraphicsContext::scale(const FloatSize& size)
         return;
     }
 
-    m_data->scale(size);
+    ASSERT(hasPlatformContext());
+    Direct2D::scale(*platformContext(), size);
     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
@@ -1664,7 +954,8 @@ void GraphicsContext::rotate(float angle)
         return;
     }
 
-    m_data->rotate(angle);
+    ASSERT(hasPlatformContext());
+    Direct2D::rotate(*platformContext(), angle);
     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
@@ -1678,7 +969,8 @@ void GraphicsContext::translate(float x, float y)
         return;
     }
 
-    m_data->translate(x, y);
+    ASSERT(hasPlatformContext());
+    Direct2D::translate(*platformContext(), x, y);
     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
@@ -1692,7 +984,8 @@ void GraphicsContext::concatCTM(const AffineTransform& transform)
         return;
     }
 
-    m_data->concatCTM(transform);
+    ASSERT(hasPlatformContext());
+    Direct2D::concatCTM(*platformContext(), transform);
     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
@@ -1706,7 +999,8 @@ void GraphicsContext::setCTM(const AffineTransform& transform)
         return;
     }
 
-    m_data->setCTM(transform);
+    ASSERT(hasPlatformContext());
+    Direct2D::State::setCTM(*platformContext(), transform);
     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
@@ -1715,14 +1009,11 @@ AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
     if (paintingDisabled())
         return AffineTransform();
 
-    if (m_impl) {
-        WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
-        return AffineTransform();
-    }
+    if (m_impl)
+        return m_impl->getCTM(includeScale);
 
-    D2D1_MATRIX_3X2_F currentTransform;
-    platformContext()->GetTransform(&currentTransform);
-    return currentTransform;
+    ASSERT(hasPlatformContext());
+    return Direct2D::State::getCTM(*platformContext());
 }
 
 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
@@ -1730,14 +1021,10 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMo
     if (paintingDisabled())
         return rect;
 
-    if (m_impl) {
-        WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
-        return rect;
-    }
-
-    notImplemented();
+    if (m_impl)
+        return m_impl->roundToDevicePixels(rect, roundingMode);
 
-    return rect;
+    return Direct2D::State::roundToDevicePixels(*platformContext(), rect);
 }
 
 void GraphicsContext::drawLineForText(const FloatRect& rect, bool printing, bool doubleLines, StrokeStyle strokeStyle)
@@ -1748,19 +1035,21 @@ void GraphicsContext::drawLineForText(const FloatRect& rect, bool printing, bool
     drawLinesForText(rect.location(), rect.height(), widths, printing, doubleLines, strokeStyle);
 }
 
-void GraphicsContext::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
+void GraphicsContext::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle strokeStyle)
 {
     if (paintingDisabled())
+        return;
 
     if (!widths.size())
         return;
 
     if (m_impl) {
-        m_impl->drawLinesForText(point, thickness, widths, printing, doubleLines);
+        m_impl->drawLinesForText(point, thickness, widths, printing, doubleUnderlines);
         return;
     }
 
-    notImplemented();
+    ASSERT(hasPlatformContext());
+    Direct2D::drawLinesForText(*platformContext(), point, thickness, widths, printing, doubleUnderlines, m_state.strokeColor);
 }
 
 void GraphicsContext::setURLForRect(const URL& link, const FloatRect& destRect)
@@ -1845,13 +1134,8 @@ bool GraphicsContext::isAcceleratedContext() const
     if (paintingDisabled())
         return false;
 
-    // FIXME
-    if (m_impl)
-        return false;
-
-    // This function is probabaly not needed.
-    notImplemented();
-    return false;
+    ASSERT(hasPlatformContext());
+    return Direct2D::State::isAcceleratedContext(*platformContext());
 }
 
 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
@@ -1863,10 +1147,8 @@ void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
 void GraphicsContext::setPlatformStrokeColor(const Color& color)
 {
     ASSERT(m_state.strokeColor == color);
-
-    m_data->m_solidStrokeBrush = nullptr;
-
-    m_data->m_solidStrokeBrush = brushWithColor(strokeColor());
+    ASSERT(hasPlatformContext());
+    platformContext()->m_solidStrokeBrush = brushWithColor(strokeColor());
 }
 
 void GraphicsContext::setPlatformStrokeThickness(float thickness)
@@ -1878,10 +1160,8 @@ void GraphicsContext::setPlatformStrokeThickness(float thickness)
 void GraphicsContext::setPlatformFillColor(const Color& color)
 {
     ASSERT(m_state.fillColor == color);
-
-    m_data->m_solidFillBrush = nullptr;
-
-    m_data->m_solidFillBrush = brushWithColor(fillColor());
+    ASSERT(hasPlatformContext());
+    platformContext()->m_solidFillBrush = brushWithColor(fillColor());
 }
 
 void GraphicsContext::setPlatformShouldAntialias(bool enable)
@@ -1890,9 +1170,8 @@ void GraphicsContext::setPlatformShouldAntialias(bool enable)
         return;
 
     ASSERT(!m_impl);
-
-    auto antialiasMode = enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED;
-    platformContext()->SetAntialiasMode(antialiasMode);
+    ASSERT(hasPlatformContext());
+    Direct2D::State::setShouldAntialias(*platformContext(), enable);
 }
 
 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
@@ -1903,7 +1182,7 @@ void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
     ASSERT(!m_impl);
 
     auto fontSmoothingMode = enable ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
-    platformContext()->SetTextAntialiasMode(fontSmoothingMode);
+    platformContext()->renderTarget()->SetTextAntialiasMode(fontSmoothingMode);
 }
 
 void GraphicsContext::setPlatformAlpha(float alpha)
@@ -1915,121 +1194,15 @@ void GraphicsContext::setPlatformAlpha(float alpha)
     m_data->setAlpha(alpha);
 }
 
-void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode blendMode)
+void GraphicsContext::setPlatformCompositeOperation(CompositeOperator compositeOperator, BlendMode blendMode)
 {
     if (paintingDisabled())
         return;
 
     ASSERT(!m_impl);
 
-    D2D1_BLEND_MODE targetBlendMode = D2D1_BLEND_MODE_SCREEN;
-    D2D1_COMPOSITE_MODE targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP; // ???
-
-    if (blendMode != BlendMode::Normal) {
-        switch (blendMode) {
-        case BlendMode::Multiply:
-            targetBlendMode = D2D1_BLEND_MODE_MULTIPLY;
-            break;
-        case BlendMode::Screen:
-            targetBlendMode = D2D1_BLEND_MODE_SCREEN;
-            break;
-        case BlendMode::Overlay:
-            targetBlendMode = D2D1_BLEND_MODE_OVERLAY;
-            break;
-        case BlendMode::Darken:
-            targetBlendMode = D2D1_BLEND_MODE_DARKEN;
-            break;
-        case BlendMode::Lighten:
-            targetBlendMode = D2D1_BLEND_MODE_LIGHTEN;
-            break;
-        case BlendMode::ColorDodge:
-            targetBlendMode = D2D1_BLEND_MODE_COLOR_DODGE;
-            break;
-        case BlendMode::ColorBurn:
-            targetBlendMode = D2D1_BLEND_MODE_COLOR_BURN;
-            break;
-        case BlendMode::HardLight:
-            targetBlendMode = D2D1_BLEND_MODE_HARD_LIGHT;
-            break;
-        case BlendMode::SoftLight:
-            targetBlendMode = D2D1_BLEND_MODE_SOFT_LIGHT;
-            break;
-        case BlendMode::Difference:
-            targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
-            break;
-        case BlendMode::Exclusion:
-            targetBlendMode = D2D1_BLEND_MODE_EXCLUSION;
-            break;
-        case BlendMode::Hue:
-            targetBlendMode = D2D1_BLEND_MODE_HUE;
-            break;
-        case BlendMode::Saturation:
-            targetBlendMode = D2D1_BLEND_MODE_SATURATION;
-            break;
-        case BlendMode::Color:
-            targetBlendMode = D2D1_BLEND_MODE_COLOR;
-            break;
-        case BlendMode::Luminosity:
-            targetBlendMode = D2D1_BLEND_MODE_LUMINOSITY;
-            break;
-        case BlendMode::PlusDarker:
-            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
-            break;
-        case BlendMode::PlusLighter:
-            targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
-            break;
-        default:
-            break;
-        }
-    } else {
-        switch (mode) {
-        case CompositeClear:
-            // FIXME: targetBlendMode = D2D1_BLEND_MODE_CLEAR;
-            break;
-        case CompositeCopy:
-            // FIXME: targetBlendMode = D2D1_BLEND_MODE_COPY;
-            break;
-        case CompositeSourceOver:
-            // FIXME: kCGBlendModeNormal
-            break;
-        case CompositeSourceIn:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_IN;
-            break;
-        case CompositeSourceOut:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_OUT;
-            break;
-        case CompositeSourceAtop:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP;
-            break;
-        case CompositeDestinationOver:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OVER;
-            break;
-        case CompositeDestinationIn:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_IN;
-            break;
-        case CompositeDestinationOut:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OUT;
-            break;
-        case CompositeDestinationAtop:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
-            break;
-        case CompositeXOR:
-            targetCompositeMode = D2D1_COMPOSITE_MODE_XOR;
-            break;
-        case CompositePlusDarker:
-            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
-            break;
-        case CompositePlusLighter:
-            targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
-            break;
-        case CompositeDifference:
-            targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
-            break;
-        }
-    }
-
-    m_data->m_blendMode = targetBlendMode;
-    m_data->m_compositeMode = targetCompositeMode;
+    ASSERT(hasPlatformContext());
+    Direct2D::State::setCompositeOperation(*platformContext(), compositeOperator, blendMode);
 }
 
 void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
@@ -2050,13 +1223,8 @@ void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
         return;
     }
 
-    auto d2dEllipse = D2D1::Ellipse(ellipse.center(), 0.5 * ellipse.width(), 0.5 * ellipse.height());
-
-    platformContext()->SetTags(1, __LINE__);
-
-    drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
-        renderTarget->FillEllipse(&d2dEllipse, solidFillBrush());
-    });
+    ASSERT(hasPlatformContext());
+    Direct2D::fillEllipse(*platformContext(), ellipse, m_state.fillColor, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
 }
 
 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
@@ -2072,13 +1240,8 @@ void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
         return;
     }
 
-    auto d2dEllipse = D2D1::Ellipse(ellipse.center(), 0.5 * ellipse.width(), 0.5 * ellipse.height());
-
-    platformContext()->SetTags(1, __LINE__);
-
-    drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
-        renderTarget->DrawEllipse(&d2dEllipse, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
-    });
+    ASSERT(hasPlatformContext());
+    Direct2D::drawEllipse(*platformContext(), ellipse, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
 }
 
 }
diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.cpp
new file mode 100644 (file)
index 0000000..2648932
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "GraphicsContextImplDirect2D.h"
+
+#if USE(DIRECT2D)
+
+#include "Direct2DOperations.h"
+#include "FloatRoundedRect.h"
+#include "Font.h"
+#include "GlyphBuffer.h"
+#include "GraphicsContext.h"
+#include "GraphicsContextPlatformPrivateDirect2D.h"
+#include "ImageBuffer.h"
+#include "IntRect.h"
+#include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
+
+namespace WebCore {
+
+GraphicsContext::GraphicsContextImplFactory GraphicsContextImplDirect2D::createFactory(PlatformContextDirect2D& platformContext)
+{
+    return GraphicsContext::GraphicsContextImplFactory(
+        [&platformContext](GraphicsContext& context)
+        {
+            return std::make_unique<GraphicsContextImplDirect2D>(context, platformContext);
+        });
+}
+
+GraphicsContext::GraphicsContextImplFactory GraphicsContextImplDirect2D::createFactory(ID2D1RenderTarget* renderTarget)
+{
+    return GraphicsContext::GraphicsContextImplFactory(
+        [renderTarget](GraphicsContext& context)
+        {
+            return std::make_unique<GraphicsContextImplDirect2D>(context, renderTarget);
+        });
+}
+
+GraphicsContextImplDirect2D::GraphicsContextImplDirect2D(GraphicsContext& context, PlatformContextDirect2D& platformContext)
+    : GraphicsContextImpl(context, FloatRect { }, AffineTransform { })
+    , m_platformContext(platformContext)
+    , m_private(std::make_unique<GraphicsContextPlatformPrivate>(m_platformContext, GraphicsContext::BitmapRenderingContextType::GPUMemory))
+{
+    m_platformContext.setGraphicsContextPrivate(m_private.get());
+    m_private->syncContext(m_platformContext);
+}
+
+GraphicsContextImplDirect2D::GraphicsContextImplDirect2D(GraphicsContext& context, ID2D1RenderTarget* renderTarget)
+    : GraphicsContextImpl(context, FloatRect { }, AffineTransform { })
+    , m_ownedPlatformContext(std::make_unique<PlatformContextDirect2D>(renderTarget))
+    , m_platformContext(*m_ownedPlatformContext)
+    , m_private(std::make_unique<GraphicsContextPlatformPrivate>(m_platformContext, GraphicsContext::BitmapRenderingContextType::GPUMemory))
+{
+    m_platformContext.setGraphicsContextPrivate(m_private.get());
+    m_private->syncContext(m_platformContext);
+}
+
+GraphicsContextImplDirect2D::~GraphicsContextImplDirect2D()
+{
+    m_platformContext.setGraphicsContextPrivate(nullptr);
+}
+
+bool GraphicsContextImplDirect2D::hasPlatformContext() const
+{
+    return true;
+}
+
+PlatformContextDirect2D* GraphicsContextImplDirect2D::platformContext() const
+{
+    return &m_platformContext;
+}
+
+void GraphicsContextImplDirect2D::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+{
+    if (flags & GraphicsContextState::StrokeThicknessChange)
+        Direct2D::State::setStrokeThickness(m_platformContext, state.strokeThickness);
+
+    if (flags & GraphicsContextState::StrokeStyleChange)
+        Direct2D::State::setStrokeStyle(m_platformContext, state.strokeStyle);
+
+    if (flags & GraphicsContextState::ShadowChange) {
+        if (state.shadowsIgnoreTransforms) {
+            // Meaning that this graphics context is associated with a CanvasRenderingContext
+            // We flip the height since CG and HTML5 Canvas have opposite Y axis
+            auto& mutableState = const_cast<GraphicsContextState&>(graphicsContext().state());
+            auto& shadowOffset = state.shadowOffset;
+            mutableState.shadowOffset = { shadowOffset.width(), -shadowOffset.height() };
+        }
+    }
+
+    if (flags & GraphicsContextState::CompositeOperationChange)
+        Direct2D::State::setCompositeOperation(m_platformContext, state.compositeOperator, state.blendMode);
+
+    if (flags & GraphicsContextState::ShouldAntialiasChange)
+        Direct2D::State::setShouldAntialias(m_platformContext, state.shouldAntialias);
+}
+
+void GraphicsContextImplDirect2D::clearShadow()
+{
+}
+
+void GraphicsContextImplDirect2D::setLineCap(LineCap lineCap)
+{
+    Direct2D::setLineCap(m_platformContext, lineCap);
+}
+
+void GraphicsContextImplDirect2D::setLineDash(const DashArray& dashes, float dashOffset)
+{
+    Direct2D::setLineDash(m_platformContext, dashes, dashOffset);
+}
+
+void GraphicsContextImplDirect2D::setLineJoin(LineJoin lineJoin)
+{
+    Direct2D::setLineJoin(m_platformContext, lineJoin);
+}
+
+void GraphicsContextImplDirect2D::setMiterLimit(float miterLimit)
+{
+    Direct2D::setMiterLimit(m_platformContext, miterLimit);
+}
+
+void GraphicsContextImplDirect2D::fillRect(const FloatRect& rect)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::fillRect(m_platformContext, rect, Direct2D::FillSource(state, m_platformContext), Direct2D::ShadowState(state));
+}
+
+void GraphicsContextImplDirect2D::fillRect(const FloatRect& rect, const Color& color)
+{
+    Direct2D::fillRect(m_platformContext, rect, color, Direct2D::ShadowState(graphicsContext().state()));
+}
+
+void GraphicsContextImplDirect2D::fillRect(const FloatRect& rect, Gradient& gradient)
+{
+    auto platformGradient = adoptCOM(gradient.createPlatformGradientIfNecessary(m_platformContext.renderTarget()));
+    if (!platformGradient)
+        return;
+
+    Direct2D::save(m_platformContext);
+    Direct2D::fillRectWithGradient(m_platformContext, rect, platformGradient.get());
+    Direct2D::restore(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::fillRect(const FloatRect& rect, const Color& color, CompositeOperator compositeOperator, BlendMode blendMode)
+{
+    auto& state = graphicsContext().state();
+    CompositeOperator previousOperator = state.compositeOperator;
+
+    Direct2D::State::setCompositeOperation(m_platformContext, compositeOperator, blendMode);
+    Direct2D::fillRect(m_platformContext, rect, color, Direct2D::ShadowState(state));
+    Direct2D::State::setCompositeOperation(m_platformContext, previousOperator, BlendMode::Normal);
+}
+
+void GraphicsContextImplDirect2D::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
+{
+    auto& state = graphicsContext().state();
+
+    CompositeOperator previousOperator = state.compositeOperator;
+    Direct2D::State::setCompositeOperation(m_platformContext, previousOperator, blendMode);
+
+    Direct2D::ShadowState shadowState(state);
+    if (rect.isRounded())
+        Direct2D::fillRoundedRect(m_platformContext, rect, color, shadowState);
+    else
+        Direct2D::fillRect(m_platformContext, rect.rect(), color, shadowState);
+
+    Direct2D::State::setCompositeOperation(m_platformContext, previousOperator, BlendMode::Normal);
+}
+
+void GraphicsContextImplDirect2D::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color&)
+{
+    Direct2D::fillRectWithRoundedHole(m_platformContext, rect, roundedHoleRect, { }, Direct2D::ShadowState(graphicsContext().state()));
+}
+
+void GraphicsContextImplDirect2D::fillPath(const Path& path)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::fillPath(m_platformContext, path, Direct2D::FillSource(state, m_platformContext), Direct2D::ShadowState(state));
+}
+
+void GraphicsContextImplDirect2D::fillEllipse(const FloatRect& rect)
+{
+    Path path;
+    path.addEllipse(rect);
+    fillPath(path);
+}
+
+void GraphicsContextImplDirect2D::strokeRect(const FloatRect& rect, float lineWidth)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::strokeRect(m_platformContext, rect, lineWidth, Direct2D::StrokeSource(state, m_platformContext), Direct2D::ShadowState(state));
+}
+
+void GraphicsContextImplDirect2D::strokePath(const Path& path)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::strokePath(m_platformContext, path, Direct2D::StrokeSource(state, m_platformContext), Direct2D::ShadowState(state));
+}
+
+void GraphicsContextImplDirect2D::strokeEllipse(const FloatRect& rect)
+{
+    Path path;
+    path.addEllipse(rect);
+    strokePath(path);
+}
+
+void GraphicsContextImplDirect2D::clearRect(const FloatRect& rect)
+{
+    Direct2D::clearRect(m_platformContext, rect);
+}
+
+void GraphicsContextImplDirect2D::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothing)
+{
+    UNUSED_PARAM(fontSmoothing);
+    if (!font.platformData().size())
+        return;
+
+    notImplemented();
+}
+
+ImageDrawResult GraphicsContextImplDirect2D::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
+{
+    return GraphicsContextImpl::drawImageImpl(graphicsContext(), image, destination, source, imagePaintingOptions);
+}
+
+ImageDrawResult GraphicsContextImplDirect2D::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
+{
+    return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileSize, spacing, imagePaintingOptions);
+}
+
+ImageDrawResult GraphicsContextImplDirect2D::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
+{
+    return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions);
+}
+
+void GraphicsContextImplDirect2D::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::drawNativeImage(m_platformContext, image.get(), imageSize, destRect, srcRect, compositeOperator, blendMode, orientation, state.imageInterpolationQuality, state.alpha, Direct2D::ShadowState(state));
+}
+
+void GraphicsContextImplDirect2D::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize&, CompositeOperator compositeOperator, BlendMode blendMode)
+{
+    if (auto surface = image.nativeImageForCurrentFrame())
+        Direct2D::drawPattern(m_platformContext, surface.get(), IntSize(image.size()), destRect, tileRect, patternTransform, phase, compositeOperator, blendMode);
+}
+
+void GraphicsContextImplDirect2D::drawRect(const FloatRect& rect, float borderThickness)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::drawRect(m_platformContext, rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor);
+}
+
+void GraphicsContextImplDirect2D::drawLine(const FloatPoint& point1, const FloatPoint& point2)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::drawLine(m_platformContext, point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias);
+}
+
+void GraphicsContextImplDirect2D::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::drawLinesForText(m_platformContext, point, thickness, widths, printing, doubleUnderlines, state.strokeColor);
+}
+
+void GraphicsContextImplDirect2D::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
+{
+    Direct2D::drawDotsForDocumentMarker(m_platformContext, rect, style);
+}
+
+void GraphicsContextImplDirect2D::drawEllipse(const FloatRect& rect)
+{
+    auto& state = graphicsContext().state();
+    Direct2D::fillEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness);
+}
+
+void GraphicsContextImplDirect2D::drawPath(const Path&)
+{
+}
+
+void GraphicsContextImplDirect2D::drawFocusRing(const Path& path, float width, float offset, const Color& color)
+{
+    UNUSED_PARAM(offset);
+    notImplemented();
+}
+
+void GraphicsContextImplDirect2D::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
+{
+    UNUSED_PARAM(offset);
+    notImplemented();
+}
+
+void GraphicsContextImplDirect2D::save()
+{
+    Direct2D::save(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::restore()
+{
+    Direct2D::restore(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::translate(float x, float y)
+{
+    Direct2D::translate(m_platformContext, x, y);
+}
+
+void GraphicsContextImplDirect2D::rotate(float angleInRadians)
+{
+    Direct2D::rotate(m_platformContext, angleInRadians);
+}
+
+void GraphicsContextImplDirect2D::scale(const FloatSize& size)
+{
+    Direct2D::scale(m_platformContext, size);
+}
+
+void GraphicsContextImplDirect2D::concatCTM(const AffineTransform& transform)
+{
+    Direct2D::concatCTM(m_platformContext, transform);
+}
+
+void GraphicsContextImplDirect2D::setCTM(const AffineTransform& transform)
+{
+    Direct2D::State::setCTM(m_platformContext, transform);
+}
+
+AffineTransform GraphicsContextImplDirect2D::getCTM(GraphicsContext::IncludeDeviceScale)
+{
+    return Direct2D::State::getCTM(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::beginTransparencyLayer(float opacity)
+{
+    Direct2D::beginTransparencyLayer(m_platformContext, opacity);
+}
+
+void GraphicsContextImplDirect2D::endTransparencyLayer()
+{
+    Direct2D::endTransparencyLayer(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::clip(const FloatRect& rect)
+{
+    Direct2D::clip(m_platformContext, rect);
+}
+
+void GraphicsContextImplDirect2D::clipOut(const FloatRect& rect)
+{
+    Direct2D::clipOut(m_platformContext, rect);
+}
+
+void GraphicsContextImplDirect2D::clipOut(const Path& path)
+{
+    Direct2D::clipOut(m_platformContext, path);
+}
+
+void GraphicsContextImplDirect2D::clipPath(const Path& path, WindRule clipRule)
+{
+    Direct2D::clipPath(m_platformContext, path, clipRule);
+}
+
+IntRect GraphicsContextImplDirect2D::clipBounds()
+{
+    return Direct2D::State::getClipBounds(m_platformContext);
+}
+
+void GraphicsContextImplDirect2D::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
+{
+    RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore);
+    if (!image)
+        return;
+
+
+    if (auto surface = image->nativeImageForCurrentFrame())
+        notImplemented();
+}
+
+void GraphicsContextImplDirect2D::applyDeviceScaleFactor(float)
+{
+}
+
+FloatRect GraphicsContextImplDirect2D::roundToDevicePixels(const FloatRect& rect, GraphicsContext::RoundingMode)
+{
+    return Direct2D::State::roundToDevicePixels(m_platformContext, rect);
+}
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.h b/Source/WebCore/platform/graphics/win/GraphicsContextImplDirect2D.h
new file mode 100644 (file)
index 0000000..6bb644a
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "GraphicsContextImpl.h"
+
+namespace WebCore {
+
+class PlatformContextDirect2D;
+
+class GraphicsContextImplDirect2D final : public GraphicsContextImpl {
+public:
+    WEBCORE_EXPORT static GraphicsContext::GraphicsContextImplFactory createFactory(PlatformContextDirect2D&);
+    WEBCORE_EXPORT static GraphicsContext::GraphicsContextImplFactory createFactory(ID2D1RenderTarget*);
+
+    GraphicsContextImplDirect2D(GraphicsContext&, PlatformContextDirect2D&);
+    GraphicsContextImplDirect2D(GraphicsContext&, ID2D1RenderTarget*);
+    virtual ~GraphicsContextImplDirect2D();
+
+    bool hasPlatformContext() const override;
+    PlatformContextDirect2D* platformContext() const override;
+
+    void updateState(const GraphicsContextState&, GraphicsContextState::StateChangeFlags) override;
+    void clearShadow() override;
+
+    void setLineCap(LineCap) override;
+    void setLineDash(const DashArray&, float) override;
+    void setLineJoin(LineJoin) override;
+    void setMiterLimit(float) override;
+
+    void fillRect(const FloatRect&) override;
+    void fillRect(const FloatRect&, const Color&) override;
+    void fillRect(const FloatRect&, Gradient&) override;
+    void fillRect(const FloatRect&, const Color&, CompositeOperator, BlendMode) override;
+    void fillRoundedRect(const FloatRoundedRect&, const Color&, BlendMode) override;
+    void fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect&, const Color&) override;
+    void fillPath(const Path&) override;
+    void fillEllipse(const FloatRect&) override;
+    void strokeRect(const FloatRect&, float) override;
+    void strokePath(const Path&) override;
+    void strokeEllipse(const FloatRect&) override;
+    void clearRect(const FloatRect&) override;
+
+    void drawGlyphs(const Font&, const GlyphBuffer&, unsigned, unsigned, const FloatPoint&, FontSmoothingMode) override;
+
+    ImageDrawResult drawImage(Image&, const FloatRect&, const FloatRect&, const ImagePaintingOptions&) override;
+    ImageDrawResult drawTiledImage(Image&, const FloatRect&, const FloatPoint&, const FloatSize&, const FloatSize&, const ImagePaintingOptions&) override;
+    ImageDrawResult drawTiledImage(Image&, const FloatRect&, const FloatRect&, const FloatSize&, Image::TileRule, Image::TileRule, const ImagePaintingOptions&) override;
+    void drawNativeImage(const NativeImagePtr&, const FloatSize&, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode, ImageOrientation) override;
+    void drawPattern(Image&, const FloatRect&, const FloatRect&, const AffineTransform&, const FloatPoint&, const FloatSize&, CompositeOperator, BlendMode = BlendMode::Normal) override;
+
+    void drawRect(const FloatRect&, float) override;
+    void drawLine(const FloatPoint&, const FloatPoint&) override;
+    void drawLinesForText(const FloatPoint&, float thickness, const DashArray&, bool, bool) override;
+    void drawDotsForDocumentMarker(const FloatRect&, DocumentMarkerLineStyle) override;
+    void drawEllipse(const FloatRect&) override;
+    void drawPath(const Path&) override;
+
+    void drawFocusRing(const Path&, float, float, const Color&) override;
+    void drawFocusRing(const Vector<FloatRect>&, float, float, const Color&) override;
+
+    void save() override;
+    void restore() override;
+
+    void translate(float, float) override;
+    void rotate(float) override;
+    void scale(const FloatSize&) override;
+    void concatCTM(const AffineTransform&) override;
+    void setCTM(const AffineTransform&) override;
+    AffineTransform getCTM(GraphicsContext::IncludeDeviceScale) override;
+
+    void beginTransparencyLayer(float) override;
+    void endTransparencyLayer() override;
+
+    void clip(const FloatRect&) override;
+    void clipOut(const FloatRect&) override;
+    void clipOut(const Path&) override;
+    void clipPath(const Path&, WindRule) override;
+    IntRect clipBounds() override;
+    void clipToImageBuffer(ImageBuffer&, const FloatRect&) override;
+    
+    void applyDeviceScaleFactor(float) override;
+
+    FloatRect roundToDevicePixels(const FloatRect&, GraphicsContext::RoundingMode) override;
+
+private:
+    std::unique_ptr<PlatformContextDirect2D> m_ownedPlatformContext;
+    PlatformContextDirect2D& m_platformContext;
+
+    std::unique_ptr<GraphicsContextPlatformPrivate> m_private;
+};
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
index 58404f1..7cca183 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "COMPtr.h"
 #include "Color.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <d2d1_1.h>
 #include <d2d1effects.h>
@@ -39,10 +40,11 @@ namespace WebCore {
 class GraphicsContextPlatformPrivate {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    GraphicsContextPlatformPrivate(ID2D1RenderTarget*, GraphicsContext::BitmapRenderingContextType);
+    GraphicsContextPlatformPrivate(PlatformContextDirect2D&, GraphicsContext::BitmapRenderingContextType);
+    GraphicsContextPlatformPrivate(std::unique_ptr<PlatformContextDirect2D>&&, GraphicsContext::BitmapRenderingContextType);
     ~GraphicsContextPlatformPrivate();
 
-    enum Direct2DLayerType { AxisAlignedClip, LayerClip };
+    void syncContext(PlatformContextDirect2D&);
 
     void beginTransparencyLayer(float opacity);
     void endTransparencyLayer();
@@ -73,68 +75,26 @@ public:
     void setDashes(const DashArray&);
     void setAlpha(float);
 
+    PlatformContextDirect2D* platformContext() { return &m_platformContext; }
     ID2D1RenderTarget* renderTarget();
-    ID2D1Layer* clipLayer() const { return m_renderStates.last().m_activeLayer.get(); }
-    ID2D1StrokeStyle* strokeStyle();
-
-    COMPtr<ID2D1SolidColorBrush> brushWithColor(const D2D1_COLOR_F&);
 
     HDC m_hdc { nullptr };
-    D2D1_BLEND_MODE m_blendMode { D2D1_BLEND_MODE_MULTIPLY };
-    D2D1_COMPOSITE_MODE m_compositeMode { D2D1_COMPOSITE_MODE_SOURCE_OVER };
     bool m_shouldIncludeChildWindows { false };
-    bool m_strokeSyleIsDirty { false };
-
-    COMPtr<ID2D1SolidColorBrush> m_solidStrokeBrush;
-    COMPtr<ID2D1SolidColorBrush> m_solidFillBrush;
-    COMPtr<ID2D1BitmapBrush> m_patternStrokeBrush;
-    COMPtr<ID2D1BitmapBrush> m_patternFillBrush;
 
     float currentGlobalAlpha() const;
 
     D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties() const;
 
 private:
-    void recomputeStrokeStyle();
-
-    COMPtr<ID2D1RenderTarget> m_renderTarget;
+    std::unique_ptr<PlatformContextDirect2D> m_ownedPlatformContext;
+    PlatformContextDirect2D& m_platformContext;
     const GraphicsContext::BitmapRenderingContextType m_rendererType;
-    HashMap<uint32_t, COMPtr<ID2D1SolidColorBrush>> m_solidColoredBrushCache;
-    COMPtr<ID2D1SolidColorBrush> m_whiteBrush;
-    COMPtr<ID2D1SolidColorBrush> m_zeroBrush;
-    COMPtr<ID2D1StrokeStyle> m_d2dStrokeStyle;
-
-    struct RenderState {
-        COMPtr<ID2D1DrawingStateBlock> m_drawingStateBlock;
-        COMPtr<ID2D1Layer> m_activeLayer;
-        Vector<Direct2DLayerType> m_clips;
-    };
-
-    Vector<RenderState> m_renderStates;
-
-    struct TransparencyLayerState {
-        COMPtr<ID2D1BitmapRenderTarget> renderTarget;
-        Color shadowColor;
-        FloatSize shadowOffset;
-        float opacity { 1.0 };
-        float shadowBlur { 0 };
-        bool hasShadow { false };
-    };
-    Vector<TransparencyLayerState> m_transparencyLayerStack;
-
-    D2D1_CAP_STYLE m_lineCap { D2D1_CAP_STYLE_FLAT };
-    D2D1_LINE_JOIN m_lineJoin { D2D1_LINE_JOIN_MITER };
-    StrokeStyle m_strokeStyle { SolidStroke };
-    DashArray m_dashes;
 
     unsigned beginDrawCount { 0 };
 
-    float m_miterLimit { 10.0f };
-    float m_dashOffset { 0 };
-    float m_patternWidth { 1.0f };
-    float m_patternOffset { 0 };
-    float m_strokeThickness { 0 };
     float m_alpha { 1.0 };
+
+    friend class D2DContextStateSaver;
 };
 
 class D2DContextStateSaver {
@@ -144,26 +104,26 @@ public:
         , m_saveAndRestore(saveAndRestore)
     {
         if (m_saveAndRestore)
-            m_context.save();
+            m_context.m_platformContext.save();
     }
 
     ~D2DContextStateSaver()
     {
         if (m_saveAndRestore)
-            m_context.restore();
+            m_context.m_platformContext.restore();
     }
 
     void save()
     {
         ASSERT(!m_saveAndRestore);
-        m_context.save();
+        m_context.m_platformContext.save();
         m_saveAndRestore = true;
     }
 
     void restore()
     {
         ASSERT(m_saveAndRestore);
-        m_context.restore();
+        m_context.m_platformContext.restore();
         m_saveAndRestore = false;
     }
 
@@ -173,6 +133,7 @@ public:
     }
 
 private:
+    std::unique_ptr<PlatformContextDirect2D> ownedPlatformContext;
     GraphicsContextPlatformPrivate& m_context;
     bool m_saveAndRestore { false };
 };
index d3ef53b..00d399a 100644 (file)
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
 
-interface ID2D1RenderTarget;
-
 namespace WebCore {
 
+class PlatformContextDirect2D;
+
 struct ImageBufferData {
     IntSize backingStoreSize;
     Checked<unsigned, RecordOverflow> bytesPerRow;
 
     // Only for software ImageBuffers.
     Vector<char> data;
+    std::unique_ptr<PlatformContextDirect2D> platformContext;
     std::unique_ptr<GraphicsContext> context;
     COMPtr<IWICBitmap> bitmapSource;
 
index ef85341..06c1745 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #include "BitmapImage.h"
 #include "COMPtr.h"
+#include "Direct2DUtilities.h"
 #include "GraphicsContext.h"
 #include "ImageData.h"
 #include "ImageDecoderDirect2D.h"
 #include "IntRect.h"
 #include "MIMETypeRegistry.h"
 #include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <math.h>
 #include <wincodec.h>
@@ -101,21 +103,17 @@ ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpac
 
     m_data.data = Vector<char>(numBytes.unsafeGet(), 0);
 
-    HRESULT hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapFromMemory(m_size.width(), m_size.height(), GUID_WICPixelFormat32bppPBGRA, static_cast<UINT>(m_data.bytesPerRow.unsafeGet()), static_cast<UINT>(numBytes.unsafeGet()), reinterpret_cast<BYTE*>(m_data.data.data()), &m_data.bitmapSource);
-    if (!SUCCEEDED(hr))
+    m_data.bitmapSource = Direct2D::createDirect2DImageSurfaceWithData(m_data.data.data(), m_size, m_data.bytesPerRow.unsafeGet());
+    if (!m_data.bitmapSource)
         return;
 
-    auto targetProperties = 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);
-
-    COMPtr<ID2D1RenderTarget> bitmapContext;
-    hr = GraphicsContext::systemFactory()->CreateWicBitmapRenderTarget(m_data.bitmapSource.get(), &targetProperties, &bitmapContext);
-    if (!bitmapContext || !SUCCEEDED(hr))
+    COMPtr<ID2D1RenderTarget> bitmapContext = Direct2D::createRenderTargetFromWICBitmap(m_data.bitmapSource.get());
+    if (!bitmapContext)
         return;
 
     // Note: This places the bitmapcontext into a locked state because of the BeginDraw call in the constructor.
-    m_data.context = std::make_unique<GraphicsContext>(bitmapContext.get());
+    m_data.platformContext = std::make_unique<PlatformContextDirect2D>(bitmapContext.get());
+    m_data.context = std::make_unique<GraphicsContext>(m_data.platformContext.get(), GraphicsContext::BitmapRenderingContextType::GPUMemory);
 
     success = true;
 }
index 5ac5d1d..3016329 100644 (file)
@@ -32,6 +32,7 @@
 #include "GraphicsContext.h"
 #include "IntSize.h"
 #include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <wincodec.h>
 
@@ -116,11 +117,11 @@ void drawNativeImage(const NativeImagePtr& image, GraphicsContext& context, cons
     float opacity = 1.0f;
 
     COMPtr<ID2D1Bitmap> bitmap;
-    HRESULT hr = platformContext->CreateBitmapFromWicBitmap(image.get(), &bitmap);
+    HRESULT hr = platformContext->renderTarget()->CreateBitmapFromWicBitmap(image.get(), &bitmap);
     if (!SUCCEEDED(hr))
         return;
 
-    platformContext->DrawBitmap(bitmap.get(), destRect, opacity, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, adjustedSrcRect);
+    platformContext->renderTarget()->DrawBitmap(bitmap.get(), destRect, opacity, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, adjustedSrcRect);
     context.flush();
 }
 
index 17d4891..0d9dc9f 100644 (file)
@@ -34,6 +34,7 @@
 #include "GraphicsContext.h"
 #include "IntRect.h"
 #include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
 #include "StrokeStyleApplier.h"
 #include <d2d1.h>
 #include <wtf/MathExtras.h>
@@ -249,7 +250,8 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point)
 
     ASSERT(applier);
 
-    GraphicsContext scratchContext(scratchRenderTarget(), GraphicsContext::BitmapRenderingContextType::GPUMemory);
+    PlatformContextDirect2D scratchContextD2D(scratchRenderTarget());
+    GraphicsContext scratchContext(&scratchContextD2D, GraphicsContext::BitmapRenderingContextType::GPUMemory);
     applier->strokeStyle(&scratchContext);
 
     BOOL containsPoint = false;
index 2fa3ef6..328bbcd 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "AffineTransform.h"
 #include "GraphicsContext.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #include <wtf/MainThread.h>
 
@@ -38,6 +39,14 @@ namespace WebCore {
 
 ID2D1BitmapBrush* Pattern::createPlatformPattern(const GraphicsContext& context, float alpha, const AffineTransform& userSpaceTransformation) const
 {
+    auto platformContext = context.platformContext();
+    RELEASE_ASSERT(platformContext);
+
+    return createPlatformPattern(*platformContext, alpha, userSpaceTransformation);
+}
+
+ID2D1BitmapBrush* Pattern::createPlatformPattern(PlatformGraphicsContext& context, float alpha, const AffineTransform& userSpaceTransformation) const
+{
     AffineTransform patternTransform = userSpaceTransformation * m_patternSpaceTransformation;
     auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
     bitmapBrushProperties.extendModeX = m_repeatX ? D2D1_EXTEND_MODE_WRAP : D2D1_EXTEND_MODE_CLAMP;
@@ -48,19 +57,15 @@ ID2D1BitmapBrush* Pattern::createPlatformPattern(const GraphicsContext& context,
     brushProperties.opacity = alpha;
 
     auto& patternImage = tileImage();
-
-    auto platformContext = context.platformContext();
-    RELEASE_ASSERT(platformContext);
-
-    auto nativeImage = patternImage.nativeImage(&context);
+    auto nativeImage = patternImage.nativeImage(nullptr);
 
     COMPtr<ID2D1Bitmap> bitmap;
-    HRESULT hr = platformContext->CreateBitmapFromWicBitmap(nativeImage.get(), &bitmap);
+    HRESULT hr = context.renderTarget()->CreateBitmapFromWicBitmap(nativeImage.get(), &bitmap);
     if (!SUCCEEDED(hr))
         return nullptr;
 
     ID2D1BitmapBrush* patternBrush = nullptr;
-    hr = platformContext->CreateBitmapBrush(bitmap.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
+    hr = context.renderTarget()->CreateBitmapBrush(bitmap.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
     ASSERT(SUCCEEDED(hr));
     return patternBrush;
 }
diff --git a/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp b/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp
new file mode 100644 (file)
index 0000000..56d7215
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "PlatformContextDirect2D.h"
+
+#if USE(DIRECT2D)
+
+#include "Direct2DOperations.h"
+#include <d2d1.h>
+
+namespace WebCore {
+
+// Encapsulates the additional painting state information we store for each
+// pushed graphics state.
+class PlatformContextDirect2D::State {
+public:
+    State() = default;
+};
+
+PlatformContextDirect2D::PlatformContextDirect2D(ID2D1RenderTarget* renderTarget)
+    : m_renderTarget(renderTarget)
+{
+    m_stateStack.append(State());
+    m_state = &m_stateStack.last();
+}
+
+void PlatformContextDirect2D::restore()
+{
+    ASSERT(m_renderTarget);
+
+    auto restoreState = m_renderStates.takeLast();
+    m_renderTarget->RestoreDrawingState(restoreState.m_drawingStateBlock.get());
+
+    for (auto clipType = restoreState.m_clips.rbegin(); clipType != restoreState.m_clips.rend(); ++clipType) {
+        if (*clipType == AxisAlignedClip)
+            m_renderTarget->PopAxisAlignedClip();
+        else
+            m_renderTarget->PopLayer();
+    }
+
+    m_stateStack.removeLast();
+    ASSERT(!m_stateStack.isEmpty());
+    m_state = &m_stateStack.last();
+}
+
+PlatformContextDirect2D::~PlatformContextDirect2D() = default;
+
+void PlatformContextDirect2D::save()
+{
+    ASSERT(m_renderTarget);
+
+    m_stateStack.append(State());
+    m_state = &m_stateStack.last();
+
+    RenderState currentState;
+    GraphicsContext::systemFactory()->CreateDrawingStateBlock(&currentState.m_drawingStateBlock);
+
+    m_renderTarget->SaveDrawingState(currentState.m_drawingStateBlock.get());
+
+    m_renderStates.append(currentState);
+}
+
+void PlatformContextDirect2D::pushRenderClip(Direct2DLayerType clipType)
+{
+    ASSERT(hasSavedState());
+    m_renderStates.last().m_clips.append(clipType);
+}
+
+void PlatformContextDirect2D::setActiveLayer(COMPtr<ID2D1Layer>&& layer)
+{
+    ASSERT(hasSavedState());
+    m_renderStates.last().m_activeLayer = layer;
+}
+
+COMPtr<ID2D1SolidColorBrush> PlatformContextDirect2D::brushWithColor(const D2D1_COLOR_F& color)
+{
+    RGBA32 colorKey = makeRGBA32FromFloats(color.r, color.g, color.b, color.a);
+
+    if (!colorKey) {
+        if (!m_zeroBrush)
+            m_renderTarget->CreateSolidColorBrush(color, &m_zeroBrush);
+        return m_zeroBrush;
+    }
+
+    if (colorKey == 0xFFFFFFFF) {
+        if (!m_whiteBrush)
+            m_renderTarget->CreateSolidColorBrush(color, &m_whiteBrush);
+        return m_whiteBrush;
+    }
+
+    auto existingBrush = m_solidColoredBrushCache.ensure(colorKey, [this, color] {
+        COMPtr<ID2D1SolidColorBrush> colorBrush;
+        m_renderTarget->CreateSolidColorBrush(color, &colorBrush);
+        return colorBrush;
+    });
+
+    return existingBrush.iterator->value;
+}
+
+void PlatformContextDirect2D::recomputeStrokeStyle()
+{
+    if (!m_strokeSyleIsDirty)
+        return;
+
+    m_d2dStrokeStyle = nullptr;
+
+    DashArray dashes;
+    float patternOffset = 0;
+    auto dashStyle = D2D1_DASH_STYLE_SOLID;
+
+    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_miterLimit, dashStyle, patternOffset);
+    GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes.data(), dashes.size(), &m_d2dStrokeStyle);
+
+    m_strokeSyleIsDirty = false;
+}
+
+ID2D1StrokeStyle* PlatformContextDirect2D::strokeStyle()
+{
+    recomputeStrokeStyle();
+    return m_d2dStrokeStyle.get();
+}
+
+D2D1_STROKE_STYLE_PROPERTIES PlatformContextDirect2D::strokeStyleProperties() const
+{
+    return D2D1::StrokeStyleProperties(m_lineCap, m_lineCap, m_lineCap, m_lineJoin, m_miterLimit, D2D1_DASH_STYLE_SOLID, 0.0f);
+}
+
+void PlatformContextDirect2D::setLineCap(D2D1_CAP_STYLE lineCap)
+{
+    if (m_lineCap == lineCap)
+        return;
+
+    m_lineCap = lineCap;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setLineJoin(D2D1_LINE_JOIN join)
+{
+    if (m_lineJoin == join)
+        return;
+
+    m_lineJoin = join;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setStrokeStyle(StrokeStyle strokeStyle)
+{
+    if (m_strokeStyle == strokeStyle)
+        return;
+
+    m_strokeStyle = strokeStyle;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setStrokeThickness(float thickness)
+{
+    if (WTF::areEssentiallyEqual(thickness, m_strokeThickness))
+        return;
+
+    m_strokeThickness = thickness;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::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;
+
+    m_miterLimit = miterLimit;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setDashOffset(float dashOffset)
+{
+    if (WTF::areEssentiallyEqual(dashOffset, m_dashOffset))
+        return;
+
+    m_dashOffset = dashOffset;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setDashes(const DashArray& dashes)
+{
+    if (m_dashes == dashes)
+        return;
+
+    m_dashes = dashes;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setPatternWidth(float patternWidth)
+{
+    if (WTF::areEssentiallyEqual(patternWidth, m_patternWidth))
+        return;
+
+    m_patternWidth = patternWidth;
+    m_strokeSyleIsDirty = true;
+}
+
+void PlatformContextDirect2D::setPatternOffset(float patternOffset)
+{
+    if (WTF::areEssentiallyEqual(patternOffset, m_patternOffset))
+        return;
+
+    m_patternOffset = patternOffset;
+    m_strokeSyleIsDirty = true;
+}
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
diff --git a/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h b/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h
new file mode 100644 (file)
index 0000000..c84cb9d
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#if USE(DIRECT2D)
+
+#include "COMPtr.h"
+#include "GraphicsContext.h"
+#include <d2d1.h>
+#include <d2d1_1.h>
+#include <d2d1effects.h>
+
+namespace WebCore {
+
+class GraphicsContextPlatformPrivate;
+struct GraphicsContextState;
+
+namespace Direct2D {
+struct FillSource;
+struct StrokeSource;
+struct ShadowState;
+}
+
+class PlatformContextDirect2D {
+    WTF_MAKE_NONCOPYABLE(PlatformContextDirect2D);
+public:
+    PlatformContextDirect2D(ID2D1RenderTarget*);
+    ~PlatformContextDirect2D();
+
+    ID2D1RenderTarget* renderTarget() { return m_renderTarget.get(); }
+    void setRenderTarget(ID2D1RenderTarget* renderTarget) { m_renderTarget = renderTarget; }
+
+    GraphicsContextPlatformPrivate* graphicsContextPrivate() { return m_graphicsContextPrivate; }
+    void setGraphicsContextPrivate(GraphicsContextPlatformPrivate* graphicsContextPrivate) { m_graphicsContextPrivate = graphicsContextPrivate; }
+
+    ID2D1Layer* clipLayer() const { return m_renderStates.last().m_activeLayer.get(); }
+    ID2D1StrokeStyle* strokeStyle();
+    D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties() const;
+    ID2D1StrokeStyle* platformStrokeStyle() const;
+
+    COMPtr<ID2D1SolidColorBrush> brushWithColor(const D2D1_COLOR_F&);
+
+    void save();
+    void restore();
+
+    bool hasSavedState() const { return !m_renderStates.isEmpty(); }
+
+    enum Direct2DLayerType { AxisAlignedClip, LayerClip };
+    void pushRenderClip(Direct2DLayerType);
+    void setActiveLayer(COMPtr<ID2D1Layer>&&);
+
+    void recomputeStrokeStyle();
+    float strokeThickness() const { return m_strokeThickness; }
+
+    struct RenderState {
+        COMPtr<ID2D1DrawingStateBlock> m_drawingStateBlock;
+        COMPtr<ID2D1Layer> m_activeLayer;
+        Vector<Direct2DLayerType> m_clips;
+    };
+    Vector<RenderState> m_renderStates;
+
+    struct TransparencyLayerState {
+        COMPtr<ID2D1BitmapRenderTarget> renderTarget;
+        Color shadowColor;
+        FloatSize shadowOffset;
+        float opacity { 1.0 };
+        float shadowBlur { 0 };
+        bool hasShadow { false };
+    };
+    Vector<TransparencyLayerState> m_transparencyLayerStack;
+
+    void setLineCap(D2D1_CAP_STYLE);
+    void setLineJoin(D2D1_LINE_JOIN);
+    void setStrokeStyle(StrokeStyle);
+    void setMiterLimit(float);
+    void setDashOffset(float);
+    void setPatternWidth(float);
+    void setPatternOffset(float);
+    void setStrokeThickness(float);
+    void setDashes(const DashArray&);
+
+    void setBlendMode(D2D1_BLEND_MODE mode) { m_blendMode = mode; }
+    void setCompositeMode(D2D1_COMPOSITE_MODE mode) { m_compositeMode = mode; }
+
+private:
+    GraphicsContextPlatformPrivate* m_graphicsContextPrivate { nullptr };
+
+    COMPtr<ID2D1RenderTarget> m_renderTarget;
+    COMPtr<ID2D1StrokeStyle> m_d2dStrokeStyle;
+
+    HashMap<uint32_t, COMPtr<ID2D1SolidColorBrush>> m_solidColoredBrushCache;
+    COMPtr<ID2D1SolidColorBrush> m_whiteBrush;
+    COMPtr<ID2D1SolidColorBrush> m_zeroBrush;
+    COMPtr<ID2D1SolidColorBrush> m_solidStrokeBrush;
+    COMPtr<ID2D1SolidColorBrush> m_solidFillBrush;
+    COMPtr<ID2D1BitmapBrush> m_patternStrokeBrush;
+    COMPtr<ID2D1BitmapBrush> m_patternFillBrush;
+
+    class State;
+    State* m_state { nullptr };
+    Vector<State> m_stateStack;
+
+    D2D1_CAP_STYLE m_lineCap { D2D1_CAP_STYLE_FLAT };
+    D2D1_LINE_JOIN m_lineJoin { D2D1_LINE_JOIN_MITER };
+    StrokeStyle m_strokeStyle { SolidStroke };
+    DashArray m_dashes;
+
+    float m_miterLimit { 10.0f };
+    float m_dashOffset { 0 };
+    float m_patternWidth { 1.0f };
+    float m_patternOffset { 0 };
+    float m_strokeThickness { 0 };
+    float m_alpha { 1.0 };
+
+    bool m_strokeSyleIsDirty { false };
+
+    D2D1_BLEND_MODE m_blendMode { D2D1_BLEND_MODE_MULTIPLY };
+    D2D1_COMPOSITE_MODE m_compositeMode { D2D1_COMPOSITE_MODE_SOURCE_OVER };
+
+    friend class GraphicsContext;
+    friend class GraphicsContextPlatformPrivate;
+};
+
+} // namespace WebCore
+
+#endif // USE(DIRECT2D)
index e3c57ae..7b21beb 100644 (file)
@@ -34,6 +34,7 @@
 #include "HWndDC.h"
 #include "Image.h"
 #include "NotImplemented.h"
+#include "PlatformContextDirect2D.h"
 
 #include <d2d1.h>
 #include <windows.h>
 
 namespace WebCore {
 
-void deallocContext(ID2D1RenderTarget* renderTarget)
+void deallocContext(PlatformContextDirect2D* platformContext)
 {
-    if (renderTarget)
-        renderTarget->Release();
+    if (platformContext) {
+        if (auto* renderTarget = platformContext->renderTarget())
+            renderTarget->Release();
+    }
 }
 
-GDIObject<HBITMAP> allocImage(HDC dc, IntSize size, ID2D1RenderTarget** targetRef)
+GDIObject<HBITMAP> allocImage(HDC dc, IntSize size, PlatformContextDirect2D** platformContext)
 {
     BitmapInfo bmpInfo = BitmapInfo::create(size);
 
     LPVOID bits = nullptr;
     auto hbmp = adoptGDIObject(::CreateDIBSection(dc, &bmpInfo, DIB_RGB_COLORS, &bits, 0, 0));
 
-    if (!targetRef || !hbmp)
+    if (!platformContext || !hbmp)
         return hbmp;
 
     // FIXME: Use GDI Interop layer to create HBITMAP from D2D Bitmap
index 4f7b3b9..40ee991 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006 Eric Seidel <eric@webkit.org>
- * Copyright (C) 2008-2009, 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -68,7 +68,9 @@
 
 #if USE(DIRECT2D)
 #include "COMPtr.h"
+#include "Direct2DUtilities.h"
 #include "ImageDecoderDirect2D.h"
+#include "PlatformContextDirect2D.h"
 #include <d2d1.h>
 #endif
 
@@ -238,17 +240,12 @@ NativeImagePtr SVGImage::nativeImage(const GraphicsContext* targetContext)
     if (!SUCCEEDED(hr))
         return nullptr;
 
-    auto targetProperties = 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);
-
-    // Draw the SVG into a bitmap.
-    COMPtr<ID2D1RenderTarget> nativeImageTarget;
-    hr = GraphicsContext::systemFactory()->CreateWicBitmapRenderTarget(nativeImage.get(), &targetProperties, &nativeImageTarget);
-    if (!nativeImageTarget || !SUCCEEDED(hr))
+    COMPtr<ID2D1RenderTarget> nativeImageTarget = Direct2D::createRenderTargetFromWICBitmap(nativeImage.get());
+    if (!nativeImageTarget)
         return nullptr;
 
-    GraphicsContext localContext(nativeImageTarget.get(), GraphicsContext::BitmapRenderingContextType::CPUMemory);
+    PlatformContextDirect2D platformContext(nativeImageTarget.get());
+    GraphicsContext localContext(&platformContext, GraphicsContext::BitmapRenderingContextType::GPUMemory);
 
     draw(localContext, rect(), rect(), CompositeSourceOver, BlendMode::Normal, DecodingMode::Synchronous, ImageOrientationDescription());