[Cairo] Improve ShadowBlur performance using tiling optimization
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 3 May 2019 18:31:06 +0000 (18:31 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 3 May 2019 18:31:06 +0000 (18:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197308
Patch by Tomoki Imai <Tomoki.Imai@sony.com> on 2019-05-03
Reviewed by Žan Doberšek.

Enable tiling tiling-based optimization for drawRectShadow() and drawInsetShadow().
Since r228776, cairo ports doesn't have tiling-based optimization.

For AppleWin, this patch refactors code and it shares almost same code as cairo port.
Only the difference is that AppleWin uses ScratchBuffer, but cairo ports doesn't.
This should avoid a performance regression for AppleWin.

No new tests, covered by existing tests.

* platform/graphics/ShadowBlur.cpp:
(WebCore::calculateLobes):
Fix stylecheck errors

(WebCore::ShadowBlur::blurLayerImage):
Fix stylecheck errors

(WebCore::ShadowBlur::calculateLayerBoundingRect):
We don't use position of m_sourceRect, so change the type to FloatSize.

(WebCore::ShadowBlur::drawShadowBuffer):
Use m_layerSize instead of m_shadowedResultSize to fillRect, as m_layerSize is always smaller than m_shadowedResultSize.
It's because in m_layerSize is equal to m_shadowedResultSize if it's not clipped.
Clipping doesn't increase size of m_layerSize, so m_layerSize is always smaller than or equal to m_shadowedResultSize.

(WebCore::ShadowBlur::templateSize const):
Fix stylecheck errors

(WebCore::ShadowBlur::drawRectShadow):
(WebCore::ShadowBlur::drawInsetShadow):
(WebCore::ShadowBlur::drawRectShadowWithoutTiling):
(WebCore::ShadowBlur::drawInsetShadowWithoutTiling):
(WebCore::ShadowBlur::drawRectShadowWithTiling):
(WebCore::ShadowBlur::drawInsetShadowWithTiling):
Incorporate tile-based drawing.
To accomplish it, this patch abstracts GraphicsContext::drawImageBuffer to ShadowBlur::DrawImageCallback,
GraphicsContext::fillRect to ShadowBlur::FillRectCallback, drawing rect with hole to  ShadowBlur::FillRectWithHoleCallback.

Variants which takes GraphicsContext as parameter now just calls another drawRectShadow.

(WebCore::ShadowBlur::drawLayerPieces):
Instead of graphicsContext.drawImageBuffer, call corresponding callback.

(WebCore::ShadowBlur::drawLayerPiecesAndFillCenter):
This function calls drawLayerPieces and fill center for outer shadow.
Drawing outer shadow requires another callback for graphicsContext.fillRect.

(WebCore::ShadowBlur::drawShadowLayer):
Use m_layerSize instead of m_shadowedResultSize to fillRect,
as m_layerSize is always smaller than m_shadowedResultSize.

* platform/graphics/ShadowBlur.h:
Rename m_sourceRect to m_shadowedResultSize, and change it to FloatSize from FloatRect.
Remove GraphicsContext usage as much as possible and replace them by corresponding callbacks.

* platform/graphics/cairo/CairoOperations.cpp:
(WebCore::Cairo::drawShadowImage):
This function corresponds to ShadowBlur::DrawImageCallback.

(WebCore::Cairo::fillShadowBuffer):
Erase sourceRect, as it's always bigger than layerSize.

(WebCore::Cairo::drawPathShadow):
(WebCore::Cairo::drawGlyphsShadow):
Erase unused parameter.

(WebCore::Cairo::fillRect):
(WebCore::Cairo::fillRoundedRect):
(WebCore::Cairo::fillRectWithRoundedHole):
For tile-based optimization, add extra arguments to drawRectShadow.

(WebCore::Cairo::drawSurface):
Erase unused parameter.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ShadowBlur.cpp
Source/WebCore/platform/graphics/ShadowBlur.h
Source/WebCore/platform/graphics/cairo/CairoOperations.cpp

index 9a74e09..12f33f4 100644 (file)
@@ -1,3 +1,82 @@
+2019-05-03  Tomoki Imai  <Tomoki.Imai@sony.com>
+
+        [Cairo] Improve ShadowBlur performance using tiling optimization
+        https://bugs.webkit.org/show_bug.cgi?id=197308
+        Reviewed by Žan Doberšek.
+
+        Enable tiling tiling-based optimization for drawRectShadow() and drawInsetShadow().
+        Since r228776, cairo ports doesn't have tiling-based optimization.
+
+        For AppleWin, this patch refactors code and it shares almost same code as cairo port.
+        Only the difference is that AppleWin uses ScratchBuffer, but cairo ports doesn't.
+        This should avoid a performance regression for AppleWin.
+
+        No new tests, covered by existing tests.
+
+        * platform/graphics/ShadowBlur.cpp:
+        (WebCore::calculateLobes):
+        Fix stylecheck errors
+
+        (WebCore::ShadowBlur::blurLayerImage):
+        Fix stylecheck errors
+
+        (WebCore::ShadowBlur::calculateLayerBoundingRect):
+        We don't use position of m_sourceRect, so change the type to FloatSize.
+
+        (WebCore::ShadowBlur::drawShadowBuffer):
+        Use m_layerSize instead of m_shadowedResultSize to fillRect, as m_layerSize is always smaller than m_shadowedResultSize.
+        It's because in m_layerSize is equal to m_shadowedResultSize if it's not clipped.
+        Clipping doesn't increase size of m_layerSize, so m_layerSize is always smaller than or equal to m_shadowedResultSize.
+
+        (WebCore::ShadowBlur::templateSize const):
+        Fix stylecheck errors
+
+        (WebCore::ShadowBlur::drawRectShadow):
+        (WebCore::ShadowBlur::drawInsetShadow):
+        (WebCore::ShadowBlur::drawRectShadowWithoutTiling):
+        (WebCore::ShadowBlur::drawInsetShadowWithoutTiling):
+        (WebCore::ShadowBlur::drawRectShadowWithTiling):
+        (WebCore::ShadowBlur::drawInsetShadowWithTiling):
+        Incorporate tile-based drawing.
+        To accomplish it, this patch abstracts GraphicsContext::drawImageBuffer to ShadowBlur::DrawImageCallback,
+        GraphicsContext::fillRect to ShadowBlur::FillRectCallback, drawing rect with hole to  ShadowBlur::FillRectWithHoleCallback.
+
+        Variants which takes GraphicsContext as parameter now just calls another drawRectShadow.
+
+        (WebCore::ShadowBlur::drawLayerPieces):
+        Instead of graphicsContext.drawImageBuffer, call corresponding callback.
+
+        (WebCore::ShadowBlur::drawLayerPiecesAndFillCenter):
+        This function calls drawLayerPieces and fill center for outer shadow.
+        Drawing outer shadow requires another callback for graphicsContext.fillRect.
+
+        (WebCore::ShadowBlur::drawShadowLayer):
+        Use m_layerSize instead of m_shadowedResultSize to fillRect,
+        as m_layerSize is always smaller than m_shadowedResultSize.
+
+        * platform/graphics/ShadowBlur.h:
+        Rename m_sourceRect to m_shadowedResultSize, and change it to FloatSize from FloatRect.
+        Remove GraphicsContext usage as much as possible and replace them by corresponding callbacks.
+
+        * platform/graphics/cairo/CairoOperations.cpp:
+        (WebCore::Cairo::drawShadowImage):
+        This function corresponds to ShadowBlur::DrawImageCallback.
+
+        (WebCore::Cairo::fillShadowBuffer):
+        Erase sourceRect, as it's always bigger than layerSize.
+
+        (WebCore::Cairo::drawPathShadow):
+        (WebCore::Cairo::drawGlyphsShadow):
+        Erase unused parameter.
+
+        (WebCore::Cairo::fillRect):
+        (WebCore::Cairo::fillRoundedRect):
+        (WebCore::Cairo::fillRectWithRoundedHole):
+        For tile-based optimization, add extra arguments to drawRectShadow.
+
+        (WebCore::Cairo::drawSurface):
+        Erase unused parameter.
+
 2019-05-03  Antti Koivisto  <antti@apple.com>
 
         Add a quirk to make youtube navigation bar scrollable without mouse hover on iOS
index fdc2b78..b01596d 100644 (file)
@@ -24,7 +24,7 @@
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
 namespace WebCore {
 
 enum {
-    leftLobe = 0,
-    rightLobe = 1
+    LeftLobe = 0,
+    RightLobe = 1
 };
 
+#if USE(CG)
 static inline int roundUpToMultipleOf32(int d)
 {
     return (1 + (d >> 5)) << 5;
@@ -65,7 +66,7 @@ public:
 #endif
     {
     }
-    
+
     ImageBuffer* getScratchBuffer(const IntSize& size)
     {
         ASSERT(!m_bufferInUse);
@@ -127,7 +128,7 @@ public:
         const Seconds scratchBufferPurgeInterval { 2_s };
         m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
     }
-    
+
     static ScratchBuffer& singleton();
 
 private:
@@ -140,7 +141,7 @@ private:
 
     std::unique_ptr<ImageBuffer> m_imageBuffer;
     Timer m_purgeTimer;
-    
+
     FloatRect m_lastInsetBounds;
     FloatRect m_lastShadowRect;
     FloatRoundedRect::Radii m_lastRadii;
@@ -148,7 +149,7 @@ private:
     FloatSize m_lastRadius;
     bool m_lastWasInset;
     FloatSize m_lastLayerSize;
-    
+
 #if !ASSERT_DISABLED
     bool m_bufferInUse;
 #endif
@@ -160,15 +161,14 @@ ScratchBuffer& ScratchBuffer::singleton()
     return scratchBuffer;
 }
 
-static const int templateSideLength = 1;
-
-#if USE(CG)
 static float radiusToLegacyRadius(float radius)
 {
     return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
 }
 #endif
 
+static const int templateSideLength = 1;
+
 ShadowBlur::ShadowBlur() = default;
 
 ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, bool shadowsIgnoreTransforms)
@@ -249,23 +249,23 @@ static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreT
     if (diameter & 1) {
         // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
         int lobeSize = (diameter - 1) / 2;
-        lobes[0][leftLobe] = lobeSize;
-        lobes[0][rightLobe] = lobeSize;
-        lobes[1][leftLobe] = lobeSize;
-        lobes[1][rightLobe] = lobeSize;
-        lobes[2][leftLobe] = lobeSize;
-        lobes[2][rightLobe] = lobeSize;
+        lobes[0][LeftLobe] = lobeSize;
+        lobes[0][RightLobe] = lobeSize;
+        lobes[1][LeftLobe] = lobeSize;
+        lobes[1][RightLobe] = lobeSize;
+        lobes[2][LeftLobe] = lobeSize;
+        lobes[2][RightLobe] = lobeSize;
     } else {
         // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
         // between the output pixel and the one to the left, the second one centered on the pixel
         // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
         int lobeSize = diameter / 2;
-        lobes[0][leftLobe] = lobeSize;
-        lobes[0][rightLobe] = lobeSize - 1;
-        lobes[1][leftLobe] = lobeSize - 1;
-        lobes[1][rightLobe] = lobeSize;
-        lobes[2][leftLobe] = lobeSize;
-        lobes[2][rightLobe] = lobeSize;
+        lobes[0][LeftLobe] = lobeSize;
+        lobes[0][RightLobe] = lobeSize - 1;
+        lobes[1][LeftLobe] = lobeSize - 1;
+        lobes[1][RightLobe] = lobeSize;
+        lobes[2][LeftLobe] = lobeSize;
+        lobes[2][RightLobe] = lobeSize;
     }
 }
 
@@ -293,7 +293,7 @@ void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, i
     // Two stages: horizontal and vertical
     for (int pass = 0; pass < 2; ++pass) {
         unsigned char* pixels = imageData;
-        
+
         if (!pass && !m_blurRadius.width())
             final = 0; // Do no work if horizonal blur is zero.
 
@@ -304,8 +304,8 @@ void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, i
             // This is much more efficient than computing the sum of each pixels
             // covered by the box kernel size for each x.
             for (int step = 0; step < 3; ++step) {
-                int side1 = lobes[step][leftLobe];
-                int side2 = lobes[step][rightLobe];
+                int side1 = lobes[step][LeftLobe];
+                int side2 = lobes[step][RightLobe];
                 int pixelCount = side1 + 1 + side2;
                 int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
                 int ofs = 1 + side2;
@@ -331,13 +331,13 @@ void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, i
                     *ptr = (sum * invCount) >> blurSumShift;
                     sum += ((ofs < dim) ? *next : alpha2) - alpha1;
                 }
-                
+
                 prev = pixels + channels[step];
                 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
                     *ptr = (sum * invCount) >> blurSumShift;
                     sum += (*next) - (*prev);
                 }
-                
+
                 for (; i < dim; ptr += stride, prev += stride, ++i) {
                     *ptr = (sum * invCount) >> blurSumShift;
                     sum += alpha2 - (*prev);
@@ -422,13 +422,13 @@ IntSize ShadowBlur::calculateLayerBoundingRect(const AffineTransform& transform,
             inflatedClip.inflateX(1);
             inflatedClip.inflateY(1);
         }
-        
+
         layerRect.intersect(inflatedClip);
     }
 
     IntSize frameSize = inflation;
     frameSize.scale(2);
-    m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize.width(), shadowedRect.height() + frameSize.height());
+    m_shadowedResultSize = FloatSize(shadowedRect.width() + frameSize.width(), shadowedRect.height() + frameSize.height());
     m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
     m_layerSize = layerRect.size();
 
