[Win][Direct2D] Add transparency layer support
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Nov 2016 00:59:53 +0000 (00:59 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Nov 2016 00:59:53 +0000 (00:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164614

Reviewed by Dean Jackson.

Provide a stackable implementation of transparency layers so that
multiple layers with opacity draw propery.

Tested by fast/layers/opacity-stacking.html and others.

* platform/graphics/win/GraphicsContextDirect2D.cpp:
(WebCore::GraphicsContext::platformContext): Remove unneeded assertion.
(WebCore::GraphicsContextPlatformPrivate::renderTarget): Return current context taking
into account the presence of transparency layers.
(WebCore::GraphicsContextPlatformPrivate::setAlpha): Added.
(WebCore::GraphicsContextPlatformPrivate::currentGlobalAlpha): Get current global
alpha for current layer.
(WebCore::GraphicsContext::colorWithGlobalAlpha): Compute proper color taking into
account the current layer.
(WebCore::drawWithShadowHelper): Helper function to share code.
(WebCore::GraphicsContext::drawWithShadow): Use new helper function.
(WebCore::GraphicsContextPlatformPrivate::beginTransparencyLayer): Added.
(WebCore::GraphicsContext::beginPlatformTransparencyLayer): Call new implementation.
(WebCore::GraphicsContextPlatformPrivate::endTransparencyLayer): Added.
(WebCore::GraphicsContext::endPlatformTransparencyLayer): Call new implementation.
(WebCore::GraphicsContext::clearRect): Clear to transparent color.
(WebCore::GraphicsContext::setPlatformAlpha): Pass new alpha on to any active
transparency layer.
* platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
(WebCore::GraphicsContextPlatformPrivate::renderTarget): Deleted.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/win/GraphicsContextDirect2D.cpp
Source/WebCore/platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h

index cb85a30..54e6cef 100644 (file)
@@ -1,3 +1,36 @@
+2016-11-10  Brent Fulgham  <bfulgham@apple.com>
+
+        [Win][Direct2D] Add transparency layer support
+        https://bugs.webkit.org/show_bug.cgi?id=164614
+
+        Reviewed by Dean Jackson.
+
+        Provide a stackable implementation of transparency layers so that
+        multiple layers with opacity draw propery.
+
+        Tested by fast/layers/opacity-stacking.html and others.
+
+        * platform/graphics/win/GraphicsContextDirect2D.cpp:
+        (WebCore::GraphicsContext::platformContext): Remove unneeded assertion.
+        (WebCore::GraphicsContextPlatformPrivate::renderTarget): Return current context taking
+        into account the presence of transparency layers.
+        (WebCore::GraphicsContextPlatformPrivate::setAlpha): Added.
+        (WebCore::GraphicsContextPlatformPrivate::currentGlobalAlpha): Get current global
+        alpha for current layer.
+        (WebCore::GraphicsContext::colorWithGlobalAlpha): Compute proper color taking into
+        account the current layer.
+        (WebCore::drawWithShadowHelper): Helper function to share code.
+        (WebCore::GraphicsContext::drawWithShadow): Use new helper function.
+        (WebCore::GraphicsContextPlatformPrivate::beginTransparencyLayer): Added.
+        (WebCore::GraphicsContext::beginPlatformTransparencyLayer): Call new implementation.
+        (WebCore::GraphicsContextPlatformPrivate::endTransparencyLayer): Added.
+        (WebCore::GraphicsContext::endPlatformTransparencyLayer): Call new implementation.
+        (WebCore::GraphicsContext::clearRect): Clear to transparent color.
+        (WebCore::GraphicsContext::setPlatformAlpha): Pass new alpha on to any active
+        transparency layer.
+        * platform/graphics/win/GraphicsContextPlatformPrivateDirect2D.h:
+        (WebCore::GraphicsContextPlatformPrivate::renderTarget): Deleted.
+
 2016-11-10  Dean Jackson  <dino@apple.com>
 
         Add CSS Color Level 4 to features.
index e699b77..6405970 100644 (file)
@@ -151,10 +151,31 @@ void GraphicsContext::platformDestroy()
 ID2D1RenderTarget* GraphicsContext::platformContext() const
 {
     ASSERT(!paintingDisabled());
-    ASSERT(m_data->renderTarget());
     return m_data->renderTarget();
 }
 
+ID2D1RenderTarget* GraphicsContextPlatformPrivate::renderTarget()
+{
+    if (!m_transparencyLayerStack.isEmpty())
+        return m_transparencyLayerStack.last().renderTarget.get();
+
+    return m_renderTarget.get();
+}
+
+void GraphicsContextPlatformPrivate::setAlpha(float alpha)
+{
+    ASSERT(m_transparencyLayerStack.isEmpty());
+    m_alpha = alpha;
+}
+
+float GraphicsContextPlatformPrivate::currentGlobalAlpha() const
+{
+    if (!m_transparencyLayerStack.isEmpty())
+        return m_transparencyLayerStack.last().opacity;
+
+    return m_alpha;
+}
+
 void GraphicsContext::savePlatformState()
 {
     ASSERT(!paintingDisabled());
@@ -489,8 +510,8 @@ void GraphicsContextPlatformPrivate::rotate(float angle)
 
 D2D1_COLOR_F GraphicsContext::colorWithGlobalAlpha(const Color& color) const
 {
-    float colorAlpha = color.alpha() / 255.0f;
-    float globalAlpha = m_state.alpha;
+    float colorAlpha = color.alphaAsFloat();
+    float globalAlpha = m_data->currentGlobalAlpha();
 
     return D2D1::ColorF(color.rgb(), globalAlpha * colorAlpha);
 }
@@ -919,26 +940,10 @@ void GraphicsContext::drawWithoutShadow(const FloatRect& /*boundingRect*/, const
     drawCommands(platformContext());
 }
 
-void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>& drawCommands)
+static void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1Bitmap* bitmap, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur)
 {
-    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);
+    HRESULT hr = context->QueryInterface(&deviceContext);
     RELEASE_ASSERT(SUCCEEDED(hr));
 
     // Create the shadow effect
@@ -946,9 +951,9 @@ void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::f
     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);
