2011-02-09 Simon Fraser <simon.fraser@apple.com>
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Feb 2011 17:13:48 +0000 (17:13 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Feb 2011 17:13:48 +0000 (17:13 +0000)
        Reviewed by Dirk Schulze.

        Enhance ShadowBlur to tile inset box shadows
        https://bugs.webkit.org/show_bug.cgi?id=51567

        Add a tiling code path to ShadowBlur for rendering inset
        box-shadows.

        Test: fast/box-shadow/inset-box-shadows.html

        * platform/graphics/ShadowBlur.cpp:
        (WebCore::computeSliceSizesFromRadii): Compute the slice sizes
        for the eight-piece shadow template.
        (WebCore::ShadowBlur::templateSize): Compute the size of the template,
        given the slice sizes.
        (WebCore::ShadowBlur::drawRectShadow): Bail early if layerRect is empty
        (which probably means we're clipped out). Call templateSize() and use
        the result to decide whether to tile.
        (WebCore::ShadowBlur::drawInsetShadow): New method for inset shadows.
        (WebCore::ShadowBlur::drawRectShadowWithoutTiling): Code moved.
        (WebCore::ShadowBlur::drawInsetShadowWithoutTiling): The non-tiling code
        path for inset shadows.
        (WebCore::ShadowBlur::drawInsetShadowWithTiling): Fill the shadow template
        buffer, paint the non-blurred area of the destination, and then call drawLayerPieces()
        to paint the eight-piece template image.
        (WebCore::ShadowBlur::drawRectShadowWithTiling): Refactored code, now
        shares the blurring code via blurAndColorShadowBuffer(), and the tiled template
        drawing via drawLayerPieces().
        (WebCore::ShadowBlur::drawLayerPieces): Draw an eight-piece image from the
        shadow template to the destination.
        (WebCore::ShadowBlur::blurAndColorShadowBuffer): Blur the pixels in the image
        buffer, and colorize them using the CompositeSourceIn operation.
        * platform/graphics/ShadowBlur.h:

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/box-shadow/inset-box-shadows.html [new file with mode: 0644]
LayoutTests/platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.checksum
LayoutTests/platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.png
LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.checksum [new file with mode: 0644]
LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/fast/box-shadow/inset-expected.checksum
LayoutTests/platform/mac/fast/box-shadow/inset-expected.png
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ShadowBlur.cpp
Source/WebCore/platform/graphics/ShadowBlur.h

index 6d83321..f235d91 100644 (file)
@@ -1,3 +1,20 @@
+2011-02-09  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Dirk Schulze.
+
+        Enhance ShadowBlur to tile inset box shadows
+        https://bugs.webkit.org/show_bug.cgi?id=51567
+        
+        Test with various combinations of inset shadow parameters to test
+        tiling.
+
+        * fast/box-shadow/inset-box-shadows.html: Added.
+        * platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.checksum:
+        * platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.png:
+        * platform/mac/fast/box-shadow/inset-box-shadows-expected.checksum: Added.
+        * platform/mac/fast/box-shadow/inset-box-shadows-expected.png: Added.
+        * platform/mac/fast/box-shadow/inset-box-shadows-expected.txt: Added.
+
 2011-02-09  Jochen Eisinger  <jochen@chromium.org>
 
         Reviewed by Adam Barth.
diff --git a/LayoutTests/fast/box-shadow/inset-box-shadows.html b/LayoutTests/fast/box-shadow/inset-box-shadows.html
new file mode 100644 (file)
index 0000000..ed85849
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      width: 200px;
+      height: 160px;
+      margin: 10px;
+      display: inline-block;
+      border: 1px solid black;
+    }
+    
+    .rounded {
+      border-radius: 20px;
+      border-top-left-radius: 50px;
+      border-bottom-right-radius: 80px;
+    }
+  </style>
+</head>
+<body>
+  
+  <div class="box" style="box-shadow: 0 0 20px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="box" style="box-shadow: 0 100px 2px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="box" style="box-shadow: 20px 20px 20px rgba(0, 0, 0, 0.6) inset"></div>
+  <br>
+  <div class="box" style="box-shadow: 0 0 2px 10px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="box" style="box-shadow: 0 20px 20px 40px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="box" style="box-shadow: 100px 100px 2px -50px rgba(0, 0, 0, 0.6) inset"></div>
+  <br>
+  <div class="box" style="box-shadow: 100px 100px 20px 10px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="rounded box" style="box-shadow: 10px 10px 3px 15px rgba(0, 0, 0, 0.6) inset"></div>
+  <div class="rounded box" style="box-shadow: 0 0 3px 5px rgba(0, 0, 0, 0.6) inset"></div>
+
+
+</body>
+</html>
index 9f5b471..1132408 100644 (file)
Binary files a/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.png and b/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadow-radius-expected.png differ
diff --git a/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.checksum b/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.checksum
new file mode 100644 (file)
index 0000000..b41e6f9
--- /dev/null
@@ -0,0 +1 @@
+b7b332823241a72633801a598471e471
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.png b/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.png
new file mode 100644 (file)
index 0000000..c0b9256
Binary files /dev/null and b/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.png differ
diff --git a/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.txt b/LayoutTests/platform/mac/fast/box-shadow/inset-box-shadows-expected.txt
new file mode 100644 (file)
index 0000000..7c55584
--- /dev/null
@@ -0,0 +1,42 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x574
+  RenderBlock {HTML} at (0,0) size 800x574
+    RenderBody {BODY} at (8,8) size 784x558
+      RenderText {#text} at (222,168) size 4x18
+        text run at (222,168) width 4: " "
+      RenderText {#text} at (448,168) size 4x18
+        text run at (448,168) width 4: " "
+      RenderText {#text} at (674,168) size 4x18
+        text run at (674,168) width 4: " "
+      RenderBR {BR} at (0,0) size 0x0
+      RenderText {#text} at (222,354) size 4x18
+        text run at (222,354) width 4: " "
+      RenderText {#text} at (448,354) size 4x18
+        text run at (448,354) width 4: " "
+      RenderText {#text} at (674,354) size 4x18
+        text run at (674,354) width 4: " "
+      RenderBR {BR} at (0,0) size 0x0
+      RenderText {#text} at (222,540) size 4x18
+        text run at (222,540) width 4: " "
+      RenderText {#text} at (448,540) size 4x18
+        text run at (448,540) width 4: " "
+      RenderText {#text} at (0,0) size 0x0
+layer at (18,18) size 202x162
+  RenderBlock (relative positioned) {DIV} at (10,10) size 202x162 [border: (1px solid #000000)]
+layer at (244,18) size 202x162
+  RenderBlock (relative positioned) {DIV} at (236,10) size 202x162 [border: (1px solid #000000)]
+layer at (470,18) size 202x162
+  RenderBlock (relative positioned) {DIV} at (462,10) size 202x162 [border: (1px solid #000000)]
+layer at (18,204) size 202x162
+  RenderBlock (relative positioned) {DIV} at (10,196) size 202x162 [border: (1px solid #000000)]
+layer at (244,204) size 202x162
+  RenderBlock (relative positioned) {DIV} at (236,196) size 202x162 [border: (1px solid #000000)]
+layer at (470,204) size 202x162
+  RenderBlock (relative positioned) {DIV} at (462,196) size 202x162 [border: (1px solid #000000)]
+layer at (18,390) size 202x162
+  RenderBlock (relative positioned) {DIV} at (10,382) size 202x162 [border: (1px solid #000000)]
+layer at (244,390) size 202x162
+  RenderBlock (relative positioned) {DIV} at (236,382) size 202x162 [border: (1px solid #000000)]
+layer at (470,390) size 202x162
+  RenderBlock (relative positioned) {DIV} at (462,382) size 202x162 [border: (1px solid #000000)]
index 84ab03c..1ee190a 100644 (file)
@@ -1 +1 @@
-231c006b1fa43afb1ecd9e4c86adccdb
\ No newline at end of file
+3635a2bcbd44aad76f5bc8d14d2a79fb
\ No newline at end of file
index 548c970..b2970aa 100644 (file)
Binary files a/LayoutTests/platform/mac/fast/box-shadow/inset-expected.png and b/LayoutTests/platform/mac/fast/box-shadow/inset-expected.png differ
index 48c53a5..51d68b5 100644 (file)
@@ -1,3 +1,39 @@
+2011-02-09  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Dirk Schulze.
+
+        Enhance ShadowBlur to tile inset box shadows
+        https://bugs.webkit.org/show_bug.cgi?id=51567
+        
+        Add a tiling code path to ShadowBlur for rendering inset
+        box-shadows.
+
+        Test: fast/box-shadow/inset-box-shadows.html
+        
+        * platform/graphics/ShadowBlur.cpp:
+        (WebCore::computeSliceSizesFromRadii): Compute the slice sizes
+        for the eight-piece shadow template.
+        (WebCore::ShadowBlur::templateSize): Compute the size of the template,
+        given the slice sizes.
+        (WebCore::ShadowBlur::drawRectShadow): Bail early if layerRect is empty
+        (which probably means we're clipped out). Call templateSize() and use
+        the result to decide whether to tile.
+        (WebCore::ShadowBlur::drawInsetShadow): New method for inset shadows.
+        (WebCore::ShadowBlur::drawRectShadowWithoutTiling): Code moved.
+        (WebCore::ShadowBlur::drawInsetShadowWithoutTiling): The non-tiling code
+        path for inset shadows.
+        (WebCore::ShadowBlur::drawInsetShadowWithTiling): Fill the shadow template
+        buffer, paint the non-blurred area of the destination, and then call drawLayerPieces()
+        to paint the eight-piece template image.
+        (WebCore::ShadowBlur::drawRectShadowWithTiling): Refactored code, now
+        shares the blurring code via blurAndColorShadowBuffer(), and the tiled template
+        drawing via drawLayerPieces().
+        (WebCore::ShadowBlur::drawLayerPieces): Draw an eight-piece image from the
+        shadow template to the destination.
+        (WebCore::ShadowBlur::blurAndColorShadowBuffer): Blur the pixels in the image
+        buffer, and colorize them using the CompositeSourceIn operation.
+        * platform/graphics/ShadowBlur.h:
+
 2011-02-09  Jochen Eisinger  <jochen@chromium.org>
 
         Reviewed by Adam Barth.
index 60a699b..73ee219 100644 (file)
@@ -36,6 +36,7 @@
 #include "Timer.h"
 #include <wtf/MathExtras.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/UnusedParam.h>
 
 using namespace std;
 
@@ -114,6 +115,8 @@ ScratchBuffer& ScratchBuffer::shared()
     return scratchBuffer;
 }
 
+static const int templateSideLength = 1;
+
 ShadowBlur::ShadowBlur(float radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
     : m_color(color)
     , m_colorSpace(colorSpace)
@@ -404,68 +407,103 @@ void ShadowBlur::endShadowLayer(GraphicsContext* graphicsContext)
     ScratchBuffer::shared().scheduleScratchBufferPurge();
 }
 
+static void computeSliceSizesFromRadii(int twiceRadius, const RoundedIntRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
+{
+    leftSlice = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width()); 
+    rightSlice = twiceRadius + max(radii.topRight().width(), radii.bottomRight().width()); 
+
+    topSlice = twiceRadius + max(radii.topLeft().height(), radii.topRight().height());
+    bottomSlice = twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height());
+}
+
+IntSize ShadowBlur::templateSize(const RoundedIntRect::Radii& radii) const
+{
+    const int templateSideLength = 1;
+
+    int leftSlice;
+    int rightSlice;
+    int topSlice;
+    int bottomSlice;
+    computeSliceSizesFromRadii(2 * ceilf(m_blurRadius), radii, leftSlice, rightSlice, topSlice, bottomSlice);
+    
+    return IntSize(templateSideLength + leftSlice + rightSlice,
+                   templateSideLength + topSlice + bottomSlice);
+}
+
 void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii)
 {
     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
+    if (layerRect.isEmpty())
+        return;
 
-    // drawShadowedRect does not work with rotations.
+    // drawRectShadowWithTiling does not work with rotations.
     // https://bugs.webkit.org/show_bug.cgi?id=45042
     if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
         return;
     }
 
-    const float roundedRadius = ceilf(m_blurRadius);
-    const float twiceRadius = roundedRadius * 2;
-    
-    const int templateSideLength = 1;
-    
-    // Find the extra space needed from the curve of the corners.
-    int extraWidthFromCornerRadii = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width()) + twiceRadius + max(radii.topRight().width(), radii.bottomRight().width());
-    int extraHeightFromCornerRadii = twiceRadius + max(radii.topLeft().height(), radii.topRight().height()) + twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height());
-
-    // The length of a side of the buffer is the enough space for four blur radii,
-    // the radii of the corners, and then 1 pixel to draw the side tiles.
-    IntSize shadowTemplateSize = IntSize(templateSideLength + extraWidthFromCornerRadii, templateSideLength + extraHeightFromCornerRadii);
+    IntSize templateSize = this->templateSize(radii);
 
-    if (shadowTemplateSize.width() > shadowedRect.width() || shadowTemplateSize.height() > shadowedRect.height()
-        || (shadowTemplateSize.width() * shadowTemplateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
+    if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
+        || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
         return;
     }
 
-    drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, shadowTemplateSize);
+    drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize);
 }
 
 void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii)
 {
-    // FIXME: add a tiling code path here.
     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
+    if (layerRect.isEmpty())
+        return;
+
+    // drawInsetShadowWithTiling does not work with rotations.
+    // https://bugs.webkit.org/show_bug.cgi?id=45042
+    if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
+        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
+        return;
+    }
 
+    IntSize templateSize = this->templateSize(holeRadii);
+
+    if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
+        || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
+        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
+        return;
+    }
+
+    drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize);
+}
+
+void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect)
+{
     GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect);
     if (!shadowContext)
         return;
 
     Path path;
-    path.addRect(rect);
-    path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
+    path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
 
-    shadowContext->setFillRule(RULE_EVENODD);
     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
     shadowContext->fillPath(path);
     
     endShadowLayer(graphicsContext);
 }
 
-void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect)
+void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii, const IntRect& layerRect)
 {
     GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect);
     if (!shadowContext)
         return;
 
     Path path;
-    path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+    path.addRect(rect);
+    path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
 
+    shadowContext->setFillRule(RULE_EVENODD);
     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
     shadowContext->fillPath(path);
 
@@ -473,7 +511,7 @@ void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, c
 }
 
 /*
-  This function uses tiling to improve the performance of the shadow
+  These functions use tiling to improve the performance of the shadow
   drawing of rounded rectangles. The code basically does the following
   steps:
 
@@ -497,15 +535,14 @@ void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, c
 
      The corners are directly copied from the template rectangle to the
      real one and the side tiles are 1 pixel width, we use them as
-
      tiles to cover the destination side. The corner tiles are bigger
      than just the side of the rounded corner, we need to increase it
      because the modifications caused by the corner over the blur
-     effect. We fill the central part with solid color to complete the
-     shadow.
+     effect. We fill the central or outer part with solid color to complete
+     the shadow.
  */
 
-void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& shadowTemplateSize)
+void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
 {
     graphicsContext->save();
     graphicsContext->clearShadow();
@@ -513,116 +550,178 @@ void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, cons
     const float roundedRadius = ceilf(m_blurRadius);
     const float twiceRadius = roundedRadius * 2;
 
-    // Size of the tiling side.
-    const int templateSideLength = 1;
+    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
 
-    m_layerImage = ScratchBuffer::shared().getScratchBuffer(shadowTemplateSize);
+    // Draw the rectangle with hole.
+    FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
+    FloatRect templateHole = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
+    Path path;
+    path.addRect(templateBounds);
+    path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
 
     // Draw shadow into a new ImageBuffer.
     GraphicsContext* shadowContext = m_layerImage->context();
     shadowContext->save();
+    shadowContext->clearRect(templateBounds);
+    shadowContext->setFillRule(RULE_EVENODD);
+    shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
+    shadowContext->fillPath(path);
+    blurAndColorShadowBuffer(templateSize);
+    shadowContext->restore();
+
+    FloatRect boundingRect = rect;
+    boundingRect.move(m_offset);
+
+    FloatRect destHoleRect = holeRect;
+    destHoleRect.move(m_offset);
+    FloatRect destHoleBounds = destHoleRect;
+    destHoleBounds.inflate(roundedRadius);
+
+    // Fill the external part of the shadow (which may be visible because of offset).
+    Path exteriorPath;
+    exteriorPath.addRect(boundingRect);
+    exteriorPath.addRect(destHoleBounds);
+
+    graphicsContext->save();
+    graphicsContext->setFillRule(RULE_EVENODD);
+    graphicsContext->setFillColor(m_color, m_colorSpace);
+    graphicsContext->fillPath(exteriorPath);
+    graphicsContext->restore();
     
-    shadowContext->clearRect(FloatRect(0, 0, shadowTemplateSize.width(), shadowTemplateSize.height()));
+    drawLayerPieces(graphicsContext, destHoleBounds, radii, roundedRadius, templateSize, InnerShadow);
+
+    graphicsContext->restore();
+
+    m_layerImage = 0;
+    // Schedule a purge of the scratch buffer.
+    ScratchBuffer::shared().scheduleScratchBufferPurge();
+}
+
+void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
+{
+    graphicsContext->save();
+    graphicsContext->clearShadow();
+
+    const float roundedRadius = ceilf(m_blurRadius);
+    const float twiceRadius = roundedRadius * 2;
+
+    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
 
     // Draw the rectangle.
-    FloatRect templateRect = FloatRect(roundedRadius, roundedRadius, shadowTemplateSize.width() - twiceRadius, shadowTemplateSize.height() - twiceRadius);
+    FloatRect templateShadow = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
     Path path;
-    path.addRoundedRect(templateRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+    path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
 
-    shadowContext->setFillColor(Color(.0f, .0f, .0f, 1.f), ColorSpaceDeviceRGB);
+    // Draw shadow into the ImageBuffer.
+    GraphicsContext* shadowContext = m_layerImage->context();
+    shadowContext->save();
+    shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
+    shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
     shadowContext->fillPath(path);
-
-    // Blur the image.
-    {
-        IntRect blurRect(IntPoint(), shadowTemplateSize);
-        RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
-        blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
-        m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
-    }
-
-    // Mask the image with the shadow color.
-    shadowContext->setCompositeOperation(CompositeSourceIn);
-    shadowContext->setFillColor(m_color, m_colorSpace);
-    shadowContext->fillRect(FloatRect(0, 0, shadowTemplateSize.width(), shadowTemplateSize.height()));
-    
+    blurAndColorShadowBuffer(templateSize);
     shadowContext->restore();
 
     FloatRect shadowBounds = shadowedRect;
     shadowBounds.move(m_offset.width(), m_offset.height());
     shadowBounds.inflate(roundedRadius);
 
-    int leftOffset = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width()); 
-    int rightOffset = twiceRadius + max(radii.topRight().width(), radii.bottomRight().width()); 
+    drawLayerPieces(graphicsContext, shadowBounds, radii, roundedRadius, templateSize, OuterShadow);
 
-    int topOffset = twiceRadius + max(radii.topLeft().height(), radii.topRight().height());
-    int bottomOffset = twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height());
+    graphicsContext->restore();
 
-    int centerWidth = shadowBounds.width() - leftOffset - rightOffset;
-    int centerHeight = shadowBounds.height() - topOffset - bottomOffset;
+    m_layerImage = 0;
+    // Schedule a purge of the scratch buffer.
+    ScratchBuffer::shared().scheduleScratchBufferPurge();
+}
 
-    // Fill the internal part of the shadow.
-    FloatRect shadowInterior(shadowBounds.x() + leftOffset, shadowBounds.y() + topOffset, centerWidth, centerHeight);
-    if (!shadowInterior.isEmpty()) {
-        graphicsContext->save();
-        
-        graphicsContext->setFillColor(m_color, m_colorSpace);
-        graphicsContext->fillRect(shadowInterior);
-        
-        graphicsContext->restore();
+void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedIntRect::Radii& radii, float roundedRadius, const IntSize& templateSize, ShadowDirection direction)
+{
+    const float twiceRadius = roundedRadius * 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;
+
+    if (direction == OuterShadow) {
+        FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
+        if (!shadowInterior.isEmpty()) {
+            graphicsContext->save();
+            
+            graphicsContext->setFillColor(m_color, m_colorSpace);
+            graphicsContext->fillRect(shadowInterior);
+            
+            graphicsContext->restore();
+        }
     }
 
     // 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.
     
     // Top side.
-    FloatRect tileRect = FloatRect(leftOffset, 0, templateSideLength, topOffset);
-    FloatRect destRect = FloatRect(shadowBounds.x() + leftOffset, shadowBounds.y(), centerWidth, topOffset);
+    FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
+    FloatRect destRect = FloatRect(shadowBounds.x() + leftSlice, shadowBounds.y(), centerWidth, topSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Draw the bottom side.
-    tileRect.setY(shadowTemplateSize.height() - bottomOffset);
-    tileRect.setHeight(bottomOffset);
-    destRect.setY(shadowBounds.maxY() - bottomOffset);
-    destRect.setHeight(bottomOffset);
+    tileRect.setY(templateSize.height() - bottomSlice);
+    tileRect.setHeight(bottomSlice);
+    destRect.setY(shadowBounds.maxY() - bottomSlice);
+    destRect.setHeight(bottomSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Left side.
-    tileRect = FloatRect(0, topOffset, leftOffset, templateSideLength);
-    destRect = FloatRect(shadowBounds.x(), shadowBounds.y() + topOffset, leftOffset, centerHeight);
+    tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
+    destRect = FloatRect(shadowBounds.x(), shadowBounds.y() + topSlice, leftSlice, centerHeight);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Right side.
-    tileRect.setX(shadowTemplateSize.width() - rightOffset);
-    tileRect.setWidth(rightOffset);
-    destRect.setX(shadowBounds.maxX() - rightOffset);
-    destRect.setWidth(rightOffset);
+    tileRect.setX(templateSize.width() - rightSlice);
+    tileRect.setWidth(rightSlice);
+    destRect.setX(shadowBounds.maxX() - rightSlice);
+    destRect.setWidth(rightSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Top left corner.
-    tileRect = FloatRect(0, 0, leftOffset, topOffset);
-    destRect = FloatRect(shadowBounds.x(), shadowBounds.y(), leftOffset, topOffset);
+    tileRect = FloatRect(0, 0, leftSlice, topSlice);
+    destRect = FloatRect(shadowBounds.x(), shadowBounds.y(), leftSlice, topSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Top right corner.
-    tileRect = FloatRect(shadowTemplateSize.width() - rightOffset, 0, rightOffset, topOffset);
-    destRect = FloatRect(shadowBounds.maxX() - rightOffset, shadowBounds.y(), rightOffset, topOffset);
+    tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
+    destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.y(), rightSlice, topSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Bottom right corner.
-    tileRect = FloatRect(shadowTemplateSize.width() - rightOffset, shadowTemplateSize.height() - bottomOffset, rightOffset, bottomOffset);
-    destRect = FloatRect(shadowBounds.maxX() - rightOffset, shadowBounds.maxY() - bottomOffset, rightOffset, bottomOffset);
+    tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
+    destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.maxY() - bottomSlice, rightSlice, bottomSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
 
     // Bottom left corner.
-    tileRect = FloatRect(0, shadowTemplateSize.height() - bottomOffset, leftOffset, bottomOffset);
-    destRect = FloatRect(shadowBounds.x(), shadowBounds.maxY() - bottomOffset, leftOffset, bottomOffset);
+    tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
+    destRect = FloatRect(shadowBounds.x(), shadowBounds.maxY() - bottomSlice, leftSlice, bottomSlice);
     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+}
 
-    graphicsContext->restore();
 
-    m_layerImage = 0;
-    // Schedule a purge of the scratch buffer.
-    ScratchBuffer::shared().scheduleScratchBufferPurge();
+void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
+{
+    {
+        IntRect blurRect(IntPoint(), templateSize);
+        RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
+        blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
+        m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
+    }
+
+    // Mask the image with the shadow color.
+    GraphicsContext* shadowContext = m_layerImage->context();
+    shadowContext->setCompositeOperation(CompositeSourceIn);
+    shadowContext->setFillColor(m_color, m_colorSpace);
+    shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
 }
 
 } // namespace WebCore
index 4338b31..e52d6dc 100644 (file)
@@ -58,10 +58,24 @@ private:
 
     void adjustBlurRadius(GraphicsContext*);
     void blurLayerImage(unsigned char*, const IntSize&, int stride);
+    
+    enum ShadowDirection {
+        OuterShadow,
+        InnerShadow
+    };
+    
     IntRect calculateLayerBoundingRect(GraphicsContext*, const FloatRect& layerArea, const IntRect& clipRect);
+    IntSize templateSize(const RoundedIntRect::Radii&) const;
 
     void drawRectShadowWithoutTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, const IntRect& layerRect);
     void drawRectShadowWithTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, const IntSize& shadowTemplateSize);
+
+    void drawInsetShadowWithoutTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedIntRect::Radii&, const IntRect& layerRect);
+    void drawInsetShadowWithTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedIntRect::Radii&, const IntSize& shadowTemplateSize);
+    
+    void drawLayerPieces(GraphicsContext*, const FloatRect& shadowBounds, const RoundedIntRect::Radii&, float roundedRadius, const IntSize& templateSize, ShadowDirection);
+    
+    void blurAndColorShadowBuffer(const IntSize& templateSize);
     
     enum ShadowType {
         NoShadow,