Title: [250492] trunk/Source/WebCore
Revision
250492
Author
bfulg...@apple.com
Date
2019-09-29 12:33:37 -0700 (Sun, 29 Sep 2019)

Log Message

[FTW] Correct compositing, shadow, and radial gradient implementations
https://bugs.webkit.org/show_bug.cgi?id=202177

Reviewed by Fujii Hironori.

This patch corrects a number of implementation errors in basic Canvas
drawing operations.

Tested by canvas/philip/tests 

* platform/graphics/win/Direct2DOperations.cpp:
(WebCore::Direct2D::State::setCompositeOperation): Initialize blend and
composite modes to correct defaults.
(WebCore::Direct2D::drawWithShadowHelper): Correct value used for blur
standard deviation to more closely match other browser output.
* platform/graphics/win/Direct2DUtilities.cpp:
(WebCore::Direct2D::createBitmapCopyFromContext): Added helper function.
* platform/graphics/win/Direct2DUtilities.h:
* platform/graphics/win/GradientDirect2D.cpp:
(WebCore::Gradient::generateGradient): Properly handle the case of a non-zero
initial gradient radius. Properly compute the final radius.
* platform/graphics/win/ImageBufferDataDirect2D.cpp:
(WebCore::ImageBufferData::readDataFromBitmapIfNeeded const): Update to use ID2D1DeviceContext
from platformContext, rather than searching for it each time.
(WebCore::ImageBufferData::compatibleBitmap): Ditto.
* platform/graphics/win/PlatformContextDirect2D.cpp:
(WebCore::PlatformContextDirect2D::PlatformContextDirect2D): Grab the ID2D1DeviceContext for
the RenderTarget at construction time.
(WebCore::PlatformContextDirect2D::setRenderTarget): Ditto.
(WebCore::PlatformContextDirect2D::endDraw): Perform compositing operations when needed.
(WebCore::PlatformContextDirect2D::compositeIfNeeded): Added.
(WebCore::PlatformContextDirect2D::setBlendAndCompositeMode): Update to properly set the
blend and compositing mode (depending on global value set for the canvas.)
* platform/graphics/win/PlatformContextDirect2D.h:
(WebCore::PlatformContextDirect2D::deviceContext):
(WebCore::PlatformContextDirect2D::setRenderTarget): Deleted.
(WebCore::PlatformContextDirect2D::setBlendMode): Deleted.
(WebCore::PlatformContextDirect2D::setCompositeMode): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (250491 => 250492)


--- trunk/Source/WebCore/ChangeLog	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/ChangeLog	2019-09-29 19:33:37 UTC (rev 250492)
@@ -1,3 +1,44 @@
+2019-09-28  Brent Fulgham  <bfulg...@apple.com>
+
+        [FTW] Correct compositing, shadow, and radial gradient implementations
+        https://bugs.webkit.org/show_bug.cgi?id=202177
+
+        Reviewed by Fujii Hironori.
+
+        This patch corrects a number of implementation errors in basic Canvas
+        drawing operations.
+
+        Tested by canvas/philip/tests 
+
+        * platform/graphics/win/Direct2DOperations.cpp:
+        (WebCore::Direct2D::State::setCompositeOperation): Initialize blend and
+        composite modes to correct defaults.
+        (WebCore::Direct2D::drawWithShadowHelper): Correct value used for blur
+        standard deviation to more closely match other browser output.
+        * platform/graphics/win/Direct2DUtilities.cpp:
+        (WebCore::Direct2D::createBitmapCopyFromContext): Added helper function.
+        * platform/graphics/win/Direct2DUtilities.h:
+        * platform/graphics/win/GradientDirect2D.cpp:
+        (WebCore::Gradient::generateGradient): Properly handle the case of a non-zero
+        initial gradient radius. Properly compute the final radius.
+        * platform/graphics/win/ImageBufferDataDirect2D.cpp:
+        (WebCore::ImageBufferData::readDataFromBitmapIfNeeded const): Update to use ID2D1DeviceContext
+        from platformContext, rather than searching for it each time.
+        (WebCore::ImageBufferData::compatibleBitmap): Ditto.
+        * platform/graphics/win/PlatformContextDirect2D.cpp:
+        (WebCore::PlatformContextDirect2D::PlatformContextDirect2D): Grab the ID2D1DeviceContext for
+        the RenderTarget at construction time.
+        (WebCore::PlatformContextDirect2D::setRenderTarget): Ditto.
+        (WebCore::PlatformContextDirect2D::endDraw): Perform compositing operations when needed.
+        (WebCore::PlatformContextDirect2D::compositeIfNeeded): Added.
+        (WebCore::PlatformContextDirect2D::setBlendAndCompositeMode): Update to properly set the
+        blend and compositing mode (depending on global value set for the canvas.)
+        * platform/graphics/win/PlatformContextDirect2D.h:
+        (WebCore::PlatformContextDirect2D::deviceContext):
+        (WebCore::PlatformContextDirect2D::setRenderTarget): Deleted.
+        (WebCore::PlatformContextDirect2D::setBlendMode): Deleted.
+        (WebCore::PlatformContextDirect2D::setCompositeMode): Deleted.
+
 2019-09-29  Zan Dobersek  <zdober...@igalia.com>
 
         Tighten up LayerRepresentation operators