@@ -461,7 +461,7 @@ void ShadowBlur::drawShadowBuffer(GraphicsContext& graphicsContext)
     graphicsContext.setFillColor(m_color);
 
     graphicsContext.clearShadow();
-    graphicsContext.fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
+    graphicsContext.fillRect(FloatRect(m_layerOrigin, m_layerSize));
 }
 
 static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const FloatRoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
@@ -481,87 +481,131 @@ IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const FloatRounde
     int rightSlice;
     int topSlice;
     int bottomSlice;
-    
+
     IntSize blurExpansion = radiusPadding;
     blurExpansion.scale(2);
 
     computeSliceSizesFromRadii(blurExpansion, radii, leftSlice, rightSlice, topSlice, bottomSlice);
-    
-    return IntSize(templateSideLength + leftSlice + rightSlice,
-                   templateSideLength + topSlice + bottomSlice);
+
+    return IntSize(templateSideLength + leftSlice + rightSlice, templateSideLength + topSlice + bottomSlice);
 }
 
 void ShadowBlur::drawRectShadow(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect)
 {
-    IntSize layerSize = calculateLayerBoundingRect(graphicsContext.getCTM(), shadowedRect.rect(), graphicsContext.clipBounds());
+    drawRectShadow(graphicsContext.getCTM(), graphicsContext.clipBounds(), shadowedRect,
+        [this, &graphicsContext](ImageBuffer&, const FloatPoint&, const FloatSize&) {
+            // FIXME: Use parameters instead of implicit parameters defined as class variables.
+            drawShadowBuffer(graphicsContext);
+        },
+        [&graphicsContext](ImageBuffer& image, const FloatRect& destRect, const FloatRect& srcRect) {
+            GraphicsContextStateSaver stateSaver(graphicsContext);
+            graphicsContext.clearShadow();
+            graphicsContext.drawImageBuffer(image, destRect, srcRect);
+        },
+        [&graphicsContext](const FloatRect& rect, const Color& color) {
+            GraphicsContextStateSaver stateSaver(graphicsContext);
+            graphicsContext.setFillColor(color);
+            graphicsContext.clearShadow();
+            graphicsContext.fillRect(rect);
+        });
+}
+
+void ShadowBlur::drawInsetShadow(GraphicsContext& graphicsContext, const FloatRect& fullRect, const FloatRoundedRect& holeRect)
+{
+    drawInsetShadow(graphicsContext.getCTM(), graphicsContext.clipBounds(), fullRect, holeRect,
+        [this, &graphicsContext](ImageBuffer&, const FloatPoint&, const FloatSize&) {
+            // FIXME: Use parameters instead of implicit parameters defined as class variables.
+            drawShadowBuffer(graphicsContext);
+        },
+        [&graphicsContext](ImageBuffer& image, const FloatRect& destRect, const FloatRect& srcRect) {
+            // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
+            // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
+            GraphicsContextStateSaver stateSaver(graphicsContext);
+            graphicsContext.clearShadow();
+            graphicsContext.drawImageBuffer(image, destRect, srcRect);
+        },
+        [&graphicsContext](const FloatRect& rect, const FloatRect& holeRect, const Color& color) {
+            Path exteriorPath;
+            exteriorPath.addRect(rect);
+            exteriorPath.addRect(holeRect);
+
+            GraphicsContextStateSaver fillStateSaver(graphicsContext);
+            graphicsContext.setFillRule(WindRule::EvenOdd);
+            graphicsContext.setFillColor(color);
+            graphicsContext.clearShadow();
+            graphicsContext.fillPath(exteriorPath);
+        });
+}
+
+void ShadowBlur::drawRectShadow(const AffineTransform& transform, const IntRect& clipBounds, const FloatRoundedRect& shadowedRect, const DrawBufferCallback& drawBuffer, const DrawImageCallback& drawImage, const FillRectCallback& fillRect)
+{
+    IntSize layerSize = calculateLayerBoundingRect(transform, shadowedRect.rect(), clipBounds);
     if (layerSize.isEmpty())
         return;
 
-    adjustBlurRadius(graphicsContext.getCTM());
+    adjustBlurRadius(transform);
+
+    bool canUseTilingTechnique = true;
 
     // drawRectShadowWithTiling does not work with rotations.
     // https://bugs.webkit.org/show_bug.cgi?id=45042
-    if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
-        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize);
-        return;
-    }
+    if (!transform.preservesAxisAlignment() || m_type != BlurShadow)
+        canUseTilingTechnique = false;
 
     IntSize edgeSize = blurredEdgeSize();
     IntSize templateSize = this->templateSize(edgeSize, shadowedRect.radii());
     const FloatRect& rect = shadowedRect.rect();
 
     if (templateSize.width() > rect.width() || templateSize.height() > rect.height()
-        || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
-        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize);
-        return;
-    }
+        || (templateSize.width() * templateSize.height() > m_shadowedResultSize.width() * m_shadowedResultSize.height()))
+        canUseTilingTechnique = false;
 
