Title: [206773] trunk/Source/WebCore
Revision
206773
Author
[email protected]
Date
2016-10-04 11:31:21 -0700 (Tue, 04 Oct 2016)

Log Message

[Win][Direct2D] Add initial D2D GraphicsContext implementation
https://bugs.webkit.org/show_bug.cgi?id=162917

Reviewed by Dean Jackson.

This patch lands a set of new files that implement the GraphicsContext
features on Windows using Direct2D.

No new tests until complete backend lands.

* platform/graphics/Color.h: Add new casting operator.
* platform/graphics/FloatSize.h: Ditto.
* platform/graphics/GraphicsContext.cpp: Update for Direct2D
implementation.
* platform/graphics/GraphicsContext.h:
* platform/graphics/win/ColorDirect2D.cpp:
(WebCore::Color::operator D2D1_VECTOR_4F): Added.
* platform/graphics/win/GraphicsContextCGWin.cpp: Add compiler
guard to avoid building when using Direct2D.
* platform/graphics/win/GraphicsContextDirect2D.cpp: Added.
* platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h: Added.
* platform/graphics/win/GraphicsContextWin.cpp: Update for Direct2D
includes.
* platform/graphics/win/TransformationMatrixDirect2D.cpp: Fix comment.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (206772 => 206773)


--- trunk/Source/WebCore/ChangeLog	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/ChangeLog	2016-10-04 18:31:21 UTC (rev 206773)
@@ -1,3 +1,30 @@
+2016-10-04  Brent Fulgham  <[email protected]>
+
+        [Win][Direct2D] Add initial D2D GraphicsContext implementation
+        https://bugs.webkit.org/show_bug.cgi?id=162917
+
+        Reviewed by Dean Jackson.
+
+        This patch lands a set of new files that implement the GraphicsContext
+        features on Windows using Direct2D.
+
+        No new tests until complete backend lands.
+
+        * platform/graphics/Color.h: Add new casting operator.
+        * platform/graphics/FloatSize.h: Ditto.
+        * platform/graphics/GraphicsContext.cpp: Update for Direct2D
+        implementation.
+        * platform/graphics/GraphicsContext.h:
+        * platform/graphics/win/ColorDirect2D.cpp:
+        (WebCore::Color::operator D2D1_VECTOR_4F): Added.
+        * platform/graphics/win/GraphicsContextCGWin.cpp: Add compiler
+        guard to avoid building when using Direct2D.
+        * platform/graphics/win/GraphicsContextDirect2D.cpp: Added.
+        * platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h: Added.
+        * platform/graphics/win/GraphicsContextWin.cpp: Update for Direct2D
+        includes.
+        * platform/graphics/win/TransformationMatrixDirect2D.cpp: Fix comment.
+
 2016-10-04  Carlos Garcia Campos  <[email protected]>
 
         [SOUP] Remove SSLPolicyFlags from SoupNetworkSession