Modified: trunk/Source/WebCore/platform/graphics/Path.h (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/Path.h	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/Path.h	2019-09-29 19:33:37 UTC (rev 250492)
@@ -50,6 +50,10 @@
 
 typedef ID2D1GeometryGroup PlatformPath;
 
+namespace WebCore {
+class PlatformContextDirect2D;
+}
+
 #elif USE(CAIRO)
 
 namespace WebCore {
@@ -170,6 +174,7 @@
         // To keep Path() cheap, it does not allocate a PlatformPath immediately
         // meaning Path::platformPath() can return null.
 #if USE(DIRECT2D)
+        FloatRect fastBoundingRectForStroke(const PlatformContextDirect2D&) const;
         PlatformPathPtr platformPath() const { return m_path.get(); }
 #else
         PlatformPathPtr platformPath() const { return m_path; }

Modified: trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -127,9 +127,10 @@
 
 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;
 
+    D2D1_BLEND_MODE targetBlendMode = D2D1_BLEND_MODE_MULTIPLY;
+    D2D1_COMPOSITE_MODE targetCompositeMode = D2D1_COMPOSITE_MODE_FORCE_DWORD;
+
     if (blendMode != BlendMode::Normal) {
         switch (blendMode) {
         case BlendMode::Multiply:
@@ -192,10 +193,10 @@
             // FIXME: targetBlendMode = D2D1_BLEND_MODE_CLEAR;
             break;
         case CompositeCopy:
-            // FIXME: targetBlendMode = D2D1_BLEND_MODE_COPY;
+            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_COPY;
             break;
         case CompositeSourceOver:
-            // FIXME: kCGBlendModeNormal
+            targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_OVER;
             break;
         case CompositeSourceIn:
             targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_IN;
@@ -222,10 +223,10 @@
             targetCompositeMode = D2D1_COMPOSITE_MODE_XOR;
             break;
         case CompositePlusDarker:
-            targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
+            targetBlendMode = D2D1_BLEND_MODE_DARKEN;
             break;
         case CompositePlusLighter:
-            targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
+            targetBlendMode = D2D1_BLEND_MODE_LIGHTEN;
             break;
         case CompositeDifference:
             targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
@@ -233,8 +234,7 @@
         }
     }
 
-    platformContext.setBlendMode(targetBlendMode);
-    platformContext.setCompositeMode(targetCompositeMode);
+    platformContext.setBlendAndCompositeMode(targetBlendMode, targetCompositeMode);
 }
 
 void setShouldAntialias(PlatformContextDirect2D& platformContext, bool enable)
@@ -411,15 +411,10 @@
     auto context = platformContext.renderTarget();
 
     platformContext.setTags(1, __LINE__);
-    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, rect, &fillSource](ID2D1RenderTarget* renderTarget) {
+    drawWithShadow(platformContext, rect, shadowState, [&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)
@@ -427,15 +422,10 @@
     auto context = platformContext.renderTarget();
 
     platformContext.setTags(1, __LINE__);
-    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, color, rect](ID2D1RenderTarget* renderTarget) {
+    drawWithShadow(platformContext, rect, shadowState, [&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)
@@ -450,15 +440,10 @@
     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()) {
-        Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, color, rect, radii, r](ID2D1RenderTarget* renderTarget) {
+        drawWithShadow(platformContext, r, shadowState, [&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 {
         Path path;
         path.addRoundedRect(rect);
@@ -488,15 +473,11 @@
 
 void fillRectWithGradient(PlatformContextDirect2D& platformContext, const FloatRect& rect, ID2D1Brush* gradient)
 {
-    auto context = platformContext.renderTarget();
-
     platformContext.setTags(1, __LINE__);
-    Function<void(ID2D1RenderTarget*)> drawFunction = [&platformContext, rect, &gradient](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, rect, &gradient](ID2D1RenderTarget* renderTarget) {
         const D2D1_RECT_F d2dRect = rect;
         renderTarget->FillRectangle(&d2dRect, gradient);
-    };
-
-    drawWithoutShadow(platformContext, rect, drawFunction);
+    });
 }
 
 void fillPath(PlatformContextDirect2D& platformContext, const Path& path, const FillSource& fillSource, const ShadowState& shadowState)
@@ -517,11 +498,9 @@
 
     if (shadowState.isVisible()) {
         FloatRect boundingRect = path.fastBoundingRect();
-        drawWithShadow(platformContext, boundingRect, shadowState, drawFunction);
-    } else {
-        FloatRect contextRect(FloatPoint(), context->GetSize());
-        drawWithoutShadow(platformContext, contextRect, drawFunction);
-    }
+        drawWithShadow(platformContext, boundingRect, shadowState, WTFMove(drawFunction));
+    } else
+        drawWithoutShadow(platformContext, WTFMove(drawFunction));
 }
 
 void fillPath(PlatformContextDirect2D& platformContext, const Path& path, const Color& color, const ShadowState& shadowState)
@@ -541,25 +520,18 @@
 
     if (shadowState.isVisible()) {
         FloatRect boundingRect = path.fastBoundingRect();
-        drawWithShadow(platformContext, boundingRect, shadowState, drawFunction);
-    } else {
-        FloatRect contextRect(FloatPoint(), context->GetSize());
-        drawWithoutShadow(platformContext, contextRect, drawFunction);
-    }
+        drawWithShadow(platformContext, boundingRect, shadowState, WTFMove(drawFunction));
+    } else
+        drawWithoutShadow(platformContext, WTFMove(drawFunction));
 }
 
 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) {
+    drawWithShadow(platformContext, rect, shadowState, [&platformContext, &strokeSource, rect, lineWidth](ID2D1RenderTarget* renderTarget) {
         platformContext.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)
@@ -568,15 +540,15 @@
     
     platformContext.setTags(1, __LINE__);
 
-    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);
+    if (shadowState.isVisible()) {
+        auto boundingRect = path.fastBoundingRectForStroke(platformContext);
+        drawWithShadow(platformContext, boundingRect, shadowState, WTFMove(drawFunction));
+    } else
+        drawWithoutShadow(platformContext, WTFMove(drawFunction));
 }
 
 void drawPath(PlatformContextDirect2D& platformContext, const Path& path, const StrokeSource& strokeSource, const ShadowState&)