-    drawRectShadowWithTiling(graphicsContext, shadowedRect, templateSize, edgeSize);
+    if (canUseTilingTechnique)
+        drawRectShadowWithTiling(transform, shadowedRect, templateSize, edgeSize, drawImage, fillRect);
+    else
+        drawRectShadowWithoutTiling(transform, shadowedRect, layerSize, drawBuffer);
 }
 
-void ShadowBlur::drawInsetShadow(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect)
+void ShadowBlur::drawInsetShadow(const AffineTransform& transform, const IntRect& clipBounds, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const DrawBufferCallback& drawBuffer, const DrawImageCallback& drawImage, const FillRectWithHoleCallback& fillRectWithHole)
 {
-    IntSize layerSize = calculateLayerBoundingRect(graphicsContext.getCTM(), rect, graphicsContext.clipBounds());
+    IntSize layerSize = calculateLayerBoundingRect(transform, fullRect, clipBounds);
     if (layerSize.isEmpty())
         return;
 
-    adjustBlurRadius(graphicsContext.getCTM());
+    adjustBlurRadius(transform);
+
+    bool canUseTilingTechnique = true;
 
-    // drawInsetShadowWithTiling does not work with rotations.
+    // drawRectShadowWithTiling does not work with rotations.
     // https://bugs.webkit.org/show_bug.cgi?id=45042
-    if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
-        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize);
-        return;
-    }
+    if (!transform.preservesAxisAlignment() || m_type != BlurShadow)
+        canUseTilingTechnique = false;
 
     IntSize edgeSize = blurredEdgeSize();
     IntSize templateSize = this->templateSize(edgeSize, holeRect.radii());
     const FloatRect& hRect = holeRect.rect();
 
     if (templateSize.width() > hRect.width() || templateSize.height() > hRect.height()
-        || (templateSize.width() * templateSize.height() > hRect.width() * hRect.height())) {
-        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize);
-        return;
-    }
+        || (templateSize.width() * templateSize.height() > hRect.width() * hRect.height()))
+        canUseTilingTechnique = false;
 