Modified: trunk/Source/WebCore/platform/graphics/Color.h (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/Color.h	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/Color.h	2016-10-04 18:31:21 UTC (rev 206773)
@@ -44,6 +44,8 @@
 typedef _D3DCOLORVALUE D3DCOLORVALUE;
 typedef D3DCOLORVALUE D2D_COLOR_F;
 typedef D2D_COLOR_F D2D1_COLOR_F;
+struct D2D_VECTOR_4F;
+typedef D2D_VECTOR_4F D2D1_VECTOR_4F;
 #endif
 
 #if PLATFORM(GTK)
@@ -184,6 +186,7 @@
 #if PLATFORM(WIN)
     WEBCORE_EXPORT Color(D2D1_COLOR_F);
     WEBCORE_EXPORT operator D2D1_COLOR_F() const;
+    WEBCORE_EXPORT operator D2D1_VECTOR_4F() const;
 #endif
 
     static bool parseHexColor(const String&, RGBA32&);

Modified: trunk/Source/WebCore/platform/graphics/FloatSize.h (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/FloatSize.h	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/FloatSize.h	2016-10-04 18:31:21 UTC (rev 206773)
@@ -130,7 +130,7 @@
 #endif
 
 #if PLATFORM(WIN)
-    WEBCORE_EXPORT explicit FloatSize(const D2D1_SIZE_F&); // don't do this implicitly since it's lossy
+    WEBCORE_EXPORT FloatSize(const D2D1_SIZE_F&);
     operator D2D1_SIZE_F() const;
 #endif
 

Modified: trunk/Source/WebCore/platform/graphics/GraphicsContext.cpp (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/GraphicsContext.cpp	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/GraphicsContext.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -834,7 +834,7 @@
     clipOut(path);
 }
 
-#if !USE(CG) && !USE(CAIRO)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
 IntRect GraphicsContext::clipBounds() const
 {
     ASSERT_NOT_REACHED();
@@ -902,7 +902,7 @@
         fillRect(rect.rect(), color, compositeOperation(), blendMode);
 }
 
-#if !USE(CG) && !USE(CAIRO)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
 {
     if (paintingDisabled())
@@ -957,7 +957,7 @@
         m_displayListRecorder->updateState(m_state, GraphicsContextState::DrawLuminanceMaskChange);
 }
 
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
 // Implement this if you want to go push the drawing mode into your native context immediately.
 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
 {
@@ -970,13 +970,13 @@
 }
 #endif
 
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
 {
 }
 #endif
 
-#if !USE(CG) && !USE(CAIRO)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
 bool GraphicsContext::isAcceleratedContext() const
 {
     return false;
@@ -1012,7 +1012,7 @@
     }
 }
 
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
 void GraphicsContext::platformApplyDeviceScaleFactor(float)
 {
 }
@@ -1060,7 +1060,7 @@
     strokePath(path);
 }
 
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
 {
     if (paintingDisabled())

Modified: trunk/Source/WebCore/platform/graphics/GraphicsContext.h (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/GraphicsContext.h	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/GraphicsContext.h	2016-10-04 18:31:21 UTC (rev 206773)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2008-2009 Torch Mobile, Inc.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,11 @@
 
 #if USE(CG)
 typedef struct CGContext PlatformGraphicsContext;
+#elif USE(DIRECT2D)
+interface ID2D1DCRenderTarget;
+interface ID2D1RenderTarget;
+interface ID2D1Factory;
+typedef ID2D1RenderTarget PlatformGraphicsContext;
 #elif USE(CAIRO)
 namespace WebCore {
 class PlatformContextCairo;
@@ -300,11 +305,11 @@
 
     const GraphicsContextState& state() const { return m_state; }
 
-#if USE(CG) || USE(CAIRO)
+#if USE(CG) || USE(DIRECT2D) || USE(CAIRO)
     WEBCORE_EXPORT void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = ImageOrientation());
 #endif
 
-#if USE(CG)
+#if USE(CG) || USE(DIRECT2D)
     void applyStrokePattern();
     void applyFillPattern();
     void drawPath(const Path&);
@@ -504,6 +509,7 @@
     void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height);
 #else
     GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed.
+    GraphicsContext(HDC, ID2D1DCRenderTarget**, RECT, bool hasAlpha = false); // FIXME: To be removed.
 
     // When set to true, child windows should be rendered into this context
     // rather than allowing them just to render to the screen. Defaults to
@@ -540,6 +546,15 @@
     // The bitmap should be non-premultiplied.
     void drawWindowsBitmap(WindowsBitmap*, const IntPoint&);
 #endif
+    WEBCORE_EXPORT static ID2D1Factory* systemFactory();
+    WEBCORE_EXPORT static ID2D1RenderTarget* defaultRenderTarget();
+
+    WEBCORE_EXPORT void setDidBeginDraw(bool);
+    WEBCORE_EXPORT bool didBeginDraw() const;
+    D2D1_COLOR_F colorWithGlobalAlpha(const Color&) const;
+
+    ID2D1SolidColorBrush* solidStrokeBrush();
+    ID2D1SolidColorBrush* solidFillBrush();
 #else // PLATFORM(WIN)
     bool shouldIncludeChildWindows() const { return false; }
 #endif // PLATFORM(WIN)
@@ -557,8 +572,15 @@
 
 #if PLATFORM(WIN) && !USE(WINGDI)
     void platformInit(HDC, bool hasAlpha = false);
+    void platformInit(HDC, ID2D1RenderTarget**, RECT, bool hasAlpha = false);
+    void platformInit(ID2D1RenderTarget*);
 #endif
 
+#if USE(DIRECT2D)
+    void drawWithoutShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&);
+    void drawWithShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&);
+#endif
+
     void savePlatformState();
     void restorePlatformState();
 

Modified: trunk/Source/WebCore/platform/graphics/win/ColorDirect2D.cpp (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/ColorDirect2D.cpp	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/win/ColorDirect2D.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -29,6 +29,7 @@
 #if PLATFORM(WIN)
 
 #include <d2d1.h>
+#include <d2d1_1helper.h>
 
 namespace WebCore {
 
@@ -45,6 +46,13 @@
     return D2D1::ColorF(rgb(), colorAlpha);
 }
 
+Color::operator D2D1_VECTOR_4F() const
+{
+    float r, g, b, a;
+    getRGBA(r, g, b, a);
+    return D2D1::Vector4F(r, g, b, a);
 }
 
+}
+
 #endif // PLATFORM(WIN)

Modified: trunk/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -26,6 +26,8 @@
 #include "config.h"
 #include "GraphicsContextCG.h"
 
+#if USE(CG)
+
 #include "AffineTransform.h"
 #include "GraphicsContextPlatformPrivateCG.h"
 #include "Path.h"
@@ -262,3 +264,4 @@
 }
 
 }
+#endif