@@ -587,8 +559,7 @@
 
     platformContext.setTags(1, __LINE__);
 
-    auto rect = path.fastBoundingRect();
-    drawWithoutShadow(platformContext, rect, [&platformContext, &strokeSource, &path](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, &strokeSource, &path](ID2D1RenderTarget* renderTarget) {
         renderTarget->DrawGeometry(path.platformPath(), strokeSource.brush.get(), strokeSource.thickness, platformContext.strokeStyle());
     });
 
@@ -595,63 +566,135 @@
     flush(platformContext);
 }
 
-void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1Bitmap* bitmap, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur)
+static void drawWithShadowHelper(ID2D1RenderTarget* context, ID2D1BitmapRenderTarget* bitmapTarget, const FloatRect& boundingRect, const Color& shadowColor, const FloatSize& shadowOffset, float shadowBlur)
 {
-    COMPtr<ID2D1DeviceContext> deviceContext;
-    HRESULT hr = context->QueryInterface(&deviceContext);
+    COMPtr<ID2D1Bitmap> currentCanvas = Direct2D::createBitmapCopyFromContext(bitmapTarget);
+    if (!currentCanvas)
+        return;
+
+    COMPtr<ID2D1DeviceContext> shadowDeviceContext;
+    HRESULT hr = bitmapTarget->QueryInterface(&shadowDeviceContext);
     RELEASE_ASSERT(SUCCEEDED(hr));
 
     // Create the shadow effect
     COMPtr<ID2D1Effect> shadowEffect;
-    hr = deviceContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect);
+    hr = shadowDeviceContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect);
     RELEASE_ASSERT(SUCCEEDED(hr));
 
-    shadowEffect->SetInput(0, bitmap);
+    shadowEffect->SetInput(0, currentCanvas.get());
     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);
+    float blurDeviation = 0.5 * shadowBlur;
+    shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, blurDeviation);
+
+    COMPtr<ID2D1Effect> shadowTransformEffect;
+    hr = shadowDeviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &shadowTransformEffect);
     RELEASE_ASSERT(SUCCEEDED(hr));
 
-    transformEffect->SetInputEffect(0, shadowEffect.get());
+    shadowTransformEffect->SetInputEffect(0, shadowEffect.get());
 
-    auto translation = D2D1::Matrix3x2F::Translation(shadowOffset.width(), shadowOffset.height());
-    transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, translation);
+    // Must flip the axis for the shadow offset, since Direct2D's Y axis is inverted.
+    auto shadowTranslation = D2D1::Matrix3x2F::Translation(shadowOffset.width(), -shadowOffset.height());
+    shadowTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, shadowTranslation);
 
     COMPtr<ID2D1Effect> compositor;
-    hr = deviceContext->CreateEffect(CLSID_D2D1Composite, &compositor);
+    hr = shadowDeviceContext->CreateEffect(CLSID_D2D1Composite, &compositor);
     RELEASE_ASSERT(SUCCEEDED(hr));
 
-    compositor->SetInputEffect(0, transformEffect.get());
-    compositor->SetInput(1, bitmap);
+    compositor->SetInputEffect(0, shadowTransformEffect.get());
+    compositor->SetInput(1, currentCanvas.get());
 
-    deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
+    shadowDeviceContext->BeginDraw();
+    shadowDeviceContext->Clear(D2D1::ColorF(0, 0, 0, 0));
+    shadowDeviceContext->DrawImage(compositor.get());
+    hr = shadowDeviceContext->EndDraw();
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    COMPtr<ID2D1Bitmap> bitmap;
+    hr = bitmapTarget->GetBitmap(&bitmap);
+    RELEASE_ASSERT(SUCCEEDED(hr));
+
+    IntSize pixelSize = context->GetPixelSize();
+    COMPtr<ID2D1Bitmap> cropped = Direct2D::createBitmap(bitmapTarget, pixelSize);
+    if (!cropped)
+        return;
+
+    auto scaleFactor = bitmapResolution(context);
+
+    FloatRect scaledBoundingRect(boundingRect);
+    scaledBoundingRect.scale(scaleFactor.width(), scaleFactor.height());
+
+    // Take the magnitude of the offset. If the origin was outside the drawing context, we stretched to include those points,
+    // and so we need to crop them out. If the origin was inside, we want to honor the offset.
+    int x = clampTo<int>(std::abs(scaledBoundingRect.x()));
+    int y = clampTo<int>(std::abs(scaledBoundingRect.y()));
+    int width = clampTo<int>(scaledBoundingRect.width());
+    int height = clampTo<int>(scaledBoundingRect.height());
+
+    IntRect outputRect(IntPoint(x, y), IntSize(width, height));
+    IntRect destContextRect(IntPoint(), pixelSize);
+    if (outputRect.intersects(destContextRect))
+        outputRect.intersect(destContextRect);
+
+    D2D1_RECT_U cropRect = outputRect;
+    auto targetPoint = D2D1::Point2U();
+    cropped->CopyFromBitmap(&targetPoint, bitmap.get(), &cropRect);
+
+    context->DrawBitmap(cropped.get());
 }
 