-    drawInsetShadowWithTiling(graphicsContext, rect, holeRect, templateSize, edgeSize);
+    if (canUseTilingTechnique)
+        drawInsetShadowWithTiling(transform, fullRect, holeRect, templateSize, edgeSize, drawImage, fillRectWithHole);
+    else
+        drawInsetShadowWithoutTiling(transform, fullRect, holeRect, layerSize, drawBuffer);
 }
 
-void ShadowBlur::drawRectShadow(const AffineTransform& transform, const IntRect& clipBounds, const FloatRoundedRect& shadowedRect, const DrawBufferCallback& drawBuffer)
+void ShadowBlur::drawRectShadowWithoutTiling(const AffineTransform&, const FloatRoundedRect& shadowedRect, const IntSize& layerSize, const DrawBufferCallback& drawBuffer)
 {
-    // FIXME: Try incorporating tile-based rect shadow drawing for the same use case.
-
-    IntSize layerSize = calculateLayerBoundingRect(transform, shadowedRect.rect(), clipBounds);
-    if (layerSize.isEmpty())
-        return;
-
-    adjustBlurRadius(transform);
-
     auto layerImage = ImageBuffer::create(layerSize, Unaccelerated, 1);
     if (!layerImage)
         return;
     m_layerImage = layerImage.get();
 
+    GraphicsContext& shadowContext = layerImage->context();
+    GraphicsContextStateSaver stateSaver(shadowContext);
+    shadowContext.setFillColor(Color::black);
+
     {
         GraphicsContext& shadowContext = layerImage->context();
         GraphicsContextStateSaver stateSaver(shadowContext);
@@ -577,20 +621,11 @@ void ShadowBlur::drawRectShadow(const AffineTransform& transform, const IntRect&
 
         blurShadowBuffer(layerSize);
     }
-
-    drawBuffer(*layerImage, m_layerOrigin, m_layerSize, m_sourceRect);
+    drawBuffer(*layerImage, m_layerOrigin, m_layerSize);
 }
 
-void ShadowBlur::drawInsetShadow(const AffineTransform& transform, const IntRect& clipBounds, const FloatRect& rect, const FloatRoundedRect& holeRect, const DrawBufferCallback& drawBuffer)
+void ShadowBlur::drawInsetShadowWithoutTiling(const AffineTransform&, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const IntSize& layerSize, const DrawBufferCallback& drawBuffer)
 {
-    // FIXME: Try incorporating tile-based inset shadow drawing for the same use case.
-
-    IntSize layerSize = calculateLayerBoundingRect(transform, rect, clipBounds);
-    if (layerSize.isEmpty())
-        return;
-
-    adjustBlurRadius(transform);
-
     auto layerImage = ImageBuffer::create(layerSize, Unaccelerated, 1);
     if (!layerImage)
         return;
@@ -602,7 +637,7 @@ void ShadowBlur::drawInsetShadow(const AffineTransform& transform, const IntRect
         shadowContext.translate(m_layerContextTranslation);
 
         Path path;
-        path.addRect(rect);
+        path.addRect(fullRect);
         if (holeRect.radii().isZero())
             path.addRect(holeRect.rect());
         else
@@ -615,83 +650,7 @@ void ShadowBlur::drawInsetShadow(const AffineTransform& transform, const IntRect
         blurShadowBuffer(layerSize);
     }
 
-    drawBuffer(*layerImage, m_layerOrigin, m_layerSize, m_sourceRect);
-}
-
-void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& layerSize)
-{
-    m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize);
-    if (!m_layerImage)
-        return;
-
-    FloatRect bufferRelativeShadowedRect = shadowedRect.rect();
-    bufferRelativeShadowedRect.move(m_layerContextTranslation);
-
-    // Only redraw in the scratch buffer if its cached contents don't match our needs
-    bool redrawNeeded = ScratchBuffer::singleton().setCachedShadowValues(m_blurRadius, Color::black, bufferRelativeShadowedRect, shadowedRect.radii(), m_layerSize);
-    if (redrawNeeded) {
-        GraphicsContext& shadowContext = m_layerImage->context();
-        GraphicsContextStateSaver stateSaver(shadowContext);
-
-        // Add a pixel to avoid later edge aliasing when rotated.
-        shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
-        shadowContext.translate(m_layerContextTranslation);
-        shadowContext.setFillColor(Color::black);
-        if (shadowedRect.radii().isZero())
-            shadowContext.fillRect(shadowedRect.rect());
-        else {
-            Path path;
-            path.addRoundedRect(shadowedRect);
-            shadowContext.fillPath(path);
-        }
-
-        blurShadowBuffer(layerSize);
-    }
-    
-    drawShadowBuffer(graphicsContext);
-    m_layerImage = nullptr;
-    ScratchBuffer::singleton().scheduleScratchBufferPurge();
-}
-
-void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& layerSize)
-{
-    m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize);
-    if (!m_layerImage)
-        return;
-
-    FloatRect bufferRelativeRect = rect;
-    bufferRelativeRect.move(m_layerContextTranslation);
-
-    FloatRect bufferRelativeHoleRect = holeRect.rect();
-    bufferRelativeHoleRect.move(m_layerContextTranslation);
-
-    // Only redraw in the scratch buffer if its cached contents don't match our needs
-    bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, Color::black, bufferRelativeRect, bufferRelativeHoleRect, holeRect.radii());
-    if (redrawNeeded) {
-        GraphicsContext& shadowContext = m_layerImage->context();
-        GraphicsContextStateSaver stateSaver(shadowContext);
-
-        // Add a pixel to avoid later edge aliasing when rotated.
-        shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
-        shadowContext.translate(m_layerContextTranslation);
-
-        Path path;
-        path.addRect(rect);
-        if (holeRect.radii().isZero())
-            path.addRect(holeRect.rect());
-        else
-            path.addRoundedRect(holeRect);
-
-        shadowContext.setFillRule(WindRule::EvenOdd);
-        shadowContext.setFillColor(Color::black);
-        shadowContext.fillPath(path);
-
-        blurShadowBuffer(layerSize);
-    }
-    
-    drawShadowBuffer(graphicsContext);
-    m_layerImage = nullptr;
-    ScratchBuffer::singleton().scheduleScratchBufferPurge();
+    drawBuffer(*layerImage, m_layerOrigin, m_layerSize);
 }
 
 /*
@@ -726,9 +685,71 @@ void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext& graphicsContext,
      the shadow.
  */
 