Added: trunk/Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp (0 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -0,0 +1,1650 @@
+/*
+ * Copyright (C) 2016 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 "GraphicsContext.h"
+
+#include "COMPtr.h"
+#include "CurrentTime.h"
+#include "DisplayListRecorder.h"
+#include "FloatRoundedRect.h"
+#include "GraphicsContextPlatformPrivateDirect2D.h"
+#include "ImageBuffer.h"
+#include "Logging.h"
+#include "NotImplemented.h"
+#include "URL.h"
+#include <d2d1.h>
+#include <d2d1effects.h>
+#include <dwrite.h>
+
+#pragma warning (disable : 4756)
+
+using namespace std;
+
+namespace WebCore {
+
+GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha)
+{
+    platformInit(hdc, hasAlpha);
+}
+
+GraphicsContext::GraphicsContext(HDC hdc, ID2D1DCRenderTarget** renderTarget, RECT rect, bool hasAlpha)
+{
+    m_data->m_hdc = hdc;
+
+    // Create a DC render target.
+    auto targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
+        0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT);
+
+    HRESULT hr = GraphicsContext::systemFactory()->CreateDCRenderTarget(&targetProperties, renderTarget);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    (*renderTarget)->BindDC(hdc, &rect);
+
+    m_data = new GraphicsContextPlatformPrivate(*renderTarget);
+}
+
+ID2D1Factory* GraphicsContext::systemFactory()
+{
+    static ID2D1Factory* direct2DFactory = nullptr;
+    if (!direct2DFactory) {
+        HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &direct2DFactory);
+        RELEASE_ASSERT(SUCCEEDED(hr));
+    }
+
+    return direct2DFactory;
+}
+
+ID2D1RenderTarget* GraphicsContext::defaultRenderTarget()
+{
+    static ID2D1RenderTarget* defaultRenderTarget = nullptr;
+    if (!defaultRenderTarget) {
+        auto renderTargetProperties = D2D1::RenderTargetProperties();
+        renderTargetProperties.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
+        auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(::GetDesktopWindow(), D2D1::SizeU(10, 10));
+        HRESULT hr = systemFactory()->CreateHwndRenderTarget(&renderTargetProperties, &hwndRenderTargetProperties, reinterpret_cast<ID2D1HwndRenderTarget**>(&defaultRenderTarget));
+        RELEASE_ASSERT(SUCCEEDED(hr));
+    }
+
+    return defaultRenderTarget;
+}
+
+void GraphicsContext::setDidBeginDraw(bool didBeginDraw)
+{
+    RELEASE_ASSERT(m_data);
+    m_data->m_didBeginDraw = didBeginDraw;
+}
+
+bool GraphicsContext::didBeginDraw() const
+{
+    return m_data->m_didBeginDraw;
+}
+
+void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
+{
+    if (!hdc)
+        return;
+
+    COMPtr<ID2D1RenderTarget> renderTarget;
+    m_data = new GraphicsContextPlatformPrivate(renderTarget.get());
+    m_data->m_hdc = hdc;
+    // Make sure the context starts in sync with our state.
+    setPlatformFillColor(fillColor());
+    setPlatformStrokeColor(strokeColor());
+    setPlatformStrokeThickness(strokeThickness());
+    // FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
+}
+
+void GraphicsContext::platformInit(ID2D1RenderTarget* renderTarget)
+{
+    if (!renderTarget)
+        return;
+
+    m_data = new GraphicsContextPlatformPrivate(renderTarget);
+
+    // Make sure the context starts in sync with our state.
+    setPlatformFillColor(fillColor());
+    setPlatformStrokeColor(strokeColor());
+    setPlatformStrokeThickness(strokeThickness());
+    // FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
+}
+
+void GraphicsContext::platformDestroy()
+{
+    delete m_data;
+}
+
+ID2D1RenderTarget* GraphicsContext::platformContext() const
+{
+    ASSERT(!paintingDisabled());
+    ASSERT(m_data->renderTarget());
+    return m_data->renderTarget();
+}
+
+void GraphicsContext::savePlatformState()
+{
+    ASSERT(!paintingDisabled());
+    ASSERT(!isRecording());
+
+    // 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();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+    ASSERT(!paintingDisabled());
+    ASSERT(!isRecording());
+
+    // 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<ID2D1Bitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        // 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())
+        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));
+    });
+
+    if (!stateSaver.didSave())
+        context->SetTransform(ctm);
+}
+
+void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
+{
+}
+
+void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point)
+{
+}
+
+void GraphicsContext::drawFocusRing(const Path& path, float width, float offset, const Color& color)
+{
+}
+
+void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
+{
+}
+
+void GraphicsContext::updateDocumentMarkerResources()
+{
+}
+
+void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& point, float width, DocumentMarkerLineStyle style)
+{
+}
+
+COMPtr<ID2D1SolidColorBrush> GraphicsContextPlatformPrivate::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] {
+        ID2D1SolidColorBrush* colorBrush;
+        m_renderTarget->CreateSolidColorBrush(color, &colorBrush);
+        return colorBrush;
+    });
+
+    return existingBrush.iterator->value;
+}
+
+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);
+
+    m_renderTarget->SetTransform(affineTransform * AffineTransform(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::endDraw()
+{
+    ASSERT(m_renderTarget.get());
+    D2D1_TAG first, second;
+    HRESULT hr = m_renderTarget->EndDraw(&first, &second);
+
+    RELEASE_ASSERT(SUCCEEDED(hr));
+}
+
+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);
+}
+
+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
+{
+    float colorAlpha = color.alpha() / 255.0f;
+    float globalAlpha = m_state.alpha;
+
+    return D2D1::ColorF(color.rgb(), globalAlpha * colorAlpha);
+}
+
+ID2D1SolidColorBrush* GraphicsContext::solidStrokeBrush()
+{
+    return m_data->m_solidStrokeBrush.get();
+}
+
+ID2D1SolidColorBrush* GraphicsContext::solidFillBrush()
+{
+    return m_data->m_solidFillBrush.get();
+}
+
+void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
+{
+    if (paintingDisabled() || !patternTransform.isInvertible())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode);
+        return;
+    }
+
+    notImplemented();
+}
+
+void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
+{
+    if (paintingDisabled())
+        return;
+
+    FloatSize bufferDestinationSize = buffer.sizeForDestinationSize(destRect.size());
+    notImplemented();
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawRect(rect, 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->DrawRectangle(&d2dRect, solidStrokeBrush(), strokeThickness());
+        renderTarget->FillRectangle(&d2dRect, solidFillBrush());
+    });
+}
+
+// This is only used to draw borders.
+void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
+{
+    if (paintingDisabled())
+        return;
+
+    if (strokeStyle() == NoStroke)
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawLine(point1, point2);
+        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 = strokeStyle == DottedStroke ? thickness : std::min(2 * thickness, std::max(thickness, strokeWidth / 3));
+        strokeWidth -= 2 * cornerWidth;
+        float patternWidth = strokeStyle == DottedStroke ? thickness : std::min(3 * thickness, std::max(thickness, strokeWidth / 3));
+        // Check if corner drawing sufficiently covers the line.
+        if (strokeWidth <= patternWidth + 1)
+            return;
+
+        // 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 = floorf(strokeWidth / patternWidth);
+        bool oddNumberOfSegments = numberOfSegments % 2;
+        float remainder = strokeWidth - (numberOfSegments * patternWidth);
+        if (oddNumberOfSegments && remainder)
+            patternOffset -= remainder / 2;
+        else if (!oddNumberOfSegments) {
+            if (remainder)
+                patternOffset += patternOffset - (patternWidth + remainder) / 2;
+            else
+                patternOffset += patternWidth / 2;
+        }
+        const float dashes[2] = { patternWidth, patternWidth };
+
+        auto strokeStyleProperties = D2D1::StrokeStyleProperties();
+
+        GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes, ARRAYSIZE(dashes), &d2dStrokeStyle);
+    }
+
+    FloatPoint p1 = point1;
+    FloatPoint p2 = point2;
+    // Center line and cut off corners for pattern patining.
+    if (isVerticalLine) {
+        float centerOffset = (p2.x() - p1.x()) / 2;
+        p1.move(centerOffset, cornerWidth);
+        p2.move(-centerOffset, -cornerWidth);
+    } else {
+        float centerOffset = (p2.y() - p1.y()) / 2;
+        p1.move(cornerWidth, centerOffset);
+        p2.move(-cornerWidth, -centerOffset);
+    }
+
+    context->SetTags(1, __LINE__);
+
+    FloatRect boundingRect(p1, p2);
+
+    drawWithoutShadow(boundingRect, [this, p1, p2, d2dStrokeStyle](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawLine(p1, p2, solidStrokeBrush(), strokeThickness(), d2dStrokeStyle.get());
+    });
+}
+
+void GraphicsContext::drawEllipse(const FloatRect& rect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawEllipse(rect);
+        return;
+    }
+
+    auto ellipse = D2D1::Ellipse(D2D1::Point2F(rect.x(), rect.y()), rect.width(), rect.height());
+
+    auto context = platformContext();
+
+    context->SetTags(1, __LINE__);
+
+    drawWithoutShadow(rect, [this, ellipse](ID2D1RenderTarget* renderTarget) {
+        renderTarget->FillEllipse(&ellipse, solidFillBrush());
+
+        renderTarget->DrawEllipse(&ellipse, solidStrokeBrush(), strokeThickness());
+    });
+}
+
+void GraphicsContext::applyStrokePattern()
+{
+    if (paintingDisabled())
+        return;
+
+    // Note: Because of the way Direct2D draws, we may not need this explicit context 'set stroke pattern' logic.
+    notImplemented();
+}
+
+void GraphicsContext::applyFillPattern()
+{
+    if (paintingDisabled())
+        return;
+
+    // Note: Because of the way Direct2D draws, we may not need this explicit context 'set fill pattern' logic.
+    notImplemented();
+}
+
+void GraphicsContext::drawPath(const Path& path)
+{
+    if (paintingDisabled() || path.isEmpty())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawPath(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) {
+        renderTarget->DrawGeometry(path.platformPath(), solidStrokeBrush());
+    });
+}
+
+void GraphicsContext::drawWithoutShadow(const FloatRect& /*boundingRect*/, const std::function<void(ID2D1RenderTarget*)>& drawCommands)
+{
+    auto context = platformContext();
+
+    if (!didBeginDraw())
+        context->BeginDraw();
+
+    drawCommands(context);
+
+    if (!didBeginDraw())
+        m_data->endDraw();
+}
+
+void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::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));
+
+    COMPtr<ID2D1DeviceContext> deviceContext;
+    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.get());
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, static_cast<D2D1_VECTOR_4F>(m_state.shadowColor));
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, m_state.shadowBlur);
+
+    COMPtr<ID2D1Effect> transformEffect;
+    hr = deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &transformEffect);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    transformEffect->SetInputEffect(0, shadowEffect.get());
+
+    auto translation = D2D1::Matrix3x2F::Translation(m_state.shadowOffset.width(), m_state.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.get());
+
+    // 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->BeginDraw();
+    deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
+    deviceContext->EndDraw();
+}
+
+void GraphicsContext::fillPath(const Path& path)
+{
+    if (paintingDisabled() || path.isEmpty())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->fillPath(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();
+        auto drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
+            renderTarget->FillGeometry(path.platformPath(), m_state.fillGradient->createPlatformGradientIfNecessary(renderTarget));
+        };
+
+        if (hasShadow())
+            drawWithShadow(boundingRect, drawFunction);
+        else
+            drawWithoutShadow(boundingRect, drawFunction);
+        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) {
+        renderTarget->FillGeometry(pathToFill.get(), solidFillBrush());
+    });
+}
+
+void GraphicsContext::strokePath(const Path& path)
+{
+    if (paintingDisabled() || path.isEmpty())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->strokePath(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();
+        auto drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
+            renderTarget->DrawGeometry(path.platformPath(), m_state.strokeGradient->createPlatformGradientIfNecessary(renderTarget));
+        };
+
+        if (hasShadow())
+            drawWithShadow(boundingRect, drawFunction);
+        else
+            drawWithoutShadow(boundingRect, drawFunction);
+
+        return;
+    }
+
+    if (m_state.strokePattern)
+        applyStrokePattern();
+
+    context->SetTags(1, __LINE__);
+
+    FloatRect contextRect(FloatPoint(), context->GetSize());
+    drawWithoutShadow(contextRect, [this, &path](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawGeometry(path.platformPath(), solidStrokeBrush(), strokeThickness());
+    });
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->fillRect(rect);
+        return;
+    }
+
+    auto context = platformContext();
+
+    if (m_state.fillGradient) {
+        context->SetTags(1, __LINE__);
+        D2DContextStateSaver stateSaver(*m_data);
+        auto 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;
+        renderTarget->FillRectangle(&d2dRect, solidFillBrush());
+    });
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->fillRect(rect, 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, m_data->brushWithColor(colorWithGlobalAlpha(color)).get());
+    });
+}
+
+void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    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();
+    }
+
+    if (!didBeginDraw())
+        context->BeginDraw();
+    
+    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());
+        COMPtr<ID2D1SolidColorBrush> fillBrush = m_data->brushWithColor(colorWithGlobalAlpha(color));
+        context->FillRoundedRectangle(roundedRect, fillBrush.get());
+    } else {
+        D2DContextStateSaver stateSaver(*m_data);
+        setFillColor(color);
+
+        Path path;
+        path.addRoundedRect(rect);
+        fillPath(path);
+    }
+
+    if (!didBeginDraw())
+        m_data->endDraw();
+
+    if (drawOwnShadow)
+        stateSaver.restore();
+}
+
+void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->fillRectWithRoundedHole(rect, roundedHoleRect, color);
+        return;
+    }
+
+    auto context = platformContext();
+
+    if (!didBeginDraw())
+        context->BeginDraw();
+
+    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(RULE_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 (!didBeginDraw())
+        m_data->endDraw();
+
+    if (drawOwnShadow)
+        stateSaver.restore();
+
+    setFillRule(oldFillRule);
+    setFillColor(oldFillColor);
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->clip(rect);
+        return;
+    }
+
+    m_data->clip(rect);
+}
+
+void GraphicsContext::clipOut(const FloatRect& rect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->clipOut(rect);
+        return;
+    }
+
+    Path path;
+    path.addRect(rect);
+
+    clipOut(path);
+}
+
+void GraphicsContext::clipOut(const Path& path)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->clipOut(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(RULE_EVENODD, pathToClip);
+
+    m_data->clip(pathToClip.get());
+}
+
+void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->clipPath(path, clipRule);
+        return;
+    }
+
+    auto context = platformContext();
+    if (path.isEmpty()) {
+        m_data->clip(FloatRect());
+        return;
+    }
+
+    COMPtr<ID2D1GeometryGroup> pathToClip;
+    path.createGeometryWithFillMode(clipRule, pathToClip);
+
+    m_data->clip(pathToClip.get());
+}
+
+IntRect GraphicsContext::clipBounds() const
+{
+    if (paintingDisabled())
+        return IntRect();
+
+    if (isRecording()) {
+        WTFLogAlways("Getting the clip bounds not yet supported with display lists");
+        return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
+    }
+
+    D2D1_RECT_F d2dClipBounds = D2D1::InfiniteRect();
+    FloatRect clipBounds(d2dClipBounds.left, d2dClipBounds.top, d2dClipBounds.right - d2dClipBounds.left, d2dClipBounds.bottom - d2dClipBounds.top);
+
+    if (m_data->clipLayer()) {
+        auto clipSize = m_data->clipLayer()->GetSize();
+        clipBounds.setWidth(clipSize.width);
+        clipBounds.setHeight(clipSize.height);
+    }
+
+    return enclosingIntRect(clipBounds);
+}
+
+void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    save();
+
+    notImplemented();
+}
+
+void GraphicsContext::endPlatformTransparencyLayer()
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    notImplemented();
+
+    restore();
+}
+
+bool GraphicsContext::supportsTransparencyLayers()
+{
+    return false;
+}
+
+void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color)
+{
+    (void)offset;
+    (void)blur;
+    (void)color;
+    notImplemented();
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+    if (paintingDisabled())
+        return;
+    notImplemented();
+}
+
+void GraphicsContext::setMiterLimit(float limit)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        // Maybe this should be part of the state.
+        m_displayListRecorder->setMiterLimit(limit);
+        return;
+    }
+
+    notImplemented();
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->clearRect(rect);
+        return;
+    }
+
+    auto context = platformContext();
+
+    context->SetTags(1, __LINE__);
+
+    drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
+        FloatSize renderTargetSize = renderTarget->GetSize();
+        if (rect.size() == renderTargetSize)
+            renderTarget->Clear();
+        else
+            renderTarget->FillRectangle(rect, m_data->brushWithColor(colorWithGlobalAlpha(fillColor())).get());
+    });
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+    (void)rect;
+    (void)lineWidth;
+    notImplemented();
+}
+
+void GraphicsContext::setLineCap(LineCap cap)
+{
+    (void)cap;
+    notImplemented();
+}
+
+void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->setLineDash(dashes, dashOffset);
+        return;
+    }
+
+    if (dashOffset < 0) {
+        float length = 0;
+        for (size_t i = 0; i < dashes.size(); ++i)
+            length += static_cast<float>(dashes[i]);
+        if (length)
+            dashOffset = fmod(dashOffset, length) + length;
+    }
+
+    notImplemented();
+}
+
+void GraphicsContext::setLineJoin(LineJoin join)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->setLineJoin(join);
+        return;
+    }
+
+    notImplemented();
+}
+
+void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
+{
+    clipPath(path, fillRule);
+}
+
+void GraphicsContext::scale(const FloatSize& size)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->scale(size);
+        return;
+    }
+
+    m_data->scale(size);
+    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
+}
+
+void GraphicsContext::rotate(float angle)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->rotate(angle);
+        return;
+    }
+
+    m_data->rotate(angle);
+    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
+}
+
+void GraphicsContext::translate(float x, float y)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->translate(x, y);
+        return;
+    }
+
+    m_data->translate(x, y);
+    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
+}
+
+void GraphicsContext::concatCTM(const AffineTransform& transform)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->concatCTM(transform);
+        return;
+    }
+
+    m_data->concatCTM(transform);
+    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
+}
+
+void GraphicsContext::setCTM(const AffineTransform& transform)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts.");
+        return;
+    }
+
+    m_data->setCTM(transform);
+    // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
+}
+
+AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
+{
+    if (paintingDisabled())
+        return AffineTransform();
+
+    if (isRecording()) {
+        WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
+        return AffineTransform();
+    }
+
+    D2D1_MATRIX_3X2_F currentTransform;
+    platformContext()->GetTransform(&currentTransform);
+    return currentTransform;
+}
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
+{
+    if (paintingDisabled())
+        return rect;
+
+    if (isRecording()) {
+        WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
+        return rect;
+    }
+
+    notImplemented();
+
+    return rect;
+}
+
+void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing, bool doubleLines, StrokeStyle strokeStyle)
+{
+    DashArray widths;
+    widths.append(width);
+    widths.append(0);
+    drawLinesForText(point, widths, printing, doubleLines, strokeStyle);
+}
+
+void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
+{
+    if (paintingDisabled())
+        return;
+
+    if (!widths.size())
+        return;
+
+    if (isRecording()) {
+        m_displayListRecorder->drawLinesForText(point, widths, printing, doubleLines, strokeThickness());
+        return;
+    }
+
+    notImplemented();
+}
+
+void GraphicsContext::setURLForRect(const URL& link, const IntRect& destRect)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording()) {
+        WTFLogAlways("GraphicsContext::setURLForRect() is not yet compatible with recording contexts.");
+        return; // FIXME for display lists.
+    }
+
+    RetainPtr<CFURLRef> urlRef = link.createCFURL();
+    if (!urlRef)
+        return;
+
+    notImplemented();
+}
+
+void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality mode)
+{
+    ASSERT(!paintingDisabled());
+
+    D2D1_INTERPOLATION_MODE quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+
+    switch (mode) {
+    case InterpolationDefault:
+        quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+        break;
+    case InterpolationNone:
+        quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+        break;
+    case InterpolationLow:
+        quality = D2D1_INTERPOLATION_MODE_LINEAR;
+        break;
+    case InterpolationMedium:
+        quality = D2D1_INTERPOLATION_MODE_CUBIC;
+        break;
+    case InterpolationHigh:
+        quality = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
+        break;
+    }
+    // FIXME: SetInterpolationQuality(platformContext(), quality);
+}
+
+void GraphicsContext::setIsCALayerContext(bool isLayerContext)
+{
+    if (paintingDisabled())
+        return;
+
+    if (isRecording())
+        return;
+
+    // This function is probabaly not needed.
+    notImplemented();
+}
+
+bool GraphicsContext::isCALayerContext() const
+{
+    if (paintingDisabled())
+        return false;
+
+    // FIXME
+    if (isRecording())
+        return false;
+
+    // This function is probabaly not needed.
+    notImplemented();
+    return false;
+}
+
+void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
+{
+    if (paintingDisabled())
+        return;
+
+    // FIXME
+    if (isRecording())
+        return;
+
+    notImplemented();
+}
+
+bool GraphicsContext::isAcceleratedContext() const
+{
+    if (paintingDisabled())
+        return false;
+
+    // FIXME
+    if (isRecording())
+        return false;
+
+    // This function is probabaly not needed.
+    notImplemented();
+    return false;
+}
+
+void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
+{
+    (void)mode;
+    notImplemented();
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& color)
+{
+    ASSERT(m_state.strokeColor == color);
+
+    m_data->m_solidStrokeBrush = nullptr;
+
+    m_data->m_solidStrokeBrush = m_data->brushWithColor(colorWithGlobalAlpha(strokeColor()));
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float thickness)
+{
+    // This is a no-op on Windows. We fill using the GraphicsContextState::strokeThickness member.
+    ASSERT(m_state.strokeThickness == thickness);
+}
+
+void GraphicsContext::setPlatformFillColor(const Color& color)
+{
+    ASSERT(m_state.fillColor == color);
+
+    m_data->m_solidFillBrush = nullptr;
+
+    m_data->m_solidFillBrush = m_data->brushWithColor(colorWithGlobalAlpha(fillColor()));
+}
+
+void GraphicsContext::setPlatformShouldAntialias(bool enable)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    auto antialiasMode = enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED;
+    platformContext()->SetAntialiasMode(antialiasMode);
+}
+
+void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    auto fontSmoothingMode = enable ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+    platformContext()->SetTextAntialiasMode(fontSmoothingMode);
+}
+
+void GraphicsContext::setPlatformAlpha(float)
+{
+    /* No-op on this platform */
+}
+
+void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode blendMode)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    D2D1_BLEND_MODE targetBlendMode = D2D1_BLEND_MODE_SCREEN;
+    D2D1_COMPOSITE_MODE targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP; // ???
+
+    if (blendMode != BlendModeNormal) {
+        switch (blendMode) {
+        case BlendModeMultiply:
+            targetBlendMode = D2D1_BLEND_MODE_MULTIPLY;
+            break;
+        case BlendModeScreen:
+            targetBlendMode = D2D1_BLEND_MODE_SCREEN;
+            break;
+        case BlendModeOverlay:
+            targetBlendMode = D2D1_BLEND_MODE_OVERLAY;
+            break;
+        case BlendModeDarken:
+            targetBlendMode = D2D1_BLEND_MODE_DARKEN;
+            break;
+        case BlendModeLighten:
+            targetBlendMode = D2D1_BLEND_MODE_LIGHTEN;
+            break;
+        case BlendModeColorDodge:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR_DODGE;
+            break;
+        case BlendModeColorBurn:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR_BURN;
+            break;
+        case BlendModeHardLight:
+            targetBlendMode = D2D1_BLEND_MODE_HARD_LIGHT;
+            break;
+        case BlendModeSoftLight:
+            targetBlendMode = D2D1_BLEND_MODE_SOFT_LIGHT;
+            break;
+        case BlendModeDifference:
+            targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
+            break;
+        case BlendModeExclusion:
+            targetBlendMode = D2D1_BLEND_MODE_EXCLUSION;
+            break;
+        case BlendModeHue:
+            targetBlendMode = D2D1_BLEND_MODE_HUE;
+            break;
+        case BlendModeSaturation:
+            targetBlendMode = D2D1_BLEND_MODE_SATURATION;
+            break;
+        case BlendModeColor:
+            targetBlendMode = D2D1_BLEND_MODE_COLOR;
+            break;
+        case BlendModeLuminosity:
+            targetBlendMode = D2D1_BLEND_MODE_LUMINOSITY;
+            break;
+        case BlendModePlusDarker:
+            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
+            break;
+        case BlendModePlusLighter:
+            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;
+}
+
+void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
+{
+    // This is a no-op for Direct2D.
+}
+
+void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    if (m_state.fillGradient || m_state.fillPattern) {
+        // FIXME: We should be able to fill ellipses with pattern/gradient brushes in D2D.
+        fillEllipseAsPath(ellipse);
+        return;
+    }
+
+    auto d2dEllipse = D2D1::Ellipse(D2D1::Point2F(ellipse.x(), ellipse.y()), ellipse.width(), ellipse.height());
+
+    platformContext()->SetTags(1, __LINE__);
+
+    drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
+        renderTarget->FillEllipse(&d2dEllipse, solidFillBrush());
+    });
+}
+
+void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
+{
+    if (paintingDisabled())
+        return;
+
+    ASSERT(!isRecording());
+
+    if (m_state.strokeGradient || m_state.strokePattern) {
+        // FIXME: We should be able to stroke ellipses with pattern/gradient brushes in D2D.
+        strokeEllipseAsPath(ellipse);
+        return;
+    }
+
+    auto d2dEllipse = D2D1::Ellipse(D2D1::Point2F(ellipse.x(), ellipse.y()), ellipse.width(), ellipse.height());
+
+    platformContext()->SetTags(1, __LINE__);
+
+    drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
+        renderTarget->DrawEllipse(&d2dEllipse, solidStrokeBrush(), strokeThickness());
+    });
+}
+
+}