+    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);
@@ -956,7 +961,7 @@ void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::f
 
     transformEffect->SetInputEffect(0, shadowEffect.get());
 
-    auto translation = D2D1::Matrix3x2F::Translation(m_state.shadowOffset.width(), m_state.shadowOffset.height());
+    auto translation = D2D1::Matrix3x2F::Translation(shadowOffset.width(), shadowOffset.height());
     transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, translation);
 
     COMPtr<ID2D1Effect> compositor;
@@ -964,7 +969,7 @@ void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::f
     RELEASE_ASSERT(SUCCEEDED(hr));
 
     compositor->SetInputEffect(0, transformEffect.get());
-    compositor->SetInput(1, bitmap.get());
+    compositor->SetInput(1, bitmap);
 
     // Flip the context
     D2D1_MATRIX_3X2_F ctm;
@@ -976,6 +981,27 @@ void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::f
     deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
 }
 
+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));
+
+    drawWithShadowHelper(context, bitmap.get(), m_state.shadowColor, m_state.shadowOffset, m_state.shadowBlur);
+}
+
 void GraphicsContext::fillPath(const Path& path)
 {
     if (paintingDisabled() || path.isEmpty())
@@ -1335,6 +1361,19 @@ IntRect GraphicsContext::clipBounds() const
     return enclosingIntRect(clipBounds);
 }
 
+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)
 {
     if (paintingDisabled())
@@ -1344,7 +1383,40 @@ void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
 
     save();
 
-    notImplemented();
+    m_state.alpha = opacity;
+
+    m_data->beginTransparencyLayer(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()
@@ -1352,9 +1424,11 @@ void GraphicsContext::endPlatformTransparencyLayer()
     if (paintingDisabled())
         return;
 
+    m_data->endTransparencyLayer();
+
     ASSERT(!isRecording());
 
-    notImplemented();
+    m_state.alpha = m_data->currentGlobalAlpha();
 
     restore();
 }
@@ -1417,7 +1491,7 @@ void GraphicsContext::clearRect(const FloatRect& rect)
 
         if (rectToClear.contains(renderTargetRect)) {
             renderTarget->SetTags(1, __LINE__);
-            renderTarget->Clear();
+            renderTarget->Clear(D2D1::ColorF(0, 0, 0, 0));
             return;
         }
 
@@ -1426,7 +1500,7 @@ void GraphicsContext::clearRect(const FloatRect& rect)
 
         renderTarget->SetTags(1, __LINE__);
         rectToClear.intersect(renderTargetRect);
-        renderTarget->FillRectangle(rectToClear, solidFillBrush());
+        renderTarget->FillRectangle(rectToClear, brushWithColor(Color(D2D1::ColorF(0, 0, 0, 0))));
     });
 }
 
@@ -1789,9 +1863,13 @@ void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
     platformContext()->SetTextAntialiasMode(fontSmoothingMode);
 }
 
-void GraphicsContext::setPlatformAlpha(float)
+void GraphicsContext::setPlatformAlpha(float alpha)
 {
-    /* No-op on this platform */
+    if (paintingDisabled())
+        return;
+
+    ASSERT(m_state.alpha == alpha);
+    m_data->setAlpha(alpha);
 }
 
 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode blendMode)
index e70b116..7427830 100644 (file)
@@ -44,6 +44,9 @@ public:
 
     enum Direct2DLayerType { AxisAlignedClip, LayerClip };
 
+    void beginTransparencyLayer(float opacity);
+    void endTransparencyLayer();
+
     void clip(const FloatRect&);
     void clip(const Path&);
     void clip(ID2D1Geometry*);
@@ -68,8 +71,9 @@ public:
     void setPatternOffset(float);
     void setStrokeThickness(float);
     void setDashes(const DashArray&);
+    void setAlpha(float);
 
-    ID2D1RenderTarget* renderTarget() { return m_renderTarget.get(); }
+    ID2D1RenderTarget* renderTarget();
     ID2D1Layer* clipLayer() const { return m_renderStates.last().m_activeLayer.get(); }
     ID2D1StrokeStyle* strokeStyle();
 
@@ -86,6 +90,8 @@ public:
     COMPtr<ID2D1BitmapBrush> m_patternStrokeBrush;
     COMPtr<ID2D1BitmapBrush> m_patternFillBrush;
 
+    float currentGlobalAlpha() const;
+
 private:
     void recomputeStrokeStyle();
 
@@ -103,15 +109,27 @@ private:
 
     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;
+
     float m_miterLimit { 1.0f };
     float m_dashOffset { 0 };
     float m_patternWidth { 1.0f };
     float m_patternOffset { 0 };
     float m_strokeThickness { 0 };
+    float m_alpha { 1.0 };
 };
 
 class D2DContextStateSaver {