-void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& templateSize, const IntSize& edgeSize)
+void ShadowBlur::drawRectShadowWithTiling(const AffineTransform& transform, const FloatRoundedRect& shadowedRect, const IntSize& templateSize, const IntSize& edgeSize, const DrawImageCallback& drawImage, const FillRectCallback& fillRect)
+{
+#if USE(CG)
+    m_layerImage = ScratchBuffer::singleton().getScratchBuffer(templateSize);
+#else
+    auto layerImage = ImageBuffer::create(templateSize, Unaccelerated, 1);
+    m_layerImage = layerImage.get();
+#endif
+
+    if (!m_layerImage)
+        return;
+
+    FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
+
+    bool redrawNeeded = true;
+#if USE(CG)
+    // Only redraw in the scratch buffer if its cached contents don't match our needs
+    redrawNeeded = ScratchBuffer::singleton().setCachedShadowValues(m_blurRadius, m_color, templateShadow, shadowedRect.radii(), m_layerSize);
+#endif
+
+    if (redrawNeeded) {
+        // Draw shadow into the ImageBuffer.
+        GraphicsContext& shadowContext = m_layerImage->context();
+        GraphicsContextStateSaver shadowStateSaver(shadowContext);
+
+        shadowContext.clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
+        shadowContext.setFillColor(Color::black);
+
+        if (shadowedRect.radii().isZero())
+            shadowContext.fillRect(templateShadow);
+        else {
+            Path path;
+            path.addRoundedRect(FloatRoundedRect(templateShadow, shadowedRect.radii()));
+            shadowContext.fillPath(path);
+        }
+        blurAndColorShadowBuffer(templateSize);
+    }
+
+    FloatSize offset = m_offset;
+    if (shadowsIgnoreTransforms())
+        offset.scale(1 / transform.xScale(), 1 / transform.yScale());
+
+    FloatRect shadowBounds = shadowedRect.rect();
+    shadowBounds.move(offset);
+    shadowBounds.inflateX(edgeSize.width());
+    shadowBounds.inflateY(edgeSize.height());
+
+    drawLayerPiecesAndFillCenter(shadowBounds, shadowedRect.radii(), edgeSize, templateSize, drawImage, fillRect);
+
+    m_layerImage = nullptr;
+
+#if USE(CG)
+    ScratchBuffer::singleton().scheduleScratchBufferPurge();
+#endif
+}
+
+void ShadowBlur::drawInsetShadowWithTiling(const AffineTransform& transform, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const IntSize& templateSize, const IntSize& edgeSize, const DrawImageCallback& drawImage, const FillRectWithHoleCallback& fillRectWithHole)
 {
+#if USE(CG)
     m_layerImage = ScratchBuffer::singleton().getScratchBuffer(templateSize);
+#else
+    auto layerImage = ImageBuffer::create(templateSize, Unaccelerated, 1);
+    m_layerImage = layerImage.get();
+#endif
+
     if (!m_layerImage)
         return;
 
@@ -736,8 +757,12 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, con
     FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
     FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
 
+    bool redrawNeeded = true;
+#if USE(CG)
     // Only redraw in the scratch buffer if its cached contents don't match our needs
-    bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, m_color, templateBounds, templateHole, holeRect.radii());
+    redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, m_color, templateBounds, templateHole, holeRect.radii());
+#endif
+
     if (redrawNeeded) {
         // Draw shadow into a new ImageBuffer.
         GraphicsContext& shadowContext = m_layerImage->context();
@@ -758,12 +783,10 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, con
         blurAndColorShadowBuffer(templateSize);
     }
     FloatSize offset = m_offset;
