summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/ShadowBlur.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/ShadowBlur.cpp')
-rw-r--r--Source/WebCore/platform/graphics/ShadowBlur.cpp265
1 files changed, 174 insertions, 91 deletions
diff --git a/Source/WebCore/platform/graphics/ShadowBlur.cpp b/Source/WebCore/platform/graphics/ShadowBlur.cpp
index 0df51a4..2f25221 100644
--- a/Source/WebCore/platform/graphics/ShadowBlur.cpp
+++ b/Source/WebCore/platform/graphics/ShadowBlur.cpp
@@ -54,6 +54,8 @@ class ScratchBuffer {
public:
ScratchBuffer()
: m_purgeTimer(this, &ScratchBuffer::timerFired)
+ , m_lastRadius(0)
+ , m_lastWasInset(false)
#if !ASSERT_DISABLED
, m_bufferInUse(false)
#endif
@@ -77,6 +79,41 @@ public:
return m_imageBuffer.get();
}
+ void setLastShadowValues(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii)
+ {
+ m_lastWasInset = false;
+ m_lastRadius = radius;
+ m_lastColor = color;
+ m_lastColorSpace = colorSpace;
+ m_lastShadowRect = shadowRect;
+ m_lastRadii = radii;
+ }
+
+ void setLastInsetShadowValues(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii)
+ {
+ m_lastWasInset = true;
+ m_lastInsetBounds = bounds;
+ m_lastRadius = radius;
+ m_lastColor = color;
+ m_lastColorSpace = colorSpace;
+ m_lastShadowRect = shadowRect;
+ m_lastRadii = radii;
+ }
+
+ bool matchesLastShadow(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii) const
+ {
+ if (m_lastWasInset)
+ return false;
+ return m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && shadowRect == m_lastShadowRect && radii == m_lastRadii;
+ }
+
+ bool matchesLastInsetShadow(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii) const
+ {
+ if (!m_lastWasInset)
+ return false;
+ return m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii;
+ }
+
void scheduleScratchBufferPurge()
{
#if !ASSERT_DISABLED
@@ -100,10 +137,20 @@ private:
void clearScratchBuffer()
{
m_imageBuffer = 0;
+ m_lastRadius = 0;
}
OwnPtr<ImageBuffer> m_imageBuffer;
Timer<ScratchBuffer> m_purgeTimer;
+
+ FloatRect m_lastInsetBounds;
+ FloatRect m_lastShadowRect;
+ RoundedIntRect::Radii m_lastRadii;
+ Color m_lastColor;
+ ColorSpace m_lastColorSpace;
+ float m_lastRadius;
+ bool m_lastWasInset;
+
#if !ASSERT_DISABLED
bool m_bufferInUse;
#endif
@@ -147,14 +194,7 @@ static const int blurSumShift = 15;
void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
{
- const int channels[4] =
-#if CPU(BIG_ENDIAN)
- { 0, 3, 2, 0 };
-#elif CPU(MIDDLE_ENDIAN)
- { 1, 2, 3, 1 };
-#else
- { 3, 0, 1, 3 };
-#endif
+ const int channels[4] = { 3, 0, 1, 3 };
int diameter;
if (m_shadowsIgnoreTransforms)
@@ -356,40 +396,10 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F
return enclosingIntRect(layerRect);
}
-GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext* graphicsContext, const IntRect& layerRect)
-{
- adjustBlurRadius(graphicsContext);
-
- // Don't paint if we are totally outside the clip region.
- if (layerRect.isEmpty())
- return 0;
-
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
- GraphicsContext* layerContext = m_layerImage->context();
-
- layerContext->save(); // Balanced by restore() in endShadowLayer().
-
- // Always clear the surface first. FIXME: we could avoid the clear on first allocation.
- // Add a pixel to avoid later edge aliasing when rotated.
- layerContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
- layerContext->translate(m_layerContextTranslation);
-
- return layerContext;
-}
-
-void ShadowBlur::endShadowLayer(GraphicsContext* graphicsContext)
+void ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext)
{
if (!m_layerImage)
return;
-
- m_layerImage->context()->restore();
-
- if (m_type == BlurShadow) {
- IntRect blurRect = enclosingIntRect(FloatRect(FloatPoint(), m_layerSize));
- RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
- blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
- m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
- }
graphicsContext->save();
@@ -406,11 +416,6 @@ void ShadowBlur::endShadowLayer(GraphicsContext* graphicsContext)
graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
graphicsContext->restore();
-
- m_layerImage = 0;
-
- // Schedule a purge of the scratch buffer. We do not need to destroy the surface.
- ScratchBuffer::shared().scheduleScratchBufferPurge();
}
static void computeSliceSizesFromRadii(int twiceRadius, const RoundedIntRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
@@ -442,6 +447,8 @@ void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRec
if (layerRect.isEmpty())
return;
+ adjustBlurRadius(graphicsContext);
+
// drawRectShadowWithTiling does not work with rotations.
// https://bugs.webkit.org/show_bug.cgi?id=45042
if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
@@ -466,6 +473,8 @@ void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRe
if (layerRect.isEmpty())
return;
+ adjustBlurRadius(graphicsContext);
+
// drawInsetShadowWithTiling does not work with rotations.
// https://bugs.webkit.org/show_bug.cgi?id=45042
if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
@@ -486,34 +495,81 @@ void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRe
void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect)
{
- GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect);
- if (!shadowContext)
+ m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
+ if (!m_layerImage)
return;
- Path path;
- path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+ FloatRect bufferRelativeShadowedRect = shadowedRect;
+ bufferRelativeShadowedRect.move(m_layerContextTranslation);
+ if (!ScratchBuffer::shared().matchesLastShadow(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii)) {
+ GraphicsContext* shadowContext = m_layerImage->context();
+ shadowContext->save();
+
+ // 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, ColorSpaceDeviceRGB);
+ if (radii.isZero())
+ shadowContext->fillRect(shadowedRect);
+ else {
+ Path path;
+ path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+ shadowContext->fillPath(path);
+ }
+
+ blurShadowBuffer(expandedIntSize(m_layerSize));
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
- shadowContext->fillPath(path);
+ shadowContext->restore();
+
+ ScratchBuffer::shared().setLastShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii);
+ }
- endShadowLayer(graphicsContext);
+ drawShadowBuffer(graphicsContext);
+ m_layerImage = 0;
+ ScratchBuffer::shared().scheduleScratchBufferPurge();
}
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)
+ m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
+ if (!m_layerImage)
return;
- Path path;
- path.addRect(rect);
- path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
+ FloatRect bufferRelativeRect = rect;
+ bufferRelativeRect.move(m_layerContextTranslation);
+
+ FloatRect bufferRelativeHoleRect = holeRect;
+ bufferRelativeHoleRect.move(m_layerContextTranslation);
- shadowContext->setFillRule(RULE_EVENODD);
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
- shadowContext->fillPath(path);
+ if (!ScratchBuffer::shared().matchesLastInsetShadow(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii)) {
+ GraphicsContext* shadowContext = m_layerImage->context();
+ shadowContext->save();
- endShadowLayer(graphicsContext);
+ // 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 (holeRadii.isZero())
+ path.addRect(holeRect);
+ else
+ path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
+
+ shadowContext->setFillRule(RULE_EVENODD);
+ shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
+ shadowContext->fillPath(path);
+
+ blurShadowBuffer(expandedIntSize(m_layerSize));
+
+ shadowContext->restore();
+
+ ScratchBuffer::shared().setLastInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii);
+ }
+
+ drawShadowBuffer(graphicsContext);
+ m_layerImage = 0;
+ ScratchBuffer::shared().scheduleScratchBufferPurge();
}
/*
@@ -557,23 +613,35 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con
const float twiceRadius = roundedRadius * 2;
m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
+ if (!m_layerImage)
+ return;
// 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();
+ if (!ScratchBuffer::shared().matchesLastInsetShadow(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii)) {
+ // 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);
+
+ Path path;
+ path.addRect(templateBounds);
+ if (radii.isZero())
+ path.addRect(templateHole);
+ else
+ path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+
+ shadowContext->fillPath(path);
+
+ blurAndColorShadowBuffer(templateSize);
+ shadowContext->restore();
+
+ ScratchBuffer::shared().setLastInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii);
+ }
FloatRect boundingRect = rect;
boundingRect.move(m_offset);
@@ -599,7 +667,6 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con
graphicsContext->restore();
m_layerImage = 0;
- // Schedule a purge of the scratch buffer.
ScratchBuffer::shared().scheduleScratchBufferPurge();
}
@@ -612,20 +679,31 @@ void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, cons
const float twiceRadius = roundedRadius * 2;
m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
+ if (!m_layerImage)
+ return;
- // Draw the rectangle.
FloatRect templateShadow = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
- Path path;
- path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
- // 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);
- blurAndColorShadowBuffer(templateSize);
- shadowContext->restore();
+ if (!ScratchBuffer::shared().matchesLastShadow(m_blurRadius, m_color, m_colorSpace, templateShadow, radii)) {
+ // 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);
+
+ if (radii.isZero())
+ shadowContext->fillRect(templateShadow);
+ else {
+ Path path;
+ path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+ shadowContext->fillPath(path);
+ }
+
+ blurAndColorShadowBuffer(templateSize);
+ shadowContext->restore();
+
+ ScratchBuffer::shared().setLastShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii);
+ }
FloatRect shadowBounds = shadowedRect;
shadowBounds.move(m_offset.width(), m_offset.height());
@@ -636,7 +714,6 @@ void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, cons
graphicsContext->restore();
m_layerImage = 0;
- // Schedule a purge of the scratch buffer.
ScratchBuffer::shared().scheduleScratchBufferPurge();
}
@@ -714,14 +791,20 @@ void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRe
}
+void ShadowBlur::blurShadowBuffer(const IntSize& templateSize)
+{
+ if (m_type != BlurShadow)
+ return;
+
+ 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());
+}
+
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());
- }
+ blurShadowBuffer(templateSize);
// Mask the image with the shadow color.
GraphicsContext* shadowContext = m_layerImage->context();