Added: trunk/Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h (0 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h	2016-10-04 18:31:21 UTC (rev 206773)
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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
+
+#include "COMPtr.h"
+#include "Color.h"
+#include <d2d1.h>
+#include <d2d1_1.h>
+#include <d2d1effects.h>
+#include <d2d1helper.h>
+#include <windows.h>
+#include <wtf/TinyLRUCache.h>
+
+namespace WebCore {
+
+class GraphicsContextPlatformPrivate {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    GraphicsContextPlatformPrivate(ID2D1RenderTarget* renderTarget)
+        : m_renderTarget(renderTarget)
+    {
+    }
+
+    enum Direct2DLayerType { AxisAlignedClip, LayerClip };
+
+    void clip(const FloatRect&);
+    void clip(const Path&);
+    void clip(ID2D1Geometry*);
+    void endDraw();
+    void flush();
+    void save();
+    void scale(const FloatSize&);
+    void restore();
+    void rotate(float);
+    void translate(float, float);
+    void concatCTM(const AffineTransform&);
+    void setCTM(const AffineTransform&);
+
+    ID2D1RenderTarget* renderTarget() { return m_renderTarget.get(); }
+    ID2D1Layer* clipLayer() const { return m_renderStates.last().m_activeLayer.get(); }
+
+    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_didBeginDraw { false };
+
+    COMPtr<ID2D1SolidColorBrush> m_solidStrokeBrush;
+    COMPtr<ID2D1SolidColorBrush> m_solidFillBrush;
+
+private:
+    COMPtr<ID2D1RenderTarget> m_renderTarget;
+    HashMap<RGBA32, COMPtr<ID2D1SolidColorBrush>> m_solidColoredBrushCache;
+    COMPtr<ID2D1SolidColorBrush> m_whiteBrush;
+    COMPtr<ID2D1SolidColorBrush> m_zeroBrush;
+
+    struct RenderState {
+        COMPtr<ID2D1DrawingStateBlock> m_drawingStateBlock;
+        COMPtr<ID2D1Layer> m_activeLayer;
+        Vector<Direct2DLayerType> m_clips;
+    };
+
+    Vector<RenderState> m_renderStates;
+};
+
+class D2DContextStateSaver {
+public:
+    D2DContextStateSaver(GraphicsContextPlatformPrivate& context, bool saveAndRestore = true)
+        : m_context(context)
+        , m_saveAndRestore(saveAndRestore)
+    {
+        if (m_saveAndRestore)
+            m_context.save();
+    }
+
+    ~D2DContextStateSaver()
+    {
+        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:
+    GraphicsContextPlatformPrivate& m_context;
+    bool m_saveAndRestore { false };
+};
+
+}

Modified: trunk/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -28,6 +28,8 @@
 
 #if USE(CG)
 #include "GraphicsContextPlatformPrivateCG.h"
+#elif USE(DIRECT2D)
+#include "GraphicsContextPlatformPrivateDirect2D.h"
 #elif USE(CAIRO)
 #include "GraphicsContextPlatformPrivateCairo.h"
 #endif
@@ -144,7 +146,7 @@
     return m_data->m_hdc;
 }
 
-#if PLATFORM(WIN)
+#if PLATFORM(WIN) && !USE(DIRECT2D)
 void GraphicsContextPlatformPrivate::save()
 {
     if (!m_hdc)

Modified: trunk/Source/WebCore/platform/graphics/win/TransformationMatrixDirect2D.cpp (206772 => 206773)


--- trunk/Source/WebCore/platform/graphics/win/TransformationMatrixDirect2D.cpp	2016-10-04 16:53:43 UTC (rev 206772)
+++ trunk/Source/WebCore/platform/graphics/win/TransformationMatrixDirect2D.cpp	2016-10-04 18:31:21 UTC (rev 206773)
@@ -72,4 +72,4 @@
 
 }
 
-#endif // USE(DIRECT2D)
+#endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to