-    if (shadowsIgnoreTransforms()) {
-        AffineTransform transform = graphicsContext.getCTM();
+    if (shadowsIgnoreTransforms())
         offset.scale(1 / transform.xScale(), 1 / transform.yScale());
-    }
 
-    FloatRect boundingRect = rect;
+    FloatRect boundingRect = fullRect;
     boundingRect.move(offset);
 
     FloatRect destHoleRect = holeRect.rect();
@@ -773,71 +796,18 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, con
     destHoleBounds.inflateY(edgeSize.height());
 
     // Fill the external part of the shadow (which may be visible because of offset).
-    Path exteriorPath;
-    exteriorPath.addRect(boundingRect);
-    exteriorPath.addRect(destHoleBounds);
+    fillRectWithHole(boundingRect, destHoleBounds, m_color);
 
-    {
-        GraphicsContextStateSaver fillStateSaver(graphicsContext);
-        graphicsContext.setFillRule(WindRule::EvenOdd);
-        graphicsContext.setFillColor(m_color);
-        graphicsContext.clearShadow();
-        graphicsContext.fillPath(exteriorPath);
-    }
-    
-    drawLayerPieces(graphicsContext, destHoleBounds, holeRect.radii(), edgeSize, templateSize, InnerShadow);
+    drawLayerPieces(destHoleBounds, holeRect.radii(), edgeSize, templateSize, drawImage);
 
     m_layerImage = nullptr;
-    ScratchBuffer::singleton().scheduleScratchBufferPurge();
-}
-
-void ShadowBlur::drawRectShadowWithTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& templateSize, const IntSize& edgeSize)
-{
-    auto& scratchBuffer = ScratchBuffer::singleton();
-    m_layerImage = scratchBuffer.getScratchBuffer(templateSize);
-    if (!m_layerImage)
-        return;
 
-    FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
-
-    // Only redraw in the scratch buffer if its cached contents don't match our needs
-    bool redrawNeeded = scratchBuffer.setCachedShadowValues(m_blurRadius, m_color, templateShadow, shadowedRect.radii(), m_layerSize);
-    if (redrawNeeded) {
-        // Draw shadow into the ImageBuffer.
-        GraphicsContext& shadowContext = m_layerImage->context();
-        GraphicsContextStateSaver shadowStateSaver(shadowContext);
-
-        shadowContext.clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
-        shadowContext.setFillColor(Color::black);
-        
-        if (shadowedRect.radii().isZero())
-            shadowContext.fillRect(templateShadow);
-        else {
-            Path path;
-            path.addRoundedRect(FloatRoundedRect(templateShadow, shadowedRect.radii()));
-            shadowContext.fillPath(path);
-        }
-
-        blurAndColorShadowBuffer(templateSize);
-    }
-    FloatSize offset = m_offset;
-    if (shadowsIgnoreTransforms()) {
-        AffineTransform transform = graphicsContext.getCTM();
-        offset.scale(1 / transform.xScale(), 1 / transform.yScale());
-    }
-
-    FloatRect shadowBounds = shadowedRect.rect();
-    shadowBounds.move(offset);
-    shadowBounds.inflateX(edgeSize.width());
-    shadowBounds.inflateY(edgeSize.height());
-
-    drawLayerPieces(graphicsContext, shadowBounds, shadowedRect.radii(), edgeSize, templateSize, OuterShadow);
-
-    m_layerImage = nullptr;
+#if USE(CG)
     ScratchBuffer::singleton().scheduleScratchBufferPurge();
+#endif
 }
 