-void drawWithShadow(PlatformContextDirect2D& platformContext, const FloatRect& boundingRect, const ShadowState& shadowState, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
+void drawWithShadow(PlatformContextDirect2D& platformContext, FloatRect boundingRect, const ShadowState& shadowState, WTF::Function<void(ID2D1RenderTarget*)>&& drawCommands)
 {
+    if (!shadowState.isVisible()) {
+        drawWithoutShadow(platformContext, WTFMove(drawCommands));
+        return;
+    }
+
     platformContext.notifyPreDrawObserver();
 
-    auto context = platformContext.renderTarget();
+    auto* context = platformContext.renderTarget();
 
+    FloatSize renderTargetSize = context->GetSize();
+    const FloatRect contextRect(FloatPoint(), renderTargetSize);
+    FloatRect workingContextRect(contextRect);
+
+    if (boundingRect.isInfinite())
+        boundingRect = workingContextRect;
+
+    if (!contextRect.contains(boundingRect))
+        workingContextRect.unite(boundingRect);
+
+    workingContextRect.expand(std::abs(shadowState.offset.width()) + shadowState.blur, std::abs(shadowState.offset.height()) + shadowState.blur);
+
+    auto scaleFactor = bitmapResolution(context);
+
+    workingContextRect.scale(scaleFactor);
+
+    IntSize pixelDimensions(clampTo<int>(workingContextRect.width()), clampTo<int>(workingContextRect.height()));
+
     // Render the current geometry to a bitmap context
-    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget = createBitmapRenderTarget(context);
+    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget = createBitmapRenderTargetOfSize(pixelDimensions, context);
 
+    D2D1_MATRIX_3X2_F currentTransform;
+    bitmapTarget->GetTransform(&currentTransform);
+
+    if (!contextRect.contains(boundingRect.location())) {
+        auto translation = D2D1::Matrix3x2F::Translation(-boundingRect.location().x(), -boundingRect.location().y());
+        bitmapTarget->SetTransform(translation * currentTransform);
+    }
+
     bitmapTarget->BeginDraw();
     drawCommands(bitmapTarget.get());
     HRESULT hr = bitmapTarget->EndDraw();
     RELEASE_ASSERT(SUCCEEDED(hr));
 
-    COMPtr<ID2D1Bitmap> bitmap;
-    hr = bitmapTarget->GetBitmap(&bitmap);
-    RELEASE_ASSERT(SUCCEEDED(hr));
+    bitmapTarget->SetTransform(currentTransform);
 
-    drawWithShadowHelper(context, bitmap.get(), shadowState.color, shadowState.offset, shadowState.blur);
+    drawWithShadowHelper(context, bitmapTarget.get(), boundingRect, shadowState.color, shadowState.offset, shadowState.blur);
     platformContext.notifyPostDrawObserver();
 }
 
-void drawWithoutShadow(PlatformContextDirect2D& platformContext, const FloatRect& /*boundingRect*/, const WTF::Function<void(ID2D1RenderTarget*)>& drawCommands)
+void drawWithoutShadow(PlatformContextDirect2D& platformContext, WTF::Function<void(ID2D1RenderTarget*)>&& drawCommands)
 {
     platformContext.notifyPreDrawObserver();
     drawCommands(platformContext.renderTarget());
@@ -660,7 +703,7 @@
 
 void clearRect(PlatformContextDirect2D& platformContext, const FloatRect& rect)
 {
-    drawWithoutShadow(platformContext, rect, [&platformContext, rect](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, rect](ID2D1RenderTarget* renderTarget) {
         FloatRect renderTargetRect(FloatPoint(), renderTarget->GetSize());
         FloatRect rectToClear(rect);
 
@@ -812,15 +855,10 @@
     }
 
     platformContext.setTags(1, __LINE__);
-    Function<void(ID2D1RenderTarget*)> drawFunction = [image, adjustedDestRect, globalAlpha, srcRect](ID2D1RenderTarget* renderTarget) {
+    drawWithShadow(platformContext, adjustedDestRect, shadowState, [image, adjustedDestRect, globalAlpha, srcRect](ID2D1RenderTarget* renderTarget) {
         renderTarget->DrawBitmap(image, adjustedDestRect, globalAlpha, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, srcRect);
-    };
+    });
 
-    if (shadowState.isVisible())
-        drawWithShadow(platformContext, adjustedDestRect, shadowState, drawFunction);
-    else
-        drawWithoutShadow(platformContext, adjustedDestRect, drawFunction);
-
     if (!stateSaver.didSave())
         context->SetTransform(ctm);
 }
@@ -869,7 +907,7 @@
     if (!SUCCEEDED(hr))
         return;
 
-    drawWithoutShadow(platformContext, destRect, [destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
         const D2D1_RECT_F d2dRect = destRect;
         renderTarget->FillRectangle(&d2dRect, patternBrush.get());
     });
@@ -880,10 +918,8 @@
     // FIXME: this function does not handle patterns and gradients like drawPath does, it probably should.
     ASSERT(!rect.isEmpty());
 
-    auto context = platformContext.renderTarget();
-
     platformContext.setTags(1, __LINE__);
-    drawWithoutShadow(platformContext, rect, [&platformContext, rect, fillColor, strokeColor, strokeStyle](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, rect, fillColor, strokeColor, strokeStyle](ID2D1RenderTarget* renderTarget) {
         const D2D1_RECT_F d2dRect = rect;
         auto fillBrush = platformContext.brushWithColor(fillColor);
         renderTarget->FillRectangle(&d2dRect, fillBrush.get());
@@ -935,9 +971,7 @@
 
     platformContext.setTags(1, __LINE__);
 
-    FloatRect boundingRect(p1, p2);
-
-    drawWithoutShadow(platformContext, boundingRect, [&platformContext, p1, p2, d2dStrokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, p1, p2, d2dStrokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
         renderTarget->DrawLine(p1, p2, platformContext.brushWithColor(strokeColor).get(), strokeThickness, d2dStrokeStyle.get());
     });
 }
@@ -956,10 +990,8 @@
 {
     auto ellipse = D2D1::Ellipse(rect.center(), 0.5 * rect.width(), 0.5 * rect.height());
 
-    auto context = platformContext.renderTarget();
-
     platformContext.setTags(1, __LINE__);
-    drawWithoutShadow(platformContext, rect, [&platformContext, ellipse, fillColor, strokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, ellipse, fillColor, strokeStyle, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
         auto fillBrush = platformContext.brushWithColor(fillColor);
         renderTarget->FillEllipse(&ellipse, fillBrush.get());
         if (strokeStyle != StrokeStyle::NoStroke) {
@@ -976,10 +1008,8 @@
 
     auto ellipse = D2D1::Ellipse(rect.center(), 0.5 * rect.width(), 0.5 * rect.height());
 
-    auto context = platformContext.renderTarget();
-
     platformContext.setTags(1, __LINE__);
-    drawWithoutShadow(platformContext, rect, [&platformContext, ellipse, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
+    drawWithoutShadow(platformContext, [&platformContext, ellipse, strokeColor, strokeThickness](ID2D1RenderTarget* renderTarget) {
         auto strokeBrush = platformContext.brushWithColor(strokeColor);
         renderTarget->DrawEllipse(&ellipse, strokeBrush.get(), strokeThickness, platformContext.strokeStyle());
     });
@@ -1099,15 +1129,15 @@
     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);
+        drawWithShadowHelper(context, renderTarget.get(), FloatRect(), currentLayer.shadowColor, currentLayer.shadowOffset, currentLayer.shadowBlur);
     else {
+        COMPtr<ID2D1Bitmap> bitmap;
+        hr = renderTarget->GetBitmap(&bitmap);
+        RELEASE_ASSERT(SUCCEEDED(hr));
+
         COMPtr<ID2D1BitmapBrush> bitmapBrush;
         auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
         auto brushProperties = D2D1::BrushProperties();

Modified: trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.h (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.h	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/Direct2DOperations.h	2019-09-29 19:33:37 UTC (rev 250492)
@@ -133,9 +133,8 @@
 void drawPath(PlatformContextDirect2D&, const Path&, const StrokeSource&, const ShadowState&);
 void drawPattern(PlatformContextDirect2D&, COMPtr<ID2D1Bitmap>&&, 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 drawWithoutShadow(PlatformContextDirect2D&, WTF::Function<void(ID2D1RenderTarget*)>&& drawCommands);
+void drawWithShadow(PlatformContextDirect2D&, FloatRect boundingRect, const ShadowState&, WTF::Function<void(ID2D1RenderTarget*)>&& drawCommands);
 
 void drawRect(PlatformContextDirect2D&, const FloatRect&, float, const Color&, StrokeStyle, const Color&);
 void drawLine(PlatformContextDirect2D&, const FloatPoint&, const FloatPoint&, StrokeStyle, const Color&, float, bool);

Modified: trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -65,9 +65,9 @@
     return bitmapSource->GetSize();
 }
 
-FloatPoint bitmapResolution(IWICBitmapSource* bitmapSource)
+FloatSize bitmapResolution(IWICBitmapSource* bitmapSource)
 {
-    constexpr double dpiBase = 96.0;
+    constexpr double dpiBase = 96;
 
     double dpiX, dpiY;
     HRESULT hr = bitmapSource->GetResolution(&dpiX, &dpiY);
@@ -74,24 +74,36 @@
     if (!SUCCEEDED(hr))
         return { };
 
-    FloatPoint result(dpiX, dpiY);
+    FloatSize result(dpiX, dpiY);
     result.scale(1.0 / dpiBase);
     return result;
 }
 
-FloatPoint bitmapResolution(ID2D1Bitmap* bitmap)
+FloatSize bitmapResolution(ID2D1Bitmap* bitmap)
 {
-    constexpr double dpiBase = 96.0;
+    constexpr double dpiBase = 96;
 
     float dpiX, dpiY;
     bitmap->GetDpi(&dpiX, &dpiY);
 
-    FloatPoint result(dpiX, dpiY);
+    FloatSize result(dpiX, dpiY);
     result.scale(1.0 / dpiBase);
     return result;
 
 }
 
+FloatSize bitmapResolution(ID2D1RenderTarget* target)
+{
+    constexpr double dpiBase = 96;
+
+    float dpiX, dpiY;
+    target->GetDpi(&dpiX, &dpiY);
+
+    FloatSize result(dpiX, dpiY);
+    result.scale(1.0 / dpiBase);
+    return result;
+
+}
 unsigned bitsPerPixel(GUID bitmapFormat)
 {
     COMPtr<IWICComponentInfo> componentInfo;
@@ -472,6 +484,30 @@
     return swapChain;
 }
 
+COMPtr<ID2D1Bitmap> createBitmapCopyFromContext(ID2D1BitmapRenderTarget* bitmapTarget)
+{
+    COMPtr<ID2D1Bitmap> currentCanvas;
+    HRESULT hr = bitmapTarget->GetBitmap(&currentCanvas);
+    if (!SUCCEEDED(hr))
+        return nullptr;
+
+    auto bitmapCreateProperties = bitmapProperties();
+
+    COMPtr<ID2D1Bitmap> bitmap;
+    D2D1_SIZE_U bitmapSize = currentCanvas->GetPixelSize();
+    hr = bitmapTarget->CreateBitmap(bitmapSize, bitmapCreateProperties, &bitmap);
+    if (!SUCCEEDED(hr))
+        return nullptr;
+
+    auto targetPos = D2D1::Point2U();
+    D2D1_RECT_U dataRect = D2D1::RectU(0, 0, bitmapSize.width, bitmapSize.height);
+    hr = bitmap->CopyFromBitmap(&targetPos, currentCanvas.get(), &dataRect);
+    if (!SUCCEEDED(hr))
+        return false;
+
+    return bitmap;
+}
+
 } // namespace Direct2D
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.h (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.h	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/Direct2DUtilities.h	2019-09-29 19:33:37 UTC (rev 250492)
@@ -72,8 +72,9 @@
 
 IntSize bitmapSize(IWICBitmapSource*);
 FloatSize bitmapSize(ID2D1Bitmap*);
-FloatPoint bitmapResolution(IWICBitmapSource*);
-FloatPoint bitmapResolution(ID2D1Bitmap*);
+FloatSize bitmapResolution(IWICBitmapSource*);
+FloatSize bitmapResolution(ID2D1Bitmap*);
+FloatSize bitmapResolution(ID2D1RenderTarget*);
 unsigned bitsPerPixel(GUID);
 COMPtr<ID2D1Bitmap> createBitmap(ID2D1RenderTarget*, const IntSize&);
 COMPtr<IWICBitmap> createWicBitmap(const IntSize&);
@@ -84,6 +85,7 @@
 COMPtr<ID2D1DCRenderTarget> createGDIRenderTarget();
 COMPtr<IDXGISurface1> createDXGISurfaceOfSize(const IntSize&, ID3D11Device1*, bool crossProcess);
 COMPtr<ID2D1RenderTarget> createSurfaceRenderTarget(IDXGISurface1*);
+COMPtr<ID2D1Bitmap> createBitmapCopyFromContext(ID2D1BitmapRenderTarget*);
 
 void copyRectFromOneSurfaceToAnother(ID2D1Bitmap* from, ID2D1Bitmap* to, const IntSize& sourceOffset, const IntRect&, const IntSize& destOffset = IntSize());
 

Modified: trunk/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -30,6 +30,7 @@
 #include "GraphicsContext.h"
 #include "PlatformContextDirect2D.h"
 #include <d2d1.h>
+#include <wtf/MathExtras.h>
 #include <wtf/RetainPtr.h>
 
 #define GRADIENT_DRAWING 3
@@ -70,6 +71,35 @@
         gradientStops.append(D2D1::GradientStop(stop.offset, D2D1::ColorF(r, g, b, a)));
     }
 
+    WTF::switchOn(m_data,
+        [&] (const LinearData&) {
+            // No action needed.
+        },
+        [&] (const RadialData& data) {
+            if (data.startRadius && data.endRadius) {
+                float startPosition = std::abs(data.startRadius / data.endRadius);
+                if (startPosition > 1.0)
+                    startPosition = 1.0 / startPosition;
+                float availableRange = 1.0 - startPosition;
+                RELEASE_ASSERT(availableRange <= 1.0 && availableRange >= 0);
+
+                for (auto& stop : gradientStops) {
+                    stop.position *= availableRange;
+                    stop.position += startPosition;
+                }
+
+                // Restore the 'start' position
+                auto firstStop = gradientStops.first();
+                firstStop.position = 0;
+
+                gradientStops.insert(0, firstStop);
+            }
+        },
+        [&] (const ConicData&) {
+            // FIXME: Assess whether needed for Conic Gradients.
+        }
+    );
+
     COMPtr<ID2D1GradientStopCollection> gradientStopCollection;
     HRESULT hr = renderTarget->CreateGradientStopCollection(gradientStops.data(), gradientStops.size(), &gradientStopCollection);
     RELEASE_ASSERT(SUCCEEDED(hr));
@@ -90,12 +120,14 @@
             m_gradient = linearGradient;
         },
         [&] (const RadialData& data) {
-            FloatSize offset = data.point1 - data.point0;
+            FloatSize offset = data.point0 - data.point1;
+            FloatPoint center = data.point1;
+            float radiusX = data.endRadius;
+            float radiusY = radiusX / data.aspectRatio;
+
             ID2D1RadialGradientBrush* radialGradient = nullptr;
-            float radiusX = data.endRadius + offset.width();
-            float radiusY = radiusX / data.aspectRatio;
             hr = renderTarget->CreateRadialGradientBrush(
-                D2D1::RadialGradientBrushProperties(data.point0, D2D1::Point2F(offset.width(), offset.height()), radiusX, radiusY),
+                D2D1::RadialGradientBrushProperties(center, D2D1::Point2F(offset.width(), offset.height()), radiusX, radiusY),
                 D2D1::BrushProperties(), gradientStopCollection.get(),
                 &radialGradient);
             RELEASE_ASSERT(SUCCEEDED(hr));

Modified: trunk/Source/WebCore/platform/graphics/win/ImageBufferDataDirect2D.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/ImageBufferDataDirect2D.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/ImageBufferDataDirect2D.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -304,7 +304,7 @@
 
     context->endDraw();
 
-    COMPtr<ID2D1DeviceContext> d2dDeviceContext(Query, platformContext->renderTarget());
+    COMPtr<ID2D1DeviceContext> d2dDeviceContext = platformContext->deviceContext();
     ASSERT(!!d2dDeviceContext);
 
     auto bytesPerRowInData = pixelSize.width() * 4;
@@ -497,6 +497,7 @@
     }
 
     auto size = bitmap->GetPixelSize();
+    ASSERT(size.height && size.width);
 
     Checked<unsigned, RecordOverflow> numBytes = size.width * size.height * 4;
     if (numBytes.hasOverflowed())
@@ -506,16 +507,13 @@
     // We cannot access the data backing an IWICBitmap while an active draw session is open.
     context->endDraw();
 
-    COMPtr<ID2D1DeviceContext> sourceDeviceContext;
-    HRESULT hr = platformContext->renderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext), reinterpret_cast<void**>(&sourceDeviceContext));
-    ASSERT(SUCCEEDED(hr));
-
+    COMPtr<ID2D1DeviceContext> sourceDeviceContext = platformContext->deviceContext();
     if (!sourceDeviceContext)
         return nullptr;
 
     COMPtr<ID2D1Bitmap1> sourceCPUBitmap;
     D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, Direct2D::pixelFormat());
-    hr = sourceDeviceContext->CreateBitmap(bitmap->GetPixelSize(), nullptr, bytesPerRow.unsafeGet(), bitmapProperties, &sourceCPUBitmap);
+    HRESULT hr = sourceDeviceContext->CreateBitmap(bitmap->GetPixelSize(), nullptr, bytesPerRow.unsafeGet(), bitmapProperties, &sourceCPUBitmap);
     if (!SUCCEEDED(hr))
         return nullptr;
 

Modified: trunk/Source/WebCore/platform/graphics/win/PathDirect2D.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/PathDirect2D.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/PathDirect2D.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -327,6 +327,22 @@
     return bounds;
 }
 
+FloatRect Path::fastBoundingRectForStroke(const PlatformContextDirect2D& platformContext) const
+{
+    if (isNull())
+        return FloatRect();
+
+    float tolerance = 10;
+
+    auto* strokeStyle = const_cast<PlatformContextDirect2D&>(platformContext).strokeStyle();
+
+    D2D1_RECT_F bounds = { };
+    if (!SUCCEEDED(m_path->GetWidenedBounds(platformContext.strokeThickness(), strokeStyle, nullptr, tolerance, &bounds)))
+        return FloatRect();
+
+    return bounds;
+}
+
 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
 {
     if (isNull())

Modified: trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.cpp	2019-09-29 19:33:37 UTC (rev 250492)
@@ -29,6 +29,7 @@
 #if USE(DIRECT2D)
 
 #include "Direct2DOperations.h"
+#include "Direct2DUtilities.h"
 #include <d2d1.h>
 
 namespace WebCore {
@@ -51,8 +52,19 @@
 {
     m_stateStack.append(State());
     m_state = &m_stateStack.last();
+
+    m_deviceContext.query(m_renderTarget.get());
+    RELEASE_ASSERT(!!m_deviceContext);
 }
 
+void PlatformContextDirect2D::setRenderTarget(ID2D1RenderTarget* renderTarget)
+{
+    m_renderTarget = renderTarget;
+
+    m_deviceContext.query(m_renderTarget.get());
+    RELEASE_ASSERT(!!m_deviceContext);
+}
+
 ID2D1Layer* PlatformContextDirect2D::clipLayer() const
 {
     return m_state->m_activeLayer.get();
@@ -74,10 +86,7 @@
 {
     ASSERT(m_renderTarget);
 
-    // There should always be at least one manually saved state if a restore is being called.
-    ASSERT(m_stateStack.size() > 1);
-
-    // Can't restore if we never called save.
+    // No need to restore if we don't have a saved element on the stack.
     if (m_stateStack.size() == 1)
         return;
 
@@ -278,7 +287,7 @@
 {
     ASSERT(m_renderTarget);
 
-    while (m_stateStack.size() > 1)
+    if (m_stateStack.size() > 1)
         restore();
 
     clearClips(m_state->m_clips);
@@ -292,6 +301,8 @@
         WTFLogAlways("Failed in PlatformContextDirect2D::endDraw: hr=%xd, first=%ld, second=%ld", hr, first, second);
 
     --beginDrawCount;
+
+    compositeIfNeeded();
 }
 
 void PlatformContextDirect2D::setTags(D2D1_TAG tag1, D2D1_TAG tag2)
@@ -319,6 +330,68 @@
     m_state->m_clips.append(clipType);
 }
 
+void PlatformContextDirect2D::compositeIfNeeded()
+{
+    if (!m_compositeSource)
+        return;
+
+    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget(Query, m_renderTarget.get());
+    if (!bitmapTarget)
+        return;
+
+    COMPtr<ID2D1Bitmap> currentCanvas = Direct2D::createBitmapCopyFromContext(bitmapTarget.get());
+    if (!currentCanvas)
+        return;
+
+    COMPtr<ID2D1Effect> effect;
+
+    if (m_compositeMode != D2D1_COMPOSITE_MODE_FORCE_DWORD) {
+        m_deviceContext->CreateEffect(CLSID_D2D1Composite, &effect);
+        effect->SetInput(0, m_compositeSource.get());
+        effect->SetInput(1, currentCanvas.get());
+        effect->SetValue(D2D1_COMPOSITE_PROP_MODE, m_compositeMode);
+    } else if (m_blendMode != D2D1_BLEND_MODE_FORCE_DWORD) {
+        m_deviceContext->CreateEffect(CLSID_D2D1Blend, &effect);
+        effect->SetInput(0, currentCanvas.get());
+        effect->SetInput(1, m_compositeSource.get());
+        effect->SetValue(D2D1_BLEND_PROP_MODE, m_blendMode);
+    }
+
+    if (effect) {
+        m_deviceContext->BeginDraw();
+        m_deviceContext->Clear(D2D1::ColorF(0, 0, 0, 0));
+        m_deviceContext->DrawImage(effect.get());
+        m_deviceContext->EndDraw();
+    }
+
+    m_compositeSource = nullptr;
+}
+
+void PlatformContextDirect2D::setBlendAndCompositeMode(D2D1_BLEND_MODE blend, D2D1_COMPOSITE_MODE mode)
+{
+    if (mode == m_compositeMode && blend == m_blendMode)
+        return;
+
+    // Direct2D handles compositing based on bitmaps. If we are changing the
+    // compositing mode, we need to capture the current state of the rendering
+    // in a bitmap, perform drawing operations until we finish, then handle
+    // the composting of the two layers.
+    COMPtr<ID2D1BitmapRenderTarget> bitmapTarget(Query, m_renderTarget.get());
+    if (!bitmapTarget)
+        return;
+
+    endDraw();
+
+    m_compositeMode = mode;
+    m_blendMode = blend;
+
+    m_compositeSource = Direct2D::createBitmapCopyFromContext(bitmapTarget.get());
+
+    beginDraw();
+
+    m_deviceContext->Clear(D2D1::ColorF(0, 0, 0, 0));
+}
+
 } // namespace WebCore
 
 #endif // USE(DIRECT2D)

Modified: trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h (250491 => 250492)


--- trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h	2019-09-29 17:25:43 UTC (rev 250491)
+++ trunk/Source/WebCore/platform/graphics/win/PlatformContextDirect2D.h	2019-09-29 19:33:37 UTC (rev 250492)
@@ -53,8 +53,10 @@
     ~PlatformContextDirect2D();
 
     ID2D1RenderTarget* renderTarget() { return m_renderTarget.get(); }
-    void setRenderTarget(ID2D1RenderTarget* renderTarget) { m_renderTarget = renderTarget; }
+    void setRenderTarget(ID2D1RenderTarget*);
 
+    ID2D1DeviceContext* deviceContext() { return m_deviceContext.get(); }
+
     ID3D11Device1* d3dDevice() const { return m_d3dDevice.get(); }
     void setD3DDevice(ID3D11Device1* device) { m_d3dDevice = device; }
 
@@ -103,8 +105,7 @@
     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; }
+    void setBlendAndCompositeMode(D2D1_BLEND_MODE, D2D1_COMPOSITE_MODE);
 
     void setTags(D2D1_TAG tag1, D2D1_TAG tag2);
     void notifyPreDrawObserver();
@@ -114,10 +115,12 @@
 
 private:
     void clearClips(Vector<Direct2DLayerType>&);
+    void compositeIfNeeded();
 
     GraphicsContextPlatformPrivate* m_graphicsContextPrivate { nullptr };
 
     COMPtr<ID2D1RenderTarget> m_renderTarget;
+    COMPtr<ID2D1DeviceContext> m_deviceContext;
     COMPtr<ID2D1StrokeStyle> m_d2dStrokeStyle;
 
     HashMap<uint32_t, COMPtr<ID2D1SolidColorBrush>> m_solidColoredBrushCache;
@@ -130,6 +133,8 @@
 
     COMPtr<ID3D11Device1> m_d3dDevice;
 
+    COMPtr<ID2D1Bitmap> m_compositeSource;
+
     WTF::Function<void()> m_preDrawHandler;
     WTF::Function<void()> m_postDrawHandler;
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to