-void ShadowBlur::drawLayerPieces(GraphicsContext& graphicsContext, const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
+void ShadowBlur::drawLayerPieces(const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, const DrawImageCallback& drawImage)
 {
     const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
 
@@ -849,71 +819,73 @@ void ShadowBlur::drawLayerPieces(GraphicsContext& graphicsContext, const FloatRe
 
     int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
     int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
-
-    if (direction == OuterShadow) {
-        FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
-        if (!shadowInterior.isEmpty()) {
-            GraphicsContextStateSaver stateSaver(graphicsContext);
-            graphicsContext.setFillColor(m_color);
-            graphicsContext.clearShadow();
-            graphicsContext.fillRect(shadowInterior);
-        }
-    }
-
-    GraphicsContextStateSaver stateSaver(graphicsContext);
-    graphicsContext.setFillColor(m_color);
-    graphicsContext.clearShadow();
-
-    // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
-    // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
     FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
-    centerRect = graphicsContext.roundToDevicePixels(centerRect);
-    
+
     // Top side.
     FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
     FloatRect destRect = FloatRect(centerRect.x(), centerRect.y() - topSlice, centerRect.width(), topSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Draw the bottom side.
     tileRect.setY(templateSize.height() - bottomSlice);
     tileRect.setHeight(bottomSlice);
     destRect.setY(centerRect.maxY());
     destRect.setHeight(bottomSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Left side.
     tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y(), leftSlice, centerRect.height());
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Right side.
     tileRect.setX(templateSize.width() - rightSlice);
     tileRect.setWidth(rightSlice);
     destRect.setX(centerRect.maxX());
     destRect.setWidth(rightSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Top left corner.
     tileRect = FloatRect(0, 0, leftSlice, topSlice);
     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y() - topSlice, leftSlice, topSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Top right corner.
     tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
     destRect = FloatRect(centerRect.maxX(), centerRect.y() - topSlice, rightSlice, topSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Bottom right corner.
     tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
     destRect = FloatRect(centerRect.maxX(), centerRect.maxY(), rightSlice, bottomSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 
     // Bottom left corner.
     tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.maxY(), leftSlice, bottomSlice);
-    graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
+    drawImage(*m_layerImage, destRect, tileRect);
 }
 
+void ShadowBlur::drawLayerPiecesAndFillCenter(const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, const DrawImageCallback& drawImage, const FillRectCallback& fillRect)
+{
+    const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
+
+    int leftSlice;
+    int rightSlice;
+    int topSlice;
+    int bottomSlice;
+    computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
+
+    int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
+    int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
+    FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
+
+    // Fill center
+    if (!centerRect.isEmpty())
+        fillRect(centerRect, m_color);
+
+    drawLayerPieces(shadowBounds, radii, bufferPadding, templateSize, drawImage);
+}
 
 void ShadowBlur::blurShadowBuffer(const IntSize& templateSize)
 {
@@ -962,7 +934,7 @@ void ShadowBlur::drawShadowLayer(const AffineTransform& transform, const IntRect
     }
 
     blurAndColorShadowBuffer(expandedIntSize(m_layerSize));
-    drawBuffer(*layerImage, m_layerOrigin, m_layerSize, m_sourceRect);
+    drawBuffer(*layerImage, m_layerOrigin, m_layerSize);
 }
 
 } // namespace WebCore
index 9348f1f..d6af402 100644 (file)
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef ShadowBlur_h
-#define ShadowBlur_h
+#pragma once
 
 #include "Color.h"
 #include "FloatRect.h"
@@ -63,12 +62,17 @@ public:
     void drawRectShadow(GraphicsContext&, const FloatRoundedRect&);
     void drawInsetShadow(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect);
 
-    using DrawBufferCallback = WTF::Function<void(ImageBuffer&, const FloatPoint&, const FloatSize&, const FloatRect&)>;
-    void drawRectShadow(const AffineTransform&, const IntRect&, const FloatRoundedRect&, const DrawBufferCallback&);
-    void drawInsetShadow(const AffineTransform&, const IntRect&, const FloatRect&, const FloatRoundedRect&, const DrawBufferCallback&);
-
+    using DrawBufferCallback = WTF::Function<void(ImageBuffer&, const FloatPoint&, const FloatSize&)>;
+    using DrawImageCallback = WTF::Function<void(ImageBuffer&, const FloatRect&, const FloatRect&)>;
+    using FillRectCallback = WTF::Function<void(const FloatRect&, const Color&)>;
+    using FillRectWithHoleCallback = WTF::Function<void(const FloatRect&, const FloatRect&, const Color&)>;
     using DrawShadowCallback = WTF::Function<void(GraphicsContext&)>;
-    void drawShadowLayer(const AffineTransform&, const IntRect&, const FloatRect&, const DrawShadowCallback&, const DrawBufferCallback&);
+
+    // DrawBufferCallback is for drawing shadow without tiling.
+    // DrawImageCallback and FillRectCallback is for drawing shadow with tiling.
+    void drawRectShadow(const AffineTransform&, const IntRect& clipBounds, const FloatRoundedRect& shadowedRect, const DrawBufferCallback&, const DrawImageCallback&, const FillRectCallback&);
+    void drawInsetShadow(const AffineTransform&, const IntRect& clipBounds, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const DrawBufferCallback&, const DrawImageCallback&, const FillRectWithHoleCallback&);
+    void drawShadowLayer(const AffineTransform&, const IntRect& clipBounds, const FloatRect& layerArea, const DrawShadowCallback&, const DrawBufferCallback&);
 
     void blurLayerImage(unsigned char*, const IntSize&, int stride);
 
@@ -82,29 +86,29 @@ private:
     void drawShadowBuffer(GraphicsContext&);
 
     void adjustBlurRadius(const AffineTransform&);
-    
+
     enum ShadowDirection {
         OuterShadow,
         InnerShadow
     };
-    
+
     IntSize calculateLayerBoundingRect(const AffineTransform&, const FloatRect& layerArea, const IntRect& clipRect);
     IntSize templateSize(const IntSize& blurredEdgeSize, const FloatRoundedRect::Radii&) const;
 
-    void drawRectShadowWithoutTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& layerSize);
-    void drawRectShadowWithTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
-
-    void drawInsetShadowWithoutTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& layerSize);
-    void drawInsetShadowWithTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
-    
-    void drawLayerPieces(GraphicsContext&, const FloatRect& shadowBounds, const FloatRoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, ShadowDirection);
-    
     void blurShadowBuffer(const IntSize& templateSize);
     void blurAndColorShadowBuffer(const IntSize& templateSize);
-    
+
+    void drawInsetShadowWithoutTiling(const AffineTransform&, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const IntSize& layerSize, const DrawBufferCallback&);
+    void drawInsetShadowWithTiling(const AffineTransform&, const FloatRect& fullRect, const FloatRoundedRect& holeRect, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize, const DrawImageCallback&, const FillRectWithHoleCallback&);
+
+    void drawRectShadowWithoutTiling(const AffineTransform&, const FloatRoundedRect& shadowedRect, const IntSize& layerSize, const DrawBufferCallback&);
+    void drawRectShadowWithTiling(const AffineTransform&, const FloatRoundedRect& shadowedRect, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize, const DrawImageCallback&, const FillRectCallback&);
+
+    void drawLayerPiecesAndFillCenter(const FloatRect& shadowBounds, const FloatRoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, const DrawImageCallback&, const FillRectCallback&);
+    void drawLayerPieces(const FloatRect& shadowBounds, const FloatRoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, const DrawImageCallback&);
+
     IntSize blurredEdgeSize() const;
-    
-    
+
     ShadowType m_type { NoShadow };
 
     Color m_color;
@@ -113,7 +117,7 @@ private:
 
     ImageBuffer* m_layerImage { nullptr }; // Buffer to where the temporary shadow will be drawn to.
 
-    FloatRect m_sourceRect; // Sub-rect of m_layerImage that contains the shadow pixels.
+    FloatSize m_shadowedResultSize; // Size of the result of shadowing which is same as shadowedRect + blurred edges.
     FloatPoint m_layerOrigin; // Top-left corner of the (possibly clipped) bounding rect to draw the shadow to.
     FloatSize m_layerSize; // Size of m_layerImage pixels that need blurring.
     FloatSize m_layerContextTranslation; // Translation to apply to m_layerContext for the shadow to be correctly clipped.
@@ -122,5 +126,3 @@ private:
 };
 
 } // namespace WebCore
-
-#endif // ShadowBlur_h
index 3d9c48d..fba5456 100644 (file)
@@ -184,7 +184,20 @@ static void drawShadowLayerBuffer(PlatformContextCairo& platformContext, ImageBu
     }
 }
 
-static void fillShadowBuffer(PlatformContextCairo& platformContext, ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect& sourceRect, const ShadowState& shadowState)
+// FIXME: This is mostly same as drawShadowLayerBuffer, so we should merge two.
+static void drawShadowImage(PlatformContextCairo& platformContext, ImageBuffer& layerImage, const FloatRect& destRect, const FloatRect& srcRect, const ShadowState& shadowState)
+{
+    RefPtr<Image> image = layerImage.copyImage(DontCopyBackingStore);
+    if (!image)
+        return;
+
+    if (auto surface = image->nativeImageForCurrentFrame()) {
+        drawNativeImage(platformContext, surface.get(), destRect, srcRect, shadowState.globalCompositeOperator, BlendMode::Normal, ImageOrientation(),
+            InterpolationDefault, shadowState.globalAlpha, ShadowState());
+    }
+}
+
+static void fillShadowBuffer(PlatformContextCairo& platformContext, ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const ShadowState& shadowState)
 {
     save(platformContext);
 
@@ -197,7 +210,7 @@ static void fillShadowBuffer(PlatformContextCairo& platformContext, ImageBuffer&
     FillSource fillSource;
     fillSource.globalAlpha = shadowState.globalAlpha;
     fillSource.color = shadowState.color;
-    fillRect(platformContext, FloatRect(layerOrigin, sourceRect.size()), fillSource, ShadowState());
+    fillRect(platformContext, FloatRect(layerOrigin, expandedIntSize(layerSize)), fillSource, ShadowState());
 
     restore(platformContext);
 }
@@ -252,7 +265,7 @@ static inline void drawPathShadow(PlatformContextCairo& platformContext, const F
                 cairo_stroke(cairoShadowContext);
             }
         },
-        [&platformContext, &shadowState, &cairoContext, &path](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect&)
+        [&platformContext, &shadowState, &cairoContext, &path](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
         {
             // The original path may still be hanging around on the context and endShadowLayer
             // will take care of properly creating a path to draw the result shadow. We remove the path
@@ -266,6 +279,7 @@ static inline void drawPathShadow(PlatformContextCairo& platformContext, const F
         });
 }
 
+
 static inline void fillCurrentCairoPath(PlatformContextCairo& platformContext, const FillSource& fillSource)
 {
     cairo_t* cr = platformContext.cr();
@@ -350,7 +364,7 @@ static void drawGlyphsShadow(PlatformContextCairo& platformContext, const Shadow
         {
             drawGlyphsToContext(shadowContext.platformContext()->cr(), scaledFont, syntheticBoldOffset, glyphs);
         },
-        [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect&)
+        [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
         {
             drawShadowLayerBuffer(platformContext, layerImage, layerOrigin, layerSize, shadowState);
         });
@@ -690,9 +704,17 @@ void fillRect(PlatformContextCairo& platformContext, const FloatRect& rect, cons
     if (shadowState.isVisible()) {
         ShadowBlur shadow({ shadowState.blur, shadowState.blur }, shadowState.offset, shadowState.color, shadowState.ignoreTransforms);
         shadow.drawRectShadow(State::getCTM(platformContext), State::getClipBounds(platformContext), FloatRoundedRect(rect),
-            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect& sourceRect)
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
             {
-                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, sourceRect, shadowState);
+                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, shadowState);
+            },
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatRect& destRect, const FloatRect& srcRect)
+            {
+                drawShadowImage(platformContext, layerImage, destRect, srcRect, shadowState);
+            },
+            [&platformContext](const FloatRect& rect, const Color& color)
+            {
+                fillRectWithColor(platformContext.cr(), rect, color);
             });
     }
 
@@ -713,9 +735,17 @@ void fillRoundedRect(PlatformContextCairo& platformContext, const FloatRoundedRe
     if (shadowState.isVisible()) {
         ShadowBlur shadow({ shadowState.blur, shadowState.blur }, shadowState.offset, shadowState.color, shadowState.ignoreTransforms);
         shadow.drawRectShadow(State::getCTM(platformContext), State::getClipBounds(platformContext), rect,
-            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect& sourceRect)
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
             {
-                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, sourceRect, shadowState);
+                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, shadowState);
+            },
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatRect& destRect, const FloatRect& srcRect)
+            {
+                drawShadowImage(platformContext, layerImage, destRect, srcRect, shadowState);
+            },
+            [&platformContext](const FloatRect& rect, const Color& color)
+            {
+                fillRectWithColor(platformContext.cr(), rect, color);
             });
     }
 
@@ -738,9 +768,25 @@ void fillRectWithRoundedHole(PlatformContextCairo& platformContext, const FloatR
     if (shadowState.isVisible()) {
         ShadowBlur shadow({ shadowState.blur, shadowState.blur }, shadowState.offset, shadowState.color, shadowState.ignoreTransforms);
         shadow.drawInsetShadow(State::getCTM(platformContext), State::getClipBounds(platformContext), rect, roundedHoleRect,
-            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect& sourceRect)
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
+            {
+                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, shadowState);
+            },
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatRect& destRect, const FloatRect& srcRect)
+            {
+                drawShadowImage(platformContext, layerImage, destRect, srcRect, shadowState);
+            },
+            [&platformContext](const FloatRect& rect, const FloatRect& holeRect, const Color& color)
             {
-                fillShadowBuffer(platformContext, layerImage, layerOrigin, layerSize, sourceRect, shadowState);
+                // FIXME: We should use fillRectWithRoundedHole.
+                cairo_t* cr = platformContext.cr();
+                cairo_save(cr);
+                setSourceRGBAFromColor(cr, color);
+                cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+                cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+                cairo_rectangle(cr, holeRect.x(), holeRect.y(), holeRect.width(), holeRect.height());
+                cairo_fill(cr);
+                cairo_restore(cr);
             });
     }
 
@@ -932,7 +978,7 @@ void drawSurface(PlatformContextCairo& platformContext, cairo_surface_t* surface
             {
                 drawPatternToCairoContext(shadowContext.platformContext()->cr(), pattern.get(), destRect, 1);
             },
-            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize, const FloatRect&)
+            [&platformContext, &shadowState](ImageBuffer& layerImage, const FloatPoint& layerOrigin, const FloatSize& layerSize)
             {
                 drawShadowLayerBuffer(platformContext, layerImage, layerOrigin, layerSize, shadowState